Pipal  1.2.0
Penalty Interior-Point ALgorithm
Loading...
Searching...
No Matches
Iterate.hxx
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The Pipal project is distributed under the MIT License. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * e-mail: davide.stocco@unitn.it e-mail: enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef INCLUDE_PIPAL_ITERATE_HXX
14#define INCLUDE_PIPAL_ITERATE_HXX
15
16namespace Pipal {
17
22 template <typename Real>
24 {
25 // Create alias for easier access
27 Input<Real> & i{this->m_input};
28 Iterate<Real> & z{this->m_iterate};
29
30 // Safe initialization of all members
31 z.f = 0;
32 z.fu = 0;
33 z.g.setZero(i.nV);
34 z.r1.setZero(i.nE);
35 z.r2.setZero(i.nE);
36 z.cE.setZero(i.nE);
37 z.JE.resize(i.nE, i.nV);
38 z.s1.setZero(i.nI);
39 z.s2.setZero(i.nI);
40 z.cI.setZero(i.nI);
41 z.JI.resize(i.nI, i.nV);
42 z.H.resize(i.nV, i.nV);
43 if (this->m_bfgs) {z.H.setIdentity();}
44 z.v = 0;
45 z.vu = 0;
46 z.phi = 0;
47 z.Annz = 0;
48 z.shift = 0;
49 z.b.resize(i.nA);
50 z.kkt.setZero(3);
51 z.kkt_.setConstant(p.opt_err_mem, std::numeric_limits<Real>::infinity());
52 z.fs = 1.0;
53 z.cEs.setOnes(i.nE);
54 z.cEu.setZero(i.nE);
55 z.cIs.setOnes(i.nI);
56 z.cIu.setZero(i.nI);
57 z.A.resize(i.nA, i.nA);
58 z.shift22 = 0;
59 z.cut_ = false;
60
61 // Initialize point
62 z.x = i.x0;
63 z.rho = p.rho_init;
64 z.mu = p.mu_init;
65 z.lE.setZero(i.nE);
66 z.lI.setConstant(i.nI, 0.5);
67 z.err = 0;
68 this->evalScalings();
69 this->evalFunctions();
70 this->evalGradients();
71 this->evalDependent();
72 z.v0 = 1.0;
73 this->evalInfeasibility(z);
74 z.v0 = z.v;
75 this->evalInfeasibility(z);
76 z.v_ = z.v;
77 if (!this->m_bfgs) {this->evalHessian();}
78 z.Hnnz = static_cast<Integer>(z.H.nonZeros());
79 z.JEnnz = static_cast<Integer>(z.JE.nonZeros());
80 z.JInnz = static_cast<Integer>(z.JI.nonZeros());
82 this->evalNewtonMatrix();
83 }
84
93 template <typename Real>
95 {
96 // Create alias for easier access
97 Parameter<Real> const & p{this->m_parameter};
98 Counter const & c{this->m_counter};
99 Input<Real> const & i{this->m_input};
100 Iterate<Real> const & z{this->m_iterate};
101
102 // Update termination based on optimality error of nonlinear optimization problem
103 if (z.kkt(1) <= p.opt_err_tol && z.v <= p.opt_err_tol) return 1;
104
105 // Update termination based on optimality error of feasibility problem
106 if (z.kkt(0) <= p.opt_err_tol && z.v > p.opt_err_tol) return 2;
107
108 // Update termination based on iteration count
109 if (c.k >= p.iter_max) return 3;
110
111 // Update termination based on invalid bounds
112 if (i.vi > 0) return 4;
113
114 // Update termination based on function evaluation error
115 if (z.err > 0) return 5;
116
117 return 0;
118 }
119
124 template <typename Real>
126 {
127 // Create alias for easier access
128 Input<Real> & i{this->m_input};
129 Iterate<Real> & z{this->m_iterate};
130
131 // Evaluate x in original space
132 Vector<Real> x_orig;
133 this->evalXOriginal(x_orig);
134
135 // Initialize/Reset evaluation flag
136 z.err = 0;
137
138 // Increment function evaluation counter
140
141 // Try AMPL functions evaluation
142 Vector<Real> c_orig;
143 try
144 {
145 // Evaluate AMPL functions
146 this->m_problem->objective(x_orig, z.f);
147 this->m_problem->constraints(x_orig, c_orig);
148 }
149 catch (...)
150 {
151 // Set evaluation flag, default values, and return
152 z.err = 1;
153 z.f = std::numeric_limits<Real>::quiet_NaN();
154 z.cE.setConstant(i.nE, std::numeric_limits<Real>::quiet_NaN());
155 z.cI.setConstant(i.nI, std::numeric_limits<Real>::quiet_NaN());
156 z.fu = std::numeric_limits<Real>::quiet_NaN();
157 z.cEu.setConstant(i.nE, std::numeric_limits<Real>::quiet_NaN());
158 z.cIu.setConstant(i.nI, std::numeric_limits<Real>::quiet_NaN());
159 return;
160 }
161
162 // Set equality constraint values
163 if (i.nE > 0) {z.cE = c_orig(i.I6) - i.b6;}
164
165 // Initialize inequality constraint values
166 if (i.nI > 0) {z.cI.setZero(i.nI);}
167
168 // Set inequality constraint values
169 if (i.n3 > 0) {
170 z.cI.head(i.n3) = i.l3 - z.x.segment(i.n1, i.n3);
171 }
172 if (i.n4 > 0) {
173 z.cI.segment(i.n3, i.n4) = -i.u4 + z.x.segment(i.n1+i.n3, i.n4);
174 }
175 if (i.n5 > 0) {
176 z.cI.segment(i.n3+i.n4, i.n5) = i.l5 - z.x.segment(i.n1+i.n3+i.n4, i.n5);
177 z.cI.segment(i.n3+i.n4+i.n5, i.n5) = -i.u5 + z.x.segment(i.n1+i.n3+i.n4, i.n5);
178 }
179 if (i.n7 > 0) {
180 z.cI.segment(i.n3+i.n4+i.n5+i.n5, i.n7) = i.l7 - c_orig(i.I7);
181 }
182 if (i.n8 > 0) {
183 z.cI.segment(i.n3+i.n4+i.n5+i.n5+i.n7, i.n8) = -i.u8 + c_orig(i.I8);
184 }
185 if (i.n9 > 0) {
186 z.cI.segment(i.n3+i.n4+i.n5+i.n5+i.n7+i.n8, i.n9) = i.l9 - c_orig(i.I9);
187 z.cI.segment(i.n3+i.n4+i.n5+i.n5+i.n7+i.n8+i.n9, i.n9) = -i.u9 + c_orig(i.I9);
188 }
189
190 // Store unscaled quantities
191 z.fu = z.f;
192 if (i.nE > 0) {z.cEu = z.cE;}
193 if (i.nI > 0) {z.cIu = z.cI;}
194
195 // Scale quantities
196 z.f *= z.fs;
197 if (i.nE > 0) {z.cE = (z.cEs*z.cE).matrix();}
198 if (i.nI > 0) {z.cI = (z.cIs*z.cI).matrix();}
199
200 }
201
202
203
212 template <typename Real>
214 {
215 // Create alias for easier access
216 Input<Real> & i{this->m_input};
217 Iterate<Real> & z{this->m_iterate};
218
219 // Evaluate x in original space
220 Vector<Real> x_orig;
221 this->evalXOriginal(x_orig);
222
223 // Initialize/Reset evaluation flag
224 z.err = 0;
225
226 // Increment gradient evaluation counter
228
229 // Try AMPL gradients evaluation
230 Vector<Real> g_orig;
231 SparseMatrix<Real> J_orig;
232 try
233 {
234 // Evaluate AMPL gradients
235 this->m_problem->objective_gradient(x_orig, g_orig);
236 this->m_problem->constraints_jacobian(x_orig, J_orig);
237 }
238 catch (...)
239 {
240 // Set evaluation flag, default values, and return
241 z.err = 1;
242 return;
243 }
244
245 // Set objective gradient
246 z.g << g_orig(i.I1), g_orig(i.I3), g_orig(i.I4), g_orig(i.I5);
247
248 // Initialize equality constraint Jacobian
249 z.JE.setZero();
250
251 // Set equality constraint Jacobian
252 if (i.nE > 0) {
253 Integer col_offset{0};
254 Pipal::insert_block<Real>(z.JE, J_orig, i.I6, i.I1, 0, col_offset);
255 col_offset += i.I1.size();
256 Pipal::insert_block<Real>(z.JE, J_orig, i.I6, i.I3, 0, col_offset);
257 col_offset += i.I3.size();
258 Pipal::insert_block<Real>(z.JE, J_orig, i.I6, i.I4, 0, col_offset);
259 col_offset += i.I4.size();
260 Pipal::insert_block<Real>(z.JE, J_orig, i.I6, i.I5, 0, col_offset);
261 }
262
263 // Initialize inequality constraint Jacobian
264 z.JI.setZero();
265
266 // Set inequality constraint Jacobian
267 if (i.n3 > 0) {
268 for (Integer k{0}; k < i.n3; ++k) {z.JI.coeffRef(k, i.n1+k) = -1.0;}
269 }
270 if (i.n4 > 0) {
271 Integer tmp{i.n1+i.n3};
272 for (Integer k{0}; k < i.n4; ++k) {z.JI.coeffRef(i.n3+k, tmp+k) = 1.0;}
273 }
274 if (i.n5 > 0) {
275 Integer tmp_row{i.n3+i.n4}, tmp_col{i.n1+i.n3+i.n4};
276 for (Integer k{0}; k < i.n5; ++k) {
277 z.JI.coeffRef(tmp_row+k, tmp_col+k) = -1.0;
278 z.JI.coeffRef(tmp_row+i.n5+k, tmp_col+k) = 1.0;
279 }
280 }
281 if (i.n7 > 0) {
282 Integer row_offset{i.n3+i.n4+i.n5+i.n5}, col_offset{0};
283 Pipal::insert_block<Real>(z.JI, -J_orig, i.I7, i.I1, row_offset, col_offset);
284 col_offset += i.I1.size();
285 Pipal::insert_block<Real>(z.JI, J_orig, i.I7, i.I3, row_offset, col_offset);
286 col_offset += i.I3.size();
287 Pipal::insert_block<Real>(z.JI, J_orig, i.I7, i.I4, row_offset, col_offset);
288 col_offset += i.I4.size();
289 Pipal::insert_block<Real>(z.JI, J_orig, i.I7, i.I5, row_offset, col_offset);
290 }
291 if (i.n8 > 0) {
292 Integer row_offset{i.n3+i.n4+i.n5+i.n5+i.n7}, col_offset{0};
293 Pipal::insert_block<Real>(z.JI, J_orig, i.I8, i.I1, row_offset, col_offset);
294 col_offset += i.I1.size();
295 Pipal::insert_block<Real>(z.JI, J_orig, i.I8, i.I3, row_offset, col_offset);
296 col_offset += i.I3.size();
297 Pipal::insert_block<Real>(z.JI, J_orig, i.I8, i.I4, row_offset, col_offset);
298 col_offset += i.I4.size();
299 Pipal::insert_block<Real>(z.JI, J_orig, i.I8, i.I5, row_offset, col_offset);
300 }
301 if (i.n9 > 0) {
302 Integer row_offset{i.n3+i.n4+i.n5+i.n5+i.n7+i.n8}, col_offset{0};
303 Pipal::insert_block<Real>(z.JI, -J_orig, i.I9, i.I1, row_offset, col_offset);
304 col_offset += i.I1.size();
305 Pipal::insert_block<Real>(z.JI, -J_orig, i.I9, i.I3, row_offset, col_offset);
306 col_offset += i.I3.size();
307 Pipal::insert_block<Real>(z.JI, -J_orig, i.I9, i.I4, row_offset, col_offset);
308 col_offset += i.I4.size();
309 Pipal::insert_block<Real>(z.JI, -J_orig, i.I9, i.I5, row_offset, col_offset);
310 row_offset += i.n9; // Next row block
311 col_offset = 0;
312 Pipal::insert_block<Real>(z.JI, J_orig, i.I9, i.I1, row_offset, col_offset);
313 col_offset += i.I1.size();
314 Pipal::insert_block<Real>(z.JI, J_orig, i.I9, i.I3, row_offset, col_offset);
315 col_offset += i.I3.size();
316 Pipal::insert_block<Real>(z.JI, J_orig, i.I9, i.I4, row_offset, col_offset);
317 col_offset += i.I4.size();
318 Pipal::insert_block<Real>(z.JI, J_orig, i.I9, i.I5, row_offset, col_offset);
319 }
320
321 // Scale objective gradient
322 z.g *= z.fs;
323
324 // Scale constraint Jacobians
325 if (i.nE > 0) {
326 for (Integer k{0}; k < z.JE.outerSize(); ++k) {
327 for (typename SparseMatrix<Real>::InnerIterator it(z.JE, k); it; ++it) {
328 it.valueRef() *= z.cEs[it.row()];
329 }
330 }
331 }
332 if (i.nI > 0) {
333 for (Integer k{0}; k < z.JI.outerSize(); ++k) {
334 for (typename SparseMatrix<Real>::InnerIterator it(z.JI, k); it; ++it) {
335 it.valueRef() *= z.cIs[it.row()];
336 }
337 }
338 }
339 }
340
349 template <typename Real>
351 {
352 // Create alias for easier access
353 Input<Real> & i{this->m_input};
354 Iterate<Real> & z{this->m_iterate};
355
356 // Evaluate lambda in original space
357 Vector<Real> l_orig, x_orig;
358 this->evalLambdaOriginal(l_orig);
359 this->evalXOriginal(x_orig);
360
361 // Initialize/Reset evaluation flag
362 z.err = 0;
363
364 // Increment Hessian evaluation counter
366
367 // Try AMPL Hessian evaluation
368 SparseMatrix<Real> H_orig;
369 try
370 {
371 // Evaluate H_orig
372 this->m_problem->lagrangian_hessian(x_orig, l_orig, H_orig);
373 }
374 catch (...)
375 {
376 // Set evaluation flag, default values, and return
377 z.err = 1;
378 return;
379 }
380
381 // Set Hessian of the Lagrangian
382 Integer row_offset{0}, col_offset{0};
383 Pipal::insert_block<Real>(z.H, H_orig, i.I1, i.I1, row_offset, col_offset);
384 col_offset += i.I1.size();
385 Pipal::insert_block<Real>(z.H, H_orig, i.I1, i.I3, row_offset, col_offset);
386 col_offset += i.I3.size();
387 Pipal::insert_block<Real>(z.H, H_orig, i.I1, i.I4, row_offset, col_offset);
388 col_offset += i.I4.size();
389 Pipal::insert_block<Real>(z.H, H_orig, i.I1, i.I5, row_offset, col_offset);
390 row_offset += i.I1.size();
391 col_offset = 0; // Next row block
392 Pipal::insert_block<Real>(z.H, H_orig, i.I3, i.I1, row_offset, col_offset);
393 col_offset += i.I1.size();
394 Pipal::insert_block<Real>(z.H, H_orig, i.I3, i.I3, row_offset, col_offset);
395 col_offset += i.I3.size();
396 Pipal::insert_block<Real>(z.H, H_orig, i.I3, i.I4, row_offset, col_offset);
397 col_offset += i.I4.size();
398 Pipal::insert_block<Real>(z.H, H_orig, i.I3, i.I5, row_offset, col_offset);
399 row_offset += i.I3.size();
400 col_offset = 0; // Next row block
401 Pipal::insert_block<Real>(z.H, H_orig, i.I4, i.I1, row_offset, col_offset);
402 col_offset += i.I1.size();
403 Pipal::insert_block<Real>(z.H, H_orig, i.I4, i.I3, row_offset, col_offset);
404 col_offset += i.I3.size();
405 Pipal::insert_block<Real>(z.H, H_orig, i.I4, i.I4, row_offset, col_offset);
406 col_offset += i.I4.size();
407 Pipal::insert_block<Real>(z.H, H_orig, i.I4, i.I5, row_offset, col_offset);
408 row_offset += i.I4.size();
409 col_offset = 0; // Next row block
410 Pipal::insert_block<Real>(z.H, H_orig, i.I5, i.I1, row_offset, col_offset);
411 col_offset += i.I1.size();
412 Pipal::insert_block<Real>(z.H, H_orig, i.I5, i.I3, row_offset, col_offset);
413 col_offset += i.I3.size();
414 Pipal::insert_block<Real>(z.H, H_orig, i.I5, i.I4, row_offset, col_offset);
415 col_offset += i.I4.size();
416 Pipal::insert_block<Real>(z.H, H_orig, i.I5, i.I5, row_offset, col_offset);
417
418 // Workaround to ensure all diagonal entries exist (access or create them)
419 for (Integer i{0}; i < z.H.rows(); ++i) {(void) z.H.coeffRef(i, i);}
420
421 // Rescale H
422 z.H *= z.rho*z.fs;
423 }
424
432 template <typename Real>
433 Real Solver<Real>::evalKKTError(Real const rho, Real const mu)
434 {
435 // Create alias for easier access
436 Input<Real> & i{this->m_input};
437 Iterate<Real> & z{this->m_iterate};
438
439 // Initialize optimality vector
440 Vector<Real> kkt(i.nV+2*i.nE+2*i.nI);
441 kkt.setZero();
442
443 // Set gradient of penalty objective
444 kkt.head(i.nV) = rho*z.g;
445
446 // Set gradient of Lagrangian for constraints
447 if (i.nE > 0) {kkt.head(i.nV) += (z.lE.matrix().transpose()*z.JE).transpose();}
448 if (i.nI > 0) {kkt.head(i.nV) += (z.lI.matrix().transpose()*z.JI).transpose();}
449
450 // Set complementarity for constraint slacks
451 if (i.nE > 0) {
452 kkt.segment(i.nV, 2*i.nE) << z.r1*(1.0 + z.lE) - mu, z.r2*(1.0 - z.lE) - mu;
453 }
454 if (i.nI > 0) {
455 kkt.segment(i.nV+2*i.nE, 2*i.nI) << z.s1*z.lI - mu, z.s2*(1.0 - z.lI) - mu;
456 }
457
458 // Scale complementarity
459 if (rho > 0) {
460 kkt *= (1.0 / std::max(1.0, (rho*z.g).template lpNorm<Eigen::Infinity>()));
461 }
462
463 // Evaluate optimality error
464 return kkt.template lpNorm<Eigen::Infinity>();
465 }
466
471 template <typename Real>
473 {
474 // Create alias for easier access
475 Iterate<Real> & z{this->m_iterate};
476
477 // Loop to compute optimality errors
478 z.kkt(0) = evalKKTError(0.0, 0.0);
479 z.kkt(1) = evalKKTError(z.rho, 0.0);
480 z.kkt(2) = evalKKTError(z.rho, z.mu);
481 }
482
488 template <typename Real>
490 {
491 // Create alias for easier access
492 Input<Real> const & i{this->m_input};
493 Iterate<Real> const & z{this->m_iterate};
494
495 // Initialize multipliers in original space
496 l.setZero(i.nE+i.n7+i.n8+i.n9);
497
498 // Scale equality constraint multipliers
499 if (i.nE > 0) {l(i.I6) = z.lE*(z.cEs/(z.rho*z.fs));}
500
501 // Scale inequality constraint multipliers
502 Array<Real> lI;
503 if (i.n7+i.n8+i.n9 > 0) {lI = z.lI*(z.cIs/(z.rho*z.fs));}
504
505 // Set inequality constraint multipliers in original space
506 if (i.n7 > 0) {
507 l(i.I7) = -lI.segment(i.n3+i.n4+i.n5+i.n5, i.n7);
508 }
509 if (i.n8 > 0) {
510 l(i.I8) = lI.segment(i.n3+i.n4+i.n5+i.n5+i.n7, i.n8);
511 }
512 if (i.n9 > 0) {
513 l(i.I9) = lI.segment(i.n3+i.n4+i.n5+i.n5+i.n7+i.n8+i.n9, i.n9)
514 -lI.segment(i.n3+i.n4+i.n5+i.n5+i.n7+i.n8, i.n9);
515 }
516 }
517
518
523 template <typename Real>
525 {
526 // Create alias for easier access
527 Input<Real> & i{this->m_input};
528 Iterate<Real> & z{this->m_iterate};
529
530 // Initialize merit for objective
531 z.phi = z.rho*z.f;
532
533 // Update merit for slacks
534 if (i.nE > 0) {
535 Array<Real> r_all(2*i.nE); r_all << z.r1, z.r2;
536 z.phi -= z.mu * r_all.log().sum() - r_all.sum();
537 }
538 if (i.nI > 0) {
539 Array<Real> s_all(2*i.nI); s_all << z.s1, z.s2;
540 z.phi -= z.mu * s_all.log().sum() - z.s2.sum();
541 }
542 }
543
548 template <typename Real>
550 {
551 // Create alias for easier access
552 Parameter<Real> & p{this->m_parameter};
553 Input<Real> & i{this->m_input};
554 Iterate<Real> & z{this->m_iterate};
555
556 // Check for equality constraints
557 if (i.nE > 0)
558 {
559 // Set diagonal terms
560 for (Integer j{0}; j < i.nE; ++j) {
561 z.A.coeffRef(i.nV+j, i.nV+j) = (1.0 + z.lE(j))/z.r1(j);
562 z.A.coeffRef(i.nV+i.nE+j, i.nV+i.nE+j) = (1.0 - z.lE(j))/z.r2(j);
563 }
564
565 // Set constraint Jacobian
566 Pipal::insert_block<Real>(z.A, z.JE, i.nV+2*i.nE+2*i.nI, 0);
567 }
568
569 // Check for inequality constraints
570 if (i.nI > 0)
571 {
572 // Set diagonal terms
573 const Integer offset{i.nV+2*i.nE};
574 for (Integer j{0}; j < i.nI; ++j) {
575 z.A.coeffRef(offset+j, offset+j) = z.lI(j)/z.s1(j);
576 z.A.coeffRef(offset+i.nI+j, offset+i.nI+j) = (1.0 - z.lI(j))/z.s2(j);
577 }
578
579 // Set constraint Jacobian
580 Pipal::insert_block<Real>(z.A, z.JI, i.nV+3*i.nE+2*i.nI, 0);
581 }
582
583 // Set minimum potential shift
584 Real min_shift{std::max(p.shift_min, p.shift_factor1*z.shift)};
585
586 // Initialize Hessian modification
587 if (z.cut_ == true) {z.shift = std::min(p.shift_max, min_shift/p.shift_factor2);}
588 else {z.shift = 0;}
589 // Initialize inertia correction loop
590 bool done{false};
591 z.shift22 = 0;
592
593 // Loop until inertia is correct
594 while (!done && z.shift < p.shift_max)
595 {
596 // Set Hessian of Lagrangian
598
599 // Set diagonal terms
600 {
601 const Integer offset{i.nV+2*i.nE+2*i.nI};
602 for (Integer j{0}; j < i.nE; ++j) {
603 z.A.coeffRef(offset+j, offset+j) = -z.shift22;
604 }
605 }
606 {
607 const Integer offset{i.nV+3*i.nE+2*i.nI};
608 // Set diagonal terms
609 for (Integer j{0}; j < i.nI; ++j) {
610 z.A.coeffRef(offset+j, offset+j) = -z.shift22;
611 }
612 }
613
614 // Set number of nonzeros in (upper triangle of) Newton matrix
615 z.Annz = static_cast<Integer>(z.A.nonZeros());
616
617 // Factor primal-dual matrix
618 z.ldlt.compute(z.A);
619
620 // Approximate number of negative pivots (inertia)
621 Integer neig{static_cast<Integer>((z.ldlt.vectorD().array() < 0.0).count())};
622
623 // Increment factorization counter
625
626 // Set number of nonnegative eigenvalues
627 Integer peig{i.nA - neig};
628
629 // Check inertia
630 if (peig < i.nV+2*i.nE+2*i.nI) {z.shift = std::max(min_shift, z.shift/p.shift_factor2);}
631 else if (neig < i.nE+i.nI && z.shift22 == 0) {z.shift22 = p.shift_min;}
632 else {done = true;}
633 }
634
635 // Update Hessian
636 z.H.diagonal().array() += z.shift;
637
638 // Compress Newton matrix
639 z.A.makeCompressed();
640 }
641
646 template <typename Real>
648 {
649 // Create alias for easier access
650 Input<Real> & i{this->m_input};
651 Iterate<Real> & z{this->m_iterate};
652
653 // Initialize right-hand side
654 z.b.setZero();
655
656 // Set gradient of objective
657 z.b.head(i.nV) = z.rho*z.g;
658
659 // Set gradient of Lagrangian for constraints
660 if (i.nE > 0) {z.b.head(i.nV) += (z.lE.matrix().transpose()*z.JE).transpose();}
661 if (i.nI > 0) {z.b.head(i.nV) += (z.lI.matrix().transpose()*z.JI).transpose();}
662
663 // Set complementarity for constraint slacks
664 if (i.nE > 0) {
665 // Compute element-wise complementarity terms with safe element-wise division
666 Vector<Real> tmp(2*i.nE);
667 tmp << 1.0 + z.lE - z.mu * z.r1.cwiseInverse(), 1.0 - z.lE - z.mu * z.r2.cwiseInverse();
668 z.b.segment(i.nV, 2*i.nE) = tmp;
669 }
670 if (i.nI > 0) {
671 // Compute element-wise complementarity terms with safe element-wise division
672 Vector<Real> tmp(2*i.nI);
673 tmp << z.lI - z.mu * z.s1.cwiseInverse(), 1.0 - z.lI - z.mu * z.s2.cwiseInverse();
674 z.b.segment(i.nV + 2*i.nE, 2*i.nI) = tmp;
675 }
676
677 // Set penalty-interior-point constraint values
678 if (i.nE > 0) {z.b.segment(i.nV+2*i.nE+2*i.nI, i.nE) = z.cE + z.r1 - z.r2;}
679 if (i.nI > 0) {z.b.segment(i.nV+3*i.nE+2*i.nI, i.nI) = z.cI + z.s1 - z.s2;}
680 }
681
686 template <typename Real>
688 {
689 // Create alias for easier access
690 Parameter<Real> & p{this->m_parameter};
691 Input<Real> & i{this->m_input};
692 Iterate<Real> & z{this->m_iterate};
693
694 // Initialize scalings
695 z.fs = 1;
696 z.cEs.setOnes(i.nE);
697 z.cIs.setOnes(i.nI);
698
699 // Evaluate gradients
700 this->evalGradients();
701
702 // Scale down objective if norm of gradient is too large
703 z.fs = p.grad_max / std::max(z.g.template lpNorm<Eigen::Infinity>(), p.grad_max);
704
705 // Loop through equality constraints
706 for (Integer j{0}; j < i.nE; ++j)
707 {
708 // Scale down equality constraint j if norm of gradient is too large
709 Real row_inf_norm{0.0};
710 for (Integer c{0}; c < z.JE.outerSize(); ++c) {
711 for (typename SparseMatrix<Real>::InnerIterator it(z.JE, c); it; ++it) {
712 if (it.row() == j) {row_inf_norm = std::max(row_inf_norm, std::abs(it.value()));}
713 }
714 }
715 z.cEs(j) = p.grad_max / std::max(row_inf_norm, p.grad_max);
716 }
717
718 // Loop through inequality constraints
719 for (Integer j{0}; j < i.nI; ++j)
720 {
721 // Scale down inequality constraint j if norm of gradient is too large
722 Real row_inf_norm{0.0};
723 for (Integer c{0}; c < z.JI.outerSize(); ++c) {
724 for (typename SparseMatrix<Real>::InnerIterator it(z.JI, c); it; ++it) {
725 if (it.row() == j) {row_inf_norm = std::max(row_inf_norm, std::abs(it.value()));}
726 }
727 }
728 z.cIs(j) = p.grad_max / std::max(row_inf_norm, p.grad_max);
729 }
730 }
731
736 template <typename Real>
738 {
739 // Create alias for easier access
740 Parameter<Real> & p{this->m_parameter};
741 Input<Real> & i{this->m_input};
742 Iterate<Real> & z{this->m_iterate};
743
744 // Check for equality constraints
745 if (i.nE > 0)
746 {
747 // Set slacks
748 z.r1 = 0.5*((z.mu - z.cE) + (z.cE.square() + z.mu*z.mu).sqrt());
749 z.r2 = 0.5*((z.mu + z.cE) + (z.cE.square() + z.mu*z.mu).sqrt());
750
751 // Adjust for numerical error
752 z.r1.cwiseMax(p.slack_min);
753 z.r2.cwiseMax(p.slack_min);
754 }
755
756 // Check for inequality constraints
757 if (i.nI > 0)
758 {
759 // Set slacks
760 z.s1 = 0.5*((2.0*z.mu - z.cI) + (z.cI.square() + 4.0*z.mu*z.mu).sqrt());
761 z.s2 = 0.5*((2.0*z.mu + z.cI) + (z.cI.square() + 4.0*z.mu*z.mu).sqrt());
762
763 // Adjust for numerical error
764 z.s1.cwiseMax(p.slack_min);
765 z.s2.cwiseMax(p.slack_min);
766 }
767 }
768
774 template <typename Real>
776 {
777 // Create alias for easier access
778 Input<Real> & i{this->m_input};
779 Iterate<Real> & z{this->m_iterate};
780
781 // Initialize x in original space
782 x.setZero(i.n0);
783
784 // Evaluate x in original space
785 x(i.I1) = z.x.head(i.n1);
786 x(i.I2) = i.b2;
787 x(i.I3) = z.x.segment(i.n1, i.n3);
788 x(i.I4) = z.x.segment(i.n1+i.n3, i.n4);
789 x(i.I5) = z.x.segment(i.n1+i.n3+i.n4, i.n5);
790 }
791
796 template <typename Real>
798 {
799 // Create alias for easier access
800 Input<Real> & i{this->m_input};
801 Iterate<Real> & z{this->m_iterate};
802
803 // Allocate memory
804 z.A.reserve(z.Hnnz + 5*i.nE + 5*i.nI + z.JEnnz + z.JInnz);
805
806 // Initialize interior-point Hessians
807 {
808 Integer diag;
809 for (Integer k{0}; k < 2*i.nE; ++k) {
810 diag = i.nV+k;
811 z.A.coeffRef(diag, diag) = 1.0;
812 }
813
814 for (Integer k{0}; k < 2*i.nI; ++k) {
815 diag = i.nV+2*i.nE+k;
816 z.A.coeffRef(diag, diag) = 1.0;
817 }
818 }
819
820 // Check for constraints
821 if (i.nE > 0)
822 {
823 // Initialize constraint Jacobian
824 Integer row, col;
825 for (Integer k{0}; k < i.nE; ++k) {
826 row = i.nV+2*i.nE+2*i.nI+k, col = i.nV+k;
827 z.A.coeffRef(row, col) = 1.0;
828 z.A.coeffRef(row, col+i.nE) = -1.0;
829 }
830 }
831
832 // Check for inequality constraints
833 if (i.nI > 0)
834 {
835 // Initialize constraint Jacobian
836 Integer row, col;
837 for (Integer k{0}; k < i.nI; ++k) {
838 row = i.nV+3*i.nE+2*i.nI+k, col = i.nV+2*i.nE+k;
839 z.A.coeffRef(row, col) = 1.0;
840 z.A.coeffRef(row, col+i.nI) = -1.0;
841 }
842 }
843 }
844
860 template <typename Real>
861 void Solver<Real>::setPrimals(Vector<Real> const & x, Array<Real> const & r1, Array<Real> const & r2,
862 Array<Real> const & s1, Array<Real> const & s2, Array<Real> const & lE, Array<Real> const & lI,
863 Real const f, Array<Real> const & cE, Array<Real> const & cI, Real const phi)
864 {
865 // Create alias for easier access
866 Input<Real> & i{this->m_input};
867 Iterate<Real> & z{this->m_iterate};
868
869 // Set primal variables
870 z.x = x; z.f = f;
871 if (i.nE > 0) {z.cE = cE; z.r1 = r1; z.r2 = r2; z.lE = lE;}
872 if (i.nI > 0) {z.cI = cI; z.s1 = s1; z.s2 = s2; z.lI = lI;}
873 z.phi = phi;
874 }
875
894 template <typename Real>
896 {
897 // Create alias for easier access
898 Iterate<Real> & z{this->m_iterate};
899
900 PIPAL_ASSERT(y.dot(s) > 0.0,
901 "Pipal::Solver::bfgsUpdate(...): update condition yáµ€s > 0 not satisfied");
902
903 // Apply BFGS update
904 Vector<Real> x(z.H * s);
905 z.H -= (x * x.transpose()).sparseView() / (s.dot(x));
906 z.H -= (y * y.transpose()).sparseView() / (y.dot(s));
907 }
908
916 template <typename Real>
918 {
919 // Create alias for easier access
920 Parameter<Real> & p{this->m_parameter};
921 Iterate<Real> & z{this->m_iterate};
923
924 // Update last quantities
925 z.v_ = z.v;
926 z.cut_ = (a.p < a.p0);
927
928 // Retrieve last primal/dual variables and gradients
929 Vector<Real> const x_old(z.x), g_old(z.g);
930
931 // Update iterate quantities
932 this->updatePoint();
933 this->evalInfeasibility(z);
934 this->evalGradients();
935 this->evalDependent();
936
937 // Update objective Hessian approximation if enabled
938 if (this->m_bfgs) {
939 if (this->m_counter.k % this->m_parameter.bfgs_update_freq == 0) {
940 z.H.setIdentity();
941 } else {
942 this->bfgsUpdate(z.x - x_old, z.g - g_old);
943 }
944 }
945
946
947 // Update last KKT errors
948 z.kkt_ << z.kkt(1), z.kkt_.head(p.opt_err_mem - 1);
949 }
950
958 template <typename Real>
960 {
961
962 Parameter<Real> & p{this->m_parameter};
963 Iterate<Real> & z{this->m_iterate};
964
965 // Check for interior-point parameter update based on optimality error
966 while (z.mu > p.mu_min && z.kkt(2) <= std::max(z.mu, p.opt_err_tol-z.mu))
967 {
968 // Restrict interior-point parameter increase
969 this->setMuMaxExpZero();
970
971 // Update interior-point parameter
972 if (z.mu > p.mu_min)
973 {
974 // Decrease interior-point
975 z.mu = std::max(p.mu_min, std::min(p.mu_factor*z.mu, std::pow(z.mu, p.mu_factor_exp)));
976
977 // Evaluate penalty and interior-point parameter dependent quantities
978 this->evalDependent();
979 }
980 }
981
982 // Check for penalty parameter update based on optimality error
983 if ((z.kkt(1) <= p.opt_err_tol && z.v > p.opt_err_tol) ||
984 z.v > std::max({1.0, z.v_, p.infeas_max}))
985 {
986 // Update penalty parameter
987 if (z.rho > p.rho_min)
988 {
989 // Decrease penalty parameter
990 z.rho = std::max(p.rho_min, p.rho_factor*z.rho);
991
992 // Evaluate penalty and interior-point parameter dependent quantities
993 this->evalDependent();
994 }
995 }
996 }
997
1002 template <typename Real>
1004 {
1005 // Create alias for easier access
1006 Input<Real> & i{this->m_input};
1007 Iterate<Real> & z{this->m_iterate};
1008 Direction<Real> & d{this->m_direction};
1009 Acceptance<Real> & a{this->m_acceptance};
1010
1011 // Update primal and dual variables
1012 z.x += a.p*d.x ;
1013 if (i.nE > 0) {z.r1 += a.p*d.r1; z.r2 += a.p*d.r2;}
1014 if (i.nI > 0) {z.s1 += a.p*d.s1; z.s2 += a.p*d.s2;}
1015 if (i.nE > 0) {z.lE += a.d*d.lE;}
1016 if (i.nI > 0) {z.lI += a.d*d.lI;}
1017 }
1018
1026 template <typename Real>
1027 Real Solver<Real>::evalViolation(Array<Real> const & cE, Array<Real> const & cI) const
1028 {
1029 // Create alias for easier access
1030 Input<Real> const & i{this->m_input};
1031
1032 // Initialize violation vector
1033 Vector<Real> vec;
1034
1035 // Update vector for constraint values
1036 if (i.nE > 0) {vec = cE;}
1037 if (i.nI > 0) {
1038 Vector<Real> cIpos(cI.cwiseMax(0.0));
1039 if (vec.size() > 0) {
1040 Vector<Real> tmp(vec.size() + cIpos.size());
1041 tmp << vec, cIpos;
1042 vec = std::move(tmp);
1043 } else {
1044 vec = cIpos;
1045 }
1046 }
1047
1048 // Evaluate vector norm
1049 return vec.template lpNorm<1>();
1050 }
1051
1052} // namespace Pipal
1053
1054#endif // INCLUDE_PIPAL_ITERATE_HXX
#define PIPAL_ASSERT(COND, MSG)
Definition Types.hxx:50
Real evalKKTError(Real const rho, Real const mu)
Compute the infinity-norm of the KKT optimality vector.
Definition Iterate.hxx:433
void evalMerit()
Compute the merit function value for the current iterate.
Definition Iterate.hxx:524
void incrementFactorizationCount()
Increment the matrix factorization counter.
Definition Solver.hxx:173
Acceptance< Real > m_acceptance
Definition Solver.hxx:43
ProblemPtr m_problem
Definition Solver.hxx:49
Real evalViolation(Array< Real > const &cE, Array< Real > const &cI) const
Compute the 1-norm feasibility violation from equality/inequality values.
Definition Iterate.hxx:1027
Input< Real > m_input
Definition Solver.hxx:44
void initNewtonMatrix()
Reserve and initialize the internal sparse Newton matrix structure.
Definition Iterate.hxx:797
void evalDependent()
Evaluate quantities that depend on penalty/interior parameters.
Definition Solver.hxx:154
void incrementGradientCount()
Increment the gradient evaluation counter.
Definition Solver.hxx:183
void bfgsUpdate(Vector< Real > const &s, Vector< Real > const &y)
Browder-Broyden-Fletcher-Goldfarb-Shanno (BFGS) update for the Hessian approximation.
Definition Iterate.hxx:895
Direction< Real > m_direction
Definition Solver.hxx:45
void buildIterate()
Initialize an Iterate object for a given problem/input.
Definition Iterate.hxx:23
void evalInfeasibility(Iterate< Real > &z) const
Compute scaled and unscaled feasibility violations.
Definition Solver.hxx:103
void incrementHessianCount()
Increment the Hessian evaluation counter.
Definition Solver.hxx:188
void incrementFunctionCount()
Increment the function evaluation counter.
Definition Solver.hxx:178
void updateIterate()
Update the iterate after a trial step is accepted.
Definition Iterate.hxx:917
void evalNewtonRhs()
Build the right-hand side vector for the Newton system.
Definition Iterate.hxx:647
Iterate< Real > m_iterate
Definition Solver.hxx:46
bool m_bfgs
Definition Solver.hxx:53
void evalSlacks()
Compute internal slack variables from current iterate.
Definition Iterate.hxx:737
Counter m_counter
Definition Solver.hxx:42
void updatePoint()
Apply a step to the primal and dual variables of the iterate.
Definition Iterate.hxx:1003
void evalXOriginal(Vector< Real > &x)
Reconstruct the full primal vector in the original variable ordering.
Definition Iterate.hxx:775
void evalNewtonMatrix()
Assemble and (attempt to) factorize the Newton system matrix.
Definition Iterate.hxx:549
void setMuMaxExpZero()
Force mu exponent increases to use zero as maximum exponent.
Definition Solver.hxx:125
void evalGradients()
Evaluate objective gradient and constraint Jacobian.
Definition Iterate.hxx:213
Parameter< Real > m_parameter
Definition Solver.hxx:48
void setPrimals(Vector< Real > const &x, Array< Real > const &r1, Array< Real > const &r2, Array< Real > const &s1, Array< Real > const &s2, Array< Real > const &lE, Array< Real > const &lI, Real const f, Array< Real > const &cE, Array< Real > const &cI, Real const phi)
Set primal/dual blocks and associated quantities on an iterate.
Definition Iterate.hxx:861
void evalKKTErrors()
Compute the three KKT error measures used by the solver.
Definition Iterate.hxx:472
void evalLambdaOriginal(Vector< Real > &l) const
Reconstruct multipliers in the original variable/constraint space.
Definition Iterate.hxx:489
Integer checkTermination() const
Check termination criteria for the solver.
Definition Iterate.hxx:94
void evalFunctions()
Evaluate objective and constraint functions at the current iterate.
Definition Iterate.hxx:125
void evalHessian()
Evaluate the Hessian of the Lagrangian and assemble internal H.
Definition Iterate.hxx:350
void evalScalings()
Evaluate scaling multipliers for objective and constraints.
Definition Iterate.hxx:687
void updateParameters()
Update penalty and interior-point parameters based on KKT errors.
Definition Iterate.hxx:959
Namespace for the Pipal library.
Definition Acceptance.hxx:16
Eigen::SparseMatrix< Real > SparseMatrix
Definition Types.hxx:114
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic > Matrix
Definition Types.hxx:113
PIPAL_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Types.hxx:110
Eigen::Array< Real, Eigen::Dynamic, 1 > Array
Definition Types.hxx:115
static void insert_block(SparseMatrix< Real > &mat, SparseMatrix< Real > const &blk, Integer const row_offset, Integer const col_offset)
Insert a sparse block into a sparse matrix at the specified offsets.
Definition Types.hxx:150
Eigen::Vector< Real, Eigen::Dynamic > Vector
Definition Types.hxx:112
struct Counter { Integer f{0}; Integer g{0}; Integer H{0}; Integer k{0}; Integer M{0}; Counter()=default; Counter(Counter const &)=delete; Counter &operator=(Counter const &)=delete; } Counter
Internal counters for solver statistics.
Definition Types.hxx:285
Class for managing the acceptance criteria of the solver.
Definition Types.hxx:486
Real d
Definition Types.hxx:489
Real p
Definition Types.hxx:488
Real p0
Definition Types.hxx:487
Class for managing the current search direction of the solver.
Definition Types.hxx:446
Array< Real > s2
Definition Types.hxx:454
Array< Real > lI
Definition Types.hxx:455
Array< Real > r2
Definition Types.hxx:451
Array< Real > r1
Definition Types.hxx:450
Array< Real > lE
Definition Types.hxx:452
Vector< Real > x
Definition Types.hxx:447
Array< Real > s1
Definition Types.hxx:453
Input structure holding all the data defining the optimization problem.
Definition Types.hxx:316
Vector< Real > l3
Definition Types.hxx:329
Indices I1
Definition Types.hxx:318
Vector< Real > l9
Definition Types.hxx:336
Vector< Real > l5
Definition Types.hxx:331
Integer nV
Definition Types.hxx:348
Integer n3
Definition Types.hxx:341
Integer n4
Definition Types.hxx:342
Indices I7
Definition Types.hxx:324
Integer n5
Definition Types.hxx:343
Integer n1
Definition Types.hxx:339
Integer n9
Definition Types.hxx:347
Indices I3
Definition Types.hxx:320
Vector< Real > u9
Definition Types.hxx:337
Vector< Real > b6
Definition Types.hxx:333
Indices I6
Definition Types.hxx:323
Integer nA
Definition Types.hxx:351
Vector< Real > u5
Definition Types.hxx:332
Integer n0
Definition Types.hxx:338
Indices I4
Definition Types.hxx:321
Indices I9
Definition Types.hxx:326
Indices I2
Definition Types.hxx:319
Indices I8
Definition Types.hxx:325
Integer n7
Definition Types.hxx:345
Integer n8
Definition Types.hxx:346
Integer nI
Definition Types.hxx:349
Vector< Real > x0
Definition Types.hxx:327
Vector< Real > l7
Definition Types.hxx:334
Indices I5
Definition Types.hxx:322
Integer nE
Definition Types.hxx:350
Integer vi
Definition Types.hxx:352
Vector< Real > u4
Definition Types.hxx:330
Vector< Real > b2
Definition Types.hxx:328
Vector< Real > u8
Definition Types.hxx:335
Class for managing the current iterate of the solver.
Definition Types.hxx:377
Array< Real > cEs
Definition Types.hxx:414
Real rho
Definition Types.hxx:381
Array< Real > cE
Definition Types.hxx:389
Real v
Definition Types.hxx:401
Integer Hnnz
Definition Types.hxx:400
Real shift
Definition Types.hxx:407
Real mu
Definition Types.hxx:383
Array< Real > lI
Definition Types.hxx:398
SparseMatrix< Real > H
Definition Types.hxx:399
Array< Real > cIu
Definition Types.hxx:417
Array< Real > r1
Definition Types.hxx:387
Vector< Real > x
Definition Types.hxx:380
Real shift22
Definition Types.hxx:419
SparseMatrix< Real > JE
Definition Types.hxx:390
Array< Real > s2
Definition Types.hxx:394
Real fu
Definition Types.hxx:385
Array< Real > lE
Definition Types.hxx:392
Integer JEnnz
Definition Types.hxx:391
Array< Real > cI
Definition Types.hxx:395
Real v0
Definition Types.hxx:403
SparseMatrix< Real > A
Definition Types.hxx:418
Integer Annz
Definition Types.hxx:406
Vector< Real > b
Definition Types.hxx:408
SparseMatrix< Real > JI
Definition Types.hxx:396
Real fs
Definition Types.hxx:413
Real vu
Definition Types.hxx:402
Array< Real > cIs
Definition Types.hxx:416
Integer JInnz
Definition Types.hxx:397
LDLT ldlt
Definition Types.hxx:405
Real f
Definition Types.hxx:384
Real v_
Definition Types.hxx:420
Array< Real > cEu
Definition Types.hxx:415
Integer err
Definition Types.hxx:411
Vector< Real > kkt_
Definition Types.hxx:410
Vector< Real > kkt
Definition Types.hxx:409
Array< Real > s1
Definition Types.hxx:393
bool cut_
Definition Types.hxx:421
Array< Real > r2
Definition Types.hxx:388
Vector< Real > g
Definition Types.hxx:386
Real phi
Definition Types.hxx:404
Internal parameters for the solver algorithm.
Definition Types.hxx:229
static constexpr Real rho_min
Definition Types.hxx:244
static constexpr Real rho_factor
Definition Types.hxx:245
static constexpr Real grad_max
Definition Types.hxx:231
static constexpr Real shift_factor2
Definition Types.hxx:241
static constexpr Real mu_min
Definition Types.hxx:248
static constexpr Real shift_max
Definition Types.hxx:242
Integer iter_max
Definition Types.hxx:259
static constexpr Real rho_init
Definition Types.hxx:243
static constexpr Real slack_min
Definition Types.hxx:238
static constexpr Real mu_factor_exp
Definition Types.hxx:250
static constexpr Real shift_min
Definition Types.hxx:239
static constexpr Integer opt_err_mem
Definition Types.hxx:234
static constexpr Real mu_init
Definition Types.hxx:247
static constexpr Real shift_factor1
Definition Types.hxx:240
Real opt_err_tol
Definition Types.hxx:258
static constexpr Real mu_factor
Definition Types.hxx:249