Directory: | ./ |
---|---|
File: | src/eiquadprog-fast.cpp |
Date: | 2024-08-26 22:54:11 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 151 | 222 | 68.0% |
Branches: | 151 | 362 | 41.7% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | #include "eiquadprog/eiquadprog-fast.hpp" | ||
2 | |||
3 | #include <iostream> | ||
4 | |||
5 | namespace eiquadprog { | ||
6 | namespace solvers { | ||
7 | |||
8 |
14/28✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 12 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 12 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 12 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 12 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 12 times.
✗ Branch 24 not taken.
✓ Branch 26 taken 12 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 12 times.
✗ Branch 30 not taken.
✓ Branch 32 taken 12 times.
✗ Branch 33 not taken.
✓ Branch 35 taken 12 times.
✗ Branch 36 not taken.
✓ Branch 38 taken 12 times.
✗ Branch 39 not taken.
✓ Branch 41 taken 12 times.
✗ Branch 42 not taken.
|
12 | EiquadprogFast::EiquadprogFast() { |
9 | 12 | m_maxIter = DEFAULT_MAX_ITER; | |
10 | 12 | q = 0; // size of the active set A (containing the indices | |
11 | // of the active constraints) | ||
12 | 12 | is_inverse_provided_ = false; | |
13 | 12 | m_nVars = 0; | |
14 | 12 | m_nEqCon = 0; | |
15 | 12 | m_nIneqCon = 0; | |
16 | 12 | } | |
17 | |||
18 | 24 | EiquadprogFast::~EiquadprogFast() {} | |
19 | |||
20 | 12 | void EiquadprogFast::reset(size_t nVars, size_t nEqCon, size_t nIneqCon) { | |
21 | 12 | m_nVars = nVars; | |
22 | 12 | m_nEqCon = nEqCon; | |
23 | 12 | m_nIneqCon = nIneqCon; | |
24 | 12 | m_J.setZero(nVars, nVars); | |
25 | 12 | chol_.compute(m_J); | |
26 | 12 | R.resize(nVars, nVars); | |
27 | 12 | s.resize(nIneqCon); | |
28 | 12 | r.resize(nIneqCon + nEqCon); | |
29 | 12 | u.resize(nIneqCon + nEqCon); | |
30 | 12 | z.resize(nVars); | |
31 | 12 | d.resize(nVars); | |
32 | 12 | np.resize(nVars); | |
33 | 12 | A.resize(nIneqCon + nEqCon); | |
34 | 12 | iai.resize(nIneqCon); | |
35 | 12 | iaexcl.resize(nIneqCon); | |
36 | 12 | x_old.resize(nVars); | |
37 | 12 | u_old.resize(nIneqCon + nEqCon); | |
38 | 12 | A_old.resize(nIneqCon + nEqCon); | |
39 | |||
40 | #ifdef OPTIMIZE_ADD_CONSTRAINT | ||
41 | T1.resize(nVars); | ||
42 | #endif | ||
43 | 12 | } | |
44 | |||
45 | 10 | bool EiquadprogFast::add_constraint(MatrixXd &R, MatrixXd &J, VectorXd &d, | |
46 | size_t &iq, double &R_norm) { | ||
47 | 10 | size_t nVars = J.rows(); | |
48 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
49 | std::cerr << "Add constraint " << iq << '/'; | ||
50 | #endif | ||
51 | size_t j, k; | ||
52 | double cc, ss, h, t1, t2, xny; | ||
53 | |||
54 | #ifdef OPTIMIZE_ADD_CONSTRAINT | ||
55 | Eigen::Vector2d cc_ss; | ||
56 | #endif | ||
57 | |||
58 | /* we have to find the Givens rotation which will reduce the element | ||
59 | d(j) to zero. | ||
60 | if it is already zero we don't have to do anything, except of | ||
61 | decreasing j */ | ||
62 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 10 times.
|
16 | for (j = d.size() - 1; j >= iq + 1; j--) { |
63 | /* The Givens rotation is done with the matrix (cc cs, cs -cc). | ||
64 | If cc is one, then element (j) of d is zero compared with | ||
65 | element (j - 1). Hence we don't have to do anything. If cc is zero, then | ||
66 | we just have to switch column (j) and column (j - 1) of J. Since we only | ||
67 | switch columns in J, we have to be careful how we update d depending on | ||
68 | the sign of gs. Otherwise we have to apply the Givens rotation to these | ||
69 | columns. The i - 1 element of d has to be updated to h. */ | ||
70 | 6 | cc = d(j - 1); | |
71 | 6 | ss = d(j); | |
72 | 6 | h = utils::distance(cc, ss); | |
73 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (h == 0.0) continue; |
74 | 6 | d(j) = 0.0; | |
75 | 6 | ss = ss / h; | |
76 | 6 | cc = cc / h; | |
77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (cc < 0.0) { |
78 | ✗ | cc = -cc; | |
79 | ✗ | ss = -ss; | |
80 | ✗ | d(j - 1) = -h; | |
81 | } else | ||
82 | 6 | d(j - 1) = h; | |
83 | 6 | xny = ss / (1.0 + cc); | |
84 | |||
85 | // #define OPTIMIZE_ADD_CONSTRAINT | ||
86 | #ifdef OPTIMIZE_ADD_CONSTRAINT // the optimized code is actually slower than | ||
87 | // the original | ||
88 | T1 = J.col(j - 1); | ||
89 | cc_ss(0) = cc; | ||
90 | cc_ss(1) = ss; | ||
91 | J.col(j - 1).noalias() = J.middleCols<2>(j - 1) * cc_ss; | ||
92 | J.col(j) = xny * (T1 + J.col(j - 1)) - J.col(j); | ||
93 | #else | ||
94 | // J.col(j-1) = J[:,j-1:j] * [cc; ss] | ||
95 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (k = 0; k < nVars; k++) { |
96 | 12 | t1 = J(k, j - 1); | |
97 | 12 | t2 = J(k, j); | |
98 | 12 | J(k, j - 1) = t1 * cc + t2 * ss; | |
99 | 12 | J(k, j) = xny * (t1 + J(k, j - 1)) - t2; | |
100 | } | ||
101 | #endif | ||
102 | } | ||
103 | /* update the number of constraints added*/ | ||
104 | 10 | iq++; | |
105 | /* To update R we have to put the iq components of the d vector | ||
106 | into column iq - 1 of R | ||
107 | */ | ||
108 |
3/6✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
|
10 | R.col(iq - 1).head(iq) = d.head(iq); |
109 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
110 | std::cerr << iq << std::endl; | ||
111 | #endif | ||
112 | |||
113 |
2/2✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
|
10 | if (std::abs(d(iq - 1)) <= std::numeric_limits<double>::epsilon() * R_norm) |
114 | // problem degenerate | ||
115 | 1 | return false; | |
116 | 9 | R_norm = std::max<double>(R_norm, std::abs(d(iq - 1))); | |
117 | 9 | return true; | |
118 | } | ||
119 | |||
120 | ✗ | void EiquadprogFast::delete_constraint(MatrixXd &R, MatrixXd &J, VectorXi &A, | |
121 | VectorXd &u, size_t nEqCon, size_t &iq, | ||
122 | size_t l) { | ||
123 | ✗ | size_t nVars = R.rows(); | |
124 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
125 | std::cerr << "Delete constraint " << l << ' ' << iq; | ||
126 | #endif | ||
127 | size_t i, j, k; | ||
128 | ✗ | size_t qq = 0; | |
129 | double cc, ss, h, xny, t1, t2; | ||
130 | |||
131 | /* Find the index qq for active constraint l to be removed */ | ||
132 | ✗ | for (i = nEqCon; i < iq; i++) | |
133 | ✗ | if (A(i) == static_cast<VectorXi::Scalar>(l)) { | |
134 | ✗ | qq = i; | |
135 | ✗ | break; | |
136 | } | ||
137 | |||
138 | /* remove the constraint from the active set and the duals */ | ||
139 | ✗ | for (i = qq; i < iq - 1; i++) { | |
140 | ✗ | A(i) = A(i + 1); | |
141 | ✗ | u(i) = u(i + 1); | |
142 | ✗ | R.col(i) = R.col(i + 1); | |
143 | } | ||
144 | |||
145 | ✗ | A(iq - 1) = A(iq); | |
146 | ✗ | u(iq - 1) = u(iq); | |
147 | ✗ | A(iq) = 0; | |
148 | ✗ | u(iq) = 0.0; | |
149 | ✗ | for (j = 0; j < iq; j++) R(j, iq - 1) = 0.0; | |
150 | /* constraint has been fully removed */ | ||
151 | ✗ | iq--; | |
152 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
153 | std::cerr << '/' << iq << std::endl; | ||
154 | #endif | ||
155 | |||
156 | ✗ | if (iq == 0) return; | |
157 | |||
158 | ✗ | for (j = qq; j < iq; j++) { | |
159 | ✗ | cc = R(j, j); | |
160 | ✗ | ss = R(j + 1, j); | |
161 | ✗ | h = utils::distance(cc, ss); | |
162 | ✗ | if (h == 0.0) continue; | |
163 | ✗ | cc = cc / h; | |
164 | ✗ | ss = ss / h; | |
165 | ✗ | R(j + 1, j) = 0.0; | |
166 | ✗ | if (cc < 0.0) { | |
167 | ✗ | R(j, j) = -h; | |
168 | ✗ | cc = -cc; | |
169 | ✗ | ss = -ss; | |
170 | } else | ||
171 | ✗ | R(j, j) = h; | |
172 | |||
173 | ✗ | xny = ss / (1.0 + cc); | |
174 | ✗ | for (k = j + 1; k < iq; k++) { | |
175 | ✗ | t1 = R(j, k); | |
176 | ✗ | t2 = R(j + 1, k); | |
177 | ✗ | R(j, k) = t1 * cc + t2 * ss; | |
178 | ✗ | R(j + 1, k) = xny * (t1 + R(j, k)) - t2; | |
179 | } | ||
180 | ✗ | for (k = 0; k < nVars; k++) { | |
181 | ✗ | t1 = J(k, j); | |
182 | ✗ | t2 = J(k, j + 1); | |
183 | ✗ | J(k, j) = t1 * cc + t2 * ss; | |
184 | ✗ | J(k, j + 1) = xny * (J(k, j) + t1) - t2; | |
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | 12 | EiquadprogFast_status EiquadprogFast::solve_quadprog( | |
190 | const MatrixXd &Hess, const VectorXd &g0, const MatrixXd &CE, | ||
191 | const VectorXd &ce0, const MatrixXd &CI, const VectorXd &ci0, VectorXd &x) { | ||
192 | 12 | const size_t nVars = g0.size(); | |
193 | 12 | const size_t nEqCon = ce0.size(); | |
194 | 12 | const size_t nIneqCon = ci0.size(); | |
195 | |||
196 |
3/6✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
|
12 | if (nVars != m_nVars || nEqCon != m_nEqCon || nIneqCon != m_nIneqCon) |
197 | ✗ | reset(nVars, nEqCon, nIneqCon); | |
198 | |||
199 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | assert(static_cast<size_t>(Hess.rows()) == m_nVars && |
200 | static_cast<size_t>(Hess.cols()) == m_nVars); | ||
201 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
12 | assert(static_cast<size_t>(g0.size()) == m_nVars); |
202 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | assert(static_cast<size_t>(CE.rows()) == m_nEqCon && |
203 | static_cast<size_t>(CE.cols()) == m_nVars); | ||
204 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
12 | assert(static_cast<size_t>(ce0.size()) == m_nEqCon); |
205 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | assert(static_cast<size_t>(CI.rows()) == m_nIneqCon && |
206 | static_cast<size_t>(CI.cols()) == m_nVars); | ||
207 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
12 | assert(static_cast<size_t>(ci0.size()) == m_nIneqCon); |
208 | |||
209 | size_t i, k, l; // indices | ||
210 | size_t ip; // index of the chosen violated constraint | ||
211 | size_t iq; // current number of active constraints | ||
212 | double psi; // current sum of constraint violations | ||
213 | double c1; // Hessian trace | ||
214 | double c2; // Hessian Cholesky factor trace | ||
215 | double ss; // largest constraint violation (negative for violation) | ||
216 | double R_norm; // norm of matrix R | ||
217 | 12 | const double inf = std::numeric_limits<double>::infinity(); | |
218 | double t, t1, t2; | ||
219 | /* t is the step length, which is the minimum of the partial step length t1 | ||
220 | * and the full step length t2 */ | ||
221 | |||
222 | 12 | iter = 0; // active-set iteration number | |
223 | |||
224 | /* | ||
225 | * Preprocessing phase | ||
226 | */ | ||
227 | /* compute the trace of the original matrix Hess */ | ||
228 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | c1 = Hess.trace(); |
229 | |||
230 | /* decompose the matrix Hess in the form LL^T */ | ||
231 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (!is_inverse_provided_) { |
232 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_CHOLESKY_DECOMPOSITION); | ||
233 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | chol_.compute(Hess); |
234 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_CHOLESKY_DECOMPOSITION); | ||
235 | } | ||
236 | |||
237 | /* initialize the matrix R */ | ||
238 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | d.setZero(nVars); |
239 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | R.setZero(nVars, nVars); |
240 | 12 | R_norm = 1.0; | |
241 | |||
242 | /* compute the inverse of the factorized matrix Hess^-1, | ||
243 | this is the initial value for H */ | ||
244 | // m_J = L^-T | ||
245 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (!is_inverse_provided_) { |
246 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_CHOLESKY_INVERSE); | ||
247 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | m_J.setIdentity(nVars, nVars); |
248 | #ifdef OPTIMIZE_HESSIAN_INVERSE | ||
249 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | chol_.matrixU().solveInPlace(m_J); |
250 | #else | ||
251 | m_J = chol_.matrixU().solve(m_J); | ||
252 | #endif | ||
253 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_CHOLESKY_INVERSE); | ||
254 | } | ||
255 | |||
256 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | c2 = m_J.trace(); |
257 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
258 | utils::print_matrix("m_J", m_J); | ||
259 | #endif | ||
260 | |||
261 | /* c1 * c2 is an estimate for cond(Hess) */ | ||
262 | |||
263 | /* | ||
264 | * Find the unconstrained minimizer of the quadratic | ||
265 | * form 0.5 * x Hess x + g0 x | ||
266 | * this is a feasible point in the dual space | ||
267 | * x = Hess^-1 * g0 | ||
268 | */ | ||
269 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_1_UNCONSTR_MINIM); | ||
270 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (is_inverse_provided_) { |
271 | ✗ | x = m_J * (m_J.transpose() * g0); | |
272 | } else { | ||
273 | #ifdef OPTIMIZE_UNCONSTR_MINIM | ||
274 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | x = -g0; |
275 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | chol_.solveInPlace(x); |
276 | } | ||
277 | #else | ||
278 | x = chol_.solve(g0); | ||
279 | } | ||
280 | x = -x; | ||
281 | #endif | ||
282 | /* and compute the current solution value */ | ||
283 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | f_value = 0.5 * g0.dot(x); |
284 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
285 | std::cerr << "Unconstrained solution: " << f_value << std::endl; | ||
286 | utils::print_vector("x", x); | ||
287 | #endif | ||
288 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_1_UNCONSTR_MINIM); | ||
289 | |||
290 | /* Add equality constraints to the working set A */ | ||
291 | |||
292 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_ADD_EQ_CONSTR); | ||
293 | 12 | iq = 0; | |
294 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 11 times.
|
16 | for (i = 0; i < nEqCon; i++) { |
295 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_ADD_EQ_CONSTR_1); | ||
296 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | np = CE.row(i); |
297 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | compute_d(d, m_J, np); |
298 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | update_z(z, m_J, d, iq); |
299 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | update_r(R, r, d, iq); |
300 | |||
301 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
302 | utils::print_matrix("R", R); | ||
303 | utils::print_vector("z", z); | ||
304 | utils::print_vector("r", r); | ||
305 | utils::print_vector("d", d); | ||
306 | #endif | ||
307 | |||
308 | /* compute full step length t2: i.e., | ||
309 | the minimum step in primal space s.t. the contraint | ||
310 | becomes feasible */ | ||
311 | 5 | t2 = 0.0; | |
312 |
3/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 1 times.
|
5 | if (std::abs(z.dot(z)) > std::numeric_limits<double>::epsilon()) |
313 | // i.e. z != 0 | ||
314 |
3/6✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
|
4 | t2 = (-np.dot(x) - ce0(i)) / z.dot(np); |
315 | |||
316 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | x += t2 * z; |
317 | |||
318 | /* set u = u+ */ | ||
319 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | u(iq) = t2; |
320 |
4/8✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
|
5 | u.head(iq) -= t2 * r.head(iq); |
321 | |||
322 | /* compute the new solution value */ | ||
323 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | f_value += 0.5 * (t2 * t2) * z.dot(np); |
324 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | A(i) = static_cast<VectorXi::Scalar>(-i - 1); |
325 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_ADD_EQ_CONSTR_1); | ||
326 | |||
327 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_ADD_EQ_CONSTR_2); | ||
328 |
3/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
|
5 | if (!add_constraint(R, m_J, d, iq, R_norm)) { |
329 | // Equality constraints are linearly dependent | ||
330 | 1 | return EIQUADPROG_FAST_REDUNDANT_EQUALITIES; | |
331 | } | ||
332 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_ADD_EQ_CONSTR_2); | ||
333 | } | ||
334 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_ADD_EQ_CONSTR); | ||
335 | |||
336 | /* set iai = K \ A */ | ||
337 |
3/4✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 11 times.
|
22 | for (i = 0; i < nIneqCon; i++) iai(i) = static_cast<VectorXi::Scalar>(i); |
338 | |||
339 | #ifdef USE_WARM_START | ||
340 | // DEBUG_STREAM("Gonna warm start using previous active | ||
341 | // set:\n"<<A.transpose()<<"\n") | ||
342 | for (i = nEqCon; i < q; i++) { | ||
343 | iai(i - nEqCon) = -1; | ||
344 | ip = A(i); | ||
345 | np = CI.row(ip); | ||
346 | compute_d(d, m_J, np); | ||
347 | update_z(z, m_J, d, iq); | ||
348 | update_r(R, r, d, iq); | ||
349 | |||
350 | /* compute full step length t2: i.e., | ||
351 | the minimum step in primal space s.t. the contraint | ||
352 | becomes feasible */ | ||
353 | t2 = 0.0; | ||
354 | if (std::abs(z.dot(z)) > | ||
355 | std::numeric_limits<double>::epsilon()) // i.e. z != 0 | ||
356 | t2 = (-np.dot(x) - ci0(ip)) / z.dot(np); | ||
357 | else | ||
358 | DEBUG_STREAM("[WARM START] z=0\n") | ||
359 | |||
360 | x += t2 * z; | ||
361 | |||
362 | /* set u = u+ */ | ||
363 | u(iq) = t2; | ||
364 | u.head(iq) -= t2 * r.head(iq); | ||
365 | |||
366 | /* compute the new solution value */ | ||
367 | f_value += 0.5 * (t2 * t2) * z.dot(np); | ||
368 | |||
369 | if (!add_constraint(R, m_J, d, iq, R_norm)) { | ||
370 | // constraints are linearly dependent | ||
371 | std::cerr << "[WARM START] Constraints are linearly dependent\n"; | ||
372 | return RT_EIQUADPROG_REDUNDANT_EQUALITIES; | ||
373 | } | ||
374 | } | ||
375 | #else | ||
376 | |||
377 | #endif | ||
378 | |||
379 | 11 | l1: | |
380 | 16 | iter++; | |
381 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (iter >= m_maxIter) { |
382 | ✗ | q = iq; | |
383 | ✗ | return EIQUADPROG_FAST_MAX_ITER_REACHED; | |
384 | } | ||
385 | |||
386 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_1); | ||
387 | |||
388 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
389 | utils::print_vector("x", x); | ||
390 | #endif | ||
391 | /* step 1: choose a violated constraint */ | ||
392 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 16 times.
|
22 | for (i = nEqCon; i < iq; i++) { |
393 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | ip = A(i); |
394 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | iai(ip) = -1; |
395 | } | ||
396 | |||
397 | /* compute s(x) = ci^T * x + ci0 for all elements of K \ A */ | ||
398 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_1_2); | ||
399 | 16 | ss = 0.0; | |
400 | 16 | ip = 0; /* ip will be the index of the chosen violated constraint */ | |
401 | |||
402 | #ifdef OPTIMIZE_STEP_1_2 | ||
403 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | s = ci0; |
404 |
3/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
|
16 | s.noalias() += CI * x; |
405 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | iaexcl.setOnes(); |
406 |
3/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
|
16 | psi = (s.cwiseMin(VectorXd::Zero(nIneqCon))).sum(); |
407 | #else | ||
408 | psi = 0.0; /* this value will contain the sum of all infeasibilities */ | ||
409 | for (i = 0; i < nIneqCon; i++) { | ||
410 | iaexcl(i) = 1; | ||
411 | s(i) = CI.row(i).dot(x) + ci0(i); | ||
412 | psi += std::min(0.0, s(i)); | ||
413 | } | ||
414 | #endif | ||
415 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_1_2); | ||
416 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
417 | utils::print_vector("s", s); | ||
418 | #endif | ||
419 | |||
420 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_1); | ||
421 | |||
422 | 16 | if (std::abs(psi) <= static_cast<double>(nIneqCon) * | |
423 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 7 times.
|
16 | std::numeric_limits<double>::epsilon() * c1 * c2 * |
424 | 100.0) { | ||
425 | /* numerically there are not infeasibilities anymore */ | ||
426 | 9 | q = iq; | |
427 | // DEBUG_STREAM("Optimal active | ||
428 | // set:\n"<<A.head(iq).transpose()<<"\n\n") | ||
429 | 9 | return EIQUADPROG_FAST_OPTIMAL; | |
430 | } | ||
431 | |||
432 | /* save old values for u, x and A */ | ||
433 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
7 | u_old.head(iq) = u.head(iq); |
434 |
3/6✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
|
7 | A_old.head(iq) = A.head(iq); |
435 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | x_old = x; |
436 | |||
437 | 7 | l2: /* Step 2: check for feasibility and determine a new S-pair */ | |
438 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2); | ||
439 | // find constraint with highest violation | ||
440 | // (what about normalizing constraints?) | ||
441 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 7 times.
|
20 | for (i = 0; i < nIneqCon; i++) { |
442 |
9/14✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 6 times.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 7 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 7 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 7 times.
✓ Branch 16 taken 6 times.
|
13 | if (s(i) < ss && iai(i) != -1 && iaexcl(i)) { |
443 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | ss = s(i); |
444 | 7 | ip = i; | |
445 | } | ||
446 | } | ||
447 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (ss >= 0.0) { |
448 | ✗ | q = iq; | |
449 | // DEBUG_STREAM("Optimal active set:\n"<<A.transpose()<<"\n\n") | ||
450 | ✗ | return EIQUADPROG_FAST_OPTIMAL; | |
451 | } | ||
452 | |||
453 | /* set np = n(ip) */ | ||
454 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
7 | np = CI.row(ip); |
455 | /* set u = (u 0)^T */ | ||
456 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | u(iq) = 0.0; |
457 | /* add ip to the active set A */ | ||
458 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | A(iq) = static_cast<VectorXi::Scalar>(ip); |
459 | |||
460 | // DEBUG_STREAM("Add constraint "<<ip<<" to active set\n") | ||
461 | |||
462 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
463 | std::cerr << "Trying with constraint " << ip << std::endl; | ||
464 | utils::print_vector("np", np); | ||
465 | #endif | ||
466 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2); | ||
467 | |||
468 | 7 | l2a: /* Step 2a: determine step direction */ | |
469 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2A); | ||
470 | /* compute z = H np: the step direction in the primal space | ||
471 | (through m_J, see the paper) */ | ||
472 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | compute_d(d, m_J, np); |
473 | // update_z(z, m_J, d, iq); | ||
474 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
|
7 | if (iq >= nVars) { |
475 | // throw std::runtime_error("iq >= m_J.cols()"); | ||
476 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | z.setZero(); |
477 | } else { | ||
478 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | update_z(z, m_J, d, iq); |
479 | } | ||
480 | /* compute N* np (if q > 0): the negative of the | ||
481 | step direction in the dual space */ | ||
482 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | update_r(R, r, d, iq); |
483 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
484 | std::cerr << "Step direction z" << std::endl; | ||
485 | utils::print_vector("z", z); | ||
486 | utils::print_vector("r", r); | ||
487 | utils::print_vector("u", u); | ||
488 | utils::print_vector("d", d); | ||
489 | utils::print_vector("A", A); | ||
490 | #endif | ||
491 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2A); | ||
492 | |||
493 | /* Step 2b: compute step length */ | ||
494 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2B); | ||
495 | 7 | l = 0; | |
496 | /* Compute t1: partial step length (maximum step in dual | ||
497 | space without violating dual feasibility */ | ||
498 | 7 | t1 = inf; /* +inf */ | |
499 | /* find the index l s.t. it reaches the minimum of u+(x) / r */ | ||
500 | // l: index of constraint to drop (maybe) | ||
501 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
|
10 | for (k = nEqCon; k < iq; k++) { |
502 | double tmp; | ||
503 |
3/12✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
|
3 | if (r(k) > 0.0 && ((tmp = u(k) / r(k)) < t1)) { |
504 | ✗ | t1 = tmp; | |
505 | ✗ | l = A(k); | |
506 | } | ||
507 | } | ||
508 | /* Compute t2: full step length (minimum step in primal | ||
509 | space such that the constraint ip becomes feasible */ | ||
510 |
3/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 2 times.
|
7 | if (std::abs(z.dot(z)) > std::numeric_limits<double>::epsilon()) |
511 | // i.e. z != 0 | ||
512 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | t2 = -s(ip) / z.dot(np); |
513 | else | ||
514 | 2 | t2 = inf; /* +inf */ | |
515 | |||
516 | /* the step is chosen as the minimum of t1 and t2 */ | ||
517 | 7 | t = std::min(t1, t2); | |
518 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
519 | std::cerr << "Step sizes: " << t << " (t1 = " << t1 << ", t2 = " << t2 | ||
520 | << ") "; | ||
521 | #endif | ||
522 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2B); | ||
523 | |||
524 | /* Step 2c: determine new S-pair and take step: */ | ||
525 | START_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2C); | ||
526 | /* case (i): no step in primal or dual space */ | ||
527 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
|
7 | if (t >= inf) { |
528 | /* QPP is infeasible */ | ||
529 | 2 | q = iq; | |
530 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2C); | ||
531 | 2 | return EIQUADPROG_FAST_UNBOUNDED; | |
532 | } | ||
533 | /* case (ii): step in dual space */ | ||
534 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (t2 >= inf) { |
535 | /* set u = u + t * [-r 1) and drop constraint l from the active set A */ | ||
536 | ✗ | u.head(iq) -= t * r.head(iq); | |
537 | ✗ | u(iq) += t; | |
538 | ✗ | iai(l) = static_cast<VectorXi::Scalar>(l); | |
539 | ✗ | delete_constraint(R, m_J, A, u, nEqCon, iq, l); | |
540 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
541 | std::cerr << " in dual space: " << f_value << std::endl; | ||
542 | utils::print_vector("x", x); | ||
543 | utils::print_vector("z", z); | ||
544 | utils::print_vector("A", A); | ||
545 | #endif | ||
546 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2C); | ||
547 | ✗ | goto l2a; | |
548 | } | ||
549 | |||
550 | /* case (iii): step in primal and dual space */ | ||
551 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | x += t * z; |
552 | /* update the solution value */ | ||
553 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | f_value += t * z.dot(np) * (0.5 * t + u(iq)); |
554 | |||
555 |
4/8✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
|
5 | u.head(iq) -= t * r.head(iq); |
556 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | u(iq) += t; |
557 | |||
558 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
559 | std::cerr << " in both spaces: " << f_value << std::endl; | ||
560 | utils::print_vector("x", x); | ||
561 | utils::print_vector("u", u); | ||
562 | utils::print_vector("r", r); | ||
563 | utils::print_vector("A", A); | ||
564 | #endif | ||
565 | |||
566 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (t == t2) { |
567 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
568 | std::cerr << "Full step has taken " << t << std::endl; | ||
569 | utils::print_vector("x", x); | ||
570 | #endif | ||
571 | /* full step has taken */ | ||
572 | /* add constraint ip to the active set*/ | ||
573 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
|
5 | if (!add_constraint(R, m_J, d, iq, R_norm)) { |
574 | ✗ | iaexcl(ip) = 0; | |
575 | ✗ | delete_constraint(R, m_J, A, u, nEqCon, iq, ip); | |
576 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
577 | utils::print_matrix("R", R); | ||
578 | utils::print_vector("A", A); | ||
579 | #endif | ||
580 | ✗ | for (i = 0; i < nIneqCon; i++) iai(i) = static_cast<VectorXi::Scalar>(i); | |
581 | ✗ | for (i = 0; i < iq; i++) { | |
582 | ✗ | A(i) = A_old(i); | |
583 | ✗ | iai(A(i)) = -1; | |
584 | ✗ | u(i) = u_old(i); | |
585 | } | ||
586 | ✗ | x = x_old; | |
587 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2C); | ||
588 | ✗ | goto l2; /* go to step 2 */ | |
589 | } else | ||
590 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | iai(ip) = -1; |
591 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
592 | utils::print_matrix("R", R); | ||
593 | utils::print_vector("A", A); | ||
594 | #endif | ||
595 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2C); | ||
596 | 5 | goto l1; | |
597 | } | ||
598 | |||
599 | /* a partial step has been taken => drop constraint l */ | ||
600 | ✗ | iai(l) = static_cast<VectorXi::Scalar>(l); | |
601 | ✗ | delete_constraint(R, m_J, A, u, nEqCon, iq, l); | |
602 | ✗ | s(ip) = CI.row(ip).dot(x) + ci0(ip); | |
603 | |||
604 | #ifdef EIQGUADPROG_TRACE_SOLVER | ||
605 | std::cerr << "Partial step has taken " << t << std::endl; | ||
606 | utils::print_vector("x", x); | ||
607 | utils::print_matrix("R", R); | ||
608 | utils::print_vector("A", A); | ||
609 | utils::print_vector("s", s); | ||
610 | #endif | ||
611 | STOP_PROFILER_EIQUADPROG_FAST(EIQUADPROG_FAST_STEP_2C); | ||
612 | |||
613 | ✗ | goto l2a; | |
614 | } | ||
615 | |||
616 | } /* namespace solvers */ | ||
617 | } /* namespace eiquadprog */ | ||
618 |