Actual source code: gltr.c
1: #define PETSCKSP_DLL
3: #include <math.h>
4: #include include/private/kspimpl.h
5: #include include/petscblaslapack.h
6: #include src/ksp/ksp/impls/cg/gltr/gltr.h
10: /*@
11: KSPGLTRSetRadius - Sets the radius of the trust region.
13: Collective on KSP
15: Input Parameters:
16: + ksp - the iterative context
17: - radius - the trust region radius (Infinity is the default)
19: Options Database Key:
20: . -ksp_gltr_radius <r>
22: Level: advanced
24: .keywords: KSP, GLTR, set, trust region radius
25: @*/
26: PetscErrorCode KSPGLTRSetRadius(KSP ksp, PetscReal radius)
27: {
28: PetscErrorCode ierr, (*f)(KSP, PetscReal);
32: if (radius < 0.0) SETERRQ(PETSC_ERR_ARG_OUTOFRANGE, "Tolerance must be positive");
33: PetscObjectQueryFunction((PetscObject)ksp, "KSPGLTRSetRadius_C", (void (**)(void))&f);
34: if (f) {
35: (*f)(ksp, radius);
36: }
37: return(0);
38: }
42: /*@
43: KSPGLTRGetNormD - Get norm of the direction.
45: Collective on KSP
47: Input Parameters:
48: + ksp - the iterative context
49: - norm_d - the norm of the direction
51: Level: advanced
53: .keywords: KSP, GLTR, get, norm direction
54: @*/
55: PetscErrorCode KSPGLTRGetNormD(KSP ksp,PetscReal *norm_d)
56: {
57: PetscErrorCode ierr, (*f)(KSP, PetscReal *);
61: PetscObjectQueryFunction((PetscObject)ksp, "KSPGLTRGetNormD_C", (void (**)(void))&f);
62: if (f) {
63: (*f)(ksp, norm_d);
64: }
65: return(0);
66: }
70: /*@
71: KSPGLTRGetObjFcn - Get objective function value.
73: Collective on KSP
75: Input Parameters:
76: + ksp - the iterative context
77: - o_fcn - the objective function value
79: Level: advanced
81: .keywords: KSP, GLTR, get, objective function
82: @*/
83: PetscErrorCode KSPGLTRGetObjFcn(KSP ksp,PetscReal *o_fcn)
84: {
85: PetscErrorCode ierr, (*f)(KSP, PetscReal *);
89: PetscObjectQueryFunction((PetscObject)ksp, "KSPGLTRGetObjFcn_C", (void (**)(void))&f);
90: if (f) {
91: (*f)(ksp, o_fcn);
92: }
93: return(0);
94: }
98: /*@
99: KSPGLTRGetMinEig - Get minimum eigenvalue.
101: Collective on KSP
103: Input Parameters:
104: + ksp - the iterative context
105: - e_min - the minimum eigenvalue
107: Level: advanced
109: .keywords: KSP, GLTR, get, minimum eigenvalue
110: @*/
111: PetscErrorCode KSPGLTRGetMinEig(KSP ksp,PetscReal *e_min)
112: {
113: PetscErrorCode ierr, (*f)(KSP, PetscReal *);
117: PetscObjectQueryFunction((PetscObject)ksp, "KSPGLTRGetMinEig_C", (void (**)(void))&f);
118: if (f) {
119: (*f)(ksp, e_min);
120: }
121: return(0);
122: }
126: /*@
127: KSPGLTRGetLambda - Get multiplier on trust-region constraint.
129: Collective on KSP
131: Input Parameters:
132: + ksp - the iterative context
133: - lambda - the multiplier
135: Level: advanced
137: .keywords: KSP, GLTR, get, multiplier
138: @*/
139: PetscErrorCode KSPGLTRGetLambda(KSP ksp,PetscReal *lambda)
140: {
141: PetscErrorCode ierr, (*f)(KSP, PetscReal *);
145: PetscObjectQueryFunction((PetscObject)ksp, "KSPGLTRGetLambda_C", (void (**)(void))&f);
146: if (f) {
147: (*f)(ksp, lambda);
148: }
149: return(0);
150: }
154: /*
155: KSPSolve_GLTR - Use preconditioned conjugate gradient to compute
156: an approximate minimizer of the quadratic function
158: q(s) = g^T * s + .5 * s^T * H * s
160: subject to the trust region constraint
162: || s ||_M <= delta,
164: where
166: delta is the trust region radius,
167: g is the gradient vector, and
168: H is the Hessian matrix,
169: M is the positive definite preconditioner matrix.
171: KSPConvergedReason may be
172: $ KSP_CONVERGED_CG_NEG_CURVE if convergence is reached along a negative curvature direction,
173: $ KSP_CONVERGED_CG_CONSTRAINED if convergence is reached along a constrained step,
174: $ other KSP converged/diverged reasons
177: Notes:
178: The preconditioner supplied should be symmetric and positive definite.
179: */
181: PetscErrorCode KSPSolve_GLTR(KSP ksp)
182: {
183: #ifdef PETSC_USE_COMPLEX
184: SETERRQ(PETSC_ERR_SUP, "GLTR is not available for complex systems");
185: #else
186: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
187: PetscReal *t_soln, *t_diag, *t_offd, *e_valu, *e_vect, *e_rwrk;
188: PetscBLASInt *e_iblk, *e_splt, *e_iwrk;
191: MatStructure pflag;
192: Mat Qmat, Mmat;
193: Vec r, z, p, d;
194: PC pc;
195: PetscReal norm_r, norm_d, norm_dp1, norm_p, dMp;
196: PetscReal alpha, beta, kappa, rz, rzm1;
197: PetscReal rr, r2, piv, step;
198: PetscReal vl, vu;
199: PetscReal coef1, coef2, coef3, root1, root2, obj1, obj2;
200: PetscReal norm_t, norm_w, pert;
201: PetscInt i, j, max_cg_its, max_lanczos_its, max_newton_its, sigma;
202: PetscBLASInt t_size = 0, il, iu, e_valus, e_splts, info;
203: PetscBLASInt nrhs, nldb;
205: KSPConvergedReason reason;
206: PetscTruth diagonalscale;
210: /***************************************************************************/
211: /* Check the arguments and parameters. */
212: /***************************************************************************/
214: PCDiagonalScale(ksp->pc, &diagonalscale);
215: if (diagonalscale) {
216: SETERRQ1(PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ksp->type_name);
217: }
219: if (cg->radius < 0.0) {
220: SETERRQ(PETSC_ERR_ARG_OUTOFRANGE, "Input error: radius < 0");
221: }
223: /***************************************************************************/
224: /* Get the workspace vectors and initialize variables */
225: /***************************************************************************/
227: r2 = cg->radius * cg->radius;
228: r = ksp->work[0];
229: z = ksp->work[1];
230: p = ksp->work[2];
231: d = ksp->vec_sol;
232: pc = ksp->pc;
234: PCGetOperators(pc, &Qmat, &Mmat, &pflag);
236: max_cg_its = cg->max_cg_its;
237: max_lanczos_its = cg->max_lanczos_its;
238: max_newton_its = cg->max_newton_its;
239: ksp->its = 0;
241: /***************************************************************************/
242: /* Initialize objective function value and minimum eigenvalue. */
243: /***************************************************************************/
245: cg->e_min = 0.0;
246: cg->o_fcn = 0.0;
247: cg->lambda = 0.0;
249: /***************************************************************************/
250: /* The first phase of GLTR performs a standard conjugate gradient method, */
251: /* but stores the values required for the Lanczos matrix. We switch to */
252: /* the Lanczos when the conjugate gradient method breaks down. */
253: /***************************************************************************/
255: VecSet(d, 0.0); /* d = 0 */
256: VecCopy(ksp->vec_rhs, r); /* r = -grad */
257: KSP_PCApply(ksp, r, z); /* z = inv(M) r */
258: cg->norm_d = 0.0;
260: /***************************************************************************/
261: /* Check the preconditioners for numerical problems and for positive */
262: /* definiteness. The check for not-a-number and infinite values need be */
263: /* performed only once. */
264: /***************************************************************************/
266: VecDot(r, z, &rz); /* rz = r^T inv(M) r */
267: if ((rz != rz) || (rz && (rz / rz != rz / rz))) {
268: /*************************************************************************/
269: /* The preconditioner produced not-a-number or an infinite value. This */
270: /* can appear either due to r having numerical problems or M having */
271: /* numerical problems. Differentiate between the two and then use the */
272: /* gradient direction. */
273: /*************************************************************************/
275: ksp->reason = KSP_DIVERGED_NAN;
276: VecDot(r, r, &rr); /* rr = r^T r */
277: if ((rr != rr) || (rr && (rr / rr != rr / rr))) {
278: /***********************************************************************/
279: /* The right-hand side contains not-a-number or an infinite value. */
280: /* The gradient step does not work; return a zero value for the step. */
281: /***********************************************************************/
283: PetscInfo1(ksp, "KSPSolve_GLTR: bad right-hand side: rr=%g\n", rr);
284: }
285: else {
286: /***********************************************************************/
287: /* The preconditioner contains not-a-number or an infinite value. */
288: /***********************************************************************/
290: PetscInfo1(ksp, "KSPSolve_GLTR: bad preconditioner: rz=%g\n", rz);
292: if (cg->radius) {
293: alpha = sqrt(r2 / rr);
294: VecAXPY(d, alpha, r); /* d = d + alpha r */
295: cg->norm_d = cg->radius;
296:
297: /*********************************************************************/
298: /* Compute objective function. */
299: /*********************************************************************/
301: KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
302: VecAYPX(z, -0.5, ksp->vec_rhs);
303: VecDot(d, z, &cg->o_fcn);
304: cg->o_fcn = -cg->o_fcn;
305: }
306: }
307: return(0);
308: }
310: if (rz < 0.0) {
311: /*************************************************************************/
312: /* The preconditioner is indefinite. Because this is the first */
313: /* and we do not have a direction yet, we use the gradient step. Note */
314: /* that we cannot use the preconditioned norm when computing the step */
315: /* because the matrix is indefinite. */
316: /*************************************************************************/
318: ksp->reason = KSP_DIVERGED_INDEFINITE_PC;
319: PetscInfo1(ksp, "KSPSolve_GLTR: indefinite preconditioner: rz=%g\n", rz);
321: if (cg->radius) {
322: VecDot(r, r, &rr); /* rr = r^T r */
323: alpha = sqrt(r2 / rr);
324: VecAXPY(d, alpha, r); /* d = d + alpha r */
325: cg->norm_d = cg->radius;
327: /***********************************************************************/
328: /* Compute objective function. */
329: /***********************************************************************/
331: KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
332: VecAYPX(z, -0.5, ksp->vec_rhs);
333: VecDot(d, z, &cg->o_fcn);
334: cg->o_fcn = -cg->o_fcn;
335: }
336: return(0);
337: }
339: /***************************************************************************/
340: /* As far as we know, the preconditioner is positive semidefinite. Compute*/
341: /* the residual and check for convergence. Note that there is no choice */
342: /* in the residual that can be used; we must always use the natural */
343: /* residual because this is the only one that can be used by the */
344: /* preconditioned Lanzcos method. */
345: /***************************************************************************/
347: norm_r = sqrt(rz); /* norm_r = |r|_M */
348: cg->norm_r[0] = norm_r;
350: KSPLogResidualHistory(ksp, norm_r);
351: KSPMonitor(ksp, 0, norm_r);
352: ksp->rnorm = norm_r;
354: (*ksp->converged)(ksp, 0, norm_r, &ksp->reason, ksp->cnvP);
355: if (ksp->reason) {
356: return(0);
357: }
359: /***************************************************************************/
360: /* We have not converged. Compute the first direction and check the */
361: /* matrix for numerical problems. */
362: /***************************************************************************/
364: VecCopy(z, p); /* p = z */
365: KSP_MatMult(ksp, Qmat, p, z); /* z = Q * p */
366: VecDot(p, z, &kappa); /* kappa = p^T Q p */
367: if ((kappa != kappa) || (kappa && (kappa / kappa != kappa / kappa))) {
368: /*************************************************************************/
369: /* The matrix produced not-a-number or an infinite value. In this case, */
370: /* we must stop and use the gradient direction. This condition need */
371: /* only be checked once. */
372: /*************************************************************************/
374: ksp->reason = KSP_DIVERGED_NAN;
375: PetscInfo1(ksp, "KSPSolve_GLTR: bad matrix: kappa=%g\n", kappa);
377: if (cg->radius) {
378: VecDot(r, r, &rr); /* rr = r^T r */
379: alpha = sqrt(r2 / rr);
380: VecAXPY(d, alpha, r); /* d = d + alpha r */
381: cg->norm_d = cg->radius;
383: /***********************************************************************/
384: /* Compute objective function. */
385: /***********************************************************************/
387: KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
388: VecAYPX(z, -0.5, ksp->vec_rhs);
389: VecDot(d, z, &cg->o_fcn);
390: cg->o_fcn = -cg->o_fcn;
391: }
392: return(0);
393: }
395: /***************************************************************************/
396: /* Check for breakdown of the conjugate gradient method; this occurs when */
397: /* kappa is zero. */
398: /***************************************************************************/
400: if (fabs(kappa) <= 0.0) {
401: /*************************************************************************/
402: /* The curvature is zero. In this case, we must stop and use the */
403: /* since there the Lanczos matrix is not available. */
404: /*************************************************************************/
405: ksp->reason = KSP_DIVERGED_BREAKDOWN;
406: PetscInfo1(ksp, "KSPSolve_GLTR: breakdown: kappa=%g\n", kappa);
408: if (cg->radius) {
409: VecDot(r, r, &rr); /* rr = r^T r */
410: alpha = sqrt(r2 / rr);
411: VecAXPY(d, alpha, r); /* d = d + alpha r */
412: cg->norm_d = cg->radius;
414: /***********************************************************************/
415: /* Compute objective function. */
416: /***********************************************************************/
418: KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
419: VecAYPX(z, -0.5, ksp->vec_rhs);
420: VecDot(d, z, &cg->o_fcn);
421: cg->o_fcn = -cg->o_fcn;
422: }
423: return(0);
424: }
426: /***************************************************************************/
427: /* Initialize variables for calculating the norm of the direction and for */
428: /* the Lanczos tridiagonal matrix. Note that we track the diagonal value */
429: /* of the Cholesky factorization of the Lanczos matrix in order to */
430: /* determine when negative curvature is encountered. */
431: /***************************************************************************/
433: dMp = 0.0;
434: norm_p = rz;
435: norm_d = 0.0;
437: cg->diag[t_size] = kappa / rz;
438: cg->offd[t_size] = 0.0;
439: ++t_size;
441: piv = 1.0;
443: /***************************************************************************/
444: /* Begin the first part of the GLTR algorithm which runs the conjugate */
445: /* gradient method until either the problem is solved, we encounter the */
446: /* boundary of the trust region, or the conjugate gradient method breaks */
447: /* down. */
448: /***************************************************************************/
450: for (i = 0; i <= max_cg_its; ++i) {
451: /*************************************************************************/
452: /* Know that kappa is nonzero, because we have not broken down, so we */
453: /* can compute the steplength. */
454: /*************************************************************************/
456: alpha = rz / kappa;
457: cg->alpha[ksp->its] = alpha;
459: /*************************************************************************/
460: /* Compute the diagonal value of the Cholesky factorization of the */
461: /* Lanczos matrix and check to see if the Lanczos matrix is indefinite. */
462: /* This indicates a direction of negative curvature. */
463: /*************************************************************************/
465: piv = cg->diag[ksp->its] - cg->offd[ksp->its]*cg->offd[ksp->its] / piv;
466: if (piv <= 0.0) {
467: /***********************************************************************/
468: /* In this case, the matrix is indefinite and we have encountered */
469: /* a direction of negative curvature. Follow the direction to the */
470: /* boundary of the trust region. */
471: /***********************************************************************/
473: ksp->reason = KSP_CONVERGED_CG_NEG_CURVE;
474: PetscInfo1(ksp, "KSPSolve_GLTR: negative curvature: kappa=%g\n", kappa);
476: if (cg->radius) {
477: step = (sqrt(dMp*dMp+norm_p*(r2-norm_d))-dMp)/norm_p;
478: VecAXPY(d, step, p); /* d = d + step p */
479: cg->norm_d = cg->radius;
481: /*********************************************************************/
482: /* Update objective function. */
483: /*********************************************************************/
485: cg->o_fcn += step * (0.5 * step * kappa - rz);
486: }
487: break;
488: }
490: /*************************************************************************/
491: /* Compute the steplength and check for intersection with the trust */
492: /* region. */
493: /*************************************************************************/
495: norm_dp1 = norm_d + alpha*(2.0*dMp + alpha*norm_p);
496: if (cg->radius && norm_dp1 >= r2) {
497: /***********************************************************************/
498: /* In this case, the matrix is positive definite as far as we know. */
499: /* However, the direction does beyond the trust region. Follow the */
500: /* direction to the boundary of the trust region. */
501: /***********************************************************************/
503: ksp->reason = KSP_CONVERGED_CG_CONSTRAINED;
504: PetscInfo1(ksp, "KSPSolve_GLTR: constrained step: radius=%g\n", cg->radius);
506: step = (sqrt(dMp*dMp+norm_p*(r2-norm_d))-dMp)/norm_p;
507: VecAXPY(d, step, p); /* d = d + step p */
508: cg->norm_d = cg->radius;
510: /***********************************************************************/
511: /* Update objective function. */
512: /***********************************************************************/
514: cg->o_fcn += step * (0.5 * step * kappa - rz);
515: break;
516: }
518: /*************************************************************************/
519: /* Now we can update the direction, residual, and objective value. */
520: /*************************************************************************/
522: VecAXPY(d, alpha, p); /* d = d + alpha p */
523: VecAXPY(r, -alpha, z); /* r = r - alpha Q p */
524: KSP_PCApply(ksp, r, z);
526: norm_d = norm_dp1;
527: cg->norm_d = sqrt(norm_d);
529: cg->o_fcn -= 0.5 * alpha * rz;
531: /*************************************************************************/
532: /* Check that the preconditioner appears positive definite. */
533: /*************************************************************************/
535: rzm1 = rz;
536: VecDot(r, z, &rz); /* rz = r^T z */
537: if (rz < 0.0) {
538: /***********************************************************************/
539: /* The preconditioner is indefinite. */
540: /***********************************************************************/
542: ksp->reason = KSP_DIVERGED_INDEFINITE_PC;
543: PetscInfo1(ksp, "KSPSolve_GLTR: cg indefinite preconditioner: rz=%g\n", rz);
544: break;
545: }
547: /*************************************************************************/
548: /* As far as we know, the preconditioner is positive definite. Compute */
549: /* the residual and check for convergence. Note that there is no choice */
550: /* in the residual that can be used; we must always use the natural */
551: /* residual because this is the only one that can be used by the */
552: /* preconditioned Lanzcos method. */
553: /*************************************************************************/
555: norm_r = sqrt(rz); /* norm_r = |r|_M */
556: cg->norm_r[ksp->its+1] = norm_r;
558: KSPLogResidualHistory(ksp, norm_r);
559: KSPMonitor(ksp, 0, norm_r);
560: ksp->rnorm = norm_r;
561:
562: (*ksp->converged)(ksp, i+1, norm_r, &ksp->reason, ksp->cnvP);
563: if (ksp->reason) {
564: PetscInfo2(ksp,"KSPSolve_GLTR: cg truncated step: rnorm=%g, radius=%g\n",norm_r,cg->radius);
565: break;
566: }
568: /*************************************************************************/
569: /* We have not converged yet, update p and the norms. */
570: /*************************************************************************/
572: beta = rz / rzm1;
573: cg->beta[ksp->its] = beta;
574: VecAYPX(p, beta, z); /* p = z + beta p */
576: dMp = beta*(dMp + alpha*norm_p);
577: norm_p = beta*(rzm1 + beta*norm_p);
579: /*************************************************************************/
580: /* Compute the new direction */
581: /*************************************************************************/
583: KSP_MatMult(ksp, Qmat, p, z); /* z = Q * p */
584: VecDot(p, z, &kappa); /* kappa = p^T Q p */
586: /*************************************************************************/
587: /* Update the iteration and the Lanczos tridiagonal matrix. */
588: /*************************************************************************/
590: ++ksp->its;
592: cg->offd[t_size] = sqrt(beta) / fabs(alpha);
593: cg->diag[t_size] = kappa / rz + beta / alpha;
594: ++t_size;
596: /*************************************************************************/
597: /* Check for breakdown of the conjugate gradient method; this occurs */
598: /* when kappa is zero. */
599: /*************************************************************************/
601: if (fabs(kappa) <= 0.0) {
602: /***********************************************************************/
603: /* The method breaks down; move along the direction as if the matrix */
604: /* were indefinite. */
605: /***********************************************************************/
607: ksp->reason = KSP_CONVERGED_CG_NEG_CURVE;
608: PetscInfo1(ksp, "KSPSolve_GLTR: cg breakdown: kappa=%g\n", kappa);
610: if (cg->radius) {
611: step = (sqrt(dMp*dMp+norm_p*(r2-norm_d))-dMp)/norm_p;
612: VecAXPY(d, step, p); /* d = d + step p */
613: cg->norm_d = cg->radius;
614: }
615: break;
616: }
617: }
619: if (!ksp->reason) {
620: ksp->reason = KSP_DIVERGED_ITS;
621: }
623: /***************************************************************************/
624: /* Check to see if we need to continue with the Lanczos method. */
625: /***************************************************************************/
627: if (!cg->radius) {
628: /*************************************************************************/
629: /* There is no radius. Therefore, we cannot move along the boundary. */
630: /*************************************************************************/
632: return(0);
633: }
635: if ((KSP_CONVERGED_CG_CONSTRAINED != ksp->reason) &&
636: (KSP_CONVERGED_CG_NEG_CURVE != ksp->reason)) {
637: /*************************************************************************/
638: /* The method either converged to an interior point or the iteration */
639: /* limit was reached. */
640: /*************************************************************************/
642: return(0);
643: }
644: reason = ksp->reason;
646: /***************************************************************************/
647: /* Switch to contructing the Lanczos basis by way of the conjugate */
648: /* directions. */
649: /***************************************************************************/
650: for (i = 0; i < max_lanczos_its; ++i) {
651: /*************************************************************************/
652: /* Check for breakdown of the conjugate gradient method; this occurs */
653: /* when kappa is zero. */
654: /*************************************************************************/
656: if (fabs(kappa) <= 0.0) {
657: PetscInfo1(ksp, "KSPSolve_GLTR: lanczos breakdown: kappa=%g\n", kappa);
658: break;
659: }
661: /*************************************************************************/
662: /* Update the direction and residual. */
663: /*************************************************************************/
664:
665: alpha = rz / kappa;
666: cg->alpha[ksp->its] = alpha;
668: VecAXPY(r, -alpha, z); /* r = r - alpha Q p */
669: KSP_PCApply(ksp, r, z);
671: /*************************************************************************/
672: /* Check that the preconditioner appears positive definite. */
673: /*************************************************************************/
675: rzm1 = rz;
676: VecDot(r, z, &rz); /* rz = r^T z */
677: if (rz < 0.0) {
678: /***********************************************************************/
679: /* The preconditioner is indefinite. */
680: /***********************************************************************/
682: ksp->reason = KSP_DIVERGED_INDEFINITE_PC;
683: PetscInfo1(ksp, "KSPSolve_GLTR: lanczos indefinite preconditioner: rz=%g\n", rz);
684: break;
685: }
687: /*************************************************************************/
688: /* As far as we know, the preconditioner is positive definite. Compute */
689: /* the residual and check for convergence. Note that there is no choice */
690: /* in the residual that can be used; we must always use the natural */
691: /* residual because this is the only one that can be used by the */
692: /* preconditioned Lanzcos method. */
693: /*************************************************************************/
695: norm_r = sqrt(rz); /* norm_r = |r|_M */
696: cg->norm_r[ksp->its+1] = norm_r;
698: KSPLogResidualHistory(ksp, norm_r);
699: KSPMonitor(ksp, 0, norm_r);
700: ksp->rnorm = norm_r;
701:
702: (*ksp->converged)(ksp, i+1, norm_r, &ksp->reason, ksp->cnvP);
703: if (ksp->reason) {
704: PetscInfo2(ksp,"KSPSolve_GLTR: lanczos truncated step: rnorm=%g, radius=%g\n",norm_r,cg->radius);
705: break;
706: }
708: /*************************************************************************/
709: /* Update p and the norms. */
710: /*************************************************************************/
712: beta = rz / rzm1;
713: cg->beta[ksp->its] = beta;
714: VecAYPX(p, beta, z); /* p = z + beta p */
716: /*************************************************************************/
717: /* Compute the new direction */
718: /*************************************************************************/
720: KSP_MatMult(ksp, Qmat, p, z); /* z = Q * p */
721: VecDot(p, z, &kappa); /* kappa = p^T Q p */
723: /*************************************************************************/
724: /* Update the iteration and the Lanczos tridiagonal matrix. */
725: /*************************************************************************/
727: ++ksp->its;
729: cg->offd[t_size] = sqrt(beta) / fabs(alpha);
730: cg->diag[t_size] = kappa / rz + beta / alpha;
731: ++t_size;
733: norm_r = sqrt(rz); /* norm_r = |r|_M */
734: }
736: /***************************************************************************/
737: /* We have the Lanczos basis, solve the tridiagonal trust-region problem */
738: /* to obtain the Lanczos direction. We know that the solution lies on */
739: /* the boundary of the trust region. We start by checking that the */
740: /* workspace allocated is large enough. */
741: /***************************************************************************/
743: if (t_size > cg->alloced) {
744: if (cg->alloced) {
745: PetscFree(cg->rwork);
746: PetscFree(cg->iwork);
747: cg->alloced += cg->init_alloc;
748: }
749: else {
750: cg->alloced = cg->init_alloc;
751: }
753: while (t_size > cg->alloced) {
754: cg->alloced += cg->init_alloc;
755: }
756:
757: cg->alloced = PetscMin(cg->alloced, t_size);
758: PetscMalloc(10*sizeof(PetscReal)*cg->alloced, &cg->rwork);
759: PetscMalloc(5*sizeof(PetscInt)*cg->alloced, &cg->iwork);
760: }
762: /***************************************************************************/
763: /* Set up the required vectors. */
764: /***************************************************************************/
766: t_soln = cg->rwork + 0*t_size; /* Solution */
767: t_diag = cg->rwork + 1*t_size; /* Diagonal of T */
768: t_offd = cg->rwork + 2*t_size; /* Off-diagonal of T */
769: e_valu = cg->rwork + 3*t_size; /* Eigenvalues of T */
770: e_vect = cg->rwork + 4*t_size; /* Eigenvector of T */
771: e_rwrk = cg->rwork + 5*t_size; /* Eigen workspace */
772:
773: e_iblk = cg->iwork + 0*t_size; /* Eigen blocks */
774: e_splt = cg->iwork + 1*t_size; /* Eigen splits */
775: e_iwrk = cg->iwork + 2*t_size; /* Eigen workspace */
777: /***************************************************************************/
778: /* Compute the minimum eigenvalue of T. */
779: /***************************************************************************/
781: vl = 0.0;
782: vu = 0.0;
783: il = 1;
784: iu = 1;
786: #if defined(PETSC_MISSING_LAPACK_DSTEBZ)
787: SETERRQ(PETSC_ERR_SUP,"DSTEBZ - Lapack routine is unavailable.");
788: #else
789: LAPACKstebz_("I", "E", &t_size, &vl, &vu, &il, &iu, &cg->eigen_tol,
790: cg->diag, cg->offd + 1, &e_valus, &e_splts, e_valu,
791: e_iblk, e_splt, e_rwrk, e_iwrk, &info);
792: #endif
794: if ((0 != info) || (1 != e_valus)) {
795: /*************************************************************************/
796: /* Calculation of the minimum eigenvalue failed. Return the */
797: /* Steihaug-Toint direction. */
798: /*************************************************************************/
800: PetscInfo(ksp, "KSPSolve_GLTR: failed to compute eigenvalue.\n");
801: ksp->reason = reason;
802: return(0);
803: }
805: cg->e_min = e_valu[0];
807: /***************************************************************************/
808: /* Compute the initial value of lambda to make (T + lamba I) positive */
809: /* definite. */
810: /***************************************************************************/
812: pert = cg->init_pert;
813: if (e_valu[0] <= 0.0) {
814: cg->lambda = pert - e_valu[0];
815: }
817: while(1) {
818: for (i = 0; i < t_size; ++i) {
819: t_diag[i] = cg->diag[i] + cg->lambda;
820: t_offd[i] = cg->offd[i];
821: }
823: #if defined(PETSC_MISSING_LAPACK_DPTTRF)
824: SETERRQ(PETSC_ERR_SUP,"DPTTRF - Lapack routine is unavailable.");
825: #else
826: LAPACKpttrf_(&t_size, t_diag, t_offd + 1, &info);
827: #endif
828: if (0 == info) {
829: break;
830: }
832: pert += pert;
833: cg->lambda = cg->lambda * (1.0 + pert) + pert;
834: }
836: /***************************************************************************/
837: /* Compute the initial step and its norm. */
838: /***************************************************************************/
840: nrhs = 1;
841: nldb = t_size;
843: t_soln[0] = -cg->norm_r[0];
844: for (i = 1; i < t_size; ++i) {
845: t_soln[i] = 0.0;
846: }
848: #if defined(PETSC_MISSING_LAPACK_DPTTRS)
849: SETERRQ(PETSC_ERR_SUP,"DPTTRS - Lapack routine is unavailable.");
850: #else
851: LAPACKpttrs_(&t_size, &nrhs, t_diag, t_offd + 1, t_soln, &nldb, &info);
852: #endif
853: if (0 != info) {
854: /*************************************************************************/
855: /* Calculation of the initial step failed; return the Steihaug-Toint */
856: /* direction. */
857: /*************************************************************************/
859: PetscInfo(ksp, "KSPSolve_GLTR: failed to compute step.\n");
860: ksp->reason = reason;
861: return(0);
862: }
864: norm_t = 0;
865: for (i = 0; i < t_size; ++i) {
866: norm_t += t_soln[i] * t_soln[i];
867: }
868: norm_t = sqrt(norm_t);
870: /***************************************************************************/
871: /* Determine the case we are in. */
872: /***************************************************************************/
874: if (norm_t <= cg->radius) {
875: /*************************************************************************/
876: /* The step is within the trust region; check if we are in the hard case */
877: /* and need to move to the boundary by following a direction of negative */
878: /* curvature. */
879: /*************************************************************************/
880:
881: if ((e_valu[0] <= 0.0) && (norm_t < cg->radius)) {
882: /***********************************************************************/
883: /* This is the hard case; compute the eigenvector associated with the */
884: /* minimum eigenvalue and move along this direction to the boundary. */
885: /***********************************************************************/
886: #if defined(PETSC_MISSING_LAPACK_DSTEIN)
887: SETERRQ(PETSC_ERR_SUP,"DSTEIN - Lapack routine is unavailable.");
888: #else
889: LAPACKstein_(&t_size, cg->diag, cg->offd + 1, &e_valus, e_valu,
890: e_iblk, e_splt, e_vect, &nldb,
891: e_rwrk, e_iwrk, e_iwrk + t_size, &info);
892: #endif
893: if (0 != info) {
894: /*********************************************************************/
895: /* Calculation of the minimum eigenvalue failed. Return the */
896: /* Steihaug-Toint direction. */
897: /*********************************************************************/
898:
899: PetscInfo(ksp, "KSPSolve_GLTR: failed to compute eigenvector.\n");
900: ksp->reason = reason;
901: return(0);
902: }
903:
904: coef1 = 0.0;
905: coef2 = 0.0;
906: coef3 = -cg->radius * cg->radius;
907: for (i = 0; i < t_size; ++i) {
908: coef1 += e_vect[i] * e_vect[i];
909: coef2 += e_vect[i] * t_soln[i];
910: coef3 += t_soln[i] * t_soln[i];
911: }
912:
913: coef3 = sqrt(coef2 * coef2 - coef1 * coef3);
914: root1 = (-coef2 + coef3) / coef1;
915: root2 = (-coef2 - coef3) / coef1;
917: /***********************************************************************/
918: /* Compute objective value for (t_soln + root1 * e_vect) */
919: /***********************************************************************/
921: for (i = 0; i < t_size; ++i) {
922: e_rwrk[i] = t_soln[i] + root1 * e_vect[i];
923: }
924:
925: obj1 = e_rwrk[0]*(0.5*(cg->diag[0]*e_rwrk[0]+
926: cg->offd[1]*e_rwrk[1])+cg->norm_r[0]);
927: for (i = 1; i < t_size - 1; ++i) {
928: obj1 += 0.5*e_rwrk[i]*(cg->offd[i]*e_rwrk[i-1]+
929: cg->diag[i]*e_rwrk[i]+
930: cg->offd[i+1]*e_rwrk[i+1]);
931: }
932: obj1 += 0.5*e_rwrk[i]*(cg->offd[i]*e_rwrk[i-1]+
933: cg->diag[i]*e_rwrk[i]);
935: /***********************************************************************/
936: /* Compute objective value for (t_soln + root2 * e_vect) */
937: /***********************************************************************/
939: for (i = 0; i < t_size; ++i) {
940: e_rwrk[i] = t_soln[i] + root2 * e_vect[i];
941: }
942:
943: obj2 = e_rwrk[0]*(0.5*(cg->diag[0]*e_rwrk[0]+
944: cg->offd[1]*e_rwrk[1])+cg->norm_r[0]);
945: for (i = 1; i < t_size - 1; ++i) {
946: obj2 += 0.5*e_rwrk[i]*(cg->offd[i]*e_rwrk[i-1]+
947: cg->diag[i]*e_rwrk[i]+
948: cg->offd[i+1]*e_rwrk[i+1]);
949: }
950: obj2 += 0.5*e_rwrk[i]*(cg->offd[i]*e_rwrk[i-1]+
951: cg->diag[i]*e_rwrk[i]);
952:
953: /***********************************************************************/
954: /* Choose the point with the best objective function value. */
955: /***********************************************************************/
957: if (obj1 <= obj2) {
958: for (i = 0; i < t_size; ++i) {
959: t_soln[i] += root1 * e_vect[i];
960: }
961: }
962: else {
963: for (i = 0; i < t_size; ++i) {
964: t_soln[i] += root2 * e_vect[i];
965: }
966: }
967: }
968: else {
969: /***********************************************************************/
970: /* The matrix is positive definite or there was no room to move; the */
971: /* solution is already contained in t_soln. */
972: /***********************************************************************/
973: }
974: }
975: else {
976: /*************************************************************************/
977: /* The step is outside the trust-region. Compute the correct value for */
978: /* lambda by performing Newton's method. */
979: /*************************************************************************/
981: for (i = 0; i < max_newton_its; ++i) {
982: /***********************************************************************/
983: /* Check for convergence. */
984: /***********************************************************************/
986: if (fabs(norm_t - cg->radius) <= cg->newton_tol * cg->radius) {
987: break;
988: }
990: /***********************************************************************/
991: /* Compute the update. */
992: /***********************************************************************/
994: PetscMemcpy(e_rwrk, t_soln, sizeof(PetscReal)*t_size);
995: #if defined(PETSC_MISSING_LAPACK_DPTTRS)
996: SETERRQ(PETSC_ERR_SUP,"DPTTRS - Lapack routine is unavailable.");
997: #else
998: LAPACKpttrs_(&t_size, &nrhs, t_diag, t_offd + 1, e_rwrk, &nldb, &info);
999: #endif
1000: if (0 != info) {
1001: /*********************************************************************/
1002: /* Calculation of the step failed; return the Steihaug-Toint */
1003: /* direction. */
1004: /*********************************************************************/
1006: PetscInfo(ksp, "KSPSolve_GLTR: failed to compute step.\n");
1007: ksp->reason = reason;
1008: return(0);
1009: }
1011: /***********************************************************************/
1012: /* Modify lambda. */
1013: /***********************************************************************/
1015: norm_w = 0;
1016: for (j = 0; j < t_size; ++j) {
1017: norm_w += t_soln[j] * e_rwrk[j];
1018: }
1019:
1020: cg->lambda += (norm_t - cg->radius)/cg->radius * (norm_t * norm_t) / norm_w;
1022: /***********************************************************************/
1023: /* Factor T + lambda I */
1024: /***********************************************************************/
1025:
1026: for (j = 0; j < t_size; ++j) {
1027: t_diag[j] = cg->diag[j] + cg->lambda;
1028: t_offd[j] = cg->offd[j];
1029: }
1031: #if defined(PETSC_MISSING_LAPACK_DPTTRF)
1032: SETERRQ(PETSC_ERR_SUP,"DPTTRF - Lapack routine is unavailable.");
1033: #else
1034: LAPACKpttrf_(&t_size, t_diag, t_offd + 1, &info);
1035: #endif
1036: if (0 != info) {
1037: /*********************************************************************/
1038: /* Calculation of factorization failed; return the Steihaug-Toint */
1039: /* direction. */
1040: /*********************************************************************/
1042: PetscInfo(ksp, "KSPSolve_GLTR: factorization failed.\n");
1043: ksp->reason = reason;
1044: return(0);
1045: }
1047: /***********************************************************************/
1048: /* Compute the new step and its norm. */
1049: /***********************************************************************/
1051: t_soln[0] = -cg->norm_r[0];
1052: for (j = 1; j < t_size; ++j) {
1053: t_soln[j] = 0.0;
1054: }
1056: #if defined(PETSC_MISSING_LAPACK_DPTTRS)
1057: SETERRQ(PETSC_ERR_SUP,"DPTTRS - Lapack routine is unavailable.");
1058: #else
1059: LAPACKpttrs_(&t_size, &nrhs, t_diag, t_offd + 1, t_soln, &nldb, &info);
1060: #endif
1061: if (0 != info) {
1062: /*********************************************************************/
1063: /* Calculation of the step failed; return the Steihaug-Toint */
1064: /* direction. */
1065: /*********************************************************************/
1066:
1067: PetscInfo(ksp, "KSPSolve_GLTR: failed to compute step.\n");
1068: ksp->reason = reason;
1069: return(0);
1070: }
1072: norm_t = 0;
1073: for (j = 0; j < t_size; ++j) {
1074: norm_t += t_soln[j] * t_soln[j];
1075: }
1076: norm_t = sqrt(norm_t);
1077: }
1079: /*************************************************************************/
1080: /* Check for convergence. */
1081: /*************************************************************************/
1083: if (fabs(norm_t - cg->radius) > cg->newton_tol * cg->radius) {
1084: /***********************************************************************/
1085: /* Newton method failed to converge in iteration limit. */
1086: /***********************************************************************/
1088: PetscInfo(ksp, "KSPSolve_GLTR: failed to converge.\n");
1089: ksp->reason = reason;
1090: return(0);
1091: }
1092: }
1094: /***************************************************************************/
1095: /* Recover the norm of the direction and objective function value. */
1096: /***************************************************************************/
1098: cg->norm_d = norm_t;
1100: cg->o_fcn = t_soln[0]*(0.5*(cg->diag[0]*t_soln[0]+
1101: cg->offd[1]*t_soln[1])+cg->norm_r[0]);
1102: for (i = 1; i < t_size - 1; ++i) {
1103: cg->o_fcn += 0.5*t_soln[i]*(cg->offd[i]*t_soln[i-1]+
1104: cg->diag[i]*t_soln[i]+
1105: cg->offd[i+1]*t_soln[i+1]);
1106: }
1107: cg->o_fcn += 0.5*t_soln[i]*(cg->offd[i]*t_soln[i-1]+
1108: cg->diag[i]*t_soln[i]);
1110: /***************************************************************************/
1111: /* Recover the direction. */
1112: /***************************************************************************/
1114: sigma = -1;
1116: /***************************************************************************/
1117: /* Start conjugate gradient method from the beginning */
1118: /***************************************************************************/
1120: VecCopy(ksp->vec_rhs, r); /* r = -grad */
1121: KSP_PCApply(ksp, r, z); /* z = inv(M) r */
1123: /***************************************************************************/
1124: /* Accumulate Q * s */
1125: /***************************************************************************/
1127: VecCopy(z, d);
1128: VecScale(d, sigma * t_soln[0] / cg->norm_r[0]);
1129:
1130: /***************************************************************************/
1131: /* Compute the first direction. */
1132: /***************************************************************************/
1134: VecCopy(z, p); /* p = z */
1135: KSP_MatMult(ksp, Qmat, p, z); /* z = Q * p */
1137: for (i = 0; i < ksp->its - 1; ++i) {
1138: /*************************************************************************/
1139: /* Update the residual and direction. */
1140: /*************************************************************************/
1142: alpha = cg->alpha[i];
1143: if (alpha >= 0.0) {
1144: sigma = -sigma;
1145: }
1147: VecAXPY(r, -alpha, z); /* r = r - alpha Q p */
1148: KSP_PCApply(ksp, r, z);
1150: /*************************************************************************/
1151: /* Accumulate Q * s */
1152: /*************************************************************************/
1154: VecAXPY(d, sigma * t_soln[i+1] / cg->norm_r[i+1], z);
1156: /*************************************************************************/
1157: /* Update p. */
1158: /*************************************************************************/
1160: beta = cg->beta[i];
1161: VecAYPX(p, beta, z); /* p = z + beta p */
1162: KSP_MatMult(ksp, Qmat, p, z); /* z = Q * p */
1163: }
1165: if (i < ksp->its) {
1166: /*************************************************************************/
1167: /* Update the residual and direction. */
1168: /*************************************************************************/
1170: alpha = cg->alpha[i];
1171: if (alpha >= 0.0) {
1172: sigma = -sigma;
1173: }
1175: VecAXPY(r, -alpha, z); /* r = r - alpha Q p */
1176: KSP_PCApply(ksp, r, z);
1178: /*************************************************************************/
1179: /* Accumulate Q * s */
1180: /*************************************************************************/
1182: VecAXPY(d, sigma * t_soln[i+1] / cg->norm_r[i+1], z);
1183: }
1185: /***************************************************************************/
1186: /* Set the termination reason. */
1187: /***************************************************************************/
1189: ksp->reason = reason;
1190: return(0);
1191: #endif
1192: }
1196: PetscErrorCode KSPSetUp_GLTR(KSP ksp)
1197: {
1198: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1199: PetscInt size;
1203: /* This implementation of CG only handles left preconditioning
1204: * so generate an error otherwise.
1205: */
1206: if (ksp->pc_side == PC_RIGHT) {
1207: SETERRQ(PETSC_ERR_SUP, "No right preconditioning for KSPGLTR");
1208: }
1209: else if (ksp->pc_side == PC_SYMMETRIC) {
1210: SETERRQ(PETSC_ERR_SUP, "No symmetric preconditioning for KSPGLTR");
1211: }
1213: /* get work vectors needed by CG */
1214: KSPDefaultGetWork(ksp, 3);
1216: /* allocate workspace for Lanczos matrix */
1217: cg->max_its = cg->max_cg_its + cg->max_lanczos_its + 1;
1218: size = 5*cg->max_its*sizeof(PetscReal);
1220: PetscMalloc(size, &cg->diag);
1221: PetscMemzero(cg->diag, size);
1222: PetscLogObjectMemory(ksp, size);
1223: cg->offd = cg->diag + cg->max_its;
1224: cg->alpha = cg->offd + cg->max_its;
1225: cg->beta = cg->alpha + cg->max_its;
1226: cg->norm_r = cg->beta + cg->max_its;
1227: return(0);
1228: }
1232: PetscErrorCode KSPDestroy_GLTR(KSP ksp)
1233: {
1234: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1238: PetscFree(cg->diag);
1239: if (cg->alloced) {
1240: PetscFree(cg->rwork);
1241: PetscFree(cg->iwork);
1242: }
1244: KSPDefaultDestroy(ksp);
1245: /* clear composed functions */
1246: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGLTRSetRadius_C","",PETSC_NULL);
1247: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGLTRGetNormD_C","",PETSC_NULL);
1248: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGLTRGetObjFcn_C","",PETSC_NULL);
1249: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGLTRGetMinEig_C","",PETSC_NULL);
1250: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPGLTRGetLambda_C","",PETSC_NULL);
1251: return(0);
1252: }
1257: PetscErrorCode KSPGLTRSetRadius_GLTR(KSP ksp,PetscReal radius)
1258: {
1259: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1262: cg->radius = radius;
1263: return(0);
1264: }
1268: PetscErrorCode KSPGLTRGetNormD_GLTR(KSP ksp,PetscReal *norm_d)
1269: {
1270: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1273: *norm_d = cg->norm_d;
1274: return(0);
1275: }
1279: PetscErrorCode KSPGLTRGetObjFcn_GLTR(KSP ksp,PetscReal *o_fcn)
1280: {
1281: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1284: *o_fcn = cg->o_fcn;
1285: return(0);
1286: }
1290: PetscErrorCode KSPGLTRGetMinEig_GLTR(KSP ksp,PetscReal *e_min)
1291: {
1292: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1295: *e_min = cg->e_min;
1296: return(0);
1297: }
1301: PetscErrorCode KSPGLTRGetLambda_GLTR(KSP ksp,PetscReal *lambda)
1302: {
1303: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1306: *lambda = cg->lambda;
1307: return(0);
1308: }
1313: PetscErrorCode KSPSetFromOptions_GLTR(KSP ksp)
1314: {
1316: KSP_GLTR *cg = (KSP_GLTR *)ksp->data;
1319: PetscOptionsHead("KSP GLTR options");
1321: PetscOptionsReal("-ksp_gltr_radius", "Trust Region Radius", "KSPGLTRSetRadius", cg->radius, &cg->radius, PETSC_NULL);
1323: PetscOptionsReal("-ksp_gltr_init_pert", "Initial perturbation", "", cg->init_pert, &cg->init_pert, PETSC_NULL);
1324: PetscOptionsReal("-ksp_gltr_eigen_tol", "Eigenvalue tolerance", "", cg->eigen_tol, &cg->eigen_tol, PETSC_NULL);
1325: PetscOptionsReal("-ksp_gltr_newton_tol", "Newton tolerance", "", cg->newton_tol, &cg->newton_tol, PETSC_NULL);
1327: PetscOptionsInt("-ksp_gltr_max_cg_its", "Maximum Conjugate Gradient Iters", "", cg->max_cg_its, &cg->max_cg_its, PETSC_NULL);
1328: PetscOptionsInt("-ksp_gltr_max_lanczos_its", "Maximum Lanczos Iters", "", cg->max_lanczos_its, &cg->max_lanczos_its, PETSC_NULL);
1329: PetscOptionsInt("-ksp_gltr_max_newton_its", "Maximum Newton Iters", "", cg->max_newton_its, &cg->max_newton_its, PETSC_NULL);
1330: PetscOptionsTail();
1331: return(0);
1332: }
1334: /*MC
1335: KSPGLTR - Code to run conjugate gradient method subject to a constraint
1336: on the solution norm. This is used in Trust Region methods for
1337: nonlinear equations, SNESTR
1339: Options Database Keys:
1340: . -ksp_gltr_radius <r> - Trust Region Radius
1342: Notes: This is rarely used directly
1344: Level: developer
1346: .seealso: KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPGLTRSetRadius(), KSPGLTRGetNormD(), KSPGLTRGetObjFcn(), KSPGLTRGetMinEig(), KSPGLTRGetLambda()
1347: M*/
1352: PetscErrorCode KSPCreate_GLTR(KSP ksp)
1353: {
1355: KSP_GLTR *cg;
1359: PetscNew(KSP_GLTR, &cg);
1360: PetscLogObjectMemory(ksp, sizeof(KSP_GLTR));
1362: cg->radius = PETSC_MAX;
1364: cg->init_pert = 1.0e-8;
1365: cg->eigen_tol = 1.0e-10;
1366: cg->newton_tol = 1.0e-6;
1368: cg->alloced = 0;
1369: cg->init_alloc = 1024;
1371: cg->max_cg_its = 10000;
1372: cg->max_lanczos_its = 20;
1373: cg->max_newton_its = 10;
1375: cg->max_its = cg->max_cg_its + cg->max_lanczos_its + 1;
1377: ksp->data = (void *) cg;
1378: ksp->pc_side = PC_LEFT;
1380: /* Sets the functions that are associated with this data structure
1381: * (in C++ this is the same as defining virtual functions)
1382: */
1383: ksp->ops->setup = KSPSetUp_GLTR;
1384: ksp->ops->solve = KSPSolve_GLTR;
1385: ksp->ops->destroy = KSPDestroy_GLTR;
1386: ksp->ops->setfromoptions = KSPSetFromOptions_GLTR;
1387: ksp->ops->buildsolution = KSPDefaultBuildSolution;
1388: ksp->ops->buildresidual = KSPDefaultBuildResidual;
1389: ksp->ops->view = 0;
1391: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
1392: "KSPGLTRSetRadius_C",
1393: "KSPGLTRSetRadius_GLTR",
1394: KSPGLTRSetRadius_GLTR);
1395: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
1396: "KSPGLTRGetNormD_C",
1397: "KSPGLTRGetNormD_GLTR",
1398: KSPGLTRGetNormD_GLTR);
1399: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
1400: "KSPGLTRGetObjFcn_C",
1401: "KSPGLTRGetObjFcn_GLTR",
1402: KSPGLTRGetObjFcn_GLTR);
1403: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
1404: "KSPGLTRGetMinEig_C",
1405: "KSPGLTRGetMinEig_GLTR",
1406: KSPGLTRGetMinEig_GLTR);
1407: PetscObjectComposeFunctionDynamic((PetscObject)ksp,
1408: "KSPGLTRGetLambda_C",
1409: "KSPGLTRGetLambda_GLTR",
1410: KSPGLTRGetLambda_GLTR);
1411: return(0);
1412: }