GCC Code Coverage Report


Directory: ./
File: src/core/solvers/box-qp.cpp
Date: 2025-06-03 08:14:12
Exec Total Coverage
Lines: 0 166 0.0%
Functions: 0 16 0.0%
Branches: 0 400 0.0%

Line Branch Exec Source
1 ///////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (C) 2019-2022, University of Edinburgh, Heriot-Watt University
5 // Copyright note valid unless otherwise stated in individual files.
6 // All rights reserved.
7 ///////////////////////////////////////////////////////////////////////////////
8
9 #include "crocoddyl/core/solvers/box-qp.hpp"
10
11 namespace crocoddyl {
12
13 ✗ BoxQP::BoxQP(const std::size_t nx, const std::size_t maxiter,
14 ✗ const double th_acceptstep, const double th_grad, const double reg)
15 ✗ : nx_(nx),
16 ✗ maxiter_(maxiter),
17 ✗ th_acceptstep_(th_acceptstep),
18 ✗ th_grad_(th_grad),
19 ✗ reg_(reg),
20 ✗ fold_(0.),
21 ✗ fnew_(0.),
22 ✗ x_(nx),
23 ✗ xnew_(nx),
24 ✗ g_(nx),
25 ✗ dx_(nx),
26 ✗ xo_(nx),
27 ✗ dxo_(nx),
28 ✗ qo_(nx),
29 ✗ Ho_(nx, nx) {
30 // Check if values have a proper range
31 ✗ if (0. >= th_acceptstep && th_acceptstep >= 0.5) {
32 ✗ std::cerr << "Warning: th_acceptstep value should between 0 and 0.5"
33 ✗ << std::endl;
34 }
35 ✗ if (0. > th_grad) {
36 ✗ std::cerr << "Warning: th_grad value has to be positive." << std::endl;
37 }
38 ✗ if (0. > reg) {
39 ✗ std::cerr << "Warning: reg value has to be positive." << std::endl;
40 }
41
42 // Initialized the values of vectors
43 ✗ x_.setZero();
44 ✗ xnew_.setZero();
45 ✗ g_.setZero();
46 ✗ dx_.setZero();
47 ✗ xo_.setZero();
48 ✗ dxo_.setZero();
49 ✗ qo_.setZero();
50 ✗ Ho_.setZero();
51
52 // Reserve the space and compute alphas
53 ✗ solution_.x = Eigen::VectorXd::Zero(nx);
54 ✗ solution_.clamped_idx.reserve(nx_);
55 ✗ solution_.free_idx.reserve(nx_);
56 ✗ const std::size_t n_alphas_ = 10;
57 ✗ alphas_.resize(n_alphas_);
58 ✗ for (std::size_t n = 0; n < n_alphas_; ++n) {
59 ✗ alphas_[n] = 1. / pow(2., static_cast<double>(n));
60 }
61 ✗ }
62
63 ✗ BoxQP::~BoxQP() {}
64
65 ✗ const BoxQPSolution& BoxQP::solve(const Eigen::MatrixXd& H,
66 const Eigen::VectorXd& q,
67 const Eigen::VectorXd& lb,
68 const Eigen::VectorXd& ub,
69 const Eigen::VectorXd& xinit) {
70 ✗ if (static_cast<std::size_t>(H.rows()) != nx_ ||
71 ✗ static_cast<std::size_t>(H.cols()) != nx_) {
72 ✗ throw_pretty("Invalid argument: "
73 << "H has wrong dimension (it should be " +
74 std::to_string(nx_) + "," + std::to_string(nx_) + ")");
75 }
76 ✗ if (static_cast<std::size_t>(q.size()) != nx_) {
77 ✗ throw_pretty(
78 "Invalid argument: " << "q has wrong dimension (it should be " +
79 std::to_string(nx_) + ")");
80 }
81 ✗ if (static_cast<std::size_t>(lb.size()) != nx_) {
82 ✗ throw_pretty(
83 "Invalid argument: " << "lb has wrong dimension (it should be " +
84 std::to_string(nx_) + ")");
85 }
86 ✗ if (static_cast<std::size_t>(ub.size()) != nx_) {
87 ✗ throw_pretty(
88 "Invalid argument: " << "ub has wrong dimension (it should be " +
89 std::to_string(nx_) + ")");
90 }
91 ✗ if (static_cast<std::size_t>(xinit.size()) != nx_) {
92 ✗ throw_pretty(
93 "Invalid argument: " << "xinit has wrong dimension (it should be " +
94 std::to_string(nx_) + ")");
95 }
96
97 // We need to enforce feasible warm-starting of the algorithm
98 ✗ for (std::size_t i = 0; i < nx_; ++i) {
99 ✗ x_(i) = std::max(std::min(xinit(i), ub(i)), lb(i));
100 }
101
102 // Start the numerical iterations
103 ✗ for (std::size_t k = 0; k < maxiter_; ++k) {
104 ✗ solution_.clamped_idx.clear();
105 ✗ solution_.free_idx.clear();
106 // Compute the Cauchy point and active set
107 ✗ g_ = q;
108 ✗ g_.noalias() += H * x_;
109 ✗ for (std::size_t j = 0; j < nx_; ++j) {
110 ✗ const double gj = g_(j);
111 ✗ const double xj = x_(j);
112 ✗ const double lbj = lb(j);
113 ✗ const double ubj = ub(j);
114 ✗ if ((xj == lbj && gj > 0.) || (xj == ubj && gj < 0.)) {
115 ✗ solution_.clamped_idx.push_back(j);
116 } else {
117 ✗ solution_.free_idx.push_back(j);
118 }
119 }
120
121 // Compute the search direction as Newton step along the free space
122 ✗ nf_ = solution_.free_idx.size();
123 ✗ nc_ = solution_.clamped_idx.size();
124 ✗ Eigen::VectorBlock<Eigen::VectorXd> xf = xo_.head(nf_);
125 ✗ Eigen::VectorBlock<Eigen::VectorXd> xc = xo_.tail(nc_);
126 ✗ Eigen::VectorBlock<Eigen::VectorXd> dxf = dxo_.head(nf_);
127 ✗ Eigen::VectorBlock<Eigen::VectorXd> qf = qo_.head(nf_);
128 ✗ Eigen::Block<Eigen::MatrixXd> Hff = Ho_.topLeftCorner(nf_, nf_);
129 ✗ Eigen::Block<Eigen::MatrixXd> Hfc = Ho_.topRightCorner(nf_, nc_);
130 ✗ for (std::size_t i = 0; i < nf_; ++i) {
131 ✗ const std::size_t fi = solution_.free_idx[i];
132 ✗ qf(i) = q(fi);
133 ✗ xf(i) = x_(fi);
134 ✗ for (std::size_t j = 0; j < nf_; ++j) {
135 ✗ Hff(i, j) = H(fi, solution_.free_idx[j]);
136 }
137 ✗ for (std::size_t j = 0; j < nc_; ++j) {
138 ✗ const std::size_t cj = solution_.clamped_idx[j];
139 ✗ xc(j) = x_(cj);
140 ✗ Hfc(i, j) = H(fi, cj);
141 }
142 }
143 ✗ if (reg_ != 0.) {
144 ✗ Hff.diagonal().array() += reg_;
145 }
146 ✗ Hff_inv_llt_.compute(Hff);
147 ✗ const Eigen::ComputationInfo& info = Hff_inv_llt_.info();
148 ✗ if (info != Eigen::Success) {
149 ✗ throw_pretty("backward_error");
150 }
151 ✗ solution_.Hff_inv.setIdentity(nf_, nf_);
152 ✗ Hff_inv_llt_.solveInPlace(solution_.Hff_inv);
153 ✗ qf.noalias() += Hff * xf;
154 ✗ if (nc_ != 0) {
155 ✗ qf.noalias() += Hfc * xc;
156 }
157 ✗ dxf = -qf;
158 ✗ Hff_inv_llt_.solveInPlace(dxf);
159 ✗ dx_.setZero();
160 ✗ for (std::size_t i = 0; i < nf_; ++i) {
161 ✗ dx_(solution_.free_idx[i]) = dxf(i);
162 ✗ g_(solution_.free_idx[i]) = -qf(i);
163 }
164
165 // Try different step lengths
166 ✗ fold_ = 0.5 * x_.dot(H * x_) + q.dot(x_);
167 ✗ for (std::vector<double>::const_iterator it = alphas_.begin();
168 ✗ it != alphas_.end(); ++it) {
169 ✗ double steplength = *it;
170 ✗ for (std::size_t i = 0; i < nx_; ++i) {
171 ✗ xnew_(i) =
172 ✗ std::max(std::min(x_(i) + steplength * dx_(i), ub(i)), lb(i));
173 }
174 ✗ fnew_ = 0.5 * xnew_.dot(H * xnew_) + q.dot(xnew_);
175 ✗ if (fold_ - fnew_ > th_acceptstep_ * g_.dot(x_ - xnew_)) {
176 ✗ x_ = xnew_;
177 ✗ break;
178 }
179 }
180
181 // Check convergence
182 ✗ if (qf.lpNorm<Eigen::Infinity>() <= th_grad_) {
183 ✗ solution_.x = x_;
184 ✗ return solution_;
185 }
186 }
187 ✗ solution_.x = x_;
188 ✗ return solution_;
189 }
190
191 ✗ const BoxQPSolution& BoxQP::get_solution() const { return solution_; }
192
193 ✗ std::size_t BoxQP::get_nx() const { return nx_; }
194
195 ✗ std::size_t BoxQP::get_maxiter() const { return maxiter_; }
196
197 ✗ double BoxQP::get_th_acceptstep() const { return th_acceptstep_; }
198
199 ✗ double BoxQP::get_th_grad() const { return th_grad_; }
200
201 ✗ double BoxQP::get_reg() const { return reg_; }
202
203 ✗ const std::vector<double>& BoxQP::get_alphas() const { return alphas_; }
204
205 ✗ void BoxQP::set_nx(const std::size_t nx) {
206 ✗ nx_ = nx;
207 ✗ x_.conservativeResize(nx);
208 ✗ xnew_.conservativeResize(nx);
209 ✗ g_.conservativeResize(nx);
210 ✗ dx_.conservativeResize(nx);
211 ✗ xo_.conservativeResize(nx);
212 ✗ dxo_.conservativeResize(nx);
213 ✗ qo_.conservativeResize(nx);
214 ✗ Ho_.conservativeResize(nx, nx);
215 ✗ }
216
217 ✗ void BoxQP::set_maxiter(const std::size_t maxiter) { maxiter_ = maxiter; }
218
219 ✗ void BoxQP::set_th_acceptstep(const double th_acceptstep) {
220 ✗ if (0. >= th_acceptstep && th_acceptstep >= 0.5) {
221 ✗ throw_pretty(
222 "Invalid argument: " << "th_acceptstep value should between 0 and 0.5");
223 }
224 ✗ th_acceptstep_ = th_acceptstep;
225 ✗ }
226
227 ✗ void BoxQP::set_th_grad(const double th_grad) {
228 ✗ if (0. > th_grad) {
229 ✗ throw_pretty("Invalid argument: " << "th_grad value has to be positive.");
230 }
231 ✗ th_grad_ = th_grad;
232 ✗ }
233
234 ✗ void BoxQP::set_reg(const double reg) {
235 ✗ if (0. > reg) {
236 ✗ throw_pretty("Invalid argument: " << "reg value has to be positive.");
237 }
238 ✗ reg_ = reg;
239 ✗ }
240
241 ✗ void BoxQP::set_alphas(const std::vector<double>& alphas) {
242 ✗ double prev_alpha = alphas[0];
243 ✗ if (prev_alpha != 1.) {
244 ✗ std::cerr << "Warning: alpha[0] should be 1" << std::endl;
245 }
246 ✗ for (std::size_t i = 1; i < alphas.size(); ++i) {
247 ✗ double alpha = alphas[i];
248 ✗ if (0. >= alpha) {
249 ✗ throw_pretty("Invalid argument: " << "alpha values has to be positive.");
250 }
251 ✗ if (alpha >= prev_alpha) {
252 ✗ throw_pretty(
253 "Invalid argument: " << "alpha values are monotonously decreasing.");
254 }
255 ✗ prev_alpha = alpha;
256 }
257 ✗ alphas_ = alphas;
258 ✗ }
259
260 } // namespace crocoddyl
261