Actual source code: symbrdn.c
petsc-3.11.4 2019-09-28
1: #include <../src/ksp/ksp/utils/lmvm/symbrdn/symbrdn.h>
2: #include <../src/ksp/ksp/utils/lmvm/diagbrdn/diagbrdn.h>
4: /*------------------------------------------------------------*/
6: /*
7: The solution method below is the matrix-free implementation of
8: Equation 8.6a in Dennis and More "Quasi-Newton Methods, Motivation
9: and Theory" (https://epubs.siam.org/doi/abs/10.1137/1019005).
10:
11: Q[i] = (B_i)^{-1}*S[i] terms are computed ahead of time whenever
12: the matrix is updated with a new (S[i], Y[i]) pair. This allows
13: repeated calls of MatSolve without incurring redundant computation.
14:
15: dX <- J0^{-1} * F
16:
17: for i=0,1,2,...,k
18: # Q[i] = (B_i)^T{-1} Y[i]
19:
20: rho = 1.0 / (Y[i]^T S[i])
21: alpha = rho * (S[i]^T F)
22: zeta = 1.0 / (Y[i]^T Q[i])
23: gamma = zeta * (Y[i]^T dX)
24:
25: dX <- dX - (gamma * Q[i]) + (alpha * Y[i])
26: W <- (rho * S[i]) - (zeta * Q[i])
27: dX <- dX + (psi[i] * (Y[i]^T Q[i]) * (W^T F) * W)
28: end
29: */
30: static PetscErrorCode MatSolve_LMVMSymBrdn(Mat B, Vec F, Vec dX)
31: {
32: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
33: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
34: PetscErrorCode ierr;
35: PetscInt i, j;
36: PetscReal numer;
37: PetscScalar sjtpi, yjtsi, wtsi, yjtqi, sjtyi, wtyi, ytx, stf, wtf, stp, ytq;
38:
40: /* Efficient shortcuts for pure BFGS and pure DFP configurations */
41: if (lsb->phi == 0.0) {
42: MatSolve_LMVMBFGS(B, F, dX);
43: return(0);
44: }
45: if (lsb->phi == 1.0) {
46: MatSolve_LMVMDFP(B, F, dX);
47: return(0);
48: }
49:
50: VecCheckSameSize(F, 2, dX, 3);
51: VecCheckMatCompatible(B, dX, 3, F, 2);
52:
53: if (lsb->needP) {
54: /* Start the loop for (P[k] = (B_k) * S[k]) */
55: for (i = 0; i <= lmvm->k; ++i) {
56: MatSymBrdnApplyJ0Fwd(B, lmvm->S[i], lsb->P[i]);
57: for (j = 0; j <= i-1; ++j) {
58: /* Compute the necessary dot products */
59: VecDotBegin(lmvm->S[j], lsb->P[i], &sjtpi);
60: VecDotBegin(lmvm->Y[j], lmvm->S[i], &yjtsi);
61: VecDotEnd(lmvm->S[j], lsb->P[i], &sjtpi);
62: VecDotEnd(lmvm->Y[j], lmvm->S[i], &yjtsi);
63: /* Compute the pure BFGS component of the forward product */
64: VecAXPBYPCZ(lsb->P[i], -PetscRealPart(sjtpi)/lsb->stp[j], PetscRealPart(yjtsi)/lsb->yts[j], 1.0, lsb->P[j], lmvm->Y[j]);
65: /* Tack on the convexly scaled extras to the forward product */
66: if (lsb->phi > 0.0) {
67: VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->stp[j], 0.0, lmvm->Y[j], lsb->P[j]);
68: VecDot(lsb->work, lmvm->S[i], &wtsi);
69: VecAXPY(lsb->P[i], lsb->phi*lsb->stp[j]*PetscRealPart(wtsi), lsb->work);
70: }
71: }
72: VecDot(lmvm->S[i], lsb->P[i], &stp);
73: lsb->stp[i] = PetscRealPart(stp);
74: }
75: lsb->needP = PETSC_FALSE;
76: }
77: if (lsb->needQ) {
78: /* Start the loop for (Q[k] = (B_k)^{-1} * Y[k]) */
79: for (i = 0; i <= lmvm->k; ++i) {
80: MatSymBrdnApplyJ0Inv(B, lmvm->Y[i], lsb->Q[i]);
81: for (j = 0; j <= i-1; ++j) {
82: /* Compute the necessary dot products */
83: VecDotBegin(lmvm->Y[j], lsb->Q[i], &yjtqi);
84: VecDotBegin(lmvm->S[j], lmvm->Y[i], &sjtyi);
85: VecDotEnd(lmvm->Y[j], lsb->Q[i], &yjtqi);
86: VecDotEnd(lmvm->S[j], lmvm->Y[i], &sjtyi);
87: /* Compute the pure DFP component of the inverse application*/
88: VecAXPBYPCZ(lsb->Q[i], -PetscRealPart(yjtqi)/lsb->ytq[j], PetscRealPart(sjtyi)/lsb->yts[j], 1.0, lsb->Q[j], lmvm->S[j]);
89: /* Tack on the convexly scaled extras to the inverse application*/
90: if (lsb->psi[j] > 0.0) {
91: VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->ytq[j], 0.0, lmvm->S[j], lsb->Q[j]);
92: VecDot(lsb->work, lmvm->Y[i], &wtyi);
93: VecAXPY(lsb->Q[i], lsb->psi[j]*lsb->ytq[j]*PetscRealPart(wtyi), lsb->work);
94: }
95: }
96: VecDot(lmvm->Y[i], lsb->Q[i], &ytq);
97: lsb->ytq[i] = PetscRealPart(ytq);
98: if (lsb->phi == 1.0) {
99: lsb->psi[i] = 0.0;
100: } else if (lsb->phi == 0.0) {
101: lsb->psi[i] = 1.0;
102: } else {
103: numer = (1.0 - lsb->phi)*lsb->yts[i]*lsb->yts[i];
104: lsb->psi[i] = numer / (numer + (lsb->phi*lsb->ytq[i]*lsb->stp[i]));
105: }
106: }
107: lsb->needQ = PETSC_FALSE;
108: }
109:
110: /* Start the outer iterations for ((B^{-1}) * dX) */
111: MatSymBrdnApplyJ0Inv(B, F, dX);
112: for (i = 0; i <= lmvm->k; ++i) {
113: /* Compute the necessary dot products -- store yTs and yTp for inner iterations later */
114: VecDotBegin(lmvm->Y[i], dX, &ytx);
115: VecDotBegin(lmvm->S[i], F, &stf);
116: VecDotEnd(lmvm->Y[i], dX, &ytx);
117: VecDotEnd(lmvm->S[i], F, &stf);
118: /* Compute the pure DFP component */
119: VecAXPBYPCZ(dX, -PetscRealPart(ytx)/lsb->ytq[i], PetscRealPart(stf)/lsb->yts[i], 1.0, lsb->Q[i], lmvm->S[i]);
120: /* Tack on the convexly scaled extras */
121: VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[i], -1.0/lsb->ytq[i], 0.0, lmvm->S[i], lsb->Q[i]);
122: VecDot(lsb->work, F, &wtf);
123: VecAXPY(dX, lsb->psi[i]*lsb->ytq[i]*PetscRealPart(wtf), lsb->work);
124: }
126: return(0);
127: }
129: /*------------------------------------------------------------*/
131: /*
132: The forward-product below is the matrix-free implementation of
133: Equation 16 in Dennis and Wolkowicz "Sizing and Least Change Secant
134: Methods" (http://www.caam.rice.edu/caam/trs/90/TR90-05.pdf).
135:
136: P[i] = (B_i)*S[i] terms are computed ahead of time whenever
137: the matrix is updated with a new (S[i], Y[i]) pair. This allows
138: repeated calls of MatMult inside KSP solvers without unnecessarily
139: recomputing P[i] terms in expensive nested-loops.
140:
141: Z <- J0 * X
142:
143: for i=0,1,2,...,k
144: # P[i] = (B_k) * S[i]
145:
146: rho = 1.0 / (Y[i]^T S[i])
147: alpha = rho * (Y[i]^T F)
148: zeta = 1.0 / (S[i]^T P[i])
149: gamma = zeta * (S[i]^T dX)
150:
151: dX <- dX - (gamma * P[i]) + (alpha * S[i])
152: W <- (rho * Y[i]) - (zeta * P[i])
153: dX <- dX + (phi * (S[i]^T P[i]) * (W^T F) * W)
154: end
155: */
156: static PetscErrorCode MatMult_LMVMSymBrdn(Mat B, Vec X, Vec Z)
157: {
158: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
159: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
160: PetscErrorCode ierr;
161: PetscInt i, j;
162: PetscScalar sjtpi, yjtsi, wtsi, stz, ytx, wtx, stp;
163:
164:
166: /* Efficient shortcuts for pure BFGS and pure DFP configurations */
167: if (lsb->phi == 0.0) {
168: MatMult_LMVMBFGS(B, X, Z);
169: return(0);
170: }
171: if (lsb->phi == 1.0) {
172: MatMult_LMVMDFP(B, X, Z);
173: return(0);
174: }
175:
176: VecCheckSameSize(X, 2, Z, 3);
177: VecCheckMatCompatible(B, X, 2, Z, 3);
178:
179: if (lsb->needP) {
180: /* Start the loop for (P[k] = (B_k) * S[k]) */
181: for (i = 0; i <= lmvm->k; ++i) {
182: MatSymBrdnApplyJ0Fwd(B, lmvm->S[i], lsb->P[i]);
183: for (j = 0; j <= i-1; ++j) {
184: /* Compute the necessary dot products */
185: VecDotBegin(lmvm->S[j], lsb->P[i], &sjtpi);
186: VecDotBegin(lmvm->Y[j], lmvm->S[i], &yjtsi);
187: VecDotEnd(lmvm->S[j], lsb->P[i], &sjtpi);
188: VecDotEnd(lmvm->Y[j], lmvm->S[i], &yjtsi);
189: /* Compute the pure BFGS component of the forward product */
190: VecAXPBYPCZ(lsb->P[i], -PetscRealPart(sjtpi)/lsb->stp[j], PetscRealPart(yjtsi)/lsb->yts[j], 1.0, lsb->P[j], lmvm->Y[j]);
191: /* Tack on the convexly scaled extras to the forward product */
192: if (lsb->phi > 0.0) {
193: VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->stp[j], 0.0, lmvm->Y[j], lsb->P[j]);
194: VecDot(lsb->work, lmvm->S[i], &wtsi);
195: VecAXPY(lsb->P[i], lsb->phi*lsb->stp[j]*PetscRealPart(wtsi), lsb->work);
196: }
197: }
198: VecDot(lmvm->S[i], lsb->P[i], &stp);
199: lsb->stp[i] = PetscRealPart(stp);
200: }
201: lsb->needP = PETSC_FALSE;
202: }
203:
204: /* Start the outer iterations for (B * X) */
205: MatSymBrdnApplyJ0Fwd(B, X, Z);
206: for (i = 0; i <= lmvm->k; ++i) {
207: /* Compute the necessary dot products */
208: VecDotBegin(lmvm->S[i], Z, &stz);
209: VecDotBegin(lmvm->Y[i], X, &ytx);
210: VecDotEnd(lmvm->S[i], Z, &stz);
211: VecDotEnd(lmvm->Y[i], X, &ytx);
212: /* Compute the pure BFGS component */
213: VecAXPBYPCZ(Z, -PetscRealPart(stz)/lsb->stp[i], PetscRealPart(ytx)/lsb->yts[i], 1.0, lsb->P[i], lmvm->Y[i]);
214: /* Tack on the convexly scaled extras */
215: VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[i], -1.0/lsb->stp[i], 0.0, lmvm->Y[i], lsb->P[i]);
216: VecDot(lsb->work, X, &wtx);
217: VecAXPY(Z, lsb->phi*lsb->stp[i]*PetscRealPart(wtx), lsb->work);
218: }
219: return(0);
220: }
222: /*------------------------------------------------------------*/
224: static PetscErrorCode MatUpdate_LMVMSymBrdn(Mat B, Vec X, Vec F)
225: {
226: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
227: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
228: Mat_LMVM *dbase;
229: Mat_DiagBrdn *dctx;
230: PetscErrorCode ierr;
231: PetscInt old_k, i;
232: PetscReal curvtol;
233: PetscScalar curvature, ytytmp, ststmp;
236: if (!lmvm->m) return(0);
237: if (lmvm->prev_set) {
238: /* Compute the new (S = X - Xprev) and (Y = F - Fprev) vectors */
239: VecAYPX(lmvm->Xprev, -1.0, X);
240: VecAYPX(lmvm->Fprev, -1.0, F);
241: /* Test if the updates can be accepted */
242: VecDotBegin(lmvm->Xprev, lmvm->Fprev, &curvature);
243: VecDotBegin(lmvm->Xprev, lmvm->Xprev, &ststmp);
244: VecDotEnd(lmvm->Xprev, lmvm->Fprev, &curvature);
245: VecDotEnd(lmvm->Xprev, lmvm->Xprev, &ststmp);
246: if (PetscRealPart(ststmp) < lmvm->eps) {
247: curvtol = 0.0;
248: } else {
249: curvtol = lmvm->eps * PetscRealPart(ststmp);
250: }
251: if (PetscRealPart(curvature) > curvtol) {
252: /* Update is good, accept it */
253: lsb->watchdog = 0;
254: lsb->needP = lsb->needQ = PETSC_TRUE;
255: old_k = lmvm->k;
256: MatUpdateKernel_LMVM(B, lmvm->Xprev, lmvm->Fprev);
257: /* If we hit the memory limit, shift the yts, yty and sts arrays */
258: if (old_k == lmvm->k) {
259: for (i = 0; i <= lmvm->k-1; ++i) {
260: lsb->yts[i] = lsb->yts[i+1];
261: lsb->yty[i] = lsb->yty[i+1];
262: lsb->sts[i] = lsb->sts[i+1];
263: }
264: }
265: /* Update history of useful scalars */
266: VecDot(lmvm->Y[lmvm->k], lmvm->Y[lmvm->k], &ytytmp);
267: lsb->yts[lmvm->k] = PetscRealPart(curvature);
268: lsb->yty[lmvm->k] = PetscRealPart(ytytmp);
269: lsb->sts[lmvm->k] = PetscRealPart(ststmp);
270: /* Compute the scalar scale if necessary */
271: if (lsb->scale_type == SYMBRDN_SCALE_SCALAR) {
272: MatSymBrdnComputeJ0Scalar(B);
273: }
274: } else {
275: /* Update is bad, skip it */
276: ++lmvm->nrejects;
277: ++lsb->watchdog;
278: }
279: } else {
280: switch (lsb->scale_type) {
281: case SYMBRDN_SCALE_DIAG:
282: dbase = (Mat_LMVM*)lsb->D->data;
283: dctx = (Mat_DiagBrdn*)dbase->ctx;
284: VecSet(dctx->invD, lsb->delta);
285: break;
286: case SYMBRDN_SCALE_SCALAR:
287: lsb->sigma = lsb->delta;
288: break;
289: case SYMBRDN_SCALE_NONE:
290: lsb->sigma = 1.0;
291: break;
292: default:
293: break;
294: }
295: }
296:
297: /* Update the scaling */
298: if (lsb->scale_type == SYMBRDN_SCALE_DIAG) {
299: MatLMVMUpdate(lsb->D, X, F);
300: }
301:
302: if (lsb->watchdog > lsb->max_seq_rejects) {
303: MatLMVMReset(B, PETSC_FALSE);
304: if (lsb->scale_type == SYMBRDN_SCALE_DIAG) {
305: MatLMVMReset(lsb->D, PETSC_FALSE);
306: }
307: }
309: /* Save the solution and function to be used in the next update */
310: VecCopy(X, lmvm->Xprev);
311: VecCopy(F, lmvm->Fprev);
312: lmvm->prev_set = PETSC_TRUE;
313: return(0);
314: }
316: /*------------------------------------------------------------*/
318: static PetscErrorCode MatCopy_LMVMSymBrdn(Mat B, Mat M, MatStructure str)
319: {
320: Mat_LMVM *bdata = (Mat_LMVM*)B->data;
321: Mat_SymBrdn *blsb = (Mat_SymBrdn*)bdata->ctx;
322: Mat_LMVM *mdata = (Mat_LMVM*)M->data;
323: Mat_SymBrdn *mlsb = (Mat_SymBrdn*)mdata->ctx;
324: PetscErrorCode ierr;
325: PetscInt i;
328: mlsb->phi = blsb->phi;
329: mlsb->needP = blsb->needP;
330: mlsb->needQ = blsb->needQ;
331: for (i=0; i<=bdata->k; ++i) {
332: mlsb->stp[i] = blsb->stp[i];
333: mlsb->ytq[i] = blsb->ytq[i];
334: mlsb->yts[i] = blsb->yts[i];
335: mlsb->psi[i] = blsb->psi[i];
336: VecCopy(blsb->P[i], mlsb->P[i]);
337: VecCopy(blsb->Q[i], mlsb->Q[i]);
338: }
339: mlsb->scale_type = blsb->scale_type;
340: mlsb->alpha = blsb->alpha;
341: mlsb->beta = blsb->beta;
342: mlsb->rho = blsb->rho;
343: mlsb->delta = blsb->delta;
344: mlsb->sigma_hist = blsb->sigma_hist;
345: mlsb->watchdog = blsb->watchdog;
346: mlsb->max_seq_rejects = blsb->max_seq_rejects;
347: switch (blsb->scale_type) {
348: case SYMBRDN_SCALE_SCALAR:
349: mlsb->sigma = blsb->sigma;
350: break;
351: case SYMBRDN_SCALE_DIAG:
352: MatCopy(blsb->D, mlsb->D, SAME_NONZERO_PATTERN);
353: break;
354: case SYMBRDN_SCALE_NONE:
355: mlsb->sigma = 1.0;
356: break;
357: default:
358: break;
359: }
360: return(0);
361: }
363: /*------------------------------------------------------------*/
365: static PetscErrorCode MatReset_LMVMSymBrdn(Mat B, PetscBool destructive)
366: {
367: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
368: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
369: Mat_LMVM *dbase;
370: Mat_DiagBrdn *dctx;
371: PetscErrorCode ierr;
372:
374: lsb->watchdog = 0;
375: lsb->needP = lsb->needQ = PETSC_TRUE;
376: if (lsb->allocated) {
377: if (destructive) {
378: VecDestroy(&lsb->work);
379: PetscFree6(lsb->stp, lsb->ytq, lsb->yts, lsb->yty, lsb->sts, lsb->psi);
380: VecDestroyVecs(lmvm->m, &lsb->P);
381: VecDestroyVecs(lmvm->m, &lsb->Q);
382: switch (lsb->scale_type) {
383: case SYMBRDN_SCALE_DIAG:
384: MatLMVMReset(lsb->D, PETSC_TRUE);
385: break;
386: default:
387: break;
388: }
389: lsb->allocated = PETSC_FALSE;
390: } else {
391: PetscMemzero(lsb->psi, lmvm->m);
392: switch (lsb->scale_type) {
393: case SYMBRDN_SCALE_SCALAR:
394: lsb->sigma = lsb->delta;
395: break;
396: case SYMBRDN_SCALE_DIAG:
397: MatLMVMReset(lsb->D, PETSC_FALSE);
398: dbase = (Mat_LMVM*)lsb->D->data;
399: dctx = (Mat_DiagBrdn*)dbase->ctx;
400: VecSet(dctx->invD, lsb->delta);
401: break;
402: case SYMBRDN_SCALE_NONE:
403: lsb->sigma = 1.0;
404: break;
405: default:
406: break;
407: }
408: }
409: }
410: MatReset_LMVM(B, destructive);
411: return(0);
412: }
414: /*------------------------------------------------------------*/
416: static PetscErrorCode MatAllocate_LMVMSymBrdn(Mat B, Vec X, Vec F)
417: {
418: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
419: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
420: PetscErrorCode ierr;
421:
423: MatAllocate_LMVM(B, X, F);
424: if (!lsb->allocated) {
425: VecDuplicate(X, &lsb->work);
426: PetscMalloc6(lmvm->m, &lsb->stp, lmvm->m, &lsb->ytq, lmvm->m, &lsb->yts, lmvm->m, &lsb->yty, lmvm->m, &lsb->sts, lmvm->m, &lsb->psi);
427: PetscMemzero(lsb->psi, lmvm->m);
428: if (lmvm->m > 0) {
429: VecDuplicateVecs(X, lmvm->m, &lsb->P);
430: VecDuplicateVecs(X, lmvm->m, &lsb->Q);
431: }
432: switch (lsb->scale_type) {
433: case SYMBRDN_SCALE_DIAG:
434: MatLMVMAllocate(lsb->D, X, F);
435: break;
436: default:
437: break;
438: }
439: lsb->allocated = PETSC_TRUE;
440: }
441: return(0);
442: }
444: /*------------------------------------------------------------*/
446: static PetscErrorCode MatDestroy_LMVMSymBrdn(Mat B)
447: {
448: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
449: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
450: PetscErrorCode ierr;
453: if (lsb->allocated) {
454: VecDestroy(&lsb->work);
455: PetscFree6(lsb->stp, lsb->ytq, lsb->yts, lsb->yty, lsb->sts, lsb->psi);
456: VecDestroyVecs(lmvm->m, &lsb->P);
457: VecDestroyVecs(lmvm->m, &lsb->Q);
458: lsb->allocated = PETSC_FALSE;
459: }
460: MatDestroy(&lsb->D);
461: PetscFree(lmvm->ctx);
462: MatDestroy_LMVM(B);
463: return(0);
464: }
466: /*------------------------------------------------------------*/
468: static PetscErrorCode MatSetUp_LMVMSymBrdn(Mat B)
469: {
470: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
471: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
472: PetscErrorCode ierr;
473: PetscInt n, N;
474:
476: MatSetUp_LMVM(B);
477: if (!lsb->allocated) {
478: VecDuplicate(lmvm->Xprev, &lsb->work);
479: PetscMalloc6(lmvm->m, &lsb->stp, lmvm->m, &lsb->ytq, lmvm->m, &lsb->yts, lmvm->m, &lsb->yty, lmvm->m, &lsb->sts, lmvm->m, &lsb->psi);
480: PetscMemzero(lsb->psi, lmvm->m);
481: if (lmvm->m > 0) {
482: VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lsb->P);
483: VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lsb->Q);
484: }
485: switch (lsb->scale_type) {
486: case SYMBRDN_SCALE_DIAG:
487: MatGetLocalSize(B, &n, &n);
488: MatGetSize(B, &N, &N);
489: MatSetSizes(lsb->D, n, n, N, N);
490: MatSetUp(lsb->D);
491: break;
492: default:
493: break;
494: }
495: lsb->allocated = PETSC_TRUE;
496: }
497: return(0);
498: }
500: /*------------------------------------------------------------*/
502: PetscErrorCode MatView_LMVMSymBrdn(Mat B, PetscViewer pv)
503: {
504: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
505: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
506: PetscErrorCode ierr;
507: PetscBool isascii;
510: PetscObjectTypeCompare((PetscObject)pv,PETSCVIEWERASCII,&isascii);
511: if (isascii) {
512: PetscViewerASCIIPrintf(pv,"Scale type: %s\n",Scale_Table[lsb->scale_type]);
513: PetscViewerASCIIPrintf(pv,"Scale history: %d\n",lsb->sigma_hist);
514: PetscViewerASCIIPrintf(pv,"Scale params: alpha=%g, beta=%g, rho=%g\n",(double)lsb->alpha, (double)lsb->beta, (double)lsb->rho);
515: PetscViewerASCIIPrintf(pv,"Convex factors: phi=%g, theta=%g\n",(double)lsb->phi, (double)lsb->theta);
516: }
517: MatView_LMVM(B, pv);
518: if (lsb->scale_type == SYMBRDN_SCALE_DIAG) {
519: MatView(lsb->D, pv);
520: }
521: return(0);
522: }
524: /*------------------------------------------------------------*/
526: PetscErrorCode MatSetFromOptions_LMVMSymBrdn(PetscOptionItems *PetscOptionsObject, Mat B)
527: {
528: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
529: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
530: Mat_LMVM *dbase;
531: Mat_DiagBrdn *dctx;
532: PetscErrorCode ierr;
535: MatSetFromOptions_LMVM(PetscOptionsObject, B);
536: PetscOptionsHead(PetscOptionsObject,"Restricted Broyden method for approximating SPD Jacobian actions (MATLMVMSYMBRDN)");
537: PetscOptionsEList("-mat_lmvm_scale_type", "(developer) scaling type applied to J0", "", Scale_Table, SYMBRDN_SCALE_SIZE, Scale_Table[lsb->scale_type], &lsb->scale_type,NULL);
538: PetscOptionsReal("-mat_lmvm_phi","(developer) convex ratio between BFGS and DFP components of the update","",lsb->phi,&lsb->phi,NULL);
539: PetscOptionsReal("-mat_lmvm_theta","(developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling","",lsb->theta,&lsb->theta,NULL);
540: PetscOptionsReal("-mat_lmvm_rho","(developer) update limiter in the J0 scaling","",lsb->rho,&lsb->rho,NULL);
541: PetscOptionsReal("-mat_lmvm_alpha","(developer) convex ratio in the J0 scaling","",lsb->alpha,&lsb->alpha,NULL);
542: PetscOptionsReal("-mat_lmvm_beta","(developer) exponential factor in the diagonal J0 scaling","",lsb->beta,&lsb->beta,NULL);
543: PetscOptionsInt("-mat_lmvm_sigma_hist","(developer) number of past updates to use in the default J0 scalar","",lsb->sigma_hist,&lsb->sigma_hist,NULL);
544: PetscOptionsTail();
545: if ((lsb->phi < 0.0) || (lsb->phi > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio for the update formula cannot be outside the range of [0, 1]");
546: if ((lsb->theta < 0.0) || (lsb->theta > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio for the diagonal J0 scale cannot be outside the range of [0, 1]");
547: if ((lsb->alpha < 0.0) || (lsb->alpha > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio in the J0 scaling cannot be outside the range of [0, 1]");
548: if ((lsb->rho < 0.0) || (lsb->rho > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "update limiter in the J0 scaling cannot be outside the range of [0, 1]");
549: if (lsb->sigma_hist < 0) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "J0 scaling history length cannot be negative");
550: if (lsb->scale_type == SYMBRDN_SCALE_DIAG) {
551: MatSetFromOptions(lsb->D);
552: dbase = (Mat_LMVM*)lsb->D->data;
553: dctx = (Mat_DiagBrdn*)dbase->ctx;
554: dctx->delta_min = lsb->delta_min;
555: dctx->delta_max = lsb->delta_max;
556: dctx->theta = lsb->theta;
557: dctx->rho = lsb->rho;
558: dctx->alpha = lsb->alpha;
559: dctx->beta = lsb->beta;
560: dctx->sigma_hist = lsb->sigma_hist;
561: dctx->forward = PETSC_TRUE;
562: }
563: return(0);
564: }
566: /*------------------------------------------------------------*/
568: PetscErrorCode MatCreate_LMVMSymBrdn(Mat B)
569: {
570: Mat_LMVM *lmvm;
571: Mat_SymBrdn *lsb;
572: PetscErrorCode ierr;
575: MatCreate_LMVM(B);
576: PetscObjectChangeTypeName((PetscObject)B, MATLMVMSYMBRDN);
577: MatSetOption(B, MAT_SPD, PETSC_TRUE);
578: B->ops->view = MatView_LMVMSymBrdn;
579: B->ops->setfromoptions = MatSetFromOptions_LMVMSymBrdn;
580: B->ops->setup = MatSetUp_LMVMSymBrdn;
581: B->ops->destroy = MatDestroy_LMVMSymBrdn;
582: B->ops->solve = MatSolve_LMVMSymBrdn;
583:
584: lmvm = (Mat_LMVM*)B->data;
585: lmvm->square = PETSC_TRUE;
586: lmvm->ops->allocate = MatAllocate_LMVMSymBrdn;
587: lmvm->ops->reset = MatReset_LMVMSymBrdn;
588: lmvm->ops->update = MatUpdate_LMVMSymBrdn;
589: lmvm->ops->mult = MatMult_LMVMSymBrdn;
590: lmvm->ops->copy = MatCopy_LMVMSymBrdn;
591:
592: PetscNewLog(B, &lsb);
593: lmvm->ctx = (void*)lsb;
594: lsb->allocated = PETSC_FALSE;
595: lsb->needP = lsb->needQ = PETSC_TRUE;
596: lsb->phi = 0.125;
597: lsb->theta = 0.125;
598: lsb->alpha = 1.0;
599: lsb->rho = 1.0;
600: lsb->beta = 0.5;
601: lsb->sigma = 1.0;
602: lsb->delta = 1.0;
603: lsb->delta_min = 1e-7;
604: lsb->delta_max = 100.0;
605: lsb->sigma_hist = 1;
606: lsb->scale_type = SYMBRDN_SCALE_DIAG;
607: lsb->watchdog = 0;
608: lsb->max_seq_rejects = lmvm->m/2;
609:
610: MatCreate(PetscObjectComm((PetscObject)B), &lsb->D);
611: MatSetType(lsb->D, MATLMVMDIAGBRDN);
612: MatSetOptionsPrefix(lsb->D, "J0_");
613: return(0);
614: }
616: /*------------------------------------------------------------*/
618: /*@
619: MatSymBrdnSetDelta - Sets the starting value for the diagonal scaling vector computed
620: in the SymBrdn approximations (also works for BFGS and DFP).
621:
622: Input Parameters:
623: + B - LMVM matrix
624: - delta - initial value for diagonal scaling
626: Level: intermediate
627: @*/
629: PetscErrorCode MatSymBrdnSetDelta(Mat B, PetscScalar delta)
630: {
631: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
632: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
633: PetscErrorCode ierr;
634: PetscBool is_bfgs, is_dfp, is_symbrdn, is_symbadbrdn;
635:
637: PetscObjectTypeCompare((PetscObject)B, MATLMVMBFGS, &is_bfgs);
638: PetscObjectTypeCompare((PetscObject)B, MATLMVMDFP, &is_dfp);
639: PetscObjectTypeCompare((PetscObject)B, MATLMVMSYMBRDN, &is_symbrdn);
640: PetscObjectTypeCompare((PetscObject)B, MATLMVMSYMBADBRDN, &is_symbadbrdn);
641: if (!is_bfgs && !is_dfp && !is_symbrdn && !is_symbadbrdn) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_INCOMP, "diagonal scaling is only available for DFP, BFGS and SymBrdn matrices");
642: lsb->delta = PetscAbsReal(PetscRealPart(delta));
643: lsb->delta = PetscMin(lsb->delta, lsb->delta_max);
644: lsb->delta = PetscMax(lsb->delta, lsb->delta_min);
645: return(0);
646: }
648: /*------------------------------------------------------------*/
650: /*@
651: MatCreateLMVMSymBrdn - Creates a limited-memory Symmetric Broyden-type matrix used
652: for approximating Jacobians. L-SymBrdn is a convex combination of L-DFP and
653: L-BFGS such that SymBrdn = (1 - phi)*BFGS + phi*DFP. The combination factor
654: phi is restricted to the range [0, 1], where the L-SymBrdn matrix is guaranteed
655: to be symmetric positive-definite.
656:
657: The provided local and global sizes must match the solution and function vectors
658: used with MatLMVMUpdate() and MatSolve(). The resulting L-SymBrdn matrix will have
659: storage vectors allocated with VecCreateSeq() in serial and VecCreateMPI() in
660: parallel. To use the L-SymBrdn matrix with other vector types, the matrix must be
661: created using MatCreate() and MatSetType(), followed by MatLMVMAllocate().
662: This ensures that the internal storage and work vectors are duplicated from the
663: correct type of vector.
665: Collective on MPI_Comm
667: Input Parameters:
668: + comm - MPI communicator, set to PETSC_COMM_SELF
669: . n - number of local rows for storage vectors
670: - N - global size of the storage vectors
672: Output Parameter:
673: . B - the matrix
675: It is recommended that one use the MatCreate(), MatSetType() and/or MatSetFromOptions()
676: paradigm instead of this routine directly.
678: Options Database Keys:
679: . -mat_lmvm_num_vecs - maximum number of correction vectors (i.e.: updates) stored
680: . -mat_lmvm_phi - (developer) convex ratio between BFGS and DFP components of the update
681: . -mat_lmvm_scale_type - (developer) type of scaling applied to J0 (none, scalar, diagonal)
682: . -mat_lmvm_theta - (developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling
683: . -mat_lmvm_rho - (developer) update limiter for the J0 scaling
684: . -mat_lmvm_alpha - (developer) coefficient factor for the quadratic subproblem in J0 scaling
685: . -mat_lmvm_beta - (developer) exponential factor for the diagonal J0 scaling
686: . -mat_lmvm_sigma_hist - (developer) number of past updates to use in J0 scaling
688: Level: intermediate
690: .seealso: MatCreate(), MATLMVM, MATLMVMSYMBRDN, MatCreateLMVMDFP(), MatCreateLMVMSR1(),
691: MatCreateLMVMBFGS(), MatCreateLMVMBrdn(), MatCreateLMVMBadBrdn()
692: @*/
693: PetscErrorCode MatCreateLMVMSymBrdn(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
694: {
695: PetscErrorCode ierr;
696:
698: MatCreate(comm, B);
699: MatSetSizes(*B, n, n, N, N);
700: MatSetType(*B, MATLMVMSYMBRDN);
701: MatSetUp(*B);
702: return(0);
703: }
705: /*------------------------------------------------------------*/
707: PetscErrorCode MatSymBrdnApplyJ0Fwd(Mat B, Vec X, Vec Z)
708: {
709: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
710: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
711: PetscErrorCode ierr;
712:
714: if (lmvm->J0 || lmvm->user_pc || lmvm->user_ksp || lmvm->user_scale) {
715: MatLMVMApplyJ0Fwd(B, X, Z);
716: } else {
717: switch (lsb->scale_type) {
718: case SYMBRDN_SCALE_SCALAR:
719: VecCopy(X, Z);
720: VecScale(Z, 1.0/lsb->sigma);
721: break;
722: case SYMBRDN_SCALE_DIAG:
723: MatMult(lsb->D, X, Z);
724: break;
725: case SYMBRDN_SCALE_NONE:
726: default:
727: VecCopy(X, Z);
728: break;
729: }
730: }
731: return(0);
732: }
734: /*------------------------------------------------------------*/
736: PetscErrorCode MatSymBrdnApplyJ0Inv(Mat B, Vec F, Vec dX)
737: {
738: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
739: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
740: PetscErrorCode ierr;
741:
743: if (lmvm->J0 || lmvm->user_pc || lmvm->user_ksp || lmvm->user_scale) {
744: MatLMVMApplyJ0Inv(B, F, dX);
745: } else {
746: switch (lsb->scale_type) {
747: case SYMBRDN_SCALE_SCALAR:
748: VecCopy(F, dX);
749: VecScale(dX, lsb->sigma);
750: break;
751: case SYMBRDN_SCALE_DIAG:
752: MatSolve(lsb->D, F, dX);
753: break;
754: case SYMBRDN_SCALE_NONE:
755: default:
756: VecCopy(F, dX);
757: break;
758: }
759: }
760: return(0);
761: }
763: /*------------------------------------------------------------*/
765: PetscErrorCode MatSymBrdnComputeJ0Scalar(Mat B)
766: {
767: Mat_LMVM *lmvm = (Mat_LMVM*)B->data;
768: Mat_SymBrdn *lsb = (Mat_SymBrdn*)lmvm->ctx;
769: PetscInt i, start;
770: PetscReal a, b, c, sig1, sig2, signew;
771:
773: if (lsb->sigma_hist == 0) {
774: signew = 1.0;
775: } else {
776: start = PetscMax(0, lmvm->k-lsb->sigma_hist+1);
777: signew = 0.0;
778: if (lsb->alpha == 1.0) {
779: for (i = start; i <= lmvm->k; ++i) {
780: signew += lsb->yts[i]/lsb->yty[i];
781: }
782: } else if (lsb->alpha == 0.5) {
783: for (i = start; i <= lmvm->k; ++i) {
784: signew += lsb->sts[i]/lsb->yty[i];
785: }
786: signew = PetscSqrtReal(signew);
787: } else if (lsb->alpha == 0.0) {
788: for (i = start; i <= lmvm->k; ++i) {
789: signew += lsb->sts[i]/lsb->yts[i];
790: }
791: } else {
792: /* compute coefficients of the quadratic */
793: a = b = c = 0.0;
794: for (i = start; i <= lmvm->k; ++i) {
795: a += lsb->yty[i];
796: b += lsb->yts[i];
797: c += lsb->sts[i];
798: }
799: a *= lsb->alpha;
800: b *= -(2.0*lsb->alpha - 1.0);
801: c *= lsb->alpha - 1.0;
802: /* use quadratic formula to find roots */
803: sig1 = (-b + PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
804: sig2 = (-b - PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
805: /* accept the positive root as the scalar */
806: if (sig1 > 0.0) {
807: signew = sig1;
808: } else if (sig2 > 0.0) {
809: signew = sig2;
810: } else {
811: SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
812: }
813: }
814: }
815: lsb->sigma = lsb->rho*signew + (1.0 - lsb->rho)*lsb->sigma;
816: return(0);
817: }