Actual source code: stcg.c
1: #define PETSCKSP_DLL
3: #include include/private/kspimpl.h
4: #include src/ksp/ksp/impls/cg/stcg/stcg.h
8: /*@
9: KSPSTCGSetRadius - Sets the radius of the trust region.
11: Collective on KSP
13: Input Parameters:
14: + ksp - the iterative context
15: - radius - the trust region radius (Infinity is the default)
17: Options Database Key:
18: . -ksp_stcg_radius <r>
20: Level: advanced
22: .keywords: KSP, STCG, set, trust region radius
23: @*/
24: PetscErrorCode KSPSTCGSetRadius(KSP ksp,PetscReal radius)
25: {
26: PetscErrorCode ierr, (*f)(KSP, PetscReal);
30: if (radius < 0.0) SETERRQ(PETSC_ERR_ARG_OUTOFRANGE, "Tolerance must be positive");
31: PetscObjectQueryFunction((PetscObject)ksp, "KSPSTCGSetRadius_C", (void (**)(void))&f);
32: if (f) {
33: (*f)(ksp, radius);
34: }
35: return(0);
36: }
40: /*@
41: KSPSTCGGetNormD - Got norm of the direction.
43: Collective on KSP
45: Input Parameters:
46: + ksp - the iterative context
47: - norm_d - the norm of the direction
49: Level: advanced
51: .keywords: KSP, STCG, get, norm direction
52: @*/
53: PetscErrorCode KSPSTCGGetNormD(KSP ksp,PetscReal *norm_d)
54: {
55: PetscErrorCode ierr, (*f)(KSP, PetscReal *);
59: PetscObjectQueryFunction((PetscObject)ksp, "KSPSTCGGetNormD_C", (void (**)(void))&f);
60: if (f) {
61: (*f)(ksp, norm_d);
62: }
63: return(0);
64: }
68: /*@
69: KSPSTCGGetObjFcn - Get objective function value.
71: Collective on KSP
73: Input Parameters:
74: + ksp - the iterative context
75: - o_fcn - the objective function value
77: Level: advanced
79: .keywords: KSP, STCG, get, objective function
80: @*/
81: PetscErrorCode KSPSTCGGetObjFcn(KSP ksp,PetscReal *o_fcn)
82: {
83: PetscErrorCode ierr, (*f)(KSP, PetscReal *);
87: PetscObjectQueryFunction((PetscObject)ksp, "KSPSTCGGetObjFcn_C", (void (**)(void))&f);
88: if (f) {
89: (*f)(ksp, o_fcn);
90: }
91: return(0);
92: }
96: /*
97: KSPSolve_STCG - Use preconditioned conjugate gradient to compute
98: an approximate minimizer of the quadratic function
100: q(s) = g^T * s + .5 * s^T * H * s
102: subject to the trust region constraint
104: || s ||_M <= delta,
106: where
108: delta is the trust region radius,
109: g is the gradient vector, and
110: H is the Hessian matrix,
111: M is the positive definite preconditioner matrix.
113: KSPConvergedReason may be
114: $ KSP_CONVERGED_CG_NEG_CURVE if convergence is reached along a negative curvature direction,
115: $ KSP_CONVERGED_CG_CONSTRAINED if convergence is reached along a constrained step,
116: $ other KSP converged/diverged reasons
119: Notes:
120: The preconditioner supplied should be symmetric and positive definite.
121: */
123: PetscErrorCode KSPSolve_STCG(KSP ksp)
124: {
125: #ifdef PETSC_USE_COMPLEX
126: SETERRQ(PETSC_ERR_SUP, "STCG is not available for complex systems");
127: #else
128: KSP_STCG *cg = (KSP_STCG *)ksp->data;
130: MatStructure pflag;
131: Mat Qmat, Mmat;
132: Vec r, z, p, d;
133: PC pc;
134: PetscReal norm_r, norm_d, norm_dp1, norm_p, dMp;
135: PetscReal alpha, beta, kappa, rz, rzm1;
136: PetscReal r2;
137: PetscInt i, maxit;
138: PetscTruth diagonalscale;
141: PCDiagonalScale(ksp->pc, &diagonalscale);
142: if (diagonalscale) SETERRQ1(PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ksp->type_name);
144: if (cg->radius < 0.0) {
145: SETERRQ(PETSC_ERR_ARG_OUTOFRANGE, "Input error: radius < 0");
146: }
148: cg = (KSP_STCG *)ksp->data;
149: r2 = cg->radius * cg->radius;
150: maxit = ksp->max_it;
151: r = ksp->work[0];
152: z = ksp->work[1];
153: p = ksp->work[2];
154: d = ksp->vec_sol;
155: pc = ksp->pc;
157: /* Initialize variables */
158: PCGetOperators(pc, &Qmat, &Mmat, &pflag);
160: ksp->its = 0;
161: cg->o_fcn = 0.0;
163: VecSet(d, 0.0); /* d = 0 */
164: VecCopy(ksp->vec_rhs, r); /* r = -grad */
165: KSP_PCApply(ksp, r, z); /* z = M_{-1} r */
166: cg->norm_d = 0.0;
168: /* Check for numerical problems with the preconditioner */
169: VecDot(r, z, &rz); /* rz = r^T z */
170: if ((rz != rz) || (rz && (rz / rz != rz / rz))) {
171: /* In this case, either the right-hand side contains infinity or not a */
172: /* number or the precontioner contains intinity or not a number. We */
173: /* just take the gradient step. These tests for numerical problems */
174: /* need to be performed only once since the preconditioner does not */
175: /* change. */
177: ksp->reason = KSP_DIVERGED_NAN;
178: VecDot(r, r, &rz); /* rz = r^T r */
179: if ((rz != rz) || (rz && (rz / rz != rz / rz))) {
180: /* In this case, the right-hand side contains not a number or an */
181: /* infinite value. The gradient step does not work; return a zero */
182: /* value for the step. */
184: PetscInfo1(ksp, "KSPSolve_STCG: bad right-hand side: rz=%g\n", rz);
185: }
186: else {
187: /* In this case, the preconditioner contains not a number or an */
188: /* infinite value. */
190: PetscInfo1(ksp, "KSPSolve_STCG: bad preconditioner: rz=%g\n", rz);
192: if (cg->radius) {
193: alpha = sqrt(r2 / rz);
194: VecAXPY(d, alpha, r); /* d = d + alpha r */
195: cg->norm_d = cg->radius;
197: KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
198: VecAYPX(z, -0.5, ksp->vec_rhs);
199: VecDot(d, z, &cg->o_fcn);
200: cg->o_fcn = -cg->o_fcn;
201: }
202: }
203: return(0);
204: }
206: /* Check that the preconditioner is positive definite */
207: if (rz < 0.0) {
208: VecNorm(r, NORM_2, &norm_r);
209: /* In this case, the preconditioner is indefinite, so we cannot */
210: /* measure the direction in the preconditioned norm. Therefore, we */
211: /* must use an unpreconditioned calculation. The direction in this */
212: /* case uses the right-hand side, which should be the negative */
213: /* gradient intersected with the trust region. */
215: ksp->reason = KSP_DIVERGED_INDEFINITE_PC;
216: PetscInfo1(ksp, "KSPSolve_STCG: indefinite preconditioner: rz=%g\n", rz);
218: if (cg->radius) {
219: VecDot(r, r, &rz); /* rz = r^T r */
220: alpha = sqrt(r2 / rz);
221: VecAXPY(d, alpha, r); /* d = d + alpha r */
222: cg->norm_d = cg->radius;
224: KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
225: VecAYPX(z, -0.5, ksp->vec_rhs);
226: VecDot(d, z, &cg->o_fcn);
227: cg->o_fcn = -cg->o_fcn;
228: }
229: return(0);
230: }
232: /* As far as we know, the preconditioner is positive definite. Compute */
233: /* the appropriate residual depending on what the user has set. */
234: if (ksp->normtype == KSP_NORM_PRECONDITIONED) {
235: VecNorm(z, NORM_2, &norm_r); /* norm_r = |z| */
236: }
237: else if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
238: VecNorm(r, NORM_2, &norm_r); /* norm_r = |r| */
239: }
240: else if (ksp->normtype == KSP_NORM_NATURAL) {
241: norm_r = sqrt(rz); /* norm_r = |r|_B */
242: }
243: else {
244: norm_r = 0;
245: }
247: /* Log the residual and call any registered moitor routines */
248: KSPLogResidualHistory(ksp, norm_r);
249: KSPMonitor(ksp, 0, norm_r);
250: ksp->rnorm = norm_r;
252: /* Test for convergence */
253: (*ksp->converged)(ksp, 0, norm_r, &ksp->reason, ksp->cnvP);
254: if (ksp->reason) {
255: return(0);
256: }
258: /* Compute the initial vectors and variables for trust-region computations */
259: VecCopy(z, p); /* p = z */
261: if (STCG_PRECONDITIONED_DIRECTION == cg->dtype) {
262: dMp = 0.0;
263: norm_p = rz;
264: norm_d = 0.0;
265: }
266: else {
267: dMp = 0.0;
268: VecDot(p, p, &norm_p);
269: norm_d = 0.0;
270: }
272: /* Compute the direction */
273: KSP_MatMult(ksp, Qmat, p, z); /* z = Q * p */
274: VecDot(p, z, &kappa); /* kappa = p^T z */
276: if ((kappa != kappa) || (kappa && (kappa / kappa != kappa / kappa))) {
277: /* In this case, the matrix Q contains an infinite value or not a number */
278: /* We just take the gradient step. Only needs to be checked once. */
279: ksp->reason = KSP_DIVERGED_NAN;
280: PetscInfo1(ksp, "KSPSolve_STCG: bad matrix: kappa=%g\n", kappa);
282: if (cg->radius) {
283: VecDot(r, r, &rz); /* rz = r^T r */
285: alpha = sqrt(r2 / rz);
286: VecAXPY(d, alpha, r); /* d = d + alpha r */
287: cg->norm_d = cg->radius;
289: KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
290: VecAYPX(z, -0.5, ksp->vec_rhs);
291: VecDot(d, z, &cg->o_fcn);
292: cg->o_fcn = -cg->o_fcn;
293: }
294: return(0);
295: }
297: /* Begin iterating */
298: for (i = 0; i <= maxit; i++) {
299: ++ksp->its;
301: /* Check for negative curvature */
302: if (kappa <= 0.0) {
303: /* In this case, the matrix is indefinite and we have encountered */
304: /* a direction of negative curvature. Follow the direction to the */
305: /* boundary of the trust region. */
307: ksp->reason = KSP_CONVERGED_CG_NEG_CURVE;
308: PetscInfo1(ksp, "KSPSolve_STCG: negative curvature: kappa=%g\n", kappa);
310: if (cg->radius) {
311: alpha = (sqrt(dMp*dMp+norm_p*(r2-norm_d))-dMp)/norm_p;
312: VecAXPY(d, alpha, p); /* d = d + alpha p */
313: cg->norm_d = cg->radius;
315: cg->o_fcn += alpha * (0.5 * alpha * kappa - rz);
316: }
317: break;
318: }
319: alpha = rz / kappa;
321: /* Now we can update the direction and residual. This is perhaps not */
322: /* the best order to perform the breakdown checks, but the optimization */
323: /* codes need a direction and this is the best we can do. */
325: /* First test if the new direction intersects the trust region. */
326: norm_dp1 = norm_d + alpha*(2.0*dMp + alpha*norm_p);
327: if (cg->radius && norm_dp1 >= r2) {
328: /* In this case, the matrix is positive definite as far as we know. */
329: /* However, the direction does beyond the trust region. Follow the */
330: /* direction to the boundary of the trust region. */
332: ksp->reason = KSP_CONVERGED_CG_CONSTRAINED;
333: PetscInfo1(ksp, "KSPSolve_STCG: constrained step: radius=%g\n", cg->radius);
335: alpha = (sqrt(dMp*dMp+norm_p*(r2-norm_d))-dMp)/norm_p;
336: VecAXPY(d, alpha, p); /* d = d + alpha p */
337: cg->norm_d = cg->radius;
339: cg->o_fcn += alpha * (0.5 * alpha * kappa - rz);
340: break;
341: }
343: /* Update the direction and residual */
344: VecAXPY(d, alpha, p); /* d = d + alpha p */
345: VecAXPY(r, -alpha, z); /* r = r - alpha z */
346: KSP_PCApply(ksp, r, z);
348: if (STCG_PRECONDITIONED_DIRECTION == cg->dtype) {
349: norm_d = norm_dp1;
350: }
351: else {
352: VecDot(d, d, &norm_d);
353: }
354: cg->norm_d = sqrt(norm_d);
356: cg->o_fcn -= 0.5 * alpha * rz;
358: /* Check that the preconditioner is positive semidefinite */
359: rzm1 = rz;
360: VecDot(r, z, &rz); /* rz = r^T z */
361: if (rz <= 0.0) {
362: VecNorm(r, NORM_2, &norm_r);
363: if (rz < 0.0 || norm_r > 0.0) {
364: /* In this case, the preconditioner is indefinite. We could follow */
365: /* the direction to the boundary of the trust region, but it seems */
366: /* best to stop at the current point. */
368: ksp->reason = KSP_DIVERGED_INDEFINITE_PC;
369: PetscInfo1(ksp, "KSPSolve_STCG: indefinite preconditioner: rz=%g\n", rz);
370: break;
371: }
372: else {
373: /* rz == 0 and norm_r == 0; converged */
374: }
375: }
377: /* As far as we know, the matrix and preconditioner are positive */
378: /* definite. Compute the appropriate residual depending on what the */
379: /* user has set. */
380: if (ksp->normtype == KSP_NORM_PRECONDITIONED) {
381: VecNorm(z, NORM_2, &norm_r); /* norm_r = |z| */
382: }
383: else if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
384: VecNorm(r, NORM_2, &norm_r); /* norm_r = |r| */
385: }
386: else if (ksp->normtype == KSP_NORM_NATURAL) {
387: norm_r = sqrt(rz); /* norm_r = |r|_B */
388: }
389: else {
390: norm_r = 0;
391: }
393: /* Log the residual and call any registered moitor routines */
394: KSPLogResidualHistory(ksp, norm_r);
395: KSPMonitor(ksp, 0, norm_r);
396: ksp->rnorm = norm_r;
397:
398: /* Test for convergence */
399: (*ksp->converged)(ksp, i+1, norm_r, &ksp->reason, ksp->cnvP);
400: if (ksp->reason) { /* convergence */
401: PetscInfo2(ksp,"KSPSolve_STCG: truncated step: rnorm=%g, radius=%g\n",norm_r,cg->radius);
402: break;
403: }
405: /* Update p and the norms */
406: beta = rz / rzm1;
407: VecAYPX(p, beta, z); /* p = z + beta p */
409: if (STCG_PRECONDITIONED_DIRECTION == cg->dtype) {
410: dMp = beta*(dMp + alpha*norm_p);
411: norm_p = beta*(rzm1 + beta*norm_p);
412: }
413: else {
414: VecDot(d, p, &dMp);
415: VecDot(p, p, &norm_p);
416: }
418: /* Compute new direction */
419: KSP_MatMult(ksp, Qmat, p, z); /* z = Q * p */
420: VecDot(p, z, &kappa); /* kappa = p^T z */
421: }
423: if (!ksp->reason) {
424: ksp->reason = KSP_DIVERGED_ITS;
425: }
426: return(0);
427: #endif
428: }
432: PetscErrorCode KSPSetUp_STCG(KSP ksp)
433: {
437: /* This implementation of CG only handles left preconditioning
438: * so generate an error otherwise.
439: */
440: if (ksp->pc_side == PC_RIGHT) {
441: SETERRQ(PETSC_ERR_SUP, "No right preconditioning for KSPSTCG");
442: }
443: else if (ksp->pc_side == PC_SYMMETRIC) {
444: SETERRQ(PETSC_ERR_SUP, "No symmetric preconditioning for KSPSTCG");
445: }
447: /* get work vectors needed by CG */
448: KSPDefaultGetWork(ksp, 3);
449: return(0);
450: }
454: PetscErrorCode KSPDestroy_STCG(KSP ksp)
455: {
459: KSPDefaultDestroy(ksp);
460: /* clear composed functions */
461: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGSetRadius_C","",PETSC_NULL);
462: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGGetNormD_C","",PETSC_NULL);
463: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGGetObjFcn_C","",PETSC_NULL);
464: return(0);
465: }
470: PetscErrorCode KSPSTCGSetRadius_STCG(KSP ksp,PetscReal radius)
471: {
472: KSP_STCG *cg = (KSP_STCG *)ksp->data;
475: cg->radius = radius;
476: return(0);
477: }
481: PetscErrorCode KSPSTCGGetNormD_STCG(KSP ksp,PetscReal *norm_d)
482: {
483: KSP_STCG *cg = (KSP_STCG *)ksp->data;
486: *norm_d = cg->norm_d;
487: return(0);
488: }
492: PetscErrorCode KSPSTCGGetObjFcn_STCG(KSP ksp,PetscReal *o_fcn){
493: KSP_STCG *cg = (KSP_STCG *)ksp->data;
496: *o_fcn = cg->o_fcn;
497: return(0);
498: }
501: static const char *DType_Table[64] = {
502: "preconditioned", "unpreconditioned"
503: };
507: PetscErrorCode KSPSetFromOptions_STCG(KSP ksp)
508: {
510: KSP_STCG *cg = (KSP_STCG *)ksp->data;
513: PetscOptionsHead("KSP STCG options");
514: PetscOptionsReal("-ksp_stcg_radius", "Trust Region Radius", "KSPSTCGSetRadius", cg->radius, &cg->radius, PETSC_NULL);
515: PetscOptionsEList("-ksp_stcg_dtype", "Norm used for direction", "", DType_Table, STCG_DIRECTION_TYPES, DType_Table[cg->dtype], &cg->dtype, PETSC_NULL);
516: PetscOptionsTail();
517: return(0);
518: }
520: /*MC
521: KSPSTCG - Code to run conjugate gradient method subject to a constraint
522: on the solution norm. This is used in Trust Region methods for
523: nonlinear equations, SNESTR
525: Options Database Keys:
526: . -ksp_stcg_radius <r> - Trust Region Radius
528: Notes: This is rarely used directly
530: Level: developer
532: .seealso: KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPSTCGSetRadius(), KSPSTCGGetNormD(), KSPSTCGGetObjFcn()
533: M*/
538: PetscErrorCode KSPCreate_STCG(KSP ksp)
539: {
541: KSP_STCG *cg;
544: PetscNew(KSP_STCG, &cg);
545: PetscLogObjectMemory(ksp, sizeof(KSP_STCG));
546: cg->radius = PETSC_MAX;
547: cg->dtype = STCG_UNPRECONDITIONED_DIRECTION;
548: ksp->data = (void *)cg;
549: ksp->pc_side = PC_LEFT;
551: /* Sets the functions that are associated with this data structure
552: * (in C++ this is the same as defining virtual functions)
553: */
554: ksp->ops->setup = KSPSetUp_STCG;
555: ksp->ops->solve = KSPSolve_STCG;
556: ksp->ops->destroy = KSPDestroy_STCG;
557: ksp->ops->setfromoptions = KSPSetFromOptions_STCG;
558: ksp->ops->buildsolution = KSPDefaultBuildSolution;
559: ksp->ops->buildresidual = KSPDefaultBuildResidual;
560: ksp->ops->view = 0;
562: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
563: "KSPSTCGSetRadius_C",
564: "KSPSTCGSetRadius_STCG",
565: KSPSTCGSetRadius_STCG);
566: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
567: "KSPSTCGGetNormD_C",
568: "KSPSTCGGetNormD_STCG",
569: KSPSTCGGetNormD_STCG);
570: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
571: "KSPSTCGGetObjFcn_C",
572: "KSPSTCGGetObjFcn_STCG",
573: KSPSTCGGetObjFcn_STCG);
574: return(0);
575: }