Actual source code: umtr.c
1: /*$Id: umtr.c,v 1.105 2001/03/23 23:24:16 balay Exp $*/
3: #include "src/snes/impls/umtr/umtr.h" /*I "petscsnes.h" I*/
4: #include "src/sles/ksp/kspimpl.h"
5: #include "src/sles/ksp/impls/qcg/qcg.h"
7: /*
8: SNESSolve_UM_TR - Implements Newton's Method with a trust region approach
9: for solving unconstrained minimization problems.
11: The basic algorithm is taken from MINPACK-2 (dstrn).
13: SNESSolve_UM_TR computes a local minimizer of a twice differentiable function
14: f by applying a trust region variant of Newton's method. At each stage
15: of the algorithm, we us the prconditioned conjugate gradient method to
16: determine an approximate minimizer of the quadratic equation
18: q(s) = g^T * s + .5 * s^T * H * s
20: subject to the Euclidean norm trust region constraint
22: || D * s || <= delta,
24: where delta is the trust region radius and D is a scaling matrix.
25: Here g is the gradient and H is the Hessian matrix.
27: Note: SNESSolve_UM_TR MUST use the iterative solver KSPQCG; thus, we
28: set KSPQCG in this routine regardless of what the user may have
29: previously specified.
30: */
31: static int SNESSolve_UM_TR(SNES snes,int *outits)
32: {
33: SNES_UM_TR *neP = (SNES_UM_TR*)snes->data;
34: int maxits,i,nlconv,ierr,qits;
35: PetscTruth newton;
36: double xnorm,max_val,ftrial,delta;
37: double zero = 0.0,two = 2.0,four = 4.0;
38: Scalar one = 1.0;
39: Vec X,G,S,Xtrial;
40: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
41: SLES sles;
42: KSP ksp;
43: KSP_QCG *qcgP;
44: SNESConvergedReason reason;
45: KSPConvergedReason kreason;
49: snes->reason = SNES_CONVERGED_ITERATING;
51: nlconv = 0;
52: maxits = snes->max_its; /* maximum number of iterations */
53: X = snes->vec_sol; /* solution vector */
54: G = snes->vec_func; /* gradient vector */
55: S = snes->work[0]; /* work vectors */
56: Xtrial = snes->work[1];
57: delta = neP->delta; /* trust region radius */
59: VecNorm(X,NORM_2,&xnorm); /* xnorm = || X || */
60: PetscObjectTakeAccess(snes);
61: snes->iter = 0;
62: PetscObjectGrantAccess(snes);
63: SNESComputeMinimizationFunction(snes,X,&snes->fc); /* f(X) */
64: SNESComputeGradient(snes,X,G); /* G(X) <- gradient */
65: PetscObjectTakeAccess(snes);
66: VecNorm(G,NORM_2,&snes->norm); /* &snes->norm = || G || */
67: PetscObjectGrantAccess(snes);
68: SNESLogConvHistory(snes,snes->norm,0);
69: SNESMonitor(snes,0,snes->norm);
71: SNESGetSLES(snes,&sles);
72: SLESGetKSP(sles,&ksp);
73: KSPSetType(ksp,KSPQCG);
74: PetscLogInfo(snes,"SNESSolve_UM_TR: setting KSPType = KSPQCGn");
75: qcgP = (KSP_QCG*)ksp->data;
77: for (i=0; i<maxits && !nlconv; i++) {
78: PetscObjectTakeAccess(snes);
79: snes->iter = i+1;
80: PetscObjectGrantAccess(snes);
81: newton = PETSC_FALSE;
82: neP->success = 0;
83: snes->nfailures = 0;
84: SNESComputeHessian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
85: SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg);
87: if (!i) { /* Initialize delta */
88: if (delta <= 0) {
89: if (xnorm > zero) delta = neP->factor1*xnorm;
90: else delta = neP->delta0;
91: MatNorm(snes->jacobian,NORM_1,&max_val);
92: if (ierr == PETSC_ERR_SUP) {
93: PetscLogInfo(snes,"SNESSolve_UM_TR: Initial delta computed without matrix norm infon");
94: } else {
95: if (PetscAbsDouble(max_val)<1.e-14)SETERRQ(PETSC_ERR_PLIB,"Hessian norm is too small");
96: delta = PetscMax(delta,snes->norm/max_val);
97: }
98: } else {
99: delta = neP->delta0;
100: }
101: }
102: do {
103: /* Minimize the quadratic to compute the step s */
104: qcgP->delta = delta;
105: SLESSolve(snes->sles,G,S,&qits);
106: snes->linear_its += qits;
107: KSPGetConvergedReason(ksp,&kreason);
108: if ((int)kreason < 0) SETERRQ(PETSC_ERR_PLIB,"Failure in SLESSolve");
109: if (kreason != KSP_CONVERGED_QCG_NEG_CURVE && kreason != KSP_CONVERGED_QCG_CONSTRAINED) {
110: newton = PETSC_TRUE;
111: }
112: PetscLogInfo(snes,"SNESSolve_UM_TR: %d: ltsnrm=%g, delta=%g, q=%g, qits=%dn",
113: i,qcgP->ltsnrm,delta,qcgP->quadratic,qits);
115: VecWAXPY(&one,X,S,Xtrial); /* Xtrial <- X + S */
116: VecNorm(Xtrial,NORM_2,&xnorm);
117: /* ftrial = f(Xtrial) */
118: SNESComputeMinimizationFunction(snes,Xtrial,&ftrial);
120: /* Compute the function reduction and the step size */
121: neP->actred = snes->fc - ftrial;
122: neP->prered = -qcgP->quadratic;
124: /* Adjust delta for the first Newton step */
125: if (!i && (newton)) delta = PetscMin(delta,qcgP->ltsnrm);
127: if (neP->actred < neP->eta1 * neP->prered) { /* Unsuccessful step */
129: PetscLogInfo(snes,"SNESSolve_UM_TR: Rejecting stepn");
130: snes->nfailures += 1;
132: /* If iterate is Newton step, reduce delta to current step length */
133: if (newton) {
134: delta = qcgP->ltsnrm;
135: newton = PETSC_FALSE;
136: }
137: delta /= four;
139: } else { /* Successful iteration; adjust trust radius */
141: neP->success = 1;
142: PetscLogInfo(snes,"SNESSolve_UM_TR: Accepting stepn");
143: if (newton) {
144: delta = sqrt(qcgP->ltsnrm*delta);
145: if (neP->actred < neP->eta2 * neP->prered) delta /= two;
146: } else if (neP->actred < neP->eta2 * neP->prered)
147: delta /= delta;
148: else if ((neP->actred >= neP->eta3 * neP->prered) &&
149: (neP->actred < neP->eta4 * neP->prered))
150: delta *= two;
151: else if (neP->actred >= neP->eta4 * neP->prered)
152: delta *= four;
153: else neP->sflag = 1;
154: }
156: neP->delta = delta;
157: (*snes->converged)(snes,xnorm,snes->norm,ftrial,&reason,snes->cnvP);
158: if (reason) nlconv = 1;
159: } while (!neP->success && !nlconv);
161: /* Question: If (!neP->success && break), then last step was rejected,
162: but convergence was detected. Should this update really happen? */
163: snes->fc = ftrial;
164: VecCopy(Xtrial,X);
165: snes->vec_sol_always = X;
166: /* Note: At last iteration, the gradient evaluation is unnecessary */
167: SNESComputeGradient(snes,X,G);
168: PetscObjectTakeAccess(snes);
169: VecNorm(G,NORM_2,&snes->norm);
170: PetscObjectGrantAccess(snes);
171: snes->vec_func_always = G;
173: SNESLogConvHistory(snes,snes->norm,qits);
174: SNESMonitor(snes,i+1,snes->norm);
175: }
176: /* Verify solution is in corect location */
177: if (X != snes->vec_sol) {
178: VecCopy(X,snes->vec_sol);
179: snes->vec_sol_always = snes->vec_sol;
180: snes->vec_func_always = snes->vec_func;
181: }
182: if (i == maxits) {
183: PetscLogInfo(snes,"SNESSolve_UM_TR: Maximum number of iterations reached: %dn",maxits);
184: i--;
185: reason = SNES_DIVERGED_MAX_IT;
186: }
187: PetscObjectTakeAccess(snes);
188: snes->reason = SNES_DIVERGED_MAX_IT;
189: PetscObjectGrantAccess(snes);
190: *outits = i; /* not i+1, since update for i happens in loop above */
191: return(0);
192: }
193: /*------------------------------------------------------------*/
194: static int SNESSetUp_UM_TR(SNES snes)
195: {
196: int ierr;
197: PetscTruth ilu,bjacobi;
198: SLES sles;
199: PC pc;
202: snes->nwork = 4;
203: VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work);
204: PetscLogObjectParents(snes,snes->nwork,snes->work);
205: snes->vec_sol_update_always = snes->work[3];
207: /*
208: If PC was set by default to ILU then change it to Jacobi
209: */
210: SNESGetSLES(snes,&sles);
211: SLESGetPC(sles,&pc);
212: PetscTypeCompare((PetscObject)pc,PCILU,&ilu);
213: if (ilu) {
214: PCSetType(pc,PCJACOBI);
215: } else {
216: PetscTypeCompare((PetscObject)pc,PCBJACOBI,&bjacobi);
217: if (bjacobi) {
218: /* cannot do this; since PC may not have been setup yet */
219: /* PCBJacobiGetSubSLES(pc,0,0,&subsles);
220: SLESGetPC(*subsles,&subpc);
221: PetscTypeCompare((PetscObject)subpc,PCILU,&ilu);
222: if (ilu) {
223: PCSetType(pc,PCJACOBI);
224: } */
225: /* don't really want to do this, since user may have selected BJacobi plus something
226: that is symmetric on each processor; really only want to catch the default ILU */
227: PCSetType(pc,PCJACOBI);
228: }
229: }
230: return(0);
231: }
232: /*------------------------------------------------------------*/
233: static int SNESDestroy_UM_TR(SNES snes)
234: {
235: int ierr;
238: if (snes->nwork) {
239: VecDestroyVecs(snes->work,snes->nwork);
240: }
241: PetscFree(snes->data);
242: return(0);
243: }
244: /*------------------------------------------------------------*/
245: /*@C
246: SNESConverged_UM_TR - Monitors the convergence of the SNESSolve_UM_TR()
247: routine (default).
249: Collective on SNES
251: Input Parameters:
252: + snes - the SNES context
253: . xnorm - 2-norm of current iterate
254: . gnorm - 2-norm of current gradient
255: . f - objective function value
256: - dummy - unused dummy context
258: Output Parameter:
259: . reason - one of
260: $ SNES_CONVERGED_FNORM_ABS (f < fmin),
261: $ SNES_CONVERGED_TR_REDUCTION (abs(ared) <= rtol*abs(f) && pred <= rtol*abs(f)),
262: $ SNES_CONVERGED_TR_DELTA (delta <= deltatol*xnorm),
263: $ SNES_DIVERGED_TR_REDUCTION (abs(ared) <= epsmch && pred <= epsmch),
264: $ SNES_DIVERGED_FUNCTION_COUNT (nfunc > max_func),
265: $ SNES_DIVERGED_FNORM_NAN (f = NaN),
266: $ SNES_CONVERGED_ITERATING (otherwise).
268: where
269: + ared - actual reduction
270: . delta - trust region paramenter
271: . deltatol - trust region size tolerance,
272: set with SNESSetTrustRegionTolerance()
273: . epsmch - machine epsilon
274: . fmin - lower bound on function value,
275: set with SNESSetMinimizationFunctionTolerance()
276: . nfunc - number of function evaluations
277: . maxfunc - maximum number of function evaluations,
278: set with SNESSetTolerances()
279: . pred - predicted reduction
280: - rtol - relative function tolerance,
281: set with SNESSetTolerances()
283: Level: intermediate
285: @*/
286: int SNESConverged_UM_TR(SNES snes,double xnorm,double gnorm,double f,SNESConvergedReason *reason,void *dummy)
287: {
288: SNES_UM_TR *neP = (SNES_UM_TR*)snes->data;
289: double rtol = snes->rtol,delta = neP->delta,ared = neP->actred,pred = neP->prered;
290: double epsmch = 1.0e-14; /* This must be fixed */
293: if (snes->method_class != SNES_UNCONSTRAINED_MINIMIZATION) {
294: SETERRQ(PETSC_ERR_ARG_WRONG,"For SNES_UNCONSTRAINED_MINIMIZATION only");
295: } else if (f != f) {
296: PetscLogInfo(snes,"SNESConverged_UM_TR:Failed to converged, function is NaNn");
297: *reason = SNES_DIVERGED_FNORM_NAN;
298: } else if ((!neP->success || neP->sflag) && (delta <= snes->deltatol * xnorm)) {
299: neP->sflag = 0;
300: PetscLogInfo(snes,"SNESConverged_UM_TR: Trust region param satisfies tolerance: %g<=%g*%gn",
301: delta,snes->deltatol,xnorm);
302: *reason = SNES_CONVERGED_TR_DELTA;
303: } else if ((PetscAbsDouble(ared) <= PetscAbsDouble(f) * rtol) && (pred) <= rtol*PetscAbsDouble(f)) {
304: PetscLogInfo(snes,"SNESConverged_UM_TR:Actual (%g) and predicted (%g) reductions<%g*%gn",
305: PetscAbsDouble(ared),pred,rtol,PetscAbsDouble(f));
306: *reason = SNES_CONVERGED_TR_REDUCTION;
307: } else if (f < snes->fmin) {
308: PetscLogInfo(snes,"SNESConverged_UM_TR:Function value (%g)<f_{minimum} (%g)n",f,snes->fmin);
309: *reason = SNES_CONVERGED_FNORM_ABS ;
310: } else if ((PetscAbsDouble(ared) <= epsmch) && pred <= epsmch) {
311: PetscLogInfo(snes,"SNESConverged_UM_TR:Actual (%g) and predicted (%g) reductions<epsmch (%g)n",
312: PetscAbsDouble(ared),pred,epsmch);
313: *reason = SNES_DIVERGED_TR_REDUCTION;
314: } else if (snes->nfuncs > snes->max_funcs) {
315: PetscLogInfo(snes,"SNESConverged_UM_TR:Exceeded maximum number of function evaluations:%d>%dn",
316: snes->nfuncs,snes->max_funcs);
317: *reason = SNES_DIVERGED_FUNCTION_COUNT;
318: } else {
319: *reason = SNES_CONVERGED_ITERATING;
320: }
321: return(0);
322: }
323: /*------------------------------------------------------------*/
324: static int SNESSetFromOptions_UM_TR(SNES snes)
325: {
326: SNES_UM_TR *ctx = (SNES_UM_TR *)snes->data;
327: int ierr;
328: SLES sles;
329: PC pc;
330: PetscTruth ismatshell,nopcset;
331: Mat pmat;
334: PetscOptionsHead("SNES trust region options for minimization");
335: PetscOptionsDouble("-snes_trtol","Trust region tolerance","SNESSetTrustRegionTolerance",snes->deltatol,&snes->deltatol,0);
336: PetscOptionsDouble("-snes_um_eta1","eta1","None",ctx->eta1,&ctx->eta1,0);
337: PetscOptionsDouble("-snes_um_eta2","step unsuccessful if reduction < eta1 * predicted reduction","None",ctx->eta2,&ctx->eta2,0);
338: PetscOptionsDouble("-snes_um_eta3","eta3","None",ctx->eta3,&ctx->eta3,0);
339: PetscOptionsDouble("-snes_um_eta4","eta4","None",ctx->eta4,&ctx->eta4,0);
340: PetscOptionsDouble("-snes_um_delta0","delta0","None",ctx->delta,&ctx->delta,0);
341: PetscOptionsDouble("-snes_um_factor1","factor1","None",ctx->factor1,&ctx->factor1,0);
342: PetscOptionsTail();
344: /* if preconditioner has not been set yet, and not using a matrix shell then
345: set preconditioner to Jacobi. This is to prevent PCSetFromOptions() from
346: setting a default of ILU or block Jacobi-ILU which won't work since TR
347: requires a symmetric preconditioner
348: */
349: SNESGetSLES(snes,&sles);
350: SLESGetPC(sles,&pc);
351: PetscTypeCompare((PetscObject)pc,0,&nopcset);
352: if (nopcset) {
353: PCGetOperators(pc,PETSC_NULL,&pmat,PETSC_NULL);
354: if (pmat) {
355: PetscTypeCompare((PetscObject)pmat,MATSHELL,&ismatshell);
356: if (!ismatshell) {
357: PCSetType(pc,PCJACOBI);
358: }
359: }
360: }
362: return(0);
363: }
365: /*------------------------------------------------------------*/
366: static int SNESView_UM_TR(SNES snes,PetscViewer viewer)
367: {
368: SNES_UM_TR *tr = (SNES_UM_TR *)snes->data;
369: int ierr;
370: PetscTruth isascii;
373: PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&isascii);
374: if (isascii) {
375: PetscViewerASCIIPrintf(viewer," eta1=%g, eta1=%g, eta3=%g, eta4=%gn",tr->eta1,tr->eta2,tr->eta3,tr->eta4);
376: PetscViewerASCIIPrintf(viewer," delta0=%g, factor1=%gn",tr->delta0,tr->factor1);
377: } else {
378: SETERRQ1(1,"Viewer type %s not supported for SNES UM TR",((PetscObject)viewer)->type_name);
379: }
380: return(0);
381: }
382: /*------------------------------------------------------------*/
383: EXTERN_C_BEGIN
384: int SNESCreate_UM_TR(SNES snes)
385: {
386: SNES_UM_TR *neP;
387: int ierr;
390: if (snes->method_class != SNES_UNCONSTRAINED_MINIMIZATION) {
391: SETERRQ(PETSC_ERR_ARG_WRONG,"For SNES_UNCONSTRAINED_MINIMIZATION only");
392: }
393: snes->setup = SNESSetUp_UM_TR;
394: snes->solve = SNESSolve_UM_TR;
395: snes->destroy = SNESDestroy_UM_TR;
396: snes->converged = SNESConverged_UM_TR;
397: snes->setfromoptions = SNESSetFromOptions_UM_TR;
398: snes->view = SNESView_UM_TR;
400: snes->nwork = 0;
402: ierr = PetscNew(SNES_UM_TR,&neP);
403: PetscLogObjectMemory(snes,sizeof(SNES_UM_TR));
404: snes->data = (void*)neP;
405: neP->delta0 = 1.0e-6;
406: neP->delta = 0.0;
407: neP->eta1 = 1.0e-4;
408: neP->eta2 = 0.25;
409: neP->eta3 = 0.50;
410: neP->eta4 = 0.90;
411: neP->factor1 = 1.0e-8;
412: neP->actred = 0.0;
413: neP->prered = 0.0;
414: neP->success = 0;
415: neP->sflag = 0;
416:
417: return(0);
418: }
419: EXTERN_C_END