| 29 | |
| 30 | template <class MatrixType> |
| 31 | class L1Solver { |
| 32 | public: |
| 33 | L1Solver(const L1SolverOptions& options, const MatrixType& mat) |
| 34 | : options_(options), a_(mat) { |
| 35 | // Pre-compute the sparsity pattern. |
| 36 | const MatrixType spd_mat = a_.transpose() * a_; |
| 37 | linear_solver_.compute(spd_mat); |
| 38 | } |
| 39 | |
| 40 | void Solve(const Eigen::VectorXd& rhs, Eigen::VectorXd* solution) { |
| 41 | Eigen::VectorXd& x = *solution; |
| 42 | Eigen::VectorXd z(a_.rows()), u(a_.rows()); |
| 43 | z.setZero(); |
| 44 | u.setZero(); |
| 45 | |
| 46 | Eigen::VectorXd a_times_x(a_.rows()), z_old(z.size()), ax_hat(a_.rows()); |
| 47 | // Precompute some convergence terms. |
| 48 | const double rhs_norm = rhs.norm(); |
| 49 | const double primal_abs_tolerance_eps = |
| 50 | std::sqrt(a_.rows()) * options_.absolute_tolerance; |
| 51 | const double dual_abs_tolerance_eps = |
| 52 | std::sqrt(a_.cols()) * options_.absolute_tolerance; |
| 53 | |
| 54 | const std::string row_format = |
| 55 | " % 4d % 4.4e % 4.4e % 4.4e % 4.4e"; |
| 56 | for (int i = 0; i < options_.max_num_iterations; i++) { |
| 57 | // Update x. |
| 58 | x.noalias() = linear_solver_.solve(a_.transpose() * (rhs + z - u)); |
| 59 | if (linear_solver_.info() != Eigen::Success) { |
| 60 | LOG(ERROR) << "L1 Minimization failed. Could not solve the sparse " |
| 61 | "linear system with Cholesky Decomposition"; |
| 62 | return; |
| 63 | } |
| 64 | |
| 65 | a_times_x.noalias() = a_ * x; |
| 66 | ax_hat.noalias() = options_.alpha * a_times_x; |
| 67 | ax_hat.noalias() += (1.0 - options_.alpha) * (z + rhs); |
| 68 | |
| 69 | // Update z and set z_old. |
| 70 | std::swap(z, z_old); |
| 71 | z.noalias() = Shrinkage(ax_hat - rhs + u, 1.0 / options_.rho); |
| 72 | |
| 73 | // Update u. |
| 74 | u.noalias() += ax_hat - z - rhs; |
| 75 | |
| 76 | // Compute the convergence terms. |
| 77 | const double r_norm = (a_times_x - z - rhs).norm(); |
| 78 | const double s_norm = |
| 79 | (-options_.rho * a_.transpose() * (z - z_old)).norm(); |
| 80 | const double max_norm = std::max({a_times_x.norm(), z.norm(), rhs_norm}); |
| 81 | const double primal_eps = |
| 82 | primal_abs_tolerance_eps + options_.relative_tolerance * max_norm; |
| 83 | const double dual_eps = dual_abs_tolerance_eps + |
| 84 | options_.relative_tolerance * |
| 85 | (options_.rho * a_.transpose() * u).norm(); |
| 86 | |
| 87 | // Determine if the minimizer has converged. |
| 88 | if (r_norm < primal_eps && s_norm < dual_eps) { |
nothing calls this directly
no outgoing calls
no test coverage detected