casadi_ipqp.hpp
1 //
2 // MIT No Attribution
3 //
4 // Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of this
7 // software and associated documentation files (the "Software"), to deal in the Software
8 // without restriction, including without limitation the rights to use, copy, modify,
9 // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
14 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
15 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
16 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
17 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 //
19 
20 
21 // C-REPLACE "fmin" "casadi_fmin"
22 // C-REPLACE "fmax" "casadi_fmax"
23 // C-REPLACE "fabs" "casadi_fabs"
24 // C-REPLACE "std::numeric_limits<T1>::min()" "casadi_real_min"
25 // C-REPLACE "std::numeric_limits<T1>::infinity()" "casadi_inf"
26 // C-REPLACE "static_cast<int>" "(int) "
27 // C-REPLACE "nullptr" "0"
28 
29 // SYMBOL "ipqp_prob"
30 template<typename T1>
32  // Dimensions
33  casadi_int nx, na, nz;
34  // Smallest nonzero number
35  T1 dmin;
36  // Infinity
37  T1 inf;
38  // Maximum number of iterations
39  casadi_int max_iter;
40  // Error tolerance
42 };
43 // C-REPLACE "casadi_ipqp_prob<T1>" "struct casadi_ipqp_prob"
44 
45 // SYMBOL "ipqp_setup"
46 template<typename T1>
47 void casadi_ipqp_setup(casadi_ipqp_prob<T1>* p, casadi_int nx, casadi_int na) {
48  p->nx = nx;
49  p->na = na;
50  p->nz = nx + na;
51  p->dmin = std::numeric_limits<T1>::min();
52  p->inf = std::numeric_limits<T1>::infinity();
53  p->max_iter = 100;
54  p->pr_tol = 1e-8;
55  p->du_tol = 1e-8;
56  p->co_tol = 1e-8;
57  p->mu_tol = 1e-8;
58 }
59 
60 // SYMBOL "ipqp_flag_t"
61 typedef enum {
62  IPQP_SUCCESS,
63  IPQP_MAX_ITER,
64  IPQP_NO_SEARCH_DIR,
65  IPQP_MV_ERROR,
66  IPQP_FACTOR_ERROR,
67  IPQP_SOLVE_ERROR,
68  IPQP_PROGRESS_ERROR
69 } casadi_ipqp_flag_t;
70 
71 // SYMBOL "ipqp_task_t"
72 typedef enum {
73  IPQP_MV,
74  IPQP_PROGRESS,
75  IPQP_FACTOR,
76  IPQP_SOLVE} casadi_ipqp_task_t;
77 
78 // SYMBOL "ipqp_task_t"
79 typedef enum {
80  IPQP_RESET,
81  IPQP_RESIDUAL,
82  IPQP_NEWITER,
83  IPQP_PREPARE,
84  IPQP_PREDICTOR,
85  IPQP_CORRECTOR} casadi_ipqp_next_t;
86 
87 // SYMBOL "ipqp_blocker_t"
88 typedef enum {
89  IPQP_NONE = 0x0,
90  IPQP_UPPER = 0x1,
91  IPQP_LOWER = 0x2,
92  IPQP_PRIMAL = 0x4,
93  IPQP_DUAL = 0x8
94 } casadi_blocker_t;
95 
96 // SYMBOL "ipqp_data"
97 template<typename T1>
99  // Problem structure
101  // QP data
102  const T1 *g;
103  // Number of finite constraints
104  casadi_int n_con;
105  // Solver status
106  casadi_ipqp_flag_t status;
107  // User task
108  casadi_ipqp_task_t task;
109  // Next step
110  casadi_ipqp_next_t next;
111  // Linear system
112  T1* linsys;
113  // Message buffer
114  const char *msg;
115  // Complementarity measure
116  T1 mu;
117  // Stepsize
118  T1 tau;
119  // Primal and dual error, complementarity error, corresponding index
120  T1 pr, du, co;
121  casadi_int ipr, idu, ico;
122  // Iteration
123  casadi_int iter;
124  // Bounds
125  T1 *lbz, *ubz;
126  // Current solution
127  T1 *z, *lam, *lam_lbz, *lam_ubz;
128  // Step
130  // Residual
132  // Diagonal entries
133  T1* D;
134  // Scaling factor
135  T1* S;
136  // Inverse of margin to bounds (0 if no bound)
138 };
139 // C-REPLACE "casadi_ipqp_data<T1>" "struct casadi_ipqp_data"
140 
141 // SYMBOL "ipqp_sz_w"
142 template<typename T1>
143 casadi_int casadi_ipqp_sz_w(const casadi_ipqp_prob<T1>* p) {
144  // Return value
145  casadi_int sz_w = 0;
146  // Persistent work vectors
147  sz_w += p->nz; // lbz
148  sz_w += p->nz; // ubz
149  sz_w += p->nz; // z
150  sz_w += p->nz; // lam
151  sz_w += p->nz; // lam_lbz
152  sz_w += p->nz; // lam_ubz
153  sz_w += p->nz; // dz
154  sz_w += p->nz; // dlam
155  sz_w += p->nz; // dlam_lbz
156  sz_w += p->nz; // dlam_ubz
157  sz_w += p->nz; // rz
158  sz_w += p->nz; // rlam
159  sz_w += p->nz; // rlam_lbz
160  sz_w += p->nz; // rlam_ubz
161  sz_w += p->nz; // D
162  sz_w += p->nz; // S
163  sz_w += p->nz; // dinv_lbz
164  sz_w += p->nz; // dinv_ubz
165  return sz_w;
166 }
167 
168 // SYMBOL "ipqp_init"
169 template<typename T1>
170 void casadi_ipqp_init(casadi_ipqp_data<T1>* d, casadi_int** iw, T1** w) {
171  const casadi_ipqp_prob<T1>* p = d->prob;
172  // Assign memory
173  d->lbz = *w; *w += p->nz;
174  d->ubz = *w; *w += p->nz;
175  d->z = *w; *w += p->nz;
176  d->lam = *w; *w += p->nz;
177  d->lam_lbz = *w; *w += p->nz;
178  d->lam_ubz = *w; *w += p->nz;
179  d->dz = *w; *w += p->nz;
180  d->dlam = *w; *w += p->nz;
181  d->dlam_lbz = *w; *w += p->nz;
182  d->dlam_ubz = *w; *w += p->nz;
183  d->rz = *w; *w += p->nz;
184  d->rlam = *w; *w += p->nz;
185  d->rlam_lbz = *w; *w += p->nz;
186  d->rlam_ubz = *w; *w += p->nz;
187  d->D = *w; *w += p->nz;
188  d->S = *w; *w += p->nz;
189  d->dinv_lbz = *w; *w += p->nz;
190  d->dinv_ubz = *w; *w += p->nz;
191  // New QP
192  d->next = IPQP_RESET;
193 }
194 
195 // SYMBOL "ipqp_bounds"
196 template<typename T1>
197 void casadi_ipqp_bounds(casadi_ipqp_data<T1>* d, const T1* g,
198  const T1* lbx, const T1* ubx, const T1* lba, const T1* uba) {
199  // Local variables
200  const casadi_ipqp_prob<T1>* p = d->prob;
201  // Pass pointer to linear term in objective
202  d->g = g;
203  // Pass bounds on z
204  casadi_copy(lbx, p->nx, d->lbz);
205  casadi_copy(lba, p->na, d->lbz + p->nx);
206  casadi_copy(ubx, p->nx, d->ubz);
207  casadi_copy(uba, p->na, d->ubz + p->nx);
208 }
209 
210 // SYMBOL "ipqp_guess"
211 template<typename T1>
212 void casadi_ipqp_guess(casadi_ipqp_data<T1>* d,
213  const T1* x0, const T1* lam_x0, const T1* lam_a0) {
214  // Local variables
215  const casadi_ipqp_prob<T1>* p = d->prob;
216  // Pass initial guess
217  casadi_copy(x0, p->nx, d->z);
218  casadi_fill(d->z + p->nx, p->na, 0.);
219  casadi_copy(lam_x0, p->nx, d->lam);
220  casadi_copy(lam_a0, p->na, d->lam + p->nx);
221  casadi_fill(d->lam_lbz, p->nz, 0.);
222  casadi_fill(d->lam_ubz, p->nz, 0.);
223 }
224 
225 // SYMBOL "ipqp_reset"
226 template<typename T1>
227 void casadi_ipqp_reset(casadi_ipqp_data<T1>* d) {
228  // Local variables
229  casadi_int k;
230  T1 margin, mid;
231  const casadi_ipqp_prob<T1>* p = d->prob;
232  // Required margin to constraints
233  margin = .1;
234  // Reset constraint count
235  d->n_con = 0;
236  // Initialize constraints to zero
237  for (k = p->nx; k < p->nz; ++k) d->z[k] = 0;
238  // Find interior point
239  for (k = 0; k < p->nz; ++k) {
240  if (d->lbz[k] > -p->inf) {
241  if (d->ubz[k] < p->inf) {
242  // Both upper and lower bounds
243  mid = .5 * (d->lbz[k] + d->ubz[k]);
244  // Ensure margin to boundary, without crossing midpoint
245  if (d->z[k] < mid) {
246  d->z[k] = fmin(fmax(d->z[k], d->lbz[k] + margin), mid);
247  } else if (d->z[k] > mid) {
248  d->z[k] = fmax(fmin(d->z[k], d->ubz[k] - margin), mid);
249  }
250  if (d->ubz[k] > d->lbz[k] + p->dmin) {
251  d->lam_lbz[k] = 1;
252  d->lam_ubz[k] = 1;
253  d->n_con += 2;
254  }
255  } else {
256  // Only lower bound
257  d->z[k] = fmax(d->z[k], d->lbz[k] + margin);
258  d->lam_lbz[k] = 1;
259  d->n_con++;
260  }
261  } else {
262  if (d->ubz[k] < p->inf) {
263  // Only upper bound
264  d->z[k] = fmin(d->z[k], d->ubz[k] - margin);
265  d->lam_ubz[k] = 1;
266  d->n_con++;
267  }
268  }
269  }
270  // Clear residual
271  casadi_clear(d->rz, p->nz);
272  // Reset iteration counter
273  d->iter = 0;
274  // Reset iteration variables
275  d->msg = 0;
276  d->tau = -1;
277 }
278 
279 // SYMBOL "ipqp_diag"
280 template<typename T1>
281 void casadi_ipqp_diag(casadi_ipqp_data<T1>* d) {
282  // Local variables
283  casadi_int k;
284  const casadi_ipqp_prob<T1>* p = d->prob;
285  // Diagonal entries corresponding to variables
286  for (k = 0; k < p->nx; ++k) {
287  if (d->ubz[k] <= d->lbz[k] + p->dmin) {
288  // Fixed variable (eliminate)
289  d->D[k] = -1;
290  } else {
291  d->D[k] = d->lam_lbz[k] * d->dinv_lbz[k]
292  + d->lam_ubz[k] * d->dinv_ubz[k];
293  }
294  }
295  // Diagonal entries corresponding to constraints
296  for (; k < p->nz; ++k) {
297  if (d->lbz[k] <= -p->inf && d->ubz[k] >= p->inf) {
298  // Unconstrained (eliminate)
299  d->D[k] = -1;
300  } else if (d->ubz[k] <= d->lbz[k] + p->dmin) {
301  // Equality constrained
302  d->D[k] = 0;
303  } else {
304  d->D[k] = 1. / (d->lam_lbz[k] * d->dinv_lbz[k]
305  + d->lam_ubz[k] * d->dinv_ubz[k]);
306  }
307  }
308  // Scale diagonal entries
309  for (k = 0; k < p->nz; ++k) {
310  if (d->D[k] < 0) {
311  // Eliminate
312  d->S[k] = 0;
313  d->D[k] = 1;
314  } else {
315  // Scale
316  d->S[k] = fmin(1., std::sqrt(1. / d->D[k]));
317  d->D[k] = fmin(1., d->D[k]);
318  }
319  }
320 }
321 
322 // SYMBOL "ipqp_newiter"
323 template<typename T1>
324 int casadi_ipqp_newiter(casadi_ipqp_data<T1>* d) {
325  // Local variables
326  const casadi_ipqp_prob<T1>* p = d->prob;
327  // Converged?
328  if (d->pr < p->pr_tol && d->du < p->du_tol && d->co < p->co_tol
329  && d->mu < p->mu_tol) {
330  d->status = IPQP_SUCCESS;
331  return 1;
332  }
333  // Max number of iterations reached
334  if (d->iter >= p->max_iter) {
335  d->status = IPQP_MAX_ITER;
336  return 1;
337  }
338  // Start new iteration
339  d->iter++;
340  // Calculate diagonal entries and scaling factors
341  casadi_ipqp_diag(d);
342  // Success
343  return 0;
344 }
345 
346 // SYMBOL "ipqp_residual"
347 template<typename T1>
348 void casadi_ipqp_residual(casadi_ipqp_data<T1>* d) {
349  // Local variables
350  casadi_int k;
351  T1 bdiff, viol;
352  const casadi_ipqp_prob<T1>* p = d->prob;
353  // Gradient of the Lagrangian
354  casadi_axpy(p->nx, 1., d->g, d->rz);
355  for (k = 0; k < p->nx; ++k) {
356  if (d->ubz[k] <= d->lbz[k] + p->dmin) {
357  // Fixed variable: Solve to get multiplier explicitly
358  d->lam[k] = -d->rz[k];
359  d->rz[k] = 0;
360  } else {
361  // Residual
362  d->rz[k] += d->lam[k];
363  }
364  }
365  // Constraint violation (only possible for linear constraints)
366  d->ipr = -1;
367  d->pr = 0;
368  for (k = p->nx; k < p->nz; ++k) {
369  if (d->lbz[k] <= -p->inf && d->ubz[k] >= p->inf) {
370  // Unconstrained: Solve to get g explicitly
371  d->z[k] = d->rz[k];
372  d->lam[k] = 0;
373  } else {
374  // Check constraint violation
375  if (d->rz[k] + d->pr < d->lbz[k]) {
376  d->pr = d->lbz[k] - d->rz[k];
377  d->ipr = k;
378  } else if (d->rz[k] - d->pr > d->ubz[k]) {
379  d->pr = d->rz[k] - d->ubz[k];
380  d->ipr = k;
381  }
382  }
383  }
384  // Dual infeasibility
385  d->idu = -1;
386  d->du = 0;
387  for (k = 0; k < p->nx; ++k) {
388  if (fabs(d->rz[k]) > d->du) {
389  d->du = fabs(d->rz[k]);
390  d->idu = k;
391  }
392  }
393  // Linear constraint
394  casadi_axpy(p->na, -1., d->z + p->nx, d->rz + p->nx);
395  // Multiplier consistency
396  for (k = 0; k < p->nz; ++k) {
397  if (d->ubz[k] <= d->lbz[k] + p->dmin) {
398  // Fixed variable: Solve to get lam_lbz, lam_ubz
399  d->lam_ubz[k] = fmax(d->lam[k], 0.);
400  d->lam_lbz[k] = fmax(-d->lam[k], 0.);
401  d->rlam[k] = 0;
402  } else {
403  // Residual
404  d->rlam[k] = d->lam_ubz[k] - d->lam_lbz[k] - d->lam[k];
405  }
406  }
407  // Complementarity conditions, mu
408  d->mu = 0;
409  d->ico = -1;
410  d->co = 0;
411  for (k = 0; k < p->nz; ++k) {
412  // Lower bound
413  if (d->lbz[k] > -p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
414  // Inequality constraint
415  bdiff = d->z[k] - d->lbz[k];
416  d->mu += d->rlam_lbz[k] = d->lam_lbz[k] * bdiff;
417  d->dinv_lbz[k] = 1. / bdiff;
418  // Constraint violation
419  viol = bdiff * fmax(-d->lam[k], 0.);
420  if (viol > d->co) {
421  d->co = viol;
422  d->ico = k;
423  }
424  } else {
425  // No bound or equality constraint
426  d->rlam_lbz[k] = 0;
427  d->dinv_lbz[k] = 0;
428  }
429  // Upper bound
430  if (d->ubz[k] < p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
431  // Inequality constraint
432  bdiff = d->ubz[k] - d->z[k];
433  d->mu += d->rlam_ubz[k] = d->lam_ubz[k] * bdiff;
434  d->dinv_ubz[k] = 1. / bdiff;
435  // Constraint violation
436  viol = bdiff * fmax(d->lam[k], 0.);
437  if (viol > d->co) {
438  d->co = viol;
439  d->ico = k;
440  }
441  } else {
442  // No bound or equality constraint
443  d->rlam_ubz[k] = 0;
444  d->dinv_ubz[k] = 0;
445  }
446  }
447  // Divide mu by total number of finite constraints
448  if (d->n_con > 0) d->mu /= d->n_con;
449 }
450 
451 // SYMBOL "ipqp_predictor_prepare"
452 template<typename T1>
453 void casadi_ipqp_predictor_prepare(casadi_ipqp_data<T1>* d) {
454  // Local variables
455  casadi_int k;
456  const casadi_ipqp_prob<T1>* p = d->prob;
457  // Store r_lam - dinv_lbz * rlam_lbz + dinv_ubz * rlam_ubz in dz
458  casadi_copy(d->rlam, p->nz, d->dz);
459  for (k=0; k<p->nz; ++k) d->dz[k] += d->dinv_lbz[k] * d->rlam_lbz[k];
460  for (k=0; k<p->nz; ++k) d->dz[k] -= d->dinv_ubz[k] * d->rlam_ubz[k];
461  // Finish calculating x-component of right-hand-side and store in dz[:nx]
462  for (k=0; k<p->nx; ++k) d->dz[k] += d->rz[k];
463  // Copy tilde{r}_lam to dlam[nx:] (needed to calculate step in g later)
464  for (k=p->nx; k<p->nz; ++k) d->dlam[k] = d->dz[k];
465  // Finish calculating g-component of right-hand-side and store in dz[nx:]
466  for (k=p->nx; k<p->nz; ++k) {
467  if (d->S[k] == 0.) {
468  // Eliminate
469  d->dz[k] = 0;
470  } else {
471  d->dz[k] *= d->D[k] / (d->S[k] * d->S[k]);
472  d->dz[k] += d->rz[k];
473  }
474  }
475  // Scale and negate right-hand-side
476  for (k=0; k<p->nz; ++k) d->dz[k] *= -d->S[k];
477  // dlam_lbz := -rlam_lbz, dlam_ubz := -rlam_ubz
478  for (k=0; k<p->nz; ++k) d->dlam_lbz[k] = -d->rlam_lbz[k];
479  for (k=0; k<p->nz; ++k) d->dlam_ubz[k] = -d->rlam_ubz[k];
480  // dlam_x := rlam_x
481  for (k=0; k<p->nx; ++k) d->dlam[k] = d->rlam[k];
482  // Solve to get step
483  d->linsys = d->dz;
484 }
485 
486 // SYMBOL "ipqp_maxstep"
487 template<typename T1>
488 int casadi_ipqp_maxstep(casadi_ipqp_data<T1>* d, T1* alpha, casadi_int* ind) {
489  // Local variables
490  T1 test;
491  casadi_int k, blocking_k;
492  int flag;
493  const casadi_ipqp_prob<T1>* p = d->prob;
494  // Reset variables
495  blocking_k = -1;
496  flag = IPQP_NONE;
497  // Maximum step size is 1
498  *alpha = 1.;
499  // Primal step
500  for (k=0; k<p->nz; ++k) {
501  if (d->dz[k] < 0 && d->lbz[k] > -p->inf) {
502  if ((test = (d->lbz[k] - d->z[k]) / d->dz[k]) < *alpha) {
503  *alpha = test;
504  blocking_k = k;
505  flag = IPQP_PRIMAL | IPQP_LOWER;
506  }
507  }
508  if (d->dz[k] > 0 && d->ubz[k] < p->inf) {
509  if ((test = (d->ubz[k] - d->z[k]) / d->dz[k]) < *alpha) {
510  *alpha = test;
511  blocking_k = k;
512  flag = IPQP_PRIMAL | IPQP_UPPER;
513  }
514  }
515  }
516  // Dual step
517  for (k=0; k<p->nz; ++k) {
518  if (d->dlam_lbz[k] < 0.) {
519  if ((test = -d->lam_lbz[k] / d->dlam_lbz[k]) < *alpha) {
520  *alpha = test;
521  blocking_k = k;
522  flag = IPQP_DUAL | IPQP_LOWER;
523  }
524  }
525  if (d->dlam_ubz[k] < 0.) {
526  if ((test = -d->lam_ubz[k] / d->dlam_ubz[k]) < *alpha) {
527  *alpha = test;
528  blocking_k = k;
529  flag = IPQP_DUAL | IPQP_UPPER;
530  }
531  }
532  }
533  // Return information about blocking constraints
534  if (ind) *ind = blocking_k;
535  return flag;
536 }
537 
538 // SYMBOL "ipqp_predictor"
539 template<typename T1>
540 void casadi_ipqp_predictor(casadi_ipqp_data<T1>* d) {
541  // Local variables
542  casadi_int k;
543  T1 t, alpha, sigma;
544  const casadi_ipqp_prob<T1>* p = d->prob;
545  // Scale results
546  for (k=0; k<p->nz; ++k) d->dz[k] *= d->S[k];
547  // Calculate step in z(g), lam(g)
548  for (k=p->nx; k<p->nz; ++k) {
549  if (d->S[k] == 0.) {
550  // Eliminate
551  d->dlam[k] = d->dz[k] = 0;
552  } else {
553  t = d->D[k] / (d->S[k] * d->S[k]) * (d->dz[k] - d->dlam[k]);
554  d->dlam[k] = d->dz[k];
555  d->dz[k] = t;
556  }
557  }
558  // Finish calculation in dlam_lbz, dlam_ubz
559  for (k=0; k<p->nz; ++k) {
560  d->dlam_lbz[k] -= d->lam_lbz[k] * d->dz[k];
561  d->dlam_lbz[k] *= d->dinv_lbz[k];
562  }
563  for (k=0; k<p->nz; ++k) {
564  d->dlam_ubz[k] += d->lam_ubz[k] * d->dz[k];
565  d->dlam_ubz[k] *= d->dinv_ubz[k];
566  }
567  // Finish calculation of dlam(x)
568  for (k=0; k<p->nx; ++k) d->dlam[k] += d->dlam_ubz[k] - d->dlam_lbz[k];
569  // Maximum primal and dual step
570  (void)casadi_ipqp_maxstep(d, &alpha, 0);
571  // Calculate sigma
572  sigma = casadi_ipqp_sigma(d, alpha);
573  // Prepare corrector step
574  casadi_ipqp_corrector_prepare(d, -sigma * d->mu);
575  // Solve to get step
576  d->linsys = d->rz;
577 }
578 
579 // SYMBOL "ipqp_step"
580 template<typename T1>
581 void casadi_ipqp_step(casadi_ipqp_data<T1>* d, T1 alpha_pr, T1 alpha_du) {
582  // Local variables
583  casadi_int k;
584  const casadi_ipqp_prob<T1>* p = d->prob;
585  // Primal step
586  for (k=0; k<p->nz; ++k) d->z[k] += alpha_pr * d->dz[k];
587  // Dual step
588  for (k=0; k<p->nz; ++k) d->lam[k] += alpha_du * d->dlam[k];
589  for (k=0; k<p->nz; ++k) d->lam_lbz[k] += alpha_du * d->dlam_lbz[k];
590  for (k=0; k<p->nz; ++k) d->lam_ubz[k] += alpha_du * d->dlam_ubz[k];
591 }
592 
593 // SYMBOL "ipqp_mu"
594 template<typename T1>
595 T1 casadi_ipqp_mu(casadi_ipqp_data<T1>* d, T1 alpha) {
596  // Local variables
597  T1 mu;
598  casadi_int k;
599  const casadi_ipqp_prob<T1>* p = d->prob;
600  // Quick return if no inequalities
601  if (d->n_con == 0) return 0;
602  // Calculate projected mu (and save to sigma variable)
603  mu = 0;
604  for (k = 0; k < p->nz; ++k) {
605  // Lower bound
606  if (d->lbz[k] > -p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
607  mu += (d->lam_lbz[k] + alpha * d->dlam_lbz[k])
608  * (d->z[k] - d->lbz[k] + alpha * d->dz[k]);
609  }
610  // Upper bound
611  if (d->ubz[k] < p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
612  mu += (d->lam_ubz[k] + alpha * d->dlam_ubz[k])
613  * (d->ubz[k] - d->z[k] - alpha * d->dz[k]);
614  }
615  }
616  // Divide mu by total number of finite constraints
617  mu /= d->n_con;
618  return mu;
619 }
620 
621 // SYMBOL "ipqp_sigma"
622 template<typename T1>
623 T1 casadi_ipqp_sigma(casadi_ipqp_data<T1>* d, T1 alpha) {
624  // Local variables
625  T1 sigma;
626  // Quick return if no inequalities
627  if (d->n_con == 0) return 0;
628  // Calculate projected mu (and save to sigma variable)
629  sigma = casadi_ipqp_mu(d, alpha);
630  // Finish calculation of sigma := (mu_aff / mu)^3
631  sigma /= d->mu;
632  sigma *= sigma * sigma;
633  return sigma;
634 }
635 
636 // SYMBOL "ipqp_corrector_prepare"
637 template<typename T1>
638 void casadi_ipqp_corrector_prepare(casadi_ipqp_data<T1>* d, T1 shift) {
639  // Local variables
640  casadi_int k;
641  const casadi_ipqp_prob<T1>* p = d->prob;
642  // Modified residual in lam_lbz, lam_ubz
643  for (k=0; k<p->nz; ++k) d->rlam_lbz[k] = d->dlam_lbz[k] * d->dz[k] + shift;
644  for (k=0; k<p->nz; ++k) d->rlam_ubz[k] = -d->dlam_ubz[k] * d->dz[k] + shift;
645  // Difference in tilde(r)_x, tilde(r)_lamg
646  for (k=0; k<p->nz; ++k)
647  d->rz[k] = d->dinv_lbz[k] * d->rlam_lbz[k]
648  - d->dinv_ubz[k] * d->rlam_ubz[k];
649  // Difference in tilde(r)_g
650  for (k=p->nx; k<p->nz; ++k) {
651  if (d->S[k] == 0.) {
652  // Eliminate
653  d->rlam[k] = d->rz[k] = 0;
654  } else {
655  d->rlam[k] = d->rz[k];
656  d->rz[k] *= d->D[k] / (d->S[k] * d->S[k]);
657  }
658  }
659  // Scale and negate right-hand-side
660  for (k=0; k<p->nz; ++k) d->rz[k] *= -d->S[k];
661 }
662 
663 // SYMBOL "ipqp_corrector"
664 template<typename T1>
665 void casadi_ipqp_corrector(casadi_ipqp_data<T1>* d) {
666  // Local variables
667  T1 t, mu_test, primal_slack, primal_step, dual_slack, dual_step, max_tau;
668  casadi_int k;
669  int flag;
670  const casadi_ipqp_prob<T1>* p = d->prob;
671  // Scale results
672  for (k=0; k<p->nz; ++k) d->rz[k] *= d->S[k];
673  // Calculate step in z(g), lam(g)
674  for (k=p->nx; k<p->nz; ++k) {
675  if (d->S[k] == 0.) {
676  // Eliminate
677  d->rlam[k] = d->rz[k] = 0;
678  } else {
679  t = d->D[k] / (d->S[k] * d->S[k]) * (d->rz[k] - d->rlam[k]);
680  d->rlam[k] = d->rz[k];
681  d->rz[k] = t;
682  }
683  }
684  // Update step in dz, dlam
685  for (k=0; k<p->nz; ++k) d->dz[k] += d->rz[k];
686  for (k=p->nx; k<p->nz; ++k) d->dlam[k] += d->rlam[k];
687  // Update step in lam_lbz
688  for (k=0; k<p->nz; ++k) {
689  t = d->dinv_lbz[k] * (-d->rlam_lbz[k] - d->lam_lbz[k] * d->rz[k]);
690  d->dlam_lbz[k] += t;
691  if (k<p->nx) d->dlam[k] -= t;
692  }
693  // Update step in lam_ubz
694  for (k=0; k<p->nz; ++k) {
695  t = d->dinv_ubz[k] * (-d->rlam_ubz[k] + d->lam_ubz[k] * d->rz[k]);
696  d->dlam_ubz[k] += t;
697  if (k<p->nx) d->dlam[k] += t;
698  }
699  // Find the largest step size, keeping track of blocking constraints
700  flag = casadi_ipqp_maxstep(d, &max_tau, &k);
701  // Handle blocking constraints using Mehrotra's heuristic
702  if (flag == IPQP_NONE) {
703  // No blocking constraints
704  d->tau = 1;
705  } else {
706  // Calculate mu for maximum step
707  mu_test = casadi_ipqp_mu(d, max_tau);
708  // Get distance to constraints for blocking variable
709  if (flag & IPQP_UPPER) {
710  primal_slack = d->ubz[k] - d->z[k];
711  primal_step = -d->dz[k];
712  dual_slack = d->lam_ubz[k];
713  dual_step = d->dlam_ubz[k];
714  } else {
715  primal_slack = d->z[k] - d->lbz[k];
716  primal_step = d->dz[k];
717  dual_slack = d->lam_lbz[k];
718  dual_step = d->dlam_lbz[k];
719  }
720  // Mehrotra's heuristic as in in OOQP per communication with S. Wright
721  if (flag & IPQP_PRIMAL) {
722  d->tau = (0.01 * mu_test / (dual_slack + max_tau * dual_step)
723  - primal_slack) / primal_step;
724  } else {
725  d->tau = (0.01 * mu_test / (primal_slack + max_tau * primal_step)
726  - dual_slack) / dual_step;
727  }
728  d->tau = fmax(d->tau, 0.99 * max_tau);
729  }
730  // Take step
731  casadi_ipqp_step(d, d->tau, d->tau);
732  // Clear residual
733  casadi_clear(d->rz, p->nz);
734 }
735 
736 // SYMBOL "ipqp"
737 template<typename T1>
738 int casadi_ipqp(casadi_ipqp_data<T1>* d) {
739  switch (d->next) {
740  case IPQP_RESET:
741  casadi_ipqp_reset(d);
742  d->task = IPQP_MV;
743  d->next = IPQP_RESIDUAL;
744  return 1;
745  case IPQP_RESIDUAL:
746  // Calculate residual
747  if (d->status == IPQP_MV_ERROR) break;
748  casadi_ipqp_residual(d);
749  d->task = IPQP_PROGRESS;
750  d->next = IPQP_NEWITER;
751  return 1;
752  case IPQP_NEWITER:
753  // New iteration
754  if (d->status == IPQP_PROGRESS_ERROR) break;
755  if (casadi_ipqp_newiter(d)) break;
756  d->task = IPQP_FACTOR;
757  d->next = IPQP_PREPARE;
758  return 1;
759  case IPQP_PREPARE:
760  // Prepare predictor step
761  if (d->status == IPQP_FACTOR_ERROR) break;
762  casadi_ipqp_predictor_prepare(d);
763  d->task = IPQP_SOLVE;
764  d->next = IPQP_PREDICTOR;
765  return 1;
766  case IPQP_PREDICTOR:
767  // Complete predictor step
768  if (d->status == IPQP_SOLVE_ERROR) break;
769  casadi_ipqp_predictor(d);
770  d->task = IPQP_SOLVE;
771  d->next = IPQP_CORRECTOR;
772  return 1;
773  case IPQP_CORRECTOR:
774  // Complete predictor step
775  if (d->status == IPQP_SOLVE_ERROR) break;
776  casadi_ipqp_corrector(d);
777  d->task = IPQP_MV;
778  d->next = IPQP_RESIDUAL;
779  return 1;
780  default:
781  break;
782  }
783  // Done iterating
784  d->next = IPQP_RESET;
785  return 0;
786 }
787 
788 // SYMBOL "ipqp_return_status"
789 inline
790 const char* casadi_ipqp_return_status(casadi_ipqp_flag_t status) {
791  switch (status) {
792  case IPQP_SUCCESS: return "success";
793  case IPQP_MAX_ITER: return "Maximum number of iterations reached";
794  case IPQP_NO_SEARCH_DIR: return "Failed to calculate search direction";
795  case IPQP_MV_ERROR: return "Matrix-vector evaluation error";
796  case IPQP_FACTOR_ERROR: return "Linear solver factorization error";
797  case IPQP_SOLVE_ERROR: return "Linear solver solution error";
798  case IPQP_PROGRESS_ERROR: return "Printing error";
799  }
800  return nullptr;
801 }
802 
803 // SYMBOL "ipqp_solution"
804 template<typename T1>
805 void casadi_ipqp_solution(casadi_ipqp_data<T1>* d, T1* x, T1* lam_x, T1* lam_a) {
806  // Local variables
807  const casadi_ipqp_prob<T1>* p = d->prob;
808  // Copy solution
809  casadi_copy(d->z, p->nx, x);
810  casadi_copy(d->lam, p->nx, lam_x);
811  casadi_copy(d->lam + p->nx, p->na, lam_a);
812 }
813 
814 // SYMBOL "ipqp_print_header"
815 template<typename T1>
816 int casadi_ipqp_print_header(casadi_ipqp_data<T1>* d, char* buf, size_t buf_sz) {
817 #ifdef CASADI_SNPRINTF
818  int flag;
819  // Print to string
820  flag = CASADI_SNPRINTF(buf, buf_sz, "%5s %9s %9s %5s %9s %5s "
821  "%9s %5s %9s %4s",
822  "Iter", "mu", "|pr|", "con", "|du|", "var", "|co|", "con",
823  "last_tau", "Note");
824  // Check if error
825  if (flag < 0) {
826  d->status = IPQP_PROGRESS_ERROR;
827  return 1;
828  }
829 #else
830  if (buf_sz) buf[0] = '\0';
831 #endif
832  // Successful return
833  return 0;
834 }
835 
836 // SYMBOL "ipqp_print_iteration"
837 template<typename T1>
838 int casadi_ipqp_print_iteration(casadi_ipqp_data<T1>* d, char* buf, int buf_sz) {
839 #ifdef CASADI_SNPRINTF
840  int flag;
841  // Print iteration data without note to string
842  flag = CASADI_SNPRINTF(buf, buf_sz,
843  "%5d %9.2g %9.2g %5d %9.2g %5d %9.2g %5d %9.2g ",
844  static_cast<int>(d->iter), d->mu,
845  d->pr, static_cast<int>(d->ipr),
846  d->du, static_cast<int>(d->idu),
847  d->co, static_cast<int>(d->ico),
848  d->tau);
849  // Check if error
850  if (flag < 0) {
851  d->status = IPQP_PROGRESS_ERROR;
852  return 1;
853  }
854  // Rest of buffer reserved for iteration note
855  buf += flag;
856  buf_sz -= flag;
857  // Print iteration note, if any
858  if (d->msg) {
859  flag = CASADI_SNPRINTF(buf, buf_sz, "%s", d->msg);
860  // Check if error
861  if (flag < 0) {
862  d->status = IPQP_PROGRESS_ERROR;
863  return 1;
864  }
865  }
866 #else
867  if (buf_sz) buf[0] = '\0';
868 #endif
869  // Successful return
870  return 0;
871 }
casadi_int n_con
const casadi_ipqp_prob< T1 > * prob
casadi_ipqp_flag_t status
casadi_ipqp_next_t next
const char * msg
casadi_ipqp_task_t task
casadi_int max_iter
Definition: casadi_ipqp.hpp:39