Actual source code: asfls.c
1: #include <../src/tao/complementarity/impls/ssls/ssls.h>
2: /*
3: Context for ASXLS
4: -- active-set - reduced matrices formed
5: - inherit properties of original system
6: -- semismooth (S) - function not differentiable
7: - merit function continuously differentiable
8: - Fischer-Burmeister reformulation of complementarity
9: - Billups composition for two finite bounds
10: -- infeasible (I) - iterates not guaranteed to remain within bounds
11: -- feasible (F) - iterates guaranteed to remain within bounds
12: -- linesearch (LS) - Armijo rule on direction
14: Many other reformulations are possible and combinations of
15: feasible/infeasible and linesearch/trust region are possible.
17: Basic theory
18: Fischer-Burmeister reformulation is semismooth with a continuously
19: differentiable merit function and strongly semismooth if the F has
20: lipschitz continuous derivatives.
22: Every accumulation point generated by the algorithm is a stationary
23: point for the merit function. Stationary points of the merit function
24: are solutions of the complementarity problem if
25: a. the stationary point has a BD-regular subdifferential, or
26: b. the Schur complement F'/F'_ff is a P_0-matrix where ff is the
27: index set corresponding to the free variables.
29: If one of the accumulation points has a BD-regular subdifferential then
30: a. the entire sequence converges to this accumulation point at
31: a local q-superlinear rate
32: b. if in addition the reformulation is strongly semismooth near
33: this accumulation point, then the algorithm converges at a
34: local q-quadratic rate.
36: The theory for the feasible version follows from the feasible descent
37: algorithm framework. See {cite}`billups:algorithms`, {cite}`deluca.facchinei.ea:semismooth`,
38: {cite}`ferris.kanzow.ea:feasible`, {cite}`fischer:special`, and {cite}`munson.facchinei.ea:semismooth`.
39: */
41: static PetscErrorCode TaoSetUp_ASFLS(Tao tao)
42: {
43: TAO_SSLS *asls = (TAO_SSLS *)tao->data;
45: PetscFunctionBegin;
46: PetscCall(VecDuplicate(tao->solution, &tao->gradient));
47: PetscCall(VecDuplicate(tao->solution, &tao->stepdirection));
48: PetscCall(VecDuplicate(tao->solution, &asls->ff));
49: PetscCall(VecDuplicate(tao->solution, &asls->dpsi));
50: PetscCall(VecDuplicate(tao->solution, &asls->da));
51: PetscCall(VecDuplicate(tao->solution, &asls->db));
52: PetscCall(VecDuplicate(tao->solution, &asls->t1));
53: PetscCall(VecDuplicate(tao->solution, &asls->t2));
54: PetscCall(VecDuplicate(tao->solution, &asls->w));
55: asls->fixed = NULL;
56: asls->free = NULL;
57: asls->J_sub = NULL;
58: asls->Jpre_sub = NULL;
59: asls->r1 = NULL;
60: asls->r2 = NULL;
61: asls->r3 = NULL;
62: asls->dxfree = NULL;
63: PetscFunctionReturn(PETSC_SUCCESS);
64: }
66: static PetscErrorCode Tao_ASLS_FunctionGradient(TaoLineSearch ls, Vec X, PetscReal *fcn, Vec G, void *ptr)
67: {
68: Tao tao = (Tao)ptr;
69: TAO_SSLS *asls = (TAO_SSLS *)tao->data;
71: PetscFunctionBegin;
72: PetscCall(TaoComputeConstraints(tao, X, tao->constraints));
73: PetscCall(VecFischer(X, tao->constraints, tao->XL, tao->XU, asls->ff));
74: PetscCall(VecNorm(asls->ff, NORM_2, &asls->merit));
75: *fcn = 0.5 * asls->merit * asls->merit;
76: PetscCall(TaoComputeJacobian(tao, tao->solution, tao->jacobian, tao->jacobian_pre));
78: PetscCall(MatDFischer(tao->jacobian, tao->solution, tao->constraints, tao->XL, tao->XU, asls->t1, asls->t2, asls->da, asls->db));
79: PetscCall(VecPointwiseMult(asls->t1, asls->ff, asls->db));
80: PetscCall(MatMultTranspose(tao->jacobian, asls->t1, G));
81: PetscCall(VecPointwiseMult(asls->t1, asls->ff, asls->da));
82: PetscCall(VecAXPY(G, 1.0, asls->t1));
83: PetscFunctionReturn(PETSC_SUCCESS);
84: }
86: static PetscErrorCode TaoDestroy_ASFLS(Tao tao)
87: {
88: TAO_SSLS *ssls = (TAO_SSLS *)tao->data;
90: PetscFunctionBegin;
91: PetscCall(VecDestroy(&ssls->ff));
92: PetscCall(VecDestroy(&ssls->dpsi));
93: PetscCall(VecDestroy(&ssls->da));
94: PetscCall(VecDestroy(&ssls->db));
95: PetscCall(VecDestroy(&ssls->w));
96: PetscCall(VecDestroy(&ssls->t1));
97: PetscCall(VecDestroy(&ssls->t2));
98: PetscCall(VecDestroy(&ssls->r1));
99: PetscCall(VecDestroy(&ssls->r2));
100: PetscCall(VecDestroy(&ssls->r3));
101: PetscCall(VecDestroy(&ssls->dxfree));
102: PetscCall(MatDestroy(&ssls->J_sub));
103: PetscCall(MatDestroy(&ssls->Jpre_sub));
104: PetscCall(ISDestroy(&ssls->fixed));
105: PetscCall(ISDestroy(&ssls->free));
106: PetscCall(KSPDestroy(&tao->ksp));
107: PetscCall(PetscFree(tao->data));
108: PetscFunctionReturn(PETSC_SUCCESS);
109: }
111: static PetscErrorCode TaoSolve_ASFLS(Tao tao)
112: {
113: TAO_SSLS *asls = (TAO_SSLS *)tao->data;
114: PetscReal psi, ndpsi, normd, innerd, t = 0;
115: PetscInt nf;
116: TaoLineSearchConvergedReason ls_reason;
118: PetscFunctionBegin;
119: /* Assume that Setup has been called!
120: Set the structure for the Jacobian and create a linear solver. */
122: PetscCall(TaoComputeVariableBounds(tao));
123: PetscCall(TaoLineSearchSetObjectiveAndGradientRoutine(tao->linesearch, Tao_ASLS_FunctionGradient, tao));
124: PetscCall(TaoLineSearchSetObjectiveRoutine(tao->linesearch, Tao_SSLS_Function, tao));
125: PetscCall(TaoLineSearchSetVariableBounds(tao->linesearch, tao->XL, tao->XU));
127: PetscCall(VecMedian(tao->XL, tao->solution, tao->XU, tao->solution));
129: /* Calculate the function value and fischer function value at the
130: current iterate */
131: PetscCall(TaoLineSearchComputeObjectiveAndGradient(tao->linesearch, tao->solution, &psi, asls->dpsi));
132: PetscCall(VecNorm(asls->dpsi, NORM_2, &ndpsi));
134: tao->reason = TAO_CONTINUE_ITERATING;
135: while (1) {
136: /* Check the converged criteria */
137: PetscCall(PetscInfo(tao, "iter %" PetscInt_FMT ", merit: %g, ||dpsi||: %g\n", tao->niter, (double)asls->merit, (double)ndpsi));
138: PetscCall(TaoLogConvergenceHistory(tao, asls->merit, ndpsi, 0.0, tao->ksp_its));
139: PetscCall(TaoMonitor(tao, tao->niter, asls->merit, ndpsi, 0.0, t));
140: PetscUseTypeMethod(tao, convergencetest, tao->cnvP);
141: if (TAO_CONTINUE_ITERATING != tao->reason) break;
143: /* Call general purpose update function */
144: PetscTryTypeMethod(tao, update, tao->niter, tao->user_update);
145: tao->niter++;
147: /* We are going to solve a linear system of equations. We need to
148: set the tolerances for the solve so that we maintain an asymptotic
149: rate of convergence that is superlinear.
150: Note: these tolerances are for the reduced system. We really need
151: to make sure that the full system satisfies the full-space conditions.
153: This rule gives superlinear asymptotic convergence
154: asls->atol = min(0.5, asls->merit*sqrt(asls->merit));
155: asls->rtol = 0.0;
157: This rule gives quadratic asymptotic convergence
158: asls->atol = min(0.5, asls->merit*asls->merit);
159: asls->rtol = 0.0;
161: Calculate a free and fixed set of variables. The fixed set of
162: variables are those for the d_b is approximately equal to zero.
163: The definition of approximately changes as we approach the solution
164: to the problem.
166: No one rule is guaranteed to work in all cases. The following
167: definition is based on the norm of the Jacobian matrix. If the
168: norm is large, the tolerance becomes smaller. */
169: PetscCall(MatNorm(tao->jacobian, NORM_1, &asls->identifier));
170: asls->identifier = PetscMin(asls->merit, 1e-2) / (1 + asls->identifier);
172: PetscCall(VecSet(asls->t1, -asls->identifier));
173: PetscCall(VecSet(asls->t2, asls->identifier));
175: PetscCall(ISDestroy(&asls->fixed));
176: PetscCall(ISDestroy(&asls->free));
177: PetscCall(VecWhichBetweenOrEqual(asls->t1, asls->db, asls->t2, &asls->fixed));
178: PetscCall(ISComplementVec(asls->fixed, asls->t1, &asls->free));
180: PetscCall(ISGetSize(asls->fixed, &nf));
181: PetscCall(PetscInfo(tao, "Number of fixed variables: %" PetscInt_FMT "\n", nf));
183: /* We now have our partition. Now calculate the direction in the
184: fixed variable space. */
185: PetscCall(TaoVecGetSubVec(asls->ff, asls->fixed, tao->subset_type, 0.0, &asls->r1));
186: PetscCall(TaoVecGetSubVec(asls->da, asls->fixed, tao->subset_type, 1.0, &asls->r2));
187: PetscCall(VecPointwiseDivide(asls->r1, asls->r1, asls->r2));
188: PetscCall(VecSet(tao->stepdirection, 0.0));
189: PetscCall(VecISAXPY(tao->stepdirection, asls->fixed, 1.0, asls->r1));
191: /* Our direction in the Fixed Variable Set is fixed. Calculate the
192: information needed for the step in the Free Variable Set. To
193: do this, we need to know the diagonal perturbation and the
194: right hand side. */
196: PetscCall(TaoVecGetSubVec(asls->da, asls->free, tao->subset_type, 0.0, &asls->r1));
197: PetscCall(TaoVecGetSubVec(asls->ff, asls->free, tao->subset_type, 0.0, &asls->r2));
198: PetscCall(TaoVecGetSubVec(asls->db, asls->free, tao->subset_type, 1.0, &asls->r3));
199: PetscCall(VecPointwiseDivide(asls->r1, asls->r1, asls->r3));
200: PetscCall(VecPointwiseDivide(asls->r2, asls->r2, asls->r3));
202: /* r1 is the diagonal perturbation
203: r2 is the right hand side
204: r3 is no longer needed
206: Now need to modify r2 for our direction choice in the fixed
207: variable set: calculate t1 = J*d, take the reduced vector
208: of t1 and modify r2. */
210: PetscCall(MatMult(tao->jacobian, tao->stepdirection, asls->t1));
211: PetscCall(TaoVecGetSubVec(asls->t1, asls->free, tao->subset_type, 0.0, &asls->r3));
212: PetscCall(VecAXPY(asls->r2, -1.0, asls->r3));
214: /* Calculate the reduced problem matrix and the direction */
215: PetscCall(TaoMatGetSubMat(tao->jacobian, asls->free, asls->w, tao->subset_type, &asls->J_sub));
216: if (tao->jacobian != tao->jacobian_pre) {
217: PetscCall(TaoMatGetSubMat(tao->jacobian_pre, asls->free, asls->w, tao->subset_type, &asls->Jpre_sub));
218: } else {
219: PetscCall(MatDestroy(&asls->Jpre_sub));
220: asls->Jpre_sub = asls->J_sub;
221: PetscCall(PetscObjectReference((PetscObject)(asls->Jpre_sub)));
222: }
223: PetscCall(MatDiagonalSet(asls->J_sub, asls->r1, ADD_VALUES));
224: PetscCall(TaoVecGetSubVec(tao->stepdirection, asls->free, tao->subset_type, 0.0, &asls->dxfree));
225: PetscCall(VecSet(asls->dxfree, 0.0));
227: /* Calculate the reduced direction. (Really negative of Newton
228: direction. Therefore, rest of the code uses -d.) */
229: PetscCall(KSPReset(tao->ksp));
230: PetscCall(KSPSetOperators(tao->ksp, asls->J_sub, asls->Jpre_sub));
231: PetscCall(KSPSolve(tao->ksp, asls->r2, asls->dxfree));
232: PetscCall(KSPGetIterationNumber(tao->ksp, &tao->ksp_its));
233: tao->ksp_tot_its += tao->ksp_its;
235: /* Add the direction in the free variables back into the real direction. */
236: PetscCall(VecISAXPY(tao->stepdirection, asls->free, 1.0, asls->dxfree));
238: /* Check the projected real direction for descent and if not, use the negative
239: gradient direction. */
240: PetscCall(VecCopy(tao->stepdirection, asls->w));
241: PetscCall(VecScale(asls->w, -1.0));
242: PetscCall(VecBoundGradientProjection(asls->w, tao->solution, tao->XL, tao->XU, asls->w));
243: PetscCall(VecNorm(asls->w, NORM_2, &normd));
244: PetscCall(VecDot(asls->w, asls->dpsi, &innerd));
246: if (innerd >= -asls->delta * PetscPowReal(normd, asls->rho)) {
247: PetscCall(PetscInfo(tao, "Gradient direction: %5.4e.\n", (double)innerd));
248: PetscCall(PetscInfo(tao, "Iteration %" PetscInt_FMT ": newton direction not descent\n", tao->niter));
249: PetscCall(VecCopy(asls->dpsi, tao->stepdirection));
250: PetscCall(VecDot(asls->dpsi, tao->stepdirection, &innerd));
251: }
253: PetscCall(VecScale(tao->stepdirection, -1.0));
254: innerd = -innerd;
256: /* We now have a correct descent direction. Apply a linesearch to
257: find the new iterate. */
258: PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
259: PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &psi, asls->dpsi, tao->stepdirection, &t, &ls_reason));
260: PetscCall(VecNorm(asls->dpsi, NORM_2, &ndpsi));
261: }
262: PetscFunctionReturn(PETSC_SUCCESS);
263: }
265: /*MC
266: TAOASFLS - Active-set feasible linesearch algorithm for solving complementarity constraints
268: Options Database Keys:
269: + -tao_ssls_delta - descent test fraction
270: - -tao_ssls_rho - descent test power
272: Level: beginner
274: Note:
275: See {cite}`billups:algorithms`, {cite}`deluca.facchinei.ea:semismooth`,
276: {cite}`ferris.kanzow.ea:feasible`, {cite}`fischer:special`, and {cite}`munson.facchinei.ea:semismooth`.
278: .seealso: `Tao`, `TaoType`, `TAOASILS`
279: M*/
280: PETSC_EXTERN PetscErrorCode TaoCreate_ASFLS(Tao tao)
281: {
282: TAO_SSLS *asls;
283: const char *armijo_type = TAOLINESEARCHARMIJO;
285: PetscFunctionBegin;
286: PetscCall(PetscNew(&asls));
287: tao->data = (void *)asls;
288: tao->ops->solve = TaoSolve_ASFLS;
289: tao->ops->setup = TaoSetUp_ASFLS;
290: tao->ops->view = TaoView_SSLS;
291: tao->ops->setfromoptions = TaoSetFromOptions_SSLS;
292: tao->ops->destroy = TaoDestroy_ASFLS;
293: tao->subset_type = TAO_SUBSET_SUBVEC;
294: asls->delta = 1e-10;
295: asls->rho = 2.1;
296: asls->fixed = NULL;
297: asls->free = NULL;
298: asls->J_sub = NULL;
299: asls->Jpre_sub = NULL;
300: asls->w = NULL;
301: asls->r1 = NULL;
302: asls->r2 = NULL;
303: asls->r3 = NULL;
304: asls->t1 = NULL;
305: asls->t2 = NULL;
306: asls->dxfree = NULL;
307: asls->identifier = 1e-5;
309: PetscCall(TaoLineSearchCreate(((PetscObject)tao)->comm, &tao->linesearch));
310: PetscCall(PetscObjectIncrementTabLevel((PetscObject)tao->linesearch, (PetscObject)tao, 1));
311: PetscCall(TaoLineSearchSetType(tao->linesearch, armijo_type));
312: PetscCall(TaoLineSearchSetOptionsPrefix(tao->linesearch, tao->hdr.prefix));
313: PetscCall(TaoLineSearchSetFromOptions(tao->linesearch));
315: PetscCall(KSPCreate(((PetscObject)tao)->comm, &tao->ksp));
316: PetscCall(PetscObjectIncrementTabLevel((PetscObject)tao->ksp, (PetscObject)tao, 1));
317: PetscCall(KSPSetOptionsPrefix(tao->ksp, tao->hdr.prefix));
318: PetscCall(KSPSetFromOptions(tao->ksp));
320: /* Override default settings (unless already changed) */
321: if (!tao->max_it_changed) tao->max_it = 2000;
322: if (!tao->max_funcs_changed) tao->max_funcs = 4000;
323: if (!tao->gttol_changed) tao->gttol = 0;
324: if (!tao->grtol_changed) tao->grtol = 0;
325: #if defined(PETSC_USE_REAL_SINGLE)
326: if (!tao->gatol_changed) tao->gatol = 1.0e-6;
327: if (!tao->fmin_changed) tao->fmin = 1.0e-4;
328: #else
329: if (!tao->gatol_changed) tao->gatol = 1.0e-16;
330: if (!tao->fmin_changed) tao->fmin = 1.0e-8;
331: #endif
332: PetscFunctionReturn(PETSC_SUCCESS);
333: }