MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
ip.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "ip.hpp"
13
14namespace mfem
15{
16
18 : problem(problem_)
19{
20 abs_tol = 1.e-2; // Tolerance of the optimizer
21 max_iter = 20; // Maximum iterations
22 mu_k = 1.0; // Log-barrier penalty parameter
23
24 /* The following constants follow that of
25 * Wächter, Andreas, and Lorenz T. Biegler.
26 * "On the implementation of an interior-point filter line-search algorithm for large-scale nonlinear programming."
27 * Mathematical programming 106.1 (2006): 25-57.
28 */
29
30 /* line search constants */
31 tauMin =
32 0.99; // constant that controls the rate iterates approach boundary > 0, < 1
33 eta =
34 1.e-4; // Armijo backtracking sufficient-decrease condition constant > 0, < 1
35 thetaMin = 1.e-4; // allowed equality constraint violation
36 delta = 1.0; // sufficient barrier objective progress constant > 0
37 sTheta = 1.1; // sufficient barrier objective progress constant > 0
38 sPhi = 2.3; // sufficient barrier objective progress constant >= 0
39 gTheta = 1.e-5; // sufficient constraint violation decrease constant > 0, < 1
40 gPhi = 1.e-5; // sufficient barrier objective decrease constant > 0, < 1
41 thetaMax = 1.e6; // maximum constraint violation (defines initial filter)
42
43 /* interior-point continuation constants
44 * \mu_new = max{abs_tol / 10, \min{ kMu * \mu_old, (\mu_old)^thetaMu}}
45 * */
46 kMu = 0.2;
47 thetaMu = 1.5;
48 kEps = 1.e1; // constant that determines when the log-barrier parameter is decreased
49
50 kSig = 1.e10; // primal-dual Hessian deviation constant
51
52 /* The following constants follow that of
53 * Chiang, Nai-Yuan, and Victor M. Zavala.
54 * "An inertia-free filter line-search algorithm for large-scale nonlinear programming."
55 * Computational Optimization and Applications 64.2 (2016): 327-354.
56 * See Inertia-Free Regularizaton Algorithm (IFR).
57 * We use the second variant (see Section 4.1 "Alternative Inertia-Free Tests")
58 * which does not require solving for normal and tangential components.
59 */
60
61 /* inertia-regularization constants */
62 alphaCurvatureTest = 1.e-11;
63 deltaRegLast = 0.0;
64 deltaRegMin = 1.e-20;
65 deltaRegMax = 1.e40;
66 deltaReg0 = 1.e-4;
67 kRegMinus = 1. / 3.;
68 kRegBarPlus = 1.e2;
69 kRegPlus = 8.;
70
71 dimU = problem->GetDimU();
72 dimM = problem->GetDimM();
73 dimC = problem->GetDimC();
74 MFEM_VERIFY(dimM == dimC,
75 "Expecting equal numbers of equality and inequality constraints");
76
77 comm = problem->GetComm();
79
83
84 block_offsetsumlz[0] = 0;
85 block_offsetsumlz[1] = dimU; // u
86 block_offsetsumlz[2] = dimM; // m
87 block_offsetsumlz[3] = dimC; // lambda
88 block_offsetsumlz[4] = dimM; // zl
90
91 Mlump.SetSize(dimM); Mlump = 0.0;
92 int dimG = 0; // number of gap constraints
93 if (dimM < dimU)
94 {
95 dimG = dimM;
97 constraint_offsets[0] = 0;
99 Mlump.Set(1.0, Mcslump);
100 }
101 else
102 {
103 dimG = dimM - 2 * dimU;
105 constraint_offsets[0] = 0;
106 constraint_offsets[1] = dimG;
110 BlockVector Mlumpblock(constraint_offsets); Mlumpblock = 0.0;
111 Mlumpblock.GetBlock(0).Set(1.0, Mcslump);
112 Mlumpblock.GetBlock(1).Set(1.0, Mvlump);
113 Mlumpblock.GetBlock(2).Set(1.0, Mvlump);
114 Mlump.Set(1.0, Mlumpblock);
115 }
116
117 for (int i = 0; i < block_offsetsuml.Size(); i++)
118 {
120 }
121 for (int i = 0; i < block_offsetsx.Size(); i++)
122 {
124 }
125
126 lk.SetSize(dimC); lk = 0.0;
127 zlk.SetSize(dimM); zlk = 0.0;
128
129 MPI_Comm_rank(comm, &myid);
130}
131
132void IPSolver::Mult(const Vector &x0, Vector &xf)
133{
134 BlockVector x0block(block_offsetsx); x0block = 0.0;
135 x0block.GetBlock(0).Set(1.0, x0);
136 x0block.GetBlock(1) = 1.0;
137 BlockVector xfblock(block_offsetsx); xfblock = 0.0;
138 Mult(x0block, xfblock);
139 xf.Set(1.0, xfblock.GetBlock(0));
140}
141
143{
144 converged = false;
145 BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
147 Xhat = 0.0;
148 BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
149 Vector zlhat(dimM); zlhat = 0.0;
150
151 xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
152 xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
153 // running estimate of the final values of the Lagrange multipliers
154 lk = 0.0;
155 zlk = 0.0;
156
157 for (int i = 0; i < dimM; i++)
158 {
159 zlk(i) = 1.e1 * mu_k / xk(i+dimU);
160 }
161
162 Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
163 Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
164 Xk.GetBlock(2).Set(1.0, lk);
165 Xk.GetBlock(3).Set(1.0, zlk);
166
167 /* set theta0 = theta(x0)
168 * thetaMin
169 * thetaMax
170 * when theta(xk) < thetaMin and the switching condition holds
171 * then we ask for the Armijo sufficient decrease of the barrier
172 * objective to be satisfied, in order to accept the trial step length alphakl
173 *
174 * thetaMax controls how the filter is initialized for each log-barrier subproblem
175 * F0 = {(th, phi) s.t. th > thetaMax}
176 * that is the filter does not allow for iterates where the constraint violation
177 * is larger than that of thetaMax
178 */
179 real_t theta0 = GetTheta(xk);
180 thetaMin = 1.e-4 * std::max(1.0, theta0);
181 thetaMax = 1.e8 * thetaMin;
182
183 real_t OptErrSubproblem, OptErr;
184
185 int maxBarrierSolves = 10;
186 for (int j = 0; j < max_iter; j++)
187 {
188 if (myid == 0 && print_level > 0)
189 {
190 mfem::out << "\n" << std::string(50,'-') << std::endl;
191 mfem::out << "interior-point solve step # " << j << std::endl;
192 }
193 // Check convergence of optimization problem
194 OptErr = OptimalityError(xk, lk, zlk);
195 if (OptErr < abs_tol)
196 {
197 converged = true;
198 if (myid == 0 && print_level > 0)
199 {
200 mfem::out << "solved optimization problem\n";
201 }
202 break;
203 }
204
205 if (j > 0) { maxBarrierSolves = 1; }
206 for (int i = 0; i < maxBarrierSolves; i++)
207 {
208 // Check convergence of the barrier subproblem
209 OptErrSubproblem = OptimalityError(xk, lk, zlk, mu_k);
210 if (OptErrSubproblem < kEps * mu_k)
211 {
212 if (myid == 0 && print_level > 0)
213 {
214 mfem::out << "solved mu = " << mu_k << " barrier subproblem\n";
215 }
216 UpdateBarrierSubProblem();
217 }
218 else
219 {
220 break;
221 }
222 }
223
224 // Compute the search direction
225 // solve for (uhat, mhat, lhat)
226 zlhat = 0.0; Xhatuml = 0.0;
227
228 bool passedCurvatureTest = false;
229 IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, passedCurvatureTest, mu_k);
230 if (!passedCurvatureTest)
231 {
232 if (myid == 0 && print_level > 0)
233 {
234 mfem::out << "curvature test failed\n";
235 }
236 real_t deltaReg = 0.0;
237 int maxCurvatureTests = 30;
238
239 // inertia regularization initialization
241 {
242 deltaReg = deltaReg0;
243 }
244 else
245 {
246 deltaReg = fmax(deltaRegMin, kRegMinus * deltaRegLast);
247 }
248
249 // solve regularized IP-Newton linear system
250 zlhat = 0.0; Xhatuml = 0.0;
251 IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, passedCurvatureTest, mu_k, deltaReg);
252
253 for (int numCurvatureTests = 0; numCurvatureTests < maxCurvatureTests;
254 numCurvatureTests++)
255 {
256 if (myid == 0 && print_level > 0)
257 {
258 mfem::out << "deltaReg = " << deltaReg << std::endl;
259 }
260 if (passedCurvatureTest)
261 {
262 deltaRegLast = deltaReg;
263 break;
264 }
265 else
266 {
268 {
269 if (myid == 0 && print_level > 0)
270 {
271 mfem::out << "delta *= " << kRegBarPlus << "\n";
272 }
273 deltaReg *= kRegBarPlus;
274 }
275 else
276 {
277 deltaReg *= kRegPlus;
278 }
279 }
280 // solve with regularization
281 zlhat = 0.0; Xhatuml = 0.0;
282 IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, passedCurvatureTest, mu_k, deltaReg);
283 }
284 }
285
286 Xk = 0.0;
287 Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
288 Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
289 Xk.GetBlock(2).Set(1.0, lk);
290 Xk.GetBlock(3).Set(1.0, zlk);
291
292 Xhat = 0.0;
293 for (int i = 0; i < 3; i++)
294 {
295 Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
296 }
297 Xhat.GetBlock(3).Set(1.0, zlhat);
298
299 if (myid == 0 && print_level > 0)
300 {
301 mfem::out << "mu = " << mu_k << std::endl;
302 }
303 LineSearch(Xk, Xhat, mu_k);
304
306 {
307 if (myid == 0 && print_level > 0)
308 {
309 mfem::out << "lineSearch successful\n";
310 }
312 {
313 F1.Append( (1. - gTheta) * thx0);
314 F2.Append( phx0 - gPhi * thx0);
315 }
316 // Accept the trial point
317 xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
318 xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
319 lk.Add(alpha, Xhat.GetBlock(2));
320 zlk.Add(alphaz, Xhat.GetBlock(3));
321 ProjectZ(xk, zlk, mu_k);
322 }
323 else
324 {
325 if (myid == 0 && print_level > 0)
326 {
327 mfem::out << "lineSearch not successful\n";
328 }
329 converged = false;
330 break;
331 }
332 if (j + 1 == max_iter && myid == 0 && print_level > 0)
333 {
334 mfem::out << "maximum optimization iterations\n";
335 }
336 }
337 xf = 0.0;
338 xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
339 xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
340}
341
342void IPSolver::FormIPNewtonMat(BlockVector & x, Vector & l,
343 Vector &zl,
345{
346 Huu = problem->Duuf(x);
347 Hmm = problem->Dmmf(x);
348
349 delete JuT;
350 delete JmT;
351 Ju = problem->Duc(x); JuT = Ju->Transpose();
352 Jm = problem->Dmc(x); JmT = Jm->Transpose();
353
354 Vector DiagLogBar(dimM); DiagLogBar = 0.0;
355 for (int i = 0; i < dimM; i++)
356 {
357 DiagLogBar(i) = (Mlump(i) * zl(i)) / x(i + dimU) + delta * Mlump(i);
358 }
359
360 delete Wmm;
361 if (Hmm)
362 {
363 SparseMatrix * Ds = new SparseMatrix(DiagLogBar);
364 HypreParMatrix * D = new HypreParMatrix(comm,
366 HypreStealOwnership(*D,*Ds);
367 delete Ds;
368 Wmm = ParAdd(Hmm,D);
369 delete D;
370 }
371 else
372 {
373 SparseMatrix * Ds = new SparseMatrix(DiagLogBar);
374 Wmm = new HypreParMatrix(comm, problem->GetGlobalNumConstraints(),
377 delete Ds;
378 }
379
380 Vector deltaDiagVec(dimU);
381 deltaDiagVec = delta;
382 deltaDiagVec *= Mvlump;
383
384 delete Wuu;
385 if (Huu)
386 {
387 SparseMatrix * Duus = new SparseMatrix(deltaDiagVec);
388 HypreParMatrix * Duu = new HypreParMatrix(comm, problem->GetGlobalNumDofs(),
389 problem->GetDofStarts(), Duus);
390 HypreStealOwnership(*Duu, *Duus);
391 delete Duus;
392 Wuu = ParAdd(Huu, Duu);
393 delete Duu;
394 }
395 else
396 {
397 SparseMatrix * DuuS = new SparseMatrix(deltaDiagVec);
398 Wuu = new HypreParMatrix(comm, problem->GetGlobalNumDofs(),
399 problem->GetDofStarts(), DuuS);
400 HypreStealOwnership(*Wuu, *DuuS);
401 delete DuuS;
402 }
403
404 // IP-Newton system matrix
405 // Ak = [[W_(u,u) H_(u,m) J_u^T]
406 // [H_(m,u) W_(m,m) J_m^T]
407 // [ J_u J_m 0 ]]
408
409 // Ak = [[K+Dreg 0 Jᵀ ] [u] [bᵤ]
410 // [0 D+Dreg -I ] [m] = [bₘ]
411 // [J -I 0 ]] [λ] = [bₗ ]
412 Ak.SetBlock(0, 0, Wuu); Ak.SetBlock(0, 2, JuT);
413 Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
414 Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
415}
416
417// perturbed KKT system solve
418// determine the search direction
419void IPSolver::IPNewtonSolve(BlockVector &x, Vector &l,
420 Vector &zl, Vector &zlhat, BlockVector &Xhat, bool & passedCurvatureTest,
421 real_t mu,
423{
424 iter++;
425 // solve A x = b, where A is the IP-Newton matrix
426 BlockOperator A(block_offsetsuml, block_offsetsuml);
427 BlockVector b(block_offsetsuml); b = 0.0;
428 FormIPNewtonMat(x, l, zl, A, delta);
429
430 // [grad_u phi + Ju^T l]
431 // b = - [grad_m phi + Jm^T l]
432 // [ c ]
433 BlockVector gradphi(block_offsetsx); gradphi = 0.0;
434 GetDxphi(x, mu, gradphi);
435
436 for (int i = 0; i < 2; i++)
437 {
438 b.GetBlock(i).Set(1.0, gradphi.GetBlock(i));
439 A.GetBlock(i, 2).AddMult(l, b.GetBlock(i));
440 }
441 problem->c(x, b.GetBlock(2));
442 b *= -1.0;
443 Xhat = 0.0;
444
445 // form A = Huu + Ju^T D Ju, Wmm = D for contact
446 HypreParMatrix *JuTDJu = RAP(Wmm, Ju); // Ju^T D Ju
447 HypreParMatrix *Areduced = ParAdd(Huu, JuTDJu); // Huu + Ju^T D Ju
448
449 /* compute the reduced rhs */
450 // breduced = bu + Ju^T (bm + Wmm bl)
451 Vector breduced(dimU); breduced = 0.0;
452 Vector tempVec(dimM); tempVec = 0.0;
453 Wmm->Mult(b.GetBlock(2), tempVec);
454 tempVec.Add(1.0, b.GetBlock(1));
455 JuT->Mult(tempVec, breduced);
456 breduced.Add(1.0, b.GetBlock(0));
457
458 // Solver the system with the given solver
459 solver->SetOperator(*Areduced);
460 solver->Mult(breduced, Xhat.GetBlock(0));
461 auto itsolver = dynamic_cast<IterativeSolver *>(solver);
462 int numit = (itsolver) ? itsolver->GetNumIterations() : -1;
464
465 // now propagate solved uhat to obtain mhat and lhat
466 // xm = Ju xu - bl
467 Ju->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
468 Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
469
470 // xl = Wmm xm - bm
471 Wmm->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
472 Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
473
474 delete JuTDJu;
475 delete Areduced;
476
477 passedCurvatureTest = CurvatureTest(A, Xhat, l, b, delta);
478
479 /* backsolve to determine zlhat */
480 for (int i = 0; i < dimM; i++)
481 {
482 zlhat(i) = zl(i) + (zl(i) * Xhat(i + dimU) - mu) / x(i + dimU);
483 }
484 zlhat *= -1.;
485}
486
487real_t IPSolver::GetMaxStepSize(Vector &x, Vector &xhat,
488 real_t tau)
489{
490 real_t alphaMaxloc = 1.0;
491 real_t alphaTmp;
492 for (int i = 0; i < x.Size(); i++)
493 {
494 if ( xhat(i) < 0. )
495 {
496 alphaTmp = -1. * tau * x(i) / xhat(i);
497 alphaMaxloc = std::min(alphaMaxloc, alphaTmp);
498 }
499 }
500
501 real_t alphaMaxglb;
502 MPI_Allreduce(&alphaMaxloc, &alphaMaxglb, 1, MPITypeMap<real_t>::mpi_type,
503 MPI_MIN, comm);
504 return alphaMaxglb;
505}
506
507/* line-search from X0 along direction Xhat for the log-barrier
508 * subproblem with barrier parameter \mu
509 */
510void IPSolver::LineSearch(BlockVector& X0, BlockVector& Xhat,
511 real_t mu)
512{
513 int eval_err = 0;
514 real_t tau = std::max(tauMin, 1.0 - mu);
515 Vector u0 = X0.GetBlock(0);
516 Vector m0 = X0.GetBlock(1);
517 Vector l0 = X0.GetBlock(2);
518 Vector z0 = X0.GetBlock(3);
519 Vector uhat = Xhat.GetBlock(0);
520 Vector mhat = Xhat.GetBlock(1);
521 Vector lhat = Xhat.GetBlock(2);
522 Vector zhat = Xhat.GetBlock(3);
523 real_t alphaMax = GetMaxStepSize(m0, mhat, tau);
524 real_t alphaMaxz = GetMaxStepSize(z0, zhat, tau);
525 alphaz = alphaMaxz;
526
527 BlockVector x0(block_offsetsx); x0 = 0.0;
528 x0.GetBlock(0).Set(1.0, u0);
529 x0.GetBlock(1).Set(1.0, m0);
530
531 BlockVector xhat(block_offsetsx); xhat = 0.0;
532 xhat.GetBlock(0).Set(1.0, uhat);
533 xhat.GetBlock(1).Set(1.0, mhat);
534
535 BlockVector xtrial(block_offsetsx); xtrial = 0.0;
536 BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
537 int maxBacktrack = 20;
538 alpha = alphaMax;
539
540 GetDxphi(x0, mu, Dxphi0);
541
542 real_t Dxphi0_xhat = InnerProduct(comm, Dxphi0, xhat);
543 bool descentDirection = (Dxphi0_xhat < 0.);
544
545
546 if (myid == 0 && print_level > 0)
547 {
548 mfem::out << "is";
549 if (!descentDirection)
550 {
551 mfem::out << " not";
552 }
553 mfem::out << " a descent direction for the log-barrier objective\n";
554 }
555
556 thx0 = GetTheta(x0);
557 phx0 = GetPhi(x0, mu);
558
559 lineSearchSuccess = false;
560 for (int i = 0; i < maxBacktrack; i++)
561 {
562 if (myid == 0 && print_level > 0)
563 {
564 mfem::out << "\n--------- alpha = " << alpha << " ---------\n";
565 }
566 // ----- Compute trial point: xtrial = x0 + alpha * xhat
567 xtrial.Set(1.0, x0);
568 xtrial.Add(alpha, xhat);
569
570 real_t thxtrial = GetTheta(xtrial);
571 real_t phxtrial = GetPhi(xtrial, mu, eval_err);
572 if (eval_err == 1)
573 {
574 if (myid == 0 && print_level > 0)
575 {
576 mfem::out << "bad log-barrier objective eval, reducing step length\n";
577 }
578 alpha *= 0.5;
579 continue;
580 }
581
582 auto inFilterRegion = FilterCheck(thxtrial, phxtrial);
583 if (!inFilterRegion)
584 {
585 if (myid == 0 && print_level > 0)
586 {
587 mfem::out << "not in filter region\n";
588 }
589 if (!descentDirection)
590 {
591 switchCondition = false;
592 }
593 else
594 {
595 switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0,
596 sTheta));
597 }
598 if (myid == 0 && print_level > 0)
599 {
600 mfem::out << "theta(x0) = " << thx0 << ", thetaMin = "
601 <<
602 thetaMin << std::endl;
603 mfem::out << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = "
604 <<
605 (1. - gTheta) * thx0 << std::endl;
606 mfem::out << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = "
607 <<
608 phx0 - gPhi * thx0 << std::endl;
609 }
610 if (thx0 <= thetaMin && switchCondition)
611 {
612 // sufficient decrease of the log-barrier objective
613 sufficientDecrease = (phxtrial <= phx0 + eta * alpha * Dxphi0_xhat);
615 {
616 if (myid == 0 && print_level > 0)
617 {
618 mfem::out <<
619 "Line search successful: sufficient decrease in log-barrier objective.\n";
620 }
621 // accept the trial step
622 lineSearchSuccess = true;
623 break;
624 }
625 }
626 else
627 {
628 if (thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
629 {
630 if (myid == 0 && print_level > 0)
631 {
632 mfem::out <<
633 "Line search successful: infeasibility or log-barrier objective decreased.\n";
634 }
635 // accept the trial step
636 lineSearchSuccess = true;
637 break;
638 }
639 }
640 }
641 else
642 {
643 if (myid == 0 && print_level > 0)
644 {
645 mfem::out << "in filter region\n";
646 }
647 }
648 alpha *= 0.5;
649 }
650}
651
652bool IPSolver::FilterCheck(real_t thetax, real_t phix)
653{
654 bool inFilterRegion = false;
655 if (thetax > thetaMax)
656 {
657 inFilterRegion = true;
658 }
659 else
660 {
661 for (int i = 0; i < F1.Size(); i++)
662 {
663 if (thetax >= F1[i] && phix >= F2[i])
664 {
665 inFilterRegion = true;
666 break;
667 }
668 }
669 }
670 return inFilterRegion;
671}
672
673void IPSolver::ProjectZ(const Vector &x, Vector &z, real_t mu)
674{
675 real_t zdual_i;
676 real_t zprimal_i;
677 for (int i = 0; i < dimM; i++)
678 {
679 zdual_i = z(i);
680 zprimal_i = mu / x(i + dimU);
681 z(i) = std::max(std::min(zdual_i, kSig * zprimal_i), zprimal_i / kSig);
682 }
683}
684
685real_t IPSolver::GetTheta(const BlockVector &x)
686{
687 Vector cx(dimC);
688 problem->c(x, cx);
689 Vector Mcx(dimC);
690 Mcx.Set(1.0, cx);
691 Mcx *= Mlump;
692 return sqrt(InnerProduct(comm, Mcx, cx));
693}
694
695// log-barrier objective
696real_t IPSolver::GetPhi(const BlockVector &x, real_t mu,
697 int eval_err)
698{
699 real_t fx = problem->CalcObjective(x, eval_err);
700 real_t logBarrierLoc = 0.0;
701 for (int i = 0; i < dimM; i++)
702 {
703 logBarrierLoc += Mlump(i) * log(x(dimU + i));
704 }
705 real_t logBarrierGlb;
706 MPI_Allreduce(&logBarrierLoc, &logBarrierGlb, 1, MPITypeMap<real_t>::mpi_type,
707 MPI_SUM, comm);
708 return fx - mu * logBarrierGlb;
709}
710
711// gradient of log-barrier objective with respect to x = (u, m)
712void IPSolver::GetDxphi(const BlockVector &x, real_t mu,
713 BlockVector &y)
714{
716 Vector ytemp(dimM); ytemp = 1.0;
717 ytemp /= x.GetBlock(1);
718 ytemp *= Mlump;
719 y.GetBlock(1).Add(-mu, ytemp);
720}
721
722// Lagrangian function evaluation
723// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
724real_t IPSolver::EvalLangrangian(const BlockVector &x, const Vector &l,
725 const Vector &zl)
726{
727 int eval_err = 0;
728 real_t fx = problem->CalcObjective(x, eval_err);
729 Vector cx(dimC); problem->c(x, cx);
730 Vector temp(dimM); temp = 0.0;
731 temp.Set(1.0, x.GetBlock(1));
732 temp *= Mlump;
733 return (fx + InnerProduct(comm, cx, l) - InnerProduct(comm, temp, zl));
734}
735
736// Gradient of the Lagrangian
737// \nabla_x L = [ \nabla_u f + (\partial c / \partial u)^T l]
738// [ \nabla_m f + (\partial c / \partial m)^T l - zl]
739void IPSolver::EvalLagrangianGradient(const BlockVector &x, const Vector &l,
740 const Vector &zl, BlockVector &y)
741{
742 // evaluate the gradient of the objective with respect to the primal variables x = (u, m)
743 y.GetBlock(1).Set(-1.0, zl);
744 y.GetBlock(1) *= Mlump;
745
746 problem->Duc(x)->MultTranspose(l, y.GetBlock(0));
747 problem->Dmc(x)->AddMultTranspose(l, y.GetBlock(1));
748
749 BlockVector gradxf(block_offsetsx); gradxf = 0.0;
750 problem->CalcObjectiveGrad(x, gradxf);
751 y.Add(1.0, gradxf);
752}
753
754
755// curvature test
756// dk^T Wk dk + max{ -(lk + lhat)^T ck, 0.0} >= alpha * dk^T dk
757// see "An Inertia-Free Filter Line-search Algorithm for
758// Large-scale Nonlinear Programming" by Nai-Yuan Chiang and
759// Victor M Zavala, Computational Optimization and Applications (2016)
760bool IPSolver::CurvatureTest(const BlockOperator & A,
761 const BlockVector & Xhat, const Vector & l, const BlockVector & b,
762 const real_t & delta)
763{
764 Vector lplus(l.Size());
765 lplus.Set(1.0, l);
766 lplus.Add(1.0, Xhat.GetBlock(2));
767
768
769 real_t dWd = 0.0;
770 real_t dd = 0.0;
771 for (int i = 0; i < 2; i++)
772 {
773 for (int j = 0; j < 2; j++)
774 {
775 if (!A.IsZeroBlock(i, j))
776 {
777 Vector temp(A.GetBlock(i, j).Height()); temp = 0.0;
778 A.GetBlock(i, j).Mult(Xhat.GetBlock(j), temp);
779 dWd += InnerProduct(comm, Xhat.GetBlock(i), temp);
780 }
781 }
782 dd += InnerProduct(comm, Xhat.GetBlock(i), Xhat.GetBlock(i));
783 }
784 real_t lplusTck = -1.0 * InnerProduct(comm, lplus, b.GetBlock(2));
785
786 bool passed = (dWd + fmax(-lplusTck,
787 0.0) >= alphaCurvatureTest * dd);
788 return passed;
789}
790
791
792real_t IPSolver::OptimalityError(const BlockVector &x, const Vector &l,
793 const Vector &zl, real_t mu)
794{
795 /* stationarity, feasibility, and complementarity errors */
796 real_t stationarityError, feasibilityError, complementarityError;
797 real_t optimalityError;
798
799 /* gradient of Lagrangian */
800 BlockVector gradL(block_offsetsx);
801 gradL = 0.0;
802 EvalLagrangianGradient(x, l, zl, gradL);
803
804 /* constraint function c(u, m) = 0 */
805 Vector cx(dimC); cx = 0.0;
806 problem->c(x, cx);
807
808 /* regularized complementarity
809 * |z_i * m_i - mu| */
810 Vector comp(dimM); comp = 0.0; // complementarity Z m - mu 1
811 for (int i = 0; i < dimM; i++)
812 {
813 comp(i) = abs(x(dimU + i) * zl(i) - mu);
814 }
815
816 BlockVector MxinvgradL(block_offsetsx); MxinvgradL = 0.0;
817 MxinvgradL.Set(1.0, gradL);
818 MxinvgradL.GetBlock(0) /= Mvlump;
819 MxinvgradL.GetBlock(1) /= Mlump;
820 stationarityError = sqrt(InnerProduct(comm, gradL, MxinvgradL));
821 feasibilityError = GlobalLpNorm(infinity(), cx.Normlinf(), comm);
822 complementarityError = GlobalLpNorm(infinity(), comp.Normlinf(), comm);
823
824
825 optimalityError = std::max(std::max(stationarityError, feasibilityError),
826 complementarityError);
827
828 if (myid == 0 && print_level > 0)
829 {
830 mfem::out << "evaluating optimality error for mu = " << mu << std::endl;
831 mfem::out << "stationarity error = " << stationarityError << std::endl;
832 mfem::out << "feasibility error = " << feasibilityError << std::endl;
833 mfem::out << "complimentarity error = " << complementarityError << std::endl;
834 mfem::out << "optimality error = " << optimalityError << std::endl;
835 }
836 return optimalityError;
837}
838
840{
841 if (iter > 0)
842 {
843 delete JuT;
844 delete JmT;
845 delete Wuu;
846 delete Wmm;
847 }
848}
849
850}
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
int Size() const
Return the logical size of the array.
Definition array.hpp:166
void PartialSum()
Fill the entries of the array with the cumulative sum of the entries.
Definition array.cpp:104
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:912
A class to handle Block systems in a matrix-free implementation.
void SetBlock(int iRow, int iCol, Operator *op, real_t c=1.0)
Add a block op in the block-entry (iblock, jblock).
A class to handle Vectors in a block fashion.
Vector & GetBlock(int i)
Get the i-th vector in the block.
HYPRE_Int MultTranspose(HypreParVector &x, HypreParVector &y, real_t alpha=1.0, real_t beta=0.0) const
Computes y = alpha * A^t * x + beta * y.
Definition hypre.cpp:1999
HYPRE_Int Mult(HypreParVector &x, HypreParVector &y, real_t alpha=1.0, real_t beta=0.0) const
Computes y = alpha * A * x + beta * y.
Definition hypre.cpp:1863
void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
Operator transpose application: y+=A^t(x) (default) or y+=a*A^t(x).
Definition hypre.hpp:781
HypreParMatrix * Transpose() const
Returns the transpose of *this.
Definition hypre.cpp:1732
real_t tauMin
Definition ip.hpp:102
virtual ~IPSolver()
Definition ip.cpp:839
real_t deltaRegMin
Definition ip.hpp:138
real_t thetaMax
Definition ip.hpp:103
real_t kRegPlus
Definition ip.hpp:145
int print_level
print level, 0: no printing, > 0 various solver progress output is shown
Definition ip.hpp:154
Vector Mlump
Definition ip.hpp:133
real_t kMu
Definition ip.hpp:102
real_t deltaRegMax
Definition ip.hpp:139
OptContactProblem * problem
OptContactProblem (not owned).
Definition ip.hpp:90
Array< int > lin_solver_iterations
Definition ip.hpp:147
HypreParMatrix * JuT
Definition ip.hpp:127
Vector Mvlump
Definition ip.hpp:132
real_t thetaMin
Definition ip.hpp:102
Array< real_t > F2
Definition ip.hpp:106
real_t kRegMinus
inertia-regularization rate parameters
Definition ip.hpp:143
HypreParMatrix * JmT
Definition ip.hpp:128
bool lineSearchSuccess
Definition ip.hpp:114
real_t alphaCurvatureTest
inertia-regularization parameters
Definition ip.hpp:136
real_t deltaReg0
Definition ip.hpp:140
HypreParMatrix * Hmm
Definition ip.hpp:122
real_t kRegBarPlus
Definition ip.hpp:144
Vector Mcslump
Lumped masses.
Definition ip.hpp:131
IPSolver(OptContactProblem *)
Construct interior-point solver.
Definition ip.cpp:17
real_t deltaRegLast
Definition ip.hpp:137
real_t gTheta
Definition ip.hpp:103
bool switchCondition
Definition ip.hpp:112
real_t thx0
Definition ip.hpp:110
real_t mu_k
Definition ip.hpp:98
Array< real_t > F1
Definition ip.hpp:106
HypreParMatrix * Ju
Definition ip.hpp:125
Array< int > constraint_offsets
Definition ip.hpp:117
real_t thetaMu
Definition ip.hpp:102
Array< int > block_offsetsx
Definition ip.hpp:118
Vector zlk
Definition ip.hpp:99
real_t alphaz
Definition ip.hpp:109
real_t eta
Definition ip.hpp:102
void Mult(const Vector &, Vector &)
Apply the interior-point solver.
Definition ip.cpp:132
Array< int > block_offsetsumlz
Definition ip.hpp:118
real_t abs_tol
Definition ip.hpp:95
real_t alpha
Definition ip.hpp:109
HypreParMatrix * Huu
Operators (not owned)
Definition ip.hpp:121
real_t delta
Definition ip.hpp:102
bool converged
Definition ip.hpp:149
int max_iter
Definition ip.hpp:96
Vector lk
Definition ip.hpp:99
MPI_Comm comm
Definition ip.hpp:155
HypreParMatrix * Wuu
Definition ip.hpp:123
Array< int > block_offsetsuml
Definition ip.hpp:118
real_t sTheta
Definition ip.hpp:102
real_t kSig
interior-point algorithm parameters
Definition ip.hpp:102
real_t gPhi
Definition ip.hpp:103
HypreParMatrix * Wmm
Definition ip.hpp:124
real_t sPhi
Definition ip.hpp:102
Solver * solver
Linear solver (not owned)
Definition ip.hpp:93
HypreParMatrix * Jm
Definition ip.hpp:126
bool sufficientDecrease
Definition ip.hpp:113
real_t phx0
Definition ip.hpp:111
real_t kEps
Definition ip.hpp:103
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
Contact optimization problem with mortar and non-mortar interfaces.
MPI_Comm GetComm()
Get MPI communicator.
HYPRE_BigInt * GetConstraintsStarts()
Get distributed constraint partition offsets.
void CalcObjectiveGrad(const BlockVector &, BlockVector &)
Compute gradient of objective functional.
int GetDimM()
Return slack variable dimension.
void GetLumpedMassWeights(Vector &Mcslump_, Vector &Mvlump_)
Get lumped mass weights for contact and volume spaces.
int GetDimU()
Return displacement space dimension.
int GetDimC()
Return constraint space dimension.
HypreParMatrix * Dmc(const BlockVector &)
Jacobian of the constraints wrt slack variables.
real_t CalcObjective(const BlockVector &, int &)
Compute objective functional value.
HypreParMatrix * Duc(const BlockVector &)
Jacobian of the constraints wrt displacement.
HypreParMatrix * Duuf(const BlockVector &x)
Hessian of the objective wrt displacement.
HYPRE_BigInt GetGlobalNumConstraints()
Return global number of constraints.
void c(const BlockVector &, Vector &)
Evaluate contact constraints.
HypreParMatrix * Dmmf(const BlockVector &)
Hessian of the objective wrt the slack variables.
HYPRE_BigInt * GetDofStarts()
Get distributed DOF partition offsets.
HYPRE_BigInt GetGlobalNumDofs()
Return global number of DOFs (from Jacobian).
virtual void SetOperator(const Operator &op)=0
Set/update the solver for the given operator.
Vector data type.
Definition vector.hpp:82
Vector & Set(const real_t a, const Vector &x)
(*this) = a * x
Definition vector.cpp:341
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:326
int problem
Definition ex15.cpp:62
real_t mu
Definition ex25.cpp:140
real_t b
Definition lissajous.cpp:42
real_t delta
Definition lissajous.cpp:43
mfem::real_t real_t
MFEM_HOST_DEVICE dual< value_type, gradient_type > log(dual< value_type, gradient_type > a)
implementation of the natural logarithm function for dual numbers
Definition dual.hpp:366
MFEM_HOST_DEVICE dual< value_type, gradient_type > sqrt(dual< value_type, gradient_type > x)
implementation of square root for dual numbers
Definition dual.hpp:288
MFEM_HOST_DEVICE dual< value_type, gradient_type > pow(dual< value_type, gradient_type > a, dual< value_type, gradient_type > b)
implementation of a (dual) raised to the b (dual) power
Definition dual.hpp:374
real_t infinity()
Define a shortcut for std::numeric_limits<double>::infinity()
Definition vector.hpp:47
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
real_t GlobalLpNorm(const real_t p, real_t loc_norm, MPI_Comm comm)
Compute a global Lp norm from the local Lp norms computed by each processor.
real_t InnerProduct(HypreParVector *x, HypreParVector *y)
Definition hypre.cpp:468
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
Returns the matrix A + B.
Definition hypre.cpp:2981
void HypreStealOwnership(HypreParMatrix &A_hyp, SparseMatrix &A_diag)
Make A_hyp steal ownership of its diagonal part A_diag.
Definition hypre.cpp:2912
float real_t
Definition config.hpp:46
MFEM_HOST_DEVICE real_t abs(const Complex &z)