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  d->status = IPQP_SUCCESS;
194 }
195 
196 // SYMBOL "ipqp_bounds"
197 template<typename T1>
198 void casadi_ipqp_bounds(casadi_ipqp_data<T1>* d, const T1* g,
199  const T1* lbx, const T1* ubx, const T1* lba, const T1* uba) {
200  // Local variables
201  const casadi_ipqp_prob<T1>* p = d->prob;
202  // Pass pointer to linear term in objective
203  d->g = g;
204  // Pass bounds on z
205  casadi_copy(lbx, p->nx, d->lbz);
206  casadi_copy(lba, p->na, d->lbz + p->nx);
207  casadi_copy(ubx, p->nx, d->ubz);
208  casadi_copy(uba, p->na, d->ubz + p->nx);
209 }
210 
211 // SYMBOL "ipqp_guess"
212 template<typename T1>
213 void casadi_ipqp_guess(casadi_ipqp_data<T1>* d,
214  const T1* x0, const T1* lam_x0, const T1* lam_a0) {
215  // Local variables
216  const casadi_ipqp_prob<T1>* p = d->prob;
217  // Pass initial guess
218  casadi_copy(x0, p->nx, d->z);
219  casadi_fill(d->z + p->nx, p->na, 0.);
220  casadi_copy(lam_x0, p->nx, d->lam);
221  casadi_copy(lam_a0, p->na, d->lam + p->nx);
222  casadi_fill(d->lam_lbz, p->nz, 0.);
223  casadi_fill(d->lam_ubz, p->nz, 0.);
224 }
225 
226 // SYMBOL "ipqp_reset"
227 template<typename T1>
228 void casadi_ipqp_reset(casadi_ipqp_data<T1>* d) {
229  // Local variables
230  casadi_int k;
231  T1 margin, mid;
232  const casadi_ipqp_prob<T1>* p = d->prob;
233  // Required margin to constraints
234  margin = .1;
235  // Reset constraint count
236  d->n_con = 0;
237  // Initialize constraints to zero
238  for (k = p->nx; k < p->nz; ++k) d->z[k] = 0;
239  // Find interior point
240  for (k = 0; k < p->nz; ++k) {
241  if (d->lbz[k] > -p->inf) {
242  if (d->ubz[k] < p->inf) {
243  // Both upper and lower bounds
244  mid = .5 * (d->lbz[k] + d->ubz[k]);
245  // Ensure margin to boundary, without crossing midpoint
246  if (d->z[k] < mid) {
247  d->z[k] = fmin(fmax(d->z[k], d->lbz[k] + margin), mid);
248  } else if (d->z[k] > mid) {
249  d->z[k] = fmax(fmin(d->z[k], d->ubz[k] - margin), mid);
250  }
251  if (d->ubz[k] > d->lbz[k] + p->dmin) {
252  d->lam_lbz[k] = 1;
253  d->lam_ubz[k] = 1;
254  d->n_con += 2;
255  }
256  } else {
257  // Only lower bound
258  d->z[k] = fmax(d->z[k], d->lbz[k] + margin);
259  d->lam_lbz[k] = 1;
260  d->n_con++;
261  }
262  } else {
263  if (d->ubz[k] < p->inf) {
264  // Only upper bound
265  d->z[k] = fmin(d->z[k], d->ubz[k] - margin);
266  d->lam_ubz[k] = 1;
267  d->n_con++;
268  }
269  }
270  }
271  // Clear residual
272  casadi_clear(d->rz, p->nz);
273  // Reset iteration counter
274  d->iter = 0;
275  // Reset iteration variables
276  d->msg = 0;
277  d->tau = -1;
278 }
279 
280 // SYMBOL "ipqp_diag"
281 template<typename T1>
282 void casadi_ipqp_diag(casadi_ipqp_data<T1>* d) {
283  // Local variables
284  casadi_int k;
285  const casadi_ipqp_prob<T1>* p = d->prob;
286  // Diagonal entries corresponding to variables
287  for (k = 0; k < p->nx; ++k) {
288  if (d->ubz[k] <= d->lbz[k] + p->dmin) {
289  // Fixed variable (eliminate)
290  d->D[k] = -1;
291  } else {
292  d->D[k] = d->lam_lbz[k] * d->dinv_lbz[k]
293  + d->lam_ubz[k] * d->dinv_ubz[k];
294  }
295  }
296  // Diagonal entries corresponding to constraints
297  for (; k < p->nz; ++k) {
298  if (d->lbz[k] <= -p->inf && d->ubz[k] >= p->inf) {
299  // Unconstrained (eliminate)
300  d->D[k] = -1;
301  } else if (d->ubz[k] <= d->lbz[k] + p->dmin) {
302  // Equality constrained
303  d->D[k] = 0;
304  } else {
305  d->D[k] = 1. / (d->lam_lbz[k] * d->dinv_lbz[k]
306  + d->lam_ubz[k] * d->dinv_ubz[k]);
307  }
308  }
309  // Scale diagonal entries
310  for (k = 0; k < p->nz; ++k) {
311  if (d->D[k] < 0) {
312  // Eliminate
313  d->S[k] = 0;
314  d->D[k] = 1;
315  } else {
316  // Scale
317  d->S[k] = fmin(1., std::sqrt(1. / d->D[k]));
318  d->D[k] = fmin(1., d->D[k]);
319  }
320  }
321 }
322 
323 // SYMBOL "ipqp_newiter"
324 template<typename T1>
325 int casadi_ipqp_newiter(casadi_ipqp_data<T1>* d) {
326  // Local variables
327  const casadi_ipqp_prob<T1>* p = d->prob;
328  // Converged?
329  if (d->pr < p->pr_tol && d->du < p->du_tol && d->co < p->co_tol
330  && d->mu < p->mu_tol) {
331  d->status = IPQP_SUCCESS;
332  return 1;
333  }
334  // Max number of iterations reached
335  if (d->iter >= p->max_iter) {
336  d->status = IPQP_MAX_ITER;
337  return 1;
338  }
339  // Start new iteration
340  d->iter++;
341  // Calculate diagonal entries and scaling factors
342  casadi_ipqp_diag(d);
343  // Success
344  return 0;
345 }
346 
347 // SYMBOL "ipqp_residual"
348 template<typename T1>
349 void casadi_ipqp_residual(casadi_ipqp_data<T1>* d) {
350  // Local variables
351  casadi_int k;
352  T1 bdiff, viol;
353  const casadi_ipqp_prob<T1>* p = d->prob;
354  // Gradient of the Lagrangian
355  casadi_axpy(p->nx, 1., d->g, d->rz);
356  for (k = 0; k < p->nx; ++k) {
357  if (d->ubz[k] <= d->lbz[k] + p->dmin) {
358  // Fixed variable: Solve to get multiplier explicitly
359  d->lam[k] = -d->rz[k];
360  d->rz[k] = 0;
361  } else {
362  // Residual
363  d->rz[k] += d->lam[k];
364  }
365  }
366  // Constraint violation (only possible for linear constraints)
367  d->ipr = -1;
368  d->pr = 0;
369  for (k = p->nx; k < p->nz; ++k) {
370  if (d->lbz[k] <= -p->inf && d->ubz[k] >= p->inf) {
371  // Unconstrained: Solve to get g explicitly
372  d->z[k] = d->rz[k];
373  d->lam[k] = 0;
374  } else {
375  // Check constraint violation
376  if (d->rz[k] + d->pr < d->lbz[k]) {
377  d->pr = d->lbz[k] - d->rz[k];
378  d->ipr = k;
379  } else if (d->rz[k] - d->pr > d->ubz[k]) {
380  d->pr = d->rz[k] - d->ubz[k];
381  d->ipr = k;
382  }
383  }
384  }
385  // Dual infeasibility
386  d->idu = -1;
387  d->du = 0;
388  for (k = 0; k < p->nx; ++k) {
389  if (fabs(d->rz[k]) > d->du) {
390  d->du = fabs(d->rz[k]);
391  d->idu = k;
392  }
393  }
394  // Linear constraint
395  casadi_axpy(p->na, -1., d->z + p->nx, d->rz + p->nx);
396  // Multiplier consistency
397  for (k = 0; k < p->nz; ++k) {
398  if (d->ubz[k] <= d->lbz[k] + p->dmin) {
399  // Fixed variable: Solve to get lam_lbz, lam_ubz
400  d->lam_ubz[k] = fmax(d->lam[k], 0.);
401  d->lam_lbz[k] = fmax(-d->lam[k], 0.);
402  d->rlam[k] = 0;
403  } else {
404  // Residual
405  d->rlam[k] = d->lam_ubz[k] - d->lam_lbz[k] - d->lam[k];
406  }
407  }
408  // Complementarity conditions, mu
409  d->mu = 0;
410  d->ico = -1;
411  d->co = 0;
412  for (k = 0; k < p->nz; ++k) {
413  // Lower bound
414  if (d->lbz[k] > -p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
415  // Inequality constraint
416  bdiff = d->z[k] - d->lbz[k];
417  d->mu += d->rlam_lbz[k] = d->lam_lbz[k] * bdiff;
418  d->dinv_lbz[k] = 1. / bdiff;
419  // Constraint violation
420  viol = bdiff * fmax(-d->lam[k], 0.);
421  if (viol > d->co) {
422  d->co = viol;
423  d->ico = k;
424  }
425  } else {
426  // No bound or equality constraint
427  d->rlam_lbz[k] = 0;
428  d->dinv_lbz[k] = 0;
429  }
430  // Upper bound
431  if (d->ubz[k] < p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
432  // Inequality constraint
433  bdiff = d->ubz[k] - d->z[k];
434  d->mu += d->rlam_ubz[k] = d->lam_ubz[k] * bdiff;
435  d->dinv_ubz[k] = 1. / bdiff;
436  // Constraint violation
437  viol = bdiff * fmax(d->lam[k], 0.);
438  if (viol > d->co) {
439  d->co = viol;
440  d->ico = k;
441  }
442  } else {
443  // No bound or equality constraint
444  d->rlam_ubz[k] = 0;
445  d->dinv_ubz[k] = 0;
446  }
447  }
448  // Divide mu by total number of finite constraints
449  if (d->n_con > 0) d->mu /= d->n_con;
450 }
451 
452 // SYMBOL "ipqp_predictor_prepare"
453 template<typename T1>
454 void casadi_ipqp_predictor_prepare(casadi_ipqp_data<T1>* d) {
455  // Local variables
456  casadi_int k;
457  const casadi_ipqp_prob<T1>* p = d->prob;
458  // Store r_lam - dinv_lbz * rlam_lbz + dinv_ubz * rlam_ubz in dz
459  casadi_copy(d->rlam, p->nz, d->dz);
460  for (k=0; k<p->nz; ++k) d->dz[k] += d->dinv_lbz[k] * d->rlam_lbz[k];
461  for (k=0; k<p->nz; ++k) d->dz[k] -= d->dinv_ubz[k] * d->rlam_ubz[k];
462  // Finish calculating x-component of right-hand-side and store in dz[:nx]
463  for (k=0; k<p->nx; ++k) d->dz[k] += d->rz[k];
464  // Copy tilde{r}_lam to dlam[nx:] (needed to calculate step in g later)
465  for (k=p->nx; k<p->nz; ++k) d->dlam[k] = d->dz[k];
466  // Finish calculating g-component of right-hand-side and store in dz[nx:]
467  for (k=p->nx; k<p->nz; ++k) {
468  if (d->S[k] == 0.) {
469  // Eliminate
470  d->dz[k] = 0;
471  } else {
472  d->dz[k] *= d->D[k] / (d->S[k] * d->S[k]);
473  d->dz[k] += d->rz[k];
474  }
475  }
476  // Scale and negate right-hand-side
477  for (k=0; k<p->nz; ++k) d->dz[k] *= -d->S[k];
478  // dlam_lbz := -rlam_lbz, dlam_ubz := -rlam_ubz
479  for (k=0; k<p->nz; ++k) d->dlam_lbz[k] = -d->rlam_lbz[k];
480  for (k=0; k<p->nz; ++k) d->dlam_ubz[k] = -d->rlam_ubz[k];
481  // dlam_x := rlam_x
482  for (k=0; k<p->nx; ++k) d->dlam[k] = d->rlam[k];
483  // Solve to get step
484  d->linsys = d->dz;
485 }
486 
487 // SYMBOL "ipqp_maxstep"
488 template<typename T1>
489 int casadi_ipqp_maxstep(casadi_ipqp_data<T1>* d, T1* alpha, casadi_int* ind) {
490  // Local variables
491  T1 test;
492  casadi_int k, blocking_k;
493  int flag;
494  const casadi_ipqp_prob<T1>* p = d->prob;
495  // Reset variables
496  blocking_k = -1;
497  flag = IPQP_NONE;
498  // Maximum step size is 1
499  *alpha = 1.;
500  // Primal step
501  for (k=0; k<p->nz; ++k) {
502  if (d->dz[k] < 0 && d->lbz[k] > -p->inf) {
503  if ((test = (d->lbz[k] - d->z[k]) / d->dz[k]) < *alpha) {
504  *alpha = test;
505  blocking_k = k;
506  flag = IPQP_PRIMAL | IPQP_LOWER;
507  }
508  }
509  if (d->dz[k] > 0 && d->ubz[k] < p->inf) {
510  if ((test = (d->ubz[k] - d->z[k]) / d->dz[k]) < *alpha) {
511  *alpha = test;
512  blocking_k = k;
513  flag = IPQP_PRIMAL | IPQP_UPPER;
514  }
515  }
516  }
517  // Dual step
518  for (k=0; k<p->nz; ++k) {
519  if (d->dlam_lbz[k] < 0.) {
520  if ((test = -d->lam_lbz[k] / d->dlam_lbz[k]) < *alpha) {
521  *alpha = test;
522  blocking_k = k;
523  flag = IPQP_DUAL | IPQP_LOWER;
524  }
525  }
526  if (d->dlam_ubz[k] < 0.) {
527  if ((test = -d->lam_ubz[k] / d->dlam_ubz[k]) < *alpha) {
528  *alpha = test;
529  blocking_k = k;
530  flag = IPQP_DUAL | IPQP_UPPER;
531  }
532  }
533  }
534  // Return information about blocking constraints
535  if (ind) *ind = blocking_k;
536  return flag;
537 }
538 
539 // SYMBOL "ipqp_predictor"
540 template<typename T1>
541 void casadi_ipqp_predictor(casadi_ipqp_data<T1>* d) {
542  // Local variables
543  casadi_int k;
544  T1 t, alpha, sigma;
545  const casadi_ipqp_prob<T1>* p = d->prob;
546  // Scale results
547  for (k=0; k<p->nz; ++k) d->dz[k] *= d->S[k];
548  // Calculate step in z(g), lam(g)
549  for (k=p->nx; k<p->nz; ++k) {
550  if (d->S[k] == 0.) {
551  // Eliminate
552  d->dlam[k] = d->dz[k] = 0;
553  } else {
554  t = d->D[k] / (d->S[k] * d->S[k]) * (d->dz[k] - d->dlam[k]);
555  d->dlam[k] = d->dz[k];
556  d->dz[k] = t;
557  }
558  }
559  // Finish calculation in dlam_lbz, dlam_ubz
560  for (k=0; k<p->nz; ++k) {
561  d->dlam_lbz[k] -= d->lam_lbz[k] * d->dz[k];
562  d->dlam_lbz[k] *= d->dinv_lbz[k];
563  }
564  for (k=0; k<p->nz; ++k) {
565  d->dlam_ubz[k] += d->lam_ubz[k] * d->dz[k];
566  d->dlam_ubz[k] *= d->dinv_ubz[k];
567  }
568  // Finish calculation of dlam(x)
569  for (k=0; k<p->nx; ++k) d->dlam[k] += d->dlam_ubz[k] - d->dlam_lbz[k];
570  // Maximum primal and dual step
571  (void)casadi_ipqp_maxstep(d, &alpha, 0);
572  // Calculate sigma
573  sigma = casadi_ipqp_sigma(d, alpha);
574  // Prepare corrector step
575  casadi_ipqp_corrector_prepare(d, -sigma * d->mu);
576  // Solve to get step
577  d->linsys = d->rz;
578 }
579 
580 // SYMBOL "ipqp_step"
581 template<typename T1>
582 void casadi_ipqp_step(casadi_ipqp_data<T1>* d, T1 alpha_pr, T1 alpha_du) {
583  // Local variables
584  casadi_int k;
585  const casadi_ipqp_prob<T1>* p = d->prob;
586  // Primal step
587  for (k=0; k<p->nz; ++k) d->z[k] += alpha_pr * d->dz[k];
588  // Dual step
589  for (k=0; k<p->nz; ++k) d->lam[k] += alpha_du * d->dlam[k];
590  for (k=0; k<p->nz; ++k) d->lam_lbz[k] += alpha_du * d->dlam_lbz[k];
591  for (k=0; k<p->nz; ++k) d->lam_ubz[k] += alpha_du * d->dlam_ubz[k];
592 }
593 
594 // SYMBOL "ipqp_mu"
595 template<typename T1>
596 T1 casadi_ipqp_mu(casadi_ipqp_data<T1>* d, T1 alpha) {
597  // Local variables
598  T1 mu;
599  casadi_int k;
600  const casadi_ipqp_prob<T1>* p = d->prob;
601  // Quick return if no inequalities
602  if (d->n_con == 0) return 0;
603  // Calculate projected mu (and save to sigma variable)
604  mu = 0;
605  for (k = 0; k < p->nz; ++k) {
606  // Lower bound
607  if (d->lbz[k] > -p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
608  mu += (d->lam_lbz[k] + alpha * d->dlam_lbz[k])
609  * (d->z[k] - d->lbz[k] + alpha * d->dz[k]);
610  }
611  // Upper bound
612  if (d->ubz[k] < p->inf && d->ubz[k] > d->lbz[k] + p->dmin) {
613  mu += (d->lam_ubz[k] + alpha * d->dlam_ubz[k])
614  * (d->ubz[k] - d->z[k] - alpha * d->dz[k]);
615  }
616  }
617  // Divide mu by total number of finite constraints
618  mu /= d->n_con;
619  return mu;
620 }
621 
622 // SYMBOL "ipqp_sigma"
623 template<typename T1>
624 T1 casadi_ipqp_sigma(casadi_ipqp_data<T1>* d, T1 alpha) {
625  // Local variables
626  T1 sigma;
627  // Quick return if no inequalities
628  if (d->n_con == 0) return 0;
629  // Calculate projected mu (and save to sigma variable)
630  sigma = casadi_ipqp_mu(d, alpha);
631  // Finish calculation of sigma := (mu_aff / mu)^3
632  sigma /= d->mu;
633  sigma *= sigma * sigma;
634  return sigma;
635 }
636 
637 // SYMBOL "ipqp_corrector_prepare"
638 template<typename T1>
639 void casadi_ipqp_corrector_prepare(casadi_ipqp_data<T1>* d, T1 shift) {
640  // Local variables
641  casadi_int k;
642  const casadi_ipqp_prob<T1>* p = d->prob;
643  // Modified residual in lam_lbz, lam_ubz
644  for (k=0; k<p->nz; ++k) d->rlam_lbz[k] = d->dlam_lbz[k] * d->dz[k] + shift;
645  for (k=0; k<p->nz; ++k) d->rlam_ubz[k] = -d->dlam_ubz[k] * d->dz[k] + shift;
646  // Difference in tilde(r)_x, tilde(r)_lamg
647  for (k=0; k<p->nz; ++k)
648  d->rz[k] = d->dinv_lbz[k] * d->rlam_lbz[k]
649  - d->dinv_ubz[k] * d->rlam_ubz[k];
650  // Difference in tilde(r)_g
651  for (k=p->nx; k<p->nz; ++k) {
652  if (d->S[k] == 0.) {
653  // Eliminate
654  d->rlam[k] = d->rz[k] = 0;
655  } else {
656  d->rlam[k] = d->rz[k];
657  d->rz[k] *= d->D[k] / (d->S[k] * d->S[k]);
658  }
659  }
660  // Scale and negate right-hand-side
661  for (k=0; k<p->nz; ++k) d->rz[k] *= -d->S[k];
662 }
663 
664 // SYMBOL "ipqp_corrector"
665 template<typename T1>
666 void casadi_ipqp_corrector(casadi_ipqp_data<T1>* d) {
667  // Local variables
668  T1 t, mu_test, primal_slack, primal_step, dual_slack, dual_step, max_tau;
669  casadi_int k;
670  int flag;
671  const casadi_ipqp_prob<T1>* p = d->prob;
672  // Scale results
673  for (k=0; k<p->nz; ++k) d->rz[k] *= d->S[k];
674  // Calculate step in z(g), lam(g)
675  for (k=p->nx; k<p->nz; ++k) {
676  if (d->S[k] == 0.) {
677  // Eliminate
678  d->rlam[k] = d->rz[k] = 0;
679  } else {
680  t = d->D[k] / (d->S[k] * d->S[k]) * (d->rz[k] - d->rlam[k]);
681  d->rlam[k] = d->rz[k];
682  d->rz[k] = t;
683  }
684  }
685  // Update step in dz, dlam
686  for (k=0; k<p->nz; ++k) d->dz[k] += d->rz[k];
687  for (k=p->nx; k<p->nz; ++k) d->dlam[k] += d->rlam[k];
688  // Update step in lam_lbz
689  for (k=0; k<p->nz; ++k) {
690  t = d->dinv_lbz[k] * (-d->rlam_lbz[k] - d->lam_lbz[k] * d->rz[k]);
691  d->dlam_lbz[k] += t;
692  if (k<p->nx) d->dlam[k] -= t;
693  }
694  // Update step in lam_ubz
695  for (k=0; k<p->nz; ++k) {
696  t = d->dinv_ubz[k] * (-d->rlam_ubz[k] + d->lam_ubz[k] * d->rz[k]);
697  d->dlam_ubz[k] += t;
698  if (k<p->nx) d->dlam[k] += t;
699  }
700  // Find the largest step size, keeping track of blocking constraints
701  flag = casadi_ipqp_maxstep(d, &max_tau, &k);
702  // Handle blocking constraints using Mehrotra's heuristic
703  if (flag == IPQP_NONE) {
704  // No blocking constraints
705  d->tau = 1;
706  } else {
707  // Calculate mu for maximum step
708  mu_test = casadi_ipqp_mu(d, max_tau);
709  // Get distance to constraints for blocking variable
710  if (flag & IPQP_UPPER) {
711  primal_slack = d->ubz[k] - d->z[k];
712  primal_step = -d->dz[k];
713  dual_slack = d->lam_ubz[k];
714  dual_step = d->dlam_ubz[k];
715  } else {
716  primal_slack = d->z[k] - d->lbz[k];
717  primal_step = d->dz[k];
718  dual_slack = d->lam_lbz[k];
719  dual_step = d->dlam_lbz[k];
720  }
721  // Mehrotra's heuristic as in in OOQP per communication with S. Wright
722  if (flag & IPQP_PRIMAL) {
723  d->tau = (0.01 * mu_test / (dual_slack + max_tau * dual_step)
724  - primal_slack) / primal_step;
725  } else {
726  d->tau = (0.01 * mu_test / (primal_slack + max_tau * primal_step)
727  - dual_slack) / dual_step;
728  }
729  d->tau = fmax(d->tau, 0.99 * max_tau);
730  }
731  // Take step
732  casadi_ipqp_step(d, d->tau, d->tau);
733  // Clear residual
734  casadi_clear(d->rz, p->nz);
735 }
736 
737 // SYMBOL "ipqp"
738 template<typename T1>
739 int casadi_ipqp(casadi_ipqp_data<T1>* d) {
740  switch (d->next) {
741  case IPQP_RESET:
742  casadi_ipqp_reset(d);
743  d->task = IPQP_MV;
744  d->next = IPQP_RESIDUAL;
745  return 1;
746  case IPQP_RESIDUAL:
747  // Calculate residual
748  if (d->status == IPQP_MV_ERROR) break;
749  casadi_ipqp_residual(d);
750  d->task = IPQP_PROGRESS;
751  d->next = IPQP_NEWITER;
752  return 1;
753  case IPQP_NEWITER:
754  // New iteration
755  if (d->status == IPQP_PROGRESS_ERROR) break;
756  if (casadi_ipqp_newiter(d)) break;
757  d->task = IPQP_FACTOR;
758  d->next = IPQP_PREPARE;
759  return 1;
760  case IPQP_PREPARE:
761  // Prepare predictor step
762  if (d->status == IPQP_FACTOR_ERROR) break;
763  casadi_ipqp_predictor_prepare(d);
764  d->task = IPQP_SOLVE;
765  d->next = IPQP_PREDICTOR;
766  return 1;
767  case IPQP_PREDICTOR:
768  // Complete predictor step
769  if (d->status == IPQP_SOLVE_ERROR) break;
770  casadi_ipqp_predictor(d);
771  d->task = IPQP_SOLVE;
772  d->next = IPQP_CORRECTOR;
773  return 1;
774  case IPQP_CORRECTOR:
775  // Complete predictor step
776  if (d->status == IPQP_SOLVE_ERROR) break;
777  casadi_ipqp_corrector(d);
778  d->task = IPQP_MV;
779  d->next = IPQP_RESIDUAL;
780  return 1;
781  default:
782  break;
783  }
784  // Done iterating
785  d->next = IPQP_RESET;
786  return 0;
787 }
788 
789 // SYMBOL "ipqp_return_status"
790 inline
791 const char* casadi_ipqp_return_status(casadi_ipqp_flag_t status) {
792  switch (status) {
793  case IPQP_SUCCESS: return "success";
794  case IPQP_MAX_ITER: return "Maximum number of iterations reached";
795  case IPQP_NO_SEARCH_DIR: return "Failed to calculate search direction";
796  case IPQP_MV_ERROR: return "Matrix-vector evaluation error";
797  case IPQP_FACTOR_ERROR: return "Linear solver factorization error";
798  case IPQP_SOLVE_ERROR: return "Linear solver solution error";
799  case IPQP_PROGRESS_ERROR: return "Printing error";
800  }
801  return nullptr;
802 }
803 
804 // SYMBOL "ipqp_solution"
805 template<typename T1>
806 void casadi_ipqp_solution(casadi_ipqp_data<T1>* d, T1* x, T1* lam_x, T1* lam_a) {
807  // Local variables
808  const casadi_ipqp_prob<T1>* p = d->prob;
809  // Copy solution
810  casadi_copy(d->z, p->nx, x);
811  casadi_copy(d->lam, p->nx, lam_x);
812  casadi_copy(d->lam + p->nx, p->na, lam_a);
813 }
814 
815 // SYMBOL "ipqp_print_header"
816 template<typename T1>
817 int casadi_ipqp_print_header(casadi_ipqp_data<T1>* d, char* buf, size_t buf_sz) {
818 #ifdef CASADI_SNPRINTF
819  int flag;
820  // Print to string
821  flag = CASADI_SNPRINTF(buf, buf_sz, "%5s %9s %9s %5s %9s %5s "
822  "%9s %5s %9s %4s",
823  "Iter", "mu", "|pr|", "con", "|du|", "var", "|co|", "con",
824  "last_tau", "Note");
825  // Check if error
826  if (flag < 0) {
827  d->status = IPQP_PROGRESS_ERROR;
828  return 1;
829  }
830 #else
831  if (buf_sz) buf[0] = '\0';
832 #endif
833  // Successful return
834  return 0;
835 }
836 
837 // SYMBOL "ipqp_print_iteration"
838 template<typename T1>
839 int casadi_ipqp_print_iteration(casadi_ipqp_data<T1>* d, char* buf, int buf_sz) {
840 #ifdef CASADI_SNPRINTF
841  int flag;
842  // Print iteration data without note to string
843  flag = CASADI_SNPRINTF(buf, buf_sz,
844  "%5d %9.2g %9.2g %5d %9.2g %5d %9.2g %5d %9.2g ",
845  static_cast<int>(d->iter), d->mu,
846  d->pr, static_cast<int>(d->ipr),
847  d->du, static_cast<int>(d->idu),
848  d->co, static_cast<int>(d->ico),
849  d->tau);
850  // Check if error
851  if (flag < 0) {
852  d->status = IPQP_PROGRESS_ERROR;
853  return 1;
854  }
855  // Rest of buffer reserved for iteration note
856  buf += flag;
857  buf_sz -= flag;
858  // Print iteration note, if any
859  if (d->msg) {
860  flag = CASADI_SNPRINTF(buf, buf_sz, "%s", d->msg);
861  // Check if error
862  if (flag < 0) {
863  d->status = IPQP_PROGRESS_ERROR;
864  return 1;
865  }
866  }
867 #else
868  if (buf_sz) buf[0] = '\0';
869 #endif
870  // Successful return
871  return 0;
872 }
const long double alpha[8][8]
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