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