Actual source code: viss.c
petsc-3.3-p5 2012-12-01
2: #include <../src/snes/impls/vi/ss/vissimpl.h> /*I "petscsnes.h" I*/
3: #include <../include/petsc-private/kspimpl.h>
4: #include <../include/petsc-private/matimpl.h>
5: #include <../include/petsc-private/dmimpl.h>
8: /*
9: SNESVIComputeMeritFunction - Evaluates the merit function for the mixed complementarity problem.
11: Input Parameter:
12: . phi - the semismooth function
14: Output Parameter:
15: . merit - the merit function
16: . phinorm - ||phi||
18: Notes:
19: The merit function for the mixed complementarity problem is defined as
20: merit = 0.5*phi^T*phi
21: */
24: static PetscErrorCode SNESVIComputeMeritFunction(Vec phi, PetscReal* merit,PetscReal* phinorm)
25: {
29: VecNormBegin(phi,NORM_2,phinorm);
30: VecNormEnd(phi,NORM_2,phinorm);
32: *merit = 0.5*(*phinorm)*(*phinorm);
33: return(0);
34: }
36: PETSC_STATIC_INLINE PetscScalar Phi(PetscScalar a,PetscScalar b)
37: {
38: return a + b - PetscSqrtScalar(a*a + b*b);
39: }
41: PETSC_STATIC_INLINE PetscScalar DPhi(PetscScalar a,PetscScalar b)
42: {
43: if ((PetscAbsScalar(a) >= 1.e-6) || (PetscAbsScalar(b) >= 1.e-6)) return 1.0 - a/ PetscSqrtScalar(a*a + b*b);
44: else return .5;
45: }
47: /*
48: SNESVIComputeFunction - Reformulates a system of nonlinear equations in mixed complementarity form to a system of nonlinear equations in semismooth form.
50: Input Parameters:
51: . snes - the SNES context
52: . x - current iterate
53: . functx - user defined function context
55: Output Parameters:
56: . phi - Semismooth function
58: */
61: static PetscErrorCode SNESVIComputeFunction(SNES snes,Vec X,Vec phi,void* functx)
62: {
63: PetscErrorCode ierr;
64: SNES_VISS *vi = (SNES_VISS*)snes->data;
65: Vec Xl = snes->xl,Xu = snes->xu,F = snes->vec_func;
66: PetscScalar *phi_arr,*x_arr,*f_arr,*l,*u;
67: PetscInt i,nlocal;
70: (*vi->computeuserfunction)(snes,X,F,functx);
71: VecGetLocalSize(X,&nlocal);
72: VecGetArray(X,&x_arr);
73: VecGetArray(F,&f_arr);
74: VecGetArray(Xl,&l);
75: VecGetArray(Xu,&u);
76: VecGetArray(phi,&phi_arr);
78: for (i=0;i < nlocal;i++) {
79: if ((PetscRealPart(l[i]) <= SNES_VI_NINF) && (PetscRealPart(u[i]) >= SNES_VI_INF)) { /* no constraints on variable */
80: phi_arr[i] = f_arr[i];
81: } else if (PetscRealPart(l[i]) <= SNES_VI_NINF) { /* upper bound on variable only */
82: phi_arr[i] = -Phi(u[i] - x_arr[i],-f_arr[i]);
83: } else if (PetscRealPart(u[i]) >= SNES_VI_INF) { /* lower bound on variable only */
84: phi_arr[i] = Phi(x_arr[i] - l[i],f_arr[i]);
85: } else if (l[i] == u[i]) {
86: phi_arr[i] = l[i] - x_arr[i];
87: } else { /* both bounds on variable */
88: phi_arr[i] = Phi(x_arr[i] - l[i],-Phi(u[i] - x_arr[i],-f_arr[i]));
89: }
90: }
91:
92: VecRestoreArray(X,&x_arr);
93: VecRestoreArray(F,&f_arr);
94: VecRestoreArray(Xl,&l);
95: VecRestoreArray(Xu,&u);
96: VecRestoreArray(phi,&phi_arr);
97: return(0);
98: }
100: /*
101: SNESVIComputeBsubdifferentialVectors - Computes the diagonal shift (Da) and row scaling (Db) vectors needed for the
102: the semismooth jacobian.
103: */
106: PetscErrorCode SNESVIComputeBsubdifferentialVectors(SNES snes,Vec X,Vec F,Mat jac,Vec Da,Vec Db)
107: {
109: PetscScalar *l,*u,*x,*f,*da,*db,da1,da2,db1,db2;
110: PetscInt i,nlocal;
114: VecGetArray(X,&x);
115: VecGetArray(F,&f);
116: VecGetArray(snes->xl,&l);
117: VecGetArray(snes->xu,&u);
118: VecGetArray(Da,&da);
119: VecGetArray(Db,&db);
120: VecGetLocalSize(X,&nlocal);
121:
122: for (i=0;i< nlocal;i++) {
123: if ((PetscRealPart(l[i]) <= SNES_VI_NINF) && (PetscRealPart(u[i]) >= SNES_VI_INF)) {/* no constraints on variable */
124: da[i] = 0;
125: db[i] = 1;
126: } else if (PetscRealPart(l[i]) <= SNES_VI_NINF) { /* upper bound on variable only */
127: da[i] = DPhi(u[i] - x[i], -f[i]);
128: db[i] = DPhi(-f[i],u[i] - x[i]);
129: } else if (PetscRealPart(u[i]) >= SNES_VI_INF) { /* lower bound on variable only */
130: da[i] = DPhi(x[i] - l[i], f[i]);
131: db[i] = DPhi(f[i],x[i] - l[i]);
132: } else if (l[i] == u[i]) { /* fixed variable */
133: da[i] = 1;
134: db[i] = 0;
135: } else { /* upper and lower bounds on variable */
136: da1 = DPhi(x[i] - l[i], -Phi(u[i] - x[i], -f[i]));
137: db1 = DPhi(-Phi(u[i] - x[i], -f[i]),x[i] - l[i]);
138: da2 = DPhi(u[i] - x[i], -f[i]);
139: db2 = DPhi(-f[i],u[i] - x[i]);
140: da[i] = da1 + db1*da2;
141: db[i] = db1*db2;
142: }
143: }
145: VecRestoreArray(X,&x);
146: VecRestoreArray(F,&f);
147: VecRestoreArray(snes->xl,&l);
148: VecRestoreArray(snes->xu,&u);
149: VecRestoreArray(Da,&da);
150: VecRestoreArray(Db,&db);
151: return(0);
152: }
154: /*
155: SNESVIComputeJacobian - Computes the jacobian of the semismooth function.The Jacobian for the semismooth function is an element of the B-subdifferential of the Fischer-Burmeister function for complementarity problems.
157: Input Parameters:
158: . Da - Diagonal shift vector for the semismooth jacobian.
159: . Db - Row scaling vector for the semismooth jacobian.
161: Output Parameters:
162: . jac - semismooth jacobian
163: . jac_pre - optional preconditioning matrix
165: Notes:
166: The semismooth jacobian matrix is given by
167: jac = Da + Db*jacfun
168: where Db is the row scaling matrix stored as a vector,
169: Da is the diagonal perturbation matrix stored as a vector
170: and jacfun is the jacobian of the original nonlinear function.
171: */
174: PetscErrorCode SNESVIComputeJacobian(Mat jac, Mat jac_pre,Vec Da, Vec Db)
175: {
177:
178: /* Do row scaling and add diagonal perturbation */
179: MatDiagonalScale(jac,Db,PETSC_NULL);
180: MatDiagonalSet(jac,Da,ADD_VALUES);
181: if (jac != jac_pre) { /* If jac and jac_pre are different */
182: MatDiagonalScale(jac_pre,Db,PETSC_NULL);
183: MatDiagonalSet(jac_pre,Da,ADD_VALUES);
184: }
185: return(0);
186: }
188: /*
189: SNESVIComputeMeritFunctionGradient - Computes the gradient of the merit function psi.
191: Input Parameters:
192: phi - semismooth function.
193: H - semismooth jacobian
194:
195: Output Parameters:
196: dpsi - merit function gradient
198: Notes:
199: The merit function gradient is computed as follows
200: dpsi = H^T*phi
201: */
204: PetscErrorCode SNESVIComputeMeritFunctionGradient(Mat H, Vec phi, Vec dpsi)
205: {
207:
209: MatMultTranspose(H,phi,dpsi);
210: return(0);
211: }
215: /*
216: SNESSolve_VISS - Solves the complementarity problem with a semismooth Newton
217: method using a line search.
219: Input Parameters:
220: . snes - the SNES context
222: Output Parameter:
223: . outits - number of iterations until termination
225: Application Interface Routine: SNESSolve()
227: Notes:
228: This implements essentially a semismooth Newton method with a
229: line search. The default line search does not do any line seach
230: but rather takes a full newton step.
232: Developer Note: the code in this file should be slightly modified so that this routine need not exist and the SNESSolve_LS() routine is called directly with the appropriate wrapped function and Jacobian evaluations
234: */
237: PetscErrorCode SNESSolve_VISS(SNES snes)
238: {
239: SNES_VISS *vi = (SNES_VISS*)snes->data;
240: PetscErrorCode ierr;
241: PetscInt maxits,i,lits;
242: PetscBool lssucceed;
243: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
244: PetscReal gnorm,xnorm=0,ynorm;
245: Vec Y,X,F;
246: KSPConvergedReason kspreason;
247: DM dm;
248: SNESDM sdm;
251: SNESGetDM(snes,&dm);
252: DMSNESGetContext(dm,&sdm);
253: vi->computeuserfunction = sdm->computefunction;
254: sdm->computefunction = SNESVIComputeFunction;
256: snes->numFailures = 0;
257: snes->numLinearSolveFailures = 0;
258: snes->reason = SNES_CONVERGED_ITERATING;
260: maxits = snes->max_its; /* maximum number of iterations */
261: X = snes->vec_sol; /* solution vector */
262: F = snes->vec_func; /* residual vector */
263: Y = snes->work[0]; /* work vectors */
265: PetscObjectTakeAccess(snes);
266: snes->iter = 0;
267: snes->norm = 0.0;
268: PetscObjectGrantAccess(snes);
270: SNESVIProjectOntoBounds(snes,X);
271: SNESComputeFunction(snes,X,vi->phi);
272: if (snes->domainerror) {
273: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
274: sdm->computefunction = vi->computeuserfunction;
275: return(0);
276: }
277: /* Compute Merit function */
278: SNESVIComputeMeritFunction(vi->phi,&vi->merit,&vi->phinorm);
280: VecNormBegin(X,NORM_2,&xnorm); /* xnorm <- ||x|| */
281: VecNormEnd(X,NORM_2,&xnorm);
282: if (PetscIsInfOrNanReal(vi->merit)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
284: PetscObjectTakeAccess(snes);
285: snes->norm = vi->phinorm;
286: PetscObjectGrantAccess(snes);
287: SNESLogConvHistory(snes,vi->phinorm,0);
288: SNESMonitor(snes,0,vi->phinorm);
290: /* set parameter for default relative tolerance convergence test */
291: snes->ttol = vi->phinorm*snes->rtol;
292: /* test convergence */
293: (*snes->ops->converged)(snes,0,0.0,0.0,vi->phinorm,&snes->reason,snes->cnvP);
294: if (snes->reason) {
295: sdm->computefunction = vi->computeuserfunction;
296: return(0);
297: }
299: for (i=0; i<maxits; i++) {
301: /* Call general purpose update function */
302: if (snes->ops->update) {
303: (*snes->ops->update)(snes, snes->iter);
304: }
305:
306: /* Solve J Y = Phi, where J is the semismooth jacobian */
307: /* Get the nonlinear function jacobian */
308: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
309: /* Get the diagonal shift and row scaling vectors */
310: SNESVIComputeBsubdifferentialVectors(snes,X,F,snes->jacobian,vi->Da,vi->Db);
311: /* Compute the semismooth jacobian */
312: SNESVIComputeJacobian(snes->jacobian,snes->jacobian_pre,vi->Da,vi->Db);
313: /* Compute the merit function gradient */
314: SNESVIComputeMeritFunctionGradient(snes->jacobian,vi->phi,vi->dpsi);
315: KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
316: SNES_KSPSolve(snes,snes->ksp,vi->phi,Y);
317: KSPGetConvergedReason(snes->ksp,&kspreason);
319: if (kspreason < 0) {
320: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
321: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
322: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
323: break;
324: }
325: }
326: KSPGetIterationNumber(snes->ksp,&lits);
327: snes->linear_its += lits;
328: PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);
329: /*
330: if (snes->ops->precheckstep) {
331: PetscBool changed_y = PETSC_FALSE;
332: (*snes->ops->precheckstep)(snes,X,Y,snes->precheck,&changed_y);
333: }
335: if (PetscLogPrintInfo){
336: SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W);
337: }
338: */
339: /* Compute a (scaled) negative update in the line search routine:
340: Y <- X - lambda*Y
341: and evaluate G = function(Y) (depends on the line search).
342: */
343: VecCopy(Y,snes->vec_sol_update);
344: ynorm = 1; gnorm = vi->phinorm;
345: /* (*snes->ops->linesearch)(snes,snes->lsP,X,vi->phi,Y,vi->phinorm,xnorm,G,W,&ynorm,&gnorm,&lssucceed); */
346: SNESLineSearchApply(snes->linesearch, X, vi->phi, &gnorm, Y);
347: SNESLineSearchGetNorms(snes->linesearch, &xnorm, &gnorm, &ynorm);
348: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",(double)vi->phinorm,(double)gnorm,(double)ynorm,(int)lssucceed);
349: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
350: if (snes->domainerror) {
351: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
352: sdm->computefunction = vi->computeuserfunction;
353: return(0);
354: }
355: SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);
356: if (!lssucceed) {
357: if (++snes->numFailures >= snes->maxFailures) {
358: PetscBool ismin;
359: snes->reason = SNES_DIVERGED_LINE_SEARCH;
360: SNESVICheckLocalMin_Private(snes,snes->jacobian,vi->phi,X,gnorm,&ismin);
361: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
362: break;
363: }
364: }
365: /* Update function and solution vectors */
366: vi->phinorm = gnorm;
367: vi->merit = 0.5*vi->phinorm*vi->phinorm;
368: /* Monitor convergence */
369: PetscObjectTakeAccess(snes);
370: snes->iter = i+1;
371: snes->norm = vi->phinorm;
372: PetscObjectGrantAccess(snes);
373: SNESLogConvHistory(snes,snes->norm,lits);
374: SNESMonitor(snes,snes->iter,snes->norm);
375: /* Test for convergence, xnorm = || X || */
376: if (snes->ops->converged != SNESSkipConverged) { VecNorm(X,NORM_2,&xnorm); }
377: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,vi->phinorm,&snes->reason,snes->cnvP);
378: if (snes->reason) break;
379: }
380: if (i == maxits) {
381: PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);
382: if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
383: }
384: sdm->computefunction = vi->computeuserfunction;
385: return(0);
386: }
388: /* -------------------------------------------------------------------------- */
389: /*
390: SNESSetUp_VISS - Sets up the internal data structures for the later use
391: of the SNES nonlinear solver.
393: Input Parameter:
394: . snes - the SNES context
395: . x - the solution vector
397: Application Interface Routine: SNESSetUp()
399: Notes:
400: For basic use of the SNES solvers, the user need not explicitly call
401: SNESSetUp(), since these actions will automatically occur during
402: the call to SNESSolve().
403: */
406: PetscErrorCode SNESSetUp_VISS(SNES snes)
407: {
409: SNES_VISS *vi = (SNES_VISS*) snes->data;
412: SNESSetUp_VI(snes);
413: VecDuplicate(snes->vec_sol, &vi->dpsi);
414: VecDuplicate(snes->vec_sol, &vi->phi);
415: VecDuplicate(snes->vec_sol, &vi->Da);
416: VecDuplicate(snes->vec_sol, &vi->Db);
417: VecDuplicate(snes->vec_sol, &vi->z);
418: VecDuplicate(snes->vec_sol, &vi->t);
419: return(0);
420: }
421: /* -------------------------------------------------------------------------- */
424: PetscErrorCode SNESReset_VISS(SNES snes)
425: {
426: SNES_VISS *vi = (SNES_VISS*) snes->data;
430: SNESReset_VI(snes);
431: VecDestroy(&vi->dpsi);
432: VecDestroy(&vi->phi);
433: VecDestroy(&vi->Da);
434: VecDestroy(&vi->Db);
435: VecDestroy(&vi->z);
436: VecDestroy(&vi->t);
437: return(0);
438: }
440: /* -------------------------------------------------------------------------- */
441: /*
442: SNESSetFromOptions_VISS - Sets various parameters for the SNESVI method.
444: Input Parameter:
445: . snes - the SNES context
447: Application Interface Routine: SNESSetFromOptions()
448: */
451: static PetscErrorCode SNESSetFromOptions_VISS(SNES snes)
452: {
453: PetscErrorCode ierr;
454: SNESLineSearch linesearch;
457: SNESSetFromOptions_VI(snes);
458: PetscOptionsHead("SNES semismooth method options");
459: PetscOptionsTail();
460: /* set up the default line search */
461: if (!snes->linesearch) {
462: SNESGetSNESLineSearch(snes, &linesearch);
463: SNESLineSearchSetType(linesearch, SNESLINESEARCHBT);
464: SNESLineSearchBTSetAlpha(linesearch, 0.0);
465: }
467: return(0);
468: }
471: /* -------------------------------------------------------------------------- */
472: /*MC
473: SNESVISS - Semi-smooth solver for variational inequalities based on Newton's method
475: Options Database:
476: + -snes_vi_type <ss,rs,rsaug> a semi-smooth solver, a reduced space active set method, and a reduced space active set method that does not eliminate the active constraints from the Jacobian instead augments the Jacobian with additional variables that enforce the constraints
477: - -snes_vi_monitor - prints the number of active constraints at each iteration.
479: Level: beginner
481: References:
482: - T. S. Munson, F. Facchinei, M. C. Ferris, A. Fischer, and C. Kanzow. The semismooth
483: algorithm for large scale complementarity problems. INFORMS Journal on Computing, 13 (2001).
485: .seealso: SNESVISetVariableBounds(), SNESVISetComputeVariableBounds(), SNESCreate(), SNES, SNESSetType(), SNESVIRS, SNESVISS, SNESTR, SNESLineSearchSet(),
486: SNESLineSearchSetPostCheck(), SNESLineSearchNo(), SNESLineSearchCubic(), SNESLineSearchQuadratic(),
487: SNESLineSearchSet(), SNESLineSearchNoNorms(), SNESLineSearchSetPreCheck(), SNESLineSearchSetParams(), SNESLineSearchGetParams()
489: M*/
490: EXTERN_C_BEGIN
493: PetscErrorCode SNESCreate_VISS(SNES snes)
494: {
496: SNES_VISS *vi;
499: snes->ops->reset = SNESReset_VISS;
500: snes->ops->setup = SNESSetUp_VISS;
501: snes->ops->solve = SNESSolve_VISS;
502: snes->ops->destroy = SNESDestroy_VI;
503: snes->ops->setfromoptions = SNESSetFromOptions_VISS;
504: snes->ops->view = PETSC_NULL;
506: snes->usesksp = PETSC_TRUE;
507: snes->usespc = PETSC_FALSE;
509: PetscNewLog(snes,SNES_VISS,&vi);
510: snes->data = (void*)vi;
512: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESVISetVariableBounds_C","SNESVISetVariableBounds_VI",SNESVISetVariableBounds_VI);
513: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESVISetComputeVariableBounds_C","SNESVISetComputeVariableBounds_VI",SNESVISetComputeVariableBounds_VI);
514: return(0);
515: }
516: EXTERN_C_END