Actual source code: symbrdn.c
1: #include "symbrdn.h" /*I "petscksp.h" I*/
2: #include <petscblaslapack.h>
4: /* Compute the forward symmetric Broyden scaling parameter phi corresponding the already known value of the "bad"
5: symmetric Broyden scaling parameter psi */
6: static inline PetscScalar PhiFromPsi(PetscScalar psi, PetscScalar yts, PetscScalar stBs, PetscScalar ytHy)
7: {
8: PetscScalar numer = (1.0 - psi) * PetscRealPart(PetscConj(yts) * yts);
9: PetscScalar phi = numer / (numer + psi * stBs * ytHy);
10: return phi;
11: }
13: /* The symetric broyden update can be written as
15: [ | ] [ a_k | b_k ] [ s_k^T B_k ]
16: B_{k+1} = B_k + [ B_k s_k | y_k ] [-----+-----] [-----------]
17: [ | ] [ b_k | c_k ] [ y_k^T ]
19: We can unroll this as
21: [ | ] [ a_i | b_i ] [ s_i^T B_i ]
22: B_{k+1} = B_0 + \sum_i [ B_i s_i | y_i ] [-----+-----] [-----------]
23: [ | ] [ b_i | c_i ] [ y_i^T ]
25: The a_i, b_i, and c_i values are stored in M00, M01, and M11, and are computed in
26: SymBroydenRecursiveBasisUpdate() below
27: */
28: static PetscErrorCode SymBroydenKernel_Recursive_Inner(Mat B, MatLMVMMode mode, PetscInt oldest, PetscInt next, Vec X, Vec B0X)
29: {
30: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
31: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
32: MatLMVMBasisType Y_t = LMVMModeMap(LMBASIS_Y, mode);
33: LMBasis BkS = lsb->basis[LMVMModeMap(SYMBROYDEN_BASIS_BKS, mode)];
34: LMProducts M00 = lsb->products[LMVMModeMap(SYMBROYDEN_PRODUCTS_M00, mode)];
35: LMProducts M01 = lsb->products[LMVMModeMap(SYMBROYDEN_PRODUCTS_M01, mode)];
36: LMProducts M11 = lsb->products[LMVMModeMap(SYMBROYDEN_PRODUCTS_M11, mode)];
37: LMBasis Y;
38: Vec StBkX, YtX, U, V;
40: PetscFunctionBegin;
41: PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
42: PetscCall(MatLMVMGetWorkRow(B, &StBkX));
43: PetscCall(MatLMVMGetWorkRow(B, &YtX));
44: PetscCall(MatLMVMGetWorkRow(B, &U));
45: PetscCall(MatLMVMGetWorkRow(B, &V));
46: PetscCall(LMBasisGEMVH(BkS, oldest, next, 1.0, X, 0.0, StBkX));
47: PetscCall(LMBasisGEMVH(Y, oldest, next, 1.0, X, 0.0, YtX));
48: PetscCall(LMProductsMult(M00, oldest, next, 1.0, StBkX, 0.0, U, PETSC_FALSE));
49: PetscCall(LMProductsMult(M01, oldest, next, 1.0, YtX, 1.0, U, PETSC_FALSE));
50: PetscCall(LMProductsMult(M01, oldest, next, 1.0, StBkX, 0.0, V, PETSC_FALSE));
51: PetscCall(LMProductsMult(M11, oldest, next, 1.0, YtX, 1.0, V, PETSC_FALSE));
52: PetscCall(LMBasisGEMV(BkS, oldest, next, 1.0, U, 1.0, B0X));
53: PetscCall(LMBasisGEMV(Y, oldest, next, 1.0, V, 1.0, B0X));
54: PetscCall(MatLMVMRestoreWorkRow(B, &V));
55: PetscCall(MatLMVMRestoreWorkRow(B, &U));
56: PetscCall(MatLMVMRestoreWorkRow(B, &YtX));
57: PetscCall(MatLMVMRestoreWorkRow(B, &StBkX));
58: PetscFunctionReturn(PETSC_SUCCESS);
59: }
61: static PetscErrorCode MatLMVMSymBroydenGetConvexFactor(Mat B, SymBroydenProductsType Phi_t, LMProducts *Phi)
62: {
63: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
64: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
65: PetscScalar phi = Phi_t == SYMBROYDEN_PRODUCTS_PHI ? lsb->phi_scalar : lsb->psi_scalar;
67: PetscFunctionBegin;
68: if (!lsb->products[Phi_t]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_DIAGONAL, &lsb->products[Phi_t]));
69: *Phi = lsb->products[Phi_t];
70: if (phi != PETSC_DETERMINE) {
71: PetscInt oldest, next, start;
73: PetscCall(MatLMVMGetRange(B, &oldest, &next));
74: start = PetscMax((*Phi)->k, oldest);
75: (*Phi)->k = start;
76: for (PetscInt i = start; i < next; i++) PetscCall(LMProductsInsertNextDiagonalValue(*Phi, i, phi));
77: }
78: PetscFunctionReturn(PETSC_SUCCESS);
79: }
81: static PetscErrorCode SymBroydenRecursiveBasisUpdate(Mat B, MatLMVMMode mode, PetscBool update_phi_from_psi)
82: {
83: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
84: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
85: MatLMVMBasisType S_t = LMVMModeMap(LMBASIS_S, mode);
86: MatLMVMBasisType B0S_t = LMVMModeMap(LMBASIS_B0S, mode);
87: SymBroydenProductsType Phi_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_PHI, mode);
88: SymBroydenProductsType Psi_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_PSI, mode);
89: SymBroydenProductsType StBkS_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_STBKS, mode);
90: SymBroydenProductsType YtHkY_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_YTHKY, mode);
91: SymBroydenProductsType M00_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M00, mode);
92: SymBroydenProductsType M01_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M01, mode);
93: SymBroydenProductsType M11_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M11, mode);
94: SymBroydenProductsType M_t[3] = {M00_t, M01_t, M11_t};
95: LMProducts M[3];
96: LMProducts Phi, Psi = NULL;
97: SymBroydenBasisType BkS_t = LMVMModeMap(SYMBROYDEN_BASIS_BKS, mode);
98: LMBasis BkS;
99: LMProducts StBkS, YtHkY = NULL;
100: PetscInt oldest, start, next;
101: PetscInt products_oldest;
102: LMBasis S;
104: PetscFunctionBegin;
105: PetscCall(MatLMVMGetRange(B, &oldest, &next));
106: for (PetscInt i = 0; i < 3; i++) {
107: if (lsb->products[M_t[i]] && lsb->products[M_t[i]]->block_type != LMBLOCK_DIAGONAL) PetscCall(LMProductsDestroy(&lsb->products[M_t[i]]));
108: if (!lsb->products[M_t[i]]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_DIAGONAL, &lsb->products[M_t[i]]));
109: M[i] = lsb->products[M_t[i]];
110: }
111: if (!lsb->basis[BkS_t]) PetscCall(LMBasisCreate(MatLMVMBasisSizeOf(B0S_t) == LMBASIS_S ? lmvm->Xprev : lmvm->Fprev, lmvm->m, &lsb->basis[BkS_t]));
112: BkS = lsb->basis[BkS_t];
113: PetscCall(MatLMVMSymBroydenGetConvexFactor(B, Phi_t, &Phi));
114: if (!lsb->products[StBkS_t]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_DIAGONAL, &lsb->products[StBkS_t]));
115: StBkS = lsb->products[StBkS_t];
116: PetscCall(LMProductsPrepare(StBkS, lmvm->J0, oldest, next));
117: products_oldest = PetscMax(0, StBkS->k - lmvm->m);
118: if (oldest > products_oldest) {
119: // recursion is starting from a different starting index, it must be recomputed
120: StBkS->k = oldest;
121: }
122: BkS->k = start = StBkS->k;
123: for (PetscInt i = 0; i < 3; i++) M[i]->k = start;
124: if (start == next) PetscFunctionReturn(PETSC_SUCCESS);
126: if (update_phi_from_psi) {
127: Phi->k = start;
128: // we have to first make sure that the inverse data is up to date
129: PetscCall(SymBroydenRecursiveBasisUpdate(B, (MatLMVMMode)(mode ^ 1), PETSC_FALSE));
130: PetscCall(MatLMVMSymBroydenGetConvexFactor(B, Psi_t, &Psi));
131: YtHkY = lsb->products[YtHkY_t];
132: }
133: PetscCall(MatLMVMGetUpdatedBasis(B, S_t, &S, NULL, NULL));
135: for (PetscInt j = start; j < next; j++) {
136: Vec p_j, s_j, B0s_j;
137: PetscScalar alpha, sjtbjsj;
138: PetscScalar m_00, m_01, m_11, yjtsj;
139: PetscScalar phi_j;
141: PetscCall(LMBasisGetWorkVec(BkS, &p_j));
142: // p_j starts as B_0 * s_j
143: PetscCall(MatLMVMBasisGetVecRead(B, B0S_t, j, &B0s_j, &alpha));
144: PetscCall(VecAXPBY(p_j, alpha, 0.0, B0s_j));
145: PetscCall(MatLMVMBasisRestoreVecRead(B, B0S_t, j, &B0s_j, &alpha));
147: // Use the matmult kernel to compute p_j = B_j * p_j
148: PetscCall(LMBasisGetVecRead(S, j, &s_j));
149: if (j > oldest) PetscCall(SymBroydenKernel_Recursive_Inner(B, mode, oldest, j, s_j, p_j));
150: PetscCall(VecDot(p_j, s_j, &sjtbjsj));
151: PetscCall(LMBasisRestoreVecRead(S, j, &s_j));
152: PetscCall(LMProductsInsertNextDiagonalValue(StBkS, j, sjtbjsj));
153: PetscCall(LMBasisSetNextVec(BkS, p_j));
154: PetscCall(LMBasisRestoreWorkVec(BkS, &p_j));
156: PetscCall(MatLMVMProductsGetDiagonalValue(B, LMBASIS_Y, LMBASIS_S, j, &yjtsj));
157: if (update_phi_from_psi) {
158: PetscScalar psi_j;
159: PetscScalar yjthjyj;
161: PetscCall(LMProductsGetDiagonalValue(YtHkY, j, &yjthjyj));
162: PetscCall(LMProductsGetDiagonalValue(Psi, j, &psi_j));
164: phi_j = PhiFromPsi(psi_j, yjtsj, sjtbjsj, yjthjyj);
165: PetscCall(LMProductsInsertNextDiagonalValue(Phi, j, phi_j));
166: } else PetscCall(LMProductsGetDiagonalValue(Phi, j, &phi_j));
168: /* The symmetric Broyden update can be represented as
170: [ | ][ (phi - 1) / s_j^T p_j | -phi / y_j^T s_j ][ p_j^T ]
171: [ p_j | y_j ][-----------------------+-----------------------------------------------][-------]
172: [ | ][ -phi / y_j^T s_j | (y_j^T s_j + phi * s_j^T p_j) / (y_j^T s_j)^2 ][ y_j^T ]
174: We store diagonal vectors with these values
175: */
177: m_00 = (phi_j - 1.0) / sjtbjsj;
178: m_01 = -phi_j / yjtsj;
179: m_11 = (yjtsj + phi_j * sjtbjsj) / (yjtsj * yjtsj);
180: PetscCall(LMProductsInsertNextDiagonalValue(M[0], j, m_00));
181: PetscCall(LMProductsInsertNextDiagonalValue(M[1], j, m_01));
182: PetscCall(LMProductsInsertNextDiagonalValue(M[2], j, m_11));
183: }
184: PetscFunctionReturn(PETSC_SUCCESS);
185: }
187: PETSC_INTERN PetscErrorCode SymBroydenKernel_Recursive(Mat B, MatLMVMMode mode, Vec X, Vec Y, PetscBool update_phi_from_psi)
188: {
189: PetscInt oldest, next;
191: PetscFunctionBegin;
192: PetscCall(MatLMVMApplyJ0Mode(mode)(B, X, Y));
193: PetscCall(MatLMVMGetRange(B, &oldest, &next));
194: if (next > oldest) {
195: PetscCall(SymBroydenRecursiveBasisUpdate(B, mode, update_phi_from_psi));
196: PetscCall(SymBroydenKernel_Recursive_Inner(B, mode, oldest, next, X, Y));
197: }
198: PetscFunctionReturn(PETSC_SUCCESS);
199: }
201: static PetscErrorCode MatMult_LMVMSymBrdn_Recursive(Mat B, Vec X, Vec Y)
202: {
203: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
204: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
206: PetscFunctionBegin;
207: PetscCheck(lsb->phi_scalar != PETSC_DETERMINE, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Phi must first be set using MatLMVMSymBroydenSetPhi()");
208: if (lsb->phi_scalar == 0.0) {
209: PetscCall(BFGSKernel_Recursive(B, MATLMVM_MODE_PRIMAL, X, Y));
210: } else if (lsb->phi_scalar == 1.0) {
211: PetscCall(DFPKernel_Recursive(B, MATLMVM_MODE_PRIMAL, X, Y));
212: } else {
213: PetscCall(SymBroydenKernel_Recursive(B, MATLMVM_MODE_PRIMAL, X, Y, PETSC_FALSE));
214: }
215: PetscFunctionReturn(PETSC_SUCCESS);
216: }
218: static PetscErrorCode MatSolve_LMVMSymBrdn_Recursive(Mat B, Vec X, Vec Y)
219: {
220: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
221: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
223: PetscFunctionBegin;
224: PetscCheck(lsb->phi_scalar != PETSC_DETERMINE, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Phi must first be set using MatLMVMSymBroydenSetPhi()");
225: if (lsb->phi_scalar == 0.0) {
226: PetscCall(DFPKernel_Recursive(B, MATLMVM_MODE_DUAL, X, Y));
227: } else if (lsb->phi_scalar == 1.0) {
228: PetscCall(BFGSKernel_Recursive(B, MATLMVM_MODE_DUAL, X, Y));
229: } else {
230: PetscCall(SymBroydenKernel_Recursive(B, MATLMVM_MODE_DUAL, X, Y, PETSC_TRUE));
231: }
232: PetscFunctionReturn(PETSC_SUCCESS);
233: }
235: PetscBool ErwayMarciaCite = PETSC_FALSE;
236: const char ErwayMarciaCitation[] = "@article{Erway2015,"
237: " title = {On Efficiently Computing the Eigenvalues of Limited-Memory Quasi-Newton Matrices},"
238: " volume = {36},"
239: " ISSN = {1095-7162},"
240: " url = {http://dx.doi.org/10.1137/140997737},"
241: " DOI = {10.1137/140997737},"
242: " number = {3},"
243: " journal = {SIAM Journal on Matrix Analysis and Applications},"
244: " publisher = {Society for Industrial & Applied Mathematics (SIAM)},"
245: " author = {Erway, Jennifer B. and Marcia, Roummel F.},"
246: " year = {2015},"
247: " month = jan,"
248: " pages = {1338-1359}"
249: "}\n";
251: // TODO: on device (e.g. cuBLAS) implementation?
252: static PetscErrorCode SymBroydenCompactDenseUpdateArrays(PetscBLASInt m, PetscBLASInt oldest, PetscBLASInt next, PetscScalar M00[], PetscBLASInt lda00, PetscScalar M01[], PetscBLASInt lda01, PetscScalar M11[], PetscBLASInt lda11, const PetscScalar StB0S[], PetscBLASInt ldasbs, const PetscScalar YtS[], PetscBLASInt ldays, const PetscScalar Phi[], PetscScalar p0[], PetscScalar p1[], const PetscScalar Psi[], const PetscScalar YtHkY[], PetscScalar StBkS[])
253: {
254: PetscBLASInt i;
255: PetscScalar alpha, beta, delta;
256: PetscBLASInt ione = 1;
257: PetscScalar sone = 1.0;
258: PetscScalar szero = 0.0;
259: PetscScalar sBis;
260: PetscScalar yts;
261: PetscScalar phi;
263: PetscFunctionBegin;
264: if (next <= oldest || m <= 0) PetscFunctionReturn(PETSC_SUCCESS);
266: PetscCall(PetscCitationsRegister(ErwayMarciaCitation, &ErwayMarciaCite));
268: PetscCall(PetscArrayzero(M00, m * lda00));
269: PetscCall(PetscArrayzero(M01, m * lda01));
270: PetscCall(PetscArrayzero(M11, m * lda11));
272: i = oldest % m;
274: // Base case entries
275: sBis = StB0S[i + i * ldasbs];
276: if (StBkS) StBkS[i] = sBis;
277: yts = YtS[i + i * ldays];
278: if (Psi) {
279: phi = PhiFromPsi(Psi[i], yts, sBis, YtHkY[i]);
280: } else {
281: phi = Phi[i];
282: }
283: alpha = PetscRealPart(-(1.0 - phi) / sBis);
284: beta = -phi / yts;
285: delta = (1.0 + phi * sBis / yts) / yts;
286: M00[i + i * lda00] = alpha;
287: M01[i + i * lda01] = beta;
288: M11[i + i * lda11] = delta;
289: for (PetscBLASInt i_ = oldest + 1; i_ < next; i_++) {
290: const PetscScalar *q0, *q1;
291: PetscScalar qp;
293: i = i_ % m;
294: q0 = &StB0S[0 + i * ldasbs];
295: q1 = &YtS[0 + i * ldays];
297: // p_i <- M_{i-1} q_i
299: PetscCallBLAS("BLASgemv", BLASgemv_("N", &m, &m, &sone, M00, &lda00, q0, &ione, &szero, p0, &ione));
300: PetscCallBLAS("BLASgemv", BLASgemv_("N", &m, &m, &sone, M01, &lda01, q1, &ione, &sone, p0, &ione));
301: PetscCallBLAS("BLASgemv", BLASgemv_("C", &m, &m, &sone, M01, &lda01, q0, &ione, &szero, p1, &ione));
302: PetscCallBLAS("BLASgemv", BLASgemv_("N", &m, &m, &sone, M11, &lda11, q1, &ione, &sone, p1, &ione));
304: // q'p
305: PetscCallBLAS("BLASdot", qp = BLASdot_(&m, q0, &ione, p0, &ione));
306: PetscCallBLAS("BLASdot", qp += BLASdot_(&m, q1, &ione, p1, &ione));
308: sBis = StB0S[i + i * ldasbs] + qp;
309: if (StBkS) StBkS[i] = sBis;
310: yts = YtS[i + i * ldays];
311: if (Psi) {
312: phi = PhiFromPsi(Psi[i], yts, sBis, YtHkY[i]);
313: } else {
314: phi = Phi[i];
315: }
317: alpha = PetscRealPart(-(1.0 - phi) / sBis);
318: beta = -phi / yts;
319: delta = (1.0 + phi * sBis / yts) / yts;
321: PetscCallBLAS("LAPACKgerc", LAPACKgerc_(&m, &m, &alpha, p0, &ione, p0, &ione, M00, &lda00));
322: for (PetscInt j = 0; j < m; j++) M00[j + i * lda00] = alpha * p0[j];
323: for (PetscInt j = 0; j < m; j++) M00[i + j * lda00] = PetscConj(alpha * p0[j]);
324: M00[i + i * lda00] = alpha;
326: PetscCallBLAS("LAPACKgerc", LAPACKgerc_(&m, &m, &alpha, p0, &ione, p1, &ione, M01, &lda01));
327: for (PetscBLASInt j = 0; j < m; j++) M01[j + i * lda01] = beta * p0[j];
328: for (PetscBLASInt j = 0; j < m; j++) M01[i + j * lda01] = PetscConj(alpha * p1[j]);
329: M01[i + i * lda01] = beta;
331: PetscCallBLAS("LAPACKgerc", LAPACKgerc_(&m, &m, &alpha, p1, &ione, p1, &ione, M11, &lda11));
332: for (PetscInt j = 0; j < m; j++) M11[j + i * lda11] = beta * p1[j];
333: for (PetscInt j = 0; j < m; j++) M11[i + j * lda11] = PetscConj(beta * p1[j]);
334: M11[i + i * lda11] = delta;
335: }
336: PetscFunctionReturn(PETSC_SUCCESS);
337: }
339: static PetscErrorCode SymBroydenCompactProductsUpdate(Mat B, MatLMVMMode mode, PetscBool update_phi_from_psi)
340: {
341: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
342: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
343: PetscInt oldest, next;
344: MatLMVMBasisType S_t = LMVMModeMap(LMBASIS_S, mode);
345: MatLMVMBasisType B0S_t = LMVMModeMap(LMBASIS_B0S, mode);
346: MatLMVMBasisType Y_t = LMVMModeMap(LMBASIS_Y, mode);
347: SymBroydenProductsType Phi_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_PHI, mode);
348: SymBroydenProductsType Psi_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_PSI, mode);
349: SymBroydenProductsType StBkS_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_STBKS, mode);
350: SymBroydenProductsType YtHkY_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_YTHKY, mode);
351: SymBroydenProductsType M00_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M00, mode);
352: SymBroydenProductsType M01_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M01, mode);
353: SymBroydenProductsType M11_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M11, mode);
354: SymBroydenProductsType M_t[3] = {M00_t, M01_t, M11_t};
355: Mat M_local[3];
356: LMProducts M[3], Phi, Psi, YtS, StB0S, StBkS, YtHkY;
357: PetscInt products_oldest, k;
358: PetscBool local_is_nonempty;
360: PetscFunctionBegin;
361: PetscCall(MatLMVMGetRange(B, &oldest, &next));
362: for (PetscInt i = 0; i < 3; i++) {
363: if (lsb->products[M_t[i]] && lsb->products[M_t[i]]->block_type != LMBLOCK_FULL) PetscCall(LMProductsDestroy(&lsb->products[M_t[i]]));
364: if (!lsb->products[M_t[i]]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_FULL, &lsb->products[M_t[i]]));
365: M[i] = lsb->products[M_t[i]];
366: }
367: PetscCall(MatLMVMSymBroydenGetConvexFactor(B, Phi_t, &Phi));
368: PetscCall(MatLMVMSymBroydenGetConvexFactor(B, Psi_t, &Psi));
369: if (!lsb->products[StBkS_t]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_DIAGONAL, &lsb->products[StBkS_t]));
370: StBkS = lsb->products[StBkS_t];
371: if (!lsb->products[YtHkY_t]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_DIAGONAL, &lsb->products[YtHkY_t]));
372: YtHkY = lsb->products[YtHkY_t];
373: PetscCall(LMProductsPrepare(M[0], lmvm->J0, oldest, next));
374: PetscCall(LMProductsGetLocalMatrix(M[0], &M_local[0], &k, &local_is_nonempty));
375: products_oldest = PetscMax(0, k - lmvm->m);
376: if (products_oldest < oldest) k = oldest;
377: if (k < next) {
378: Mat StB0S_local, YtS_local;
379: Vec Psi_local = NULL, Phi_local = NULL, YtHkY_local = NULL, StBkS_local = NULL;
381: PetscCall(MatLMVMGetUpdatedProducts(B, Y_t, S_t, LMBLOCK_UPPER_TRIANGLE, &YtS));
382: PetscCall(MatLMVMGetUpdatedProducts(B, S_t, B0S_t, LMBLOCK_UPPER_TRIANGLE, &StB0S));
383: PetscCall(LMProductsGetLocalMatrix(StB0S, &StB0S_local, NULL, NULL));
384: PetscCall(LMProductsGetLocalMatrix(YtS, &YtS_local, NULL, NULL));
385: PetscCall(LMProductsGetLocalMatrix(M[1], &M_local[1], NULL, NULL));
386: PetscCall(LMProductsGetLocalMatrix(M[2], &M_local[2], NULL, NULL));
387: if (update_phi_from_psi) {
388: PetscCall(LMProductsGetLocalDiagonal(Psi, &Psi_local));
389: PetscCall(LMProductsGetLocalDiagonal(YtHkY, &YtHkY_local));
390: } else {
391: PetscCall(LMProductsGetLocalDiagonal(Phi, &Phi_local));
392: PetscCall(LMProductsGetLocalDiagonal(StBkS, &StBkS_local));
393: }
394: if (local_is_nonempty) {
395: PetscInt lda;
396: PetscBLASInt M_lda[3], StB0S_lda, YtS_lda, m_blas, oldest_blas, next_blas;
397: const PetscScalar *StB0S_;
398: const PetscScalar *YtS_;
399: PetscScalar *M_[3];
400: const PetscScalar *Phi_ = NULL;
401: const PetscScalar *Psi_ = NULL;
402: const PetscScalar *YtHkY_ = NULL;
403: PetscScalar *StBkS_ = NULL;
404: PetscScalar *p0, *p1;
406: for (PetscInt i = 0; i < 3; i++) {
407: PetscCall(MatDenseGetLDA(M_local[i], &lda));
408: PetscCall(PetscBLASIntCast(lda, &M_lda[i]));
409: PetscCall(MatDenseGetArrayWrite(M_local[i], &M_[i]));
410: }
412: PetscCall(MatDenseGetArrayRead(StB0S_local, &StB0S_));
413: PetscCall(MatDenseGetLDA(StB0S_local, &lda));
414: PetscCall(PetscBLASIntCast(lda, &StB0S_lda));
416: PetscCall(MatDenseGetArrayRead(YtS_local, &YtS_));
417: PetscCall(MatDenseGetLDA(YtS_local, &lda));
418: PetscCall(PetscBLASIntCast(lda, &YtS_lda));
420: PetscCall(PetscBLASIntCast(lmvm->m, &m_blas));
421: PetscCall(PetscBLASIntCast(oldest, &oldest_blas));
422: PetscCall(PetscBLASIntCast(next, &next_blas));
424: if (update_phi_from_psi) {
425: PetscCall(VecGetArrayRead(Psi_local, &Psi_));
426: PetscCall(VecGetArrayRead(YtHkY_local, &YtHkY_));
427: } else {
428: PetscCall(VecGetArrayRead(Phi_local, &Phi_));
429: PetscCall(VecGetArrayWrite(StBkS_local, &StBkS_));
430: }
432: PetscCall(PetscMalloc2(lmvm->m, &p0, lmvm->m, &p1));
433: PetscCall(SymBroydenCompactDenseUpdateArrays(m_blas, oldest_blas, next_blas, M_[0], M_lda[0], M_[1], M_lda[1], M_[2], M_lda[2], StB0S_, StB0S_lda, YtS_, YtS_lda, Phi_, p0, p1, Psi_, YtHkY_, StBkS_));
434: PetscCall(PetscFree2(p0, p1));
436: if (update_phi_from_psi) {
437: PetscCall(VecRestoreArrayRead(YtHkY_local, &YtHkY_));
438: PetscCall(VecRestoreArrayRead(Psi_local, &Psi_));
439: } else {
440: PetscCall(VecRestoreArrayWrite(StBkS_local, &StBkS_));
441: PetscCall(VecRestoreArrayRead(Phi_local, &Phi_));
442: }
444: for (PetscInt i = 0; i < 3; i++) PetscCall(MatDenseRestoreArrayWrite(M_local[i], &M_[i]));
445: PetscCall(MatDenseRestoreArrayRead(YtS_local, &YtS_));
446: PetscCall(MatDenseRestoreArrayRead(StB0S_local, &StB0S_));
447: }
448: if (update_phi_from_psi) {
449: PetscCall(LMProductsRestoreLocalDiagonal(YtHkY, &YtHkY_local));
450: PetscCall(LMProductsRestoreLocalDiagonal(Psi, &Psi_local));
451: } else {
452: PetscCall(LMProductsRestoreLocalDiagonal(StBkS, &StBkS_local));
453: PetscCall(LMProductsRestoreLocalDiagonal(Phi, &Phi_local));
454: }
455: PetscCall(LMProductsRestoreLocalMatrix(M[2], &M_local[2], &next));
456: PetscCall(LMProductsRestoreLocalMatrix(M[1], &M_local[1], &next));
457: PetscCall(LMProductsRestoreLocalMatrix(YtS, &YtS_local, NULL));
458: PetscCall(LMProductsRestoreLocalMatrix(StB0S, &StB0S_local, NULL));
459: }
460: PetscCall(LMProductsRestoreLocalMatrix(M[0], &M_local[0], &next));
461: PetscFunctionReturn(PETSC_SUCCESS);
462: }
464: PETSC_INTERN PetscErrorCode SymBroydenCompactDenseKernelUseB0S(Mat B, MatLMVMMode mode, Vec X, PetscBool *use_B0S)
465: {
466: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
467: PetscBool is_scalar;
468: PetscScalar J0_scale;
469: LMBasis B0S;
470: Mat J0 = lmvm->J0;
471: PetscObjectId id;
472: PetscObjectState state;
474: /*
475: The one time where we would want to compute S^T B_0 X as (B_0 S)^T X instead of S^T (B_0 X)
476: is if (B_0 S)^T X is cached.
477: */
478: PetscFunctionBegin;
479: *use_B0S = PETSC_FALSE;
480: PetscCall(MatLMVMGetJ0Scalar(B, &is_scalar, &J0_scale));
481: B0S = lmvm->basis[is_scalar ? LMVMModeMap(LMBASIS_S, mode) : LMVMModeMap(LMBASIS_B0S, mode)];
482: if ((B0S->k < lmvm->k) || (B0S->cached_product == NULL)) PetscFunctionReturn(PETSC_SUCCESS);
483: if (!is_scalar) {
484: PetscCall(PetscObjectGetId((PetscObject)J0, &id));
485: PetscCall(PetscObjectStateGet((PetscObject)J0, &state));
486: if (id != B0S->operator_id || state != B0S->operator_state) PetscFunctionReturn(PETSC_SUCCESS);
487: }
488: PetscCall(PetscObjectGetId((PetscObject)X, &id));
489: PetscCall(PetscObjectStateGet((PetscObject)X, &state));
490: if (id == B0S->cached_vec_id && state == B0S->cached_vec_state) *use_B0S = PETSC_TRUE;
491: PetscFunctionReturn(PETSC_SUCCESS);
492: }
494: PETSC_INTERN PetscErrorCode SymBroydenKernel_CompactDense(Mat B, MatLMVMMode mode, Vec X, Vec BX, PetscBool update_phi_from_psi)
495: {
496: PetscInt oldest, next;
498: PetscFunctionBegin;
499: PetscCall(MatLMVMApplyJ0Mode(mode)(B, X, BX));
500: PetscCall(MatLMVMGetRange(B, &oldest, &next));
501: if (next > oldest) {
502: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
503: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
504: Vec StB0X, YtX, u, v;
505: MatLMVMBasisType S_t = LMVMModeMap(LMBASIS_S, mode);
506: MatLMVMBasisType Y_t = LMVMModeMap(LMBASIS_Y, mode);
507: MatLMVMBasisType B0S_t = LMVMModeMap(LMBASIS_B0S, mode);
508: SymBroydenProductsType M00_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M00, mode);
509: SymBroydenProductsType M01_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M01, mode);
510: SymBroydenProductsType M11_t = LMVMModeMap(SYMBROYDEN_PRODUCTS_M11, mode);
511: LMProducts M00, M01, M11;
512: LMBasis S, Y;
513: PetscBool use_B0S;
515: if (update_phi_from_psi) PetscCall(SymBroydenCompactProductsUpdate(B, (MatLMVMMode)(mode ^ 1), PETSC_FALSE));
516: PetscCall(SymBroydenCompactProductsUpdate(B, mode, update_phi_from_psi));
517: M00 = lsb->products[M00_t];
518: M01 = lsb->products[M01_t];
519: M11 = lsb->products[M11_t];
521: PetscCall(MatLMVMGetUpdatedBasis(B, S_t, &S, NULL, NULL));
522: PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
524: PetscCall(MatLMVMGetWorkRow(B, &StB0X));
525: PetscCall(MatLMVMGetWorkRow(B, &YtX));
526: PetscCall(MatLMVMGetWorkRow(B, &u));
527: PetscCall(MatLMVMGetWorkRow(B, &v));
529: PetscCall(SymBroydenCompactDenseKernelUseB0S(B, mode, X, &use_B0S));
530: if (use_B0S) PetscCall(MatLMVMBasisGEMVH(B, B0S_t, oldest, next, 1.0, X, 0.0, StB0X));
531: else PetscCall(LMBasisGEMVH(S, oldest, next, 1.0, BX, 0.0, StB0X));
533: PetscCall(LMBasisGEMVH(Y, oldest, next, 1.0, X, 0.0, YtX));
535: PetscCall(LMProductsMult(M00, oldest, next, 1.0, StB0X, 0.0, u, PETSC_FALSE));
536: PetscCall(LMProductsMult(M01, oldest, next, 1.0, YtX, 1.0, u, PETSC_FALSE));
537: PetscCall(LMProductsMult(M01, oldest, next, 1.0, StB0X, 0.0, v, PETSC_TRUE));
538: PetscCall(LMProductsMult(M11, oldest, next, 1.0, YtX, 1.0, v, PETSC_FALSE));
540: PetscCall(LMBasisGEMV(Y, oldest, next, 1.0, v, 1.0, BX));
541: PetscCall(MatLMVMBasisGEMV(B, B0S_t, oldest, next, 1.0, u, 1.0, BX));
543: PetscCall(MatLMVMRestoreWorkRow(B, &v));
544: PetscCall(MatLMVMRestoreWorkRow(B, &u));
545: PetscCall(MatLMVMRestoreWorkRow(B, &YtX));
546: PetscCall(MatLMVMRestoreWorkRow(B, &StB0X));
547: }
548: PetscFunctionReturn(PETSC_SUCCESS);
549: }
551: static PetscErrorCode MatMult_LMVMSymBrdn_CompactDense(Mat B, Vec X, Vec BX)
552: {
553: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
554: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
556: PetscFunctionBegin;
557: PetscCheck(lsb->phi_scalar != PETSC_DETERMINE, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Phi must first be set using MatLMVMSymBroydenSetPhi()");
558: if (lsb->phi_scalar == 0.0) {
559: PetscCall(BFGSKernel_CompactDense(B, MATLMVM_MODE_PRIMAL, X, BX));
560: } else if (lsb->phi_scalar == 1.0) {
561: PetscCall(DFPKernel_CompactDense(B, MATLMVM_MODE_PRIMAL, X, BX));
562: } else {
563: PetscCall(SymBroydenKernel_CompactDense(B, MATLMVM_MODE_PRIMAL, X, BX, PETSC_FALSE));
564: }
565: PetscFunctionReturn(PETSC_SUCCESS);
566: }
568: static PetscErrorCode MatSolve_LMVMSymBrdn_CompactDense(Mat B, Vec X, Vec HX)
569: {
570: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
571: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
573: PetscFunctionBegin;
574: PetscCheck(lsb->phi_scalar != PETSC_DETERMINE, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Phi must first be set using MatLMVMSymBroydenSetPhi()");
575: if (lsb->phi_scalar == 0.0) {
576: PetscCall(DFPKernel_CompactDense(B, MATLMVM_MODE_DUAL, X, HX));
577: } else if (lsb->phi_scalar == 1.0) {
578: PetscCall(BFGSKernel_CompactDense(B, MATLMVM_MODE_DUAL, X, HX));
579: } else {
580: PetscCall(SymBroydenKernel_CompactDense(B, MATLMVM_MODE_DUAL, X, HX, PETSC_TRUE));
581: }
582: PetscFunctionReturn(PETSC_SUCCESS);
583: }
585: static PetscErrorCode MatUpdate_LMVMSymBrdn(Mat B, Vec X, Vec F)
586: {
587: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
588: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
589: PetscReal curvtol;
590: PetscScalar curvature, ststmp;
591: PetscInt oldest, next;
592: PetscBool cache_StFprev = (lmvm->mult_alg != MAT_LMVM_MULT_RECURSIVE) ? lmvm->cache_gradient_products : PETSC_FALSE;
593: PetscBool cache_YtH0Fprev = cache_StFprev;
594: LMBasis S = NULL, H0Y = NULL;
595: PetscScalar H0_alpha = 1.0;
596: MatLMVMBasisType H0Y_t = LMBASIS_H0Y;
598: PetscFunctionBegin;
599: if (!lmvm->m) PetscFunctionReturn(PETSC_SUCCESS);
600: // BFGS using the dense algorithm does not need to cache YtH0F products
601: if (lsb->phi_scalar == 0.0 && lmvm->mult_alg == MAT_LMVM_MULT_DENSE) cache_YtH0Fprev = PETSC_FALSE;
602: // Caching is no use if we are diagonally updating
603: if (lsb->rescale->scale_type == MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL) cache_YtH0Fprev = PETSC_FALSE;
604: PetscCall(MatLMVMGetRange(B, &oldest, &next));
605: if (lmvm->prev_set) {
606: LMBasis Y = NULL;
607: Vec Fprev_old = NULL;
609: if (cache_StFprev || cache_YtH0Fprev) {
610: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_Y, &Y, NULL, NULL));
611: PetscCall(LMBasisGetWorkVec(Y, &Fprev_old));
612: PetscCall(VecCopy(lmvm->Fprev, Fprev_old));
613: }
615: /* Compute the new (S = X - Xprev) and (Y = F - Fprev) vectors */
616: PetscCall(VecAYPX(lmvm->Xprev, -1.0, X));
617: PetscCall(VecAYPX(lmvm->Fprev, -1.0, F));
619: /* Test if the updates can be accepted */
620: {
621: Vec sy[2] = {lmvm->Xprev, lmvm->Fprev};
622: PetscScalar stsy[2];
624: PetscCall(VecMDot(lmvm->Xprev, 2, sy, stsy));
625: ststmp = stsy[0];
626: curvature = stsy[1];
627: }
628: curvtol = lmvm->eps * PetscRealPart(ststmp);
630: if (PetscRealPart(curvature) > curvtol) { /* Update is good, accept it */
631: LMProducts StY = NULL;
632: LMProducts YtH0Y = NULL;
633: Vec StFprev_old = NULL;
634: Vec YtH0Fprev_old = NULL;
635: PetscInt oldest_new, next_new;
637: lsb->watchdog = 0;
638: if (cache_StFprev) {
639: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_S, &S, NULL, NULL));
640: if (!lsb->StFprev) PetscCall(LMBasisCreateRow(S, &lsb->StFprev));
641: PetscCall(MatLMVMGetUpdatedProducts(B, LMBASIS_S, LMBASIS_Y, LMBLOCK_UPPER_TRIANGLE, &StY));
642: PetscCall(LMProductsGetNextColumn(StY, &StFprev_old));
643: PetscCall(VecCopy(lsb->StFprev, StFprev_old));
644: }
645: if (cache_YtH0Fprev) {
646: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_H0Y, &H0Y, &H0Y_t, &H0_alpha));
647: if (!lsb->YtH0Fprev) PetscCall(LMBasisCreateRow(H0Y, &lsb->YtH0Fprev));
648: PetscCall(MatLMVMGetUpdatedProducts(B, LMBASIS_Y, H0Y_t, LMBLOCK_UPPER_TRIANGLE, &YtH0Y));
649: PetscCall(LMProductsGetNextColumn(YtH0Y, &YtH0Fprev_old));
650: if (lsb->YtH0Fprev == H0Y->cached_product) {
651: PetscCall(VecCopy(lsb->YtH0Fprev, YtH0Fprev_old));
652: } else {
653: if (next > oldest) {
654: // need to recalculate
655: PetscCall(LMBasisGEMVH(H0Y, oldest, next, 1.0, Fprev_old, 0.0, YtH0Fprev_old));
656: } else {
657: PetscCall(VecZeroEntries(YtH0Fprev_old));
658: }
659: }
660: }
662: PetscCall(MatUpdateKernel_LMVM(B, lmvm->Xprev, lmvm->Fprev));
663: PetscCall(MatLMVMGetRange(B, &oldest_new, &next_new));
664: if (cache_StFprev) {
665: // compute the one new s_i^T F_old value
666: PetscCall(LMBasisGEMVH(S, next, next_new, 1.0, Fprev_old, 0.0, StFprev_old));
667: PetscCall(LMBasisGEMVH(S, oldest_new, next_new, 1.0, F, 0.0, lsb->StFprev));
668: PetscCall(LMBasisSetCachedProduct(S, F, lsb->StFprev));
669: PetscCall(VecAXPBY(StFprev_old, 1.0, -1.0, lsb->StFprev));
670: PetscCall(LMProductsRestoreNextColumn(StY, &StFprev_old));
671: }
672: if (cache_YtH0Fprev) {
673: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_H0Y, &H0Y, &H0Y_t, &H0_alpha));
674: // compute the one new (H_0 y_i)^T F_old value
675: PetscCall(LMBasisGEMVH(H0Y, next, next_new, 1.0, Fprev_old, 0.0, YtH0Fprev_old));
676: PetscCall(LMBasisGEMVH(H0Y, oldest_new, next_new, 1.0, F, 0.0, lsb->YtH0Fprev));
677: PetscCall(LMBasisSetCachedProduct(H0Y, F, lsb->YtH0Fprev));
678: PetscCall(VecAXPBY(YtH0Fprev_old, 1.0, -1.0, lsb->YtH0Fprev));
679: PetscCall(LMProductsRestoreNextColumn(YtH0Y, &YtH0Fprev_old));
680: }
682: PetscCall(MatLMVMProductsInsertDiagonalValue(B, LMBASIS_Y, LMBASIS_S, next, PetscRealPart(curvature)));
683: PetscCall(MatLMVMProductsInsertDiagonalValue(B, LMBASIS_S, LMBASIS_Y, next, PetscRealPart(curvature)));
684: PetscCall(MatLMVMProductsInsertDiagonalValue(B, LMBASIS_S, LMBASIS_S, next, ststmp));
685: PetscCall(SymBroydenRescaleUpdate(B, lsb->rescale));
686: } else {
687: /* Update is bad, skip it */
688: PetscCall(PetscInfo(B, "Rejecting update: curvature %g, ||s||^2 %g\n", (double)PetscRealPart(curvature), (double)PetscRealPart(ststmp)));
689: lmvm->nrejects++;
690: lsb->watchdog++;
691: if (cache_StFprev) {
692: // we still need to update the cached product
693: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_S, &S, NULL, NULL));
694: PetscCall(LMBasisGEMVH(S, oldest, next, 1.0, F, 0.0, lsb->StFprev));
695: PetscCall(LMBasisSetCachedProduct(S, F, lsb->StFprev));
696: }
697: if (cache_YtH0Fprev) {
698: // we still need to update the cached product
699: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_H0Y, &H0Y, &H0Y_t, &H0_alpha));
700: PetscCall(LMBasisGEMVH(H0Y, oldest, next, 1.0, F, 0.0, lsb->YtH0Fprev));
701: PetscCall(LMBasisSetCachedProduct(H0Y, F, lsb->StFprev));
702: }
703: }
704: if (cache_StFprev || cache_YtH0Fprev) PetscCall(LMBasisRestoreWorkVec(Y, &Fprev_old));
705: } else {
706: if (cache_StFprev) {
707: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_S, &S, NULL, NULL));
708: if (!lsb->StFprev) PetscCall(LMBasisCreateRow(S, &lsb->StFprev));
709: }
710: if (cache_YtH0Fprev) {
711: MatLMVMBasisType H0Y_t;
712: PetscScalar H0_alpha;
714: PetscCall(MatLMVMGetUpdatedBasis(B, LMBASIS_H0Y, &H0Y, &H0Y_t, &H0_alpha));
715: if (!lsb->YtH0Fprev) PetscCall(LMBasisCreateRow(H0Y, &lsb->YtH0Fprev));
716: }
717: }
719: if (lsb->watchdog > lsb->max_seq_rejects) PetscCall(MatLMVMReset(B, PETSC_FALSE));
721: /* Save the solution and function to be used in the next update */
722: PetscCall(VecCopy(X, lmvm->Xprev));
723: PetscCall(VecCopy(F, lmvm->Fprev));
724: lmvm->prev_set = PETSC_TRUE;
725: PetscFunctionReturn(PETSC_SUCCESS);
726: }
728: static PetscErrorCode MatCopy_LMVMSymBrdn(Mat B, Mat M, MatStructure str)
729: {
730: Mat_LMVM *bdata = (Mat_LMVM *)B->data;
731: Mat_SymBrdn *blsb = (Mat_SymBrdn *)bdata->ctx;
732: Mat_LMVM *mdata = (Mat_LMVM *)M->data;
733: Mat_SymBrdn *mlsb = (Mat_SymBrdn *)mdata->ctx;
735: PetscFunctionBegin;
736: mlsb->phi_scalar = blsb->phi_scalar;
737: mlsb->psi_scalar = blsb->psi_scalar;
738: mlsb->watchdog = blsb->watchdog;
739: mlsb->max_seq_rejects = blsb->max_seq_rejects;
740: PetscCall(SymBroydenRescaleCopy(blsb->rescale, mlsb->rescale));
741: PetscFunctionReturn(PETSC_SUCCESS);
742: }
744: static PetscErrorCode MatReset_LMVMSymBrdn_Internal(Mat B, MatLMVMResetMode mode)
745: {
746: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
747: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
749: PetscFunctionBegin;
750: if (MatLMVMResetClearsBases(mode)) {
751: for (PetscInt i = 0; i < SYMBROYDEN_BASIS_COUNT; i++) PetscCall(LMBasisDestroy(&lsb->basis[i]));
752: for (PetscInt i = 0; i < SYMBROYDEN_PRODUCTS_COUNT; i++) PetscCall(LMProductsDestroy(&lsb->products[i]));
753: PetscCall(VecDestroy(&lsb->StFprev));
754: PetscCall(VecDestroy(&lsb->YtH0Fprev));
755: } else {
756: for (PetscInt i = 0; i < SYMBROYDEN_BASIS_COUNT; i++) PetscCall(LMBasisReset(lsb->basis[i]));
757: for (PetscInt i = 0; i < SYMBROYDEN_PRODUCTS_COUNT; i++) PetscCall(LMProductsReset(lsb->products[i]));
758: if (lsb->StFprev) PetscCall(VecZeroEntries(lsb->StFprev));
759: if (lsb->YtH0Fprev) PetscCall(VecZeroEntries(lsb->YtH0Fprev));
760: }
761: PetscFunctionReturn(PETSC_SUCCESS);
762: }
764: static PetscErrorCode MatReset_LMVMSymBrdn(Mat B, MatLMVMResetMode mode)
765: {
766: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
767: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
769: PetscFunctionBegin;
770: lsb->watchdog = 0;
771: PetscCall(SymBroydenRescaleReset(B, lsb->rescale, mode));
772: PetscCall(MatReset_LMVMSymBrdn_Internal(B, mode));
773: PetscFunctionReturn(PETSC_SUCCESS);
774: }
776: static PetscErrorCode MatDestroy_LMVMSymBrdn(Mat B)
777: {
778: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
779: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
781: PetscFunctionBegin;
782: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBroydenGetPhi_C", NULL));
783: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBroydenSetPhi_C", NULL));
784: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBadBroydenGetPsi_C", NULL));
785: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBadBroydenSetPsi_C", NULL));
786: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBroydenSetDelta_C", NULL));
787: PetscCall(SymBroydenRescaleDestroy(&lsb->rescale));
788: PetscCall(MatReset_LMVMSymBrdn_Internal(B, MAT_LMVM_RESET_ALL));
789: PetscCall(PetscFree(lmvm->ctx));
790: PetscCall(MatDestroy_LMVM(B));
791: PetscFunctionReturn(PETSC_SUCCESS);
792: }
794: static PetscErrorCode MatSetUp_LMVMSymBrdn(Mat B)
795: {
796: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
797: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
799: PetscFunctionBegin;
800: PetscCall(MatSetUp_LMVM(B));
801: PetscCall(SymBroydenRescaleInitializeJ0(B, lsb->rescale));
802: PetscFunctionReturn(PETSC_SUCCESS);
803: }
805: static PetscErrorCode MatView_LMVMSymBrdn(Mat B, PetscViewer pv)
806: {
807: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
808: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
809: PetscBool isascii;
811: PetscFunctionBegin;
812: PetscCall(MatView_LMVM(B, pv));
813: PetscCall(PetscObjectTypeCompare((PetscObject)pv, PETSCVIEWERASCII, &isascii));
814: if (isascii) {
815: PetscBool is_other;
817: PetscCall(PetscObjectTypeCompareAny((PetscObject)B, &is_other, MATLMVMBFGS, MATLMVMDFP, ""));
818: if (!is_other) {
819: if (lsb->phi_scalar != PETSC_DETERMINE) PetscCall(PetscViewerASCIIPrintf(pv, "Convex factor phi = %g\n", (double)lsb->phi_scalar));
820: if (lsb->psi_scalar != PETSC_DETERMINE) PetscCall(PetscViewerASCIIPrintf(pv, "Dual convex factor psi = %g\n", (double)lsb->psi_scalar));
821: }
822: }
823: PetscCall(SymBroydenRescaleView(lsb->rescale, pv));
824: PetscFunctionReturn(PETSC_SUCCESS);
825: }
827: static PetscErrorCode MatLMVMSetMultAlgorithm_SymBrdn(Mat B)
828: {
829: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
831: PetscFunctionBegin;
832: switch (lmvm->mult_alg) {
833: case MAT_LMVM_MULT_RECURSIVE:
834: lmvm->ops->mult = MatMult_LMVMSymBrdn_Recursive;
835: lmvm->ops->solve = MatSolve_LMVMSymBrdn_Recursive;
836: break;
837: case MAT_LMVM_MULT_DENSE:
838: case MAT_LMVM_MULT_COMPACT_DENSE:
839: lmvm->ops->mult = MatMult_LMVMSymBrdn_CompactDense;
840: lmvm->ops->solve = MatSolve_LMVMSymBrdn_CompactDense;
841: break;
842: }
843: lmvm->ops->multht = lmvm->ops->mult;
844: lmvm->ops->solveht = lmvm->ops->solve;
845: PetscFunctionReturn(PETSC_SUCCESS);
846: }
848: static PetscErrorCode MatSetFromOptions_LMVMSymBrdn(Mat B, PetscOptionItems PetscOptionsObject)
849: {
850: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
851: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
853: PetscFunctionBegin;
854: PetscCall(MatSetFromOptions_LMVM(B, PetscOptionsObject));
855: PetscOptionsHeadBegin(PetscOptionsObject, "Restricted/Symmetric Broyden method for approximating SPD Jacobian actions (MATLMVMSYMBRDN)");
856: PetscCall(PetscOptionsReal("-mat_lmvm_phi", "convex ratio between BFGS and DFP components of the update", "", lsb->phi_scalar, &lsb->phi_scalar, NULL));
857: PetscCheck(lsb->phi_scalar >= 0.0 && lsb->phi_scalar <= 1.0, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio for the update formula cannot be outside the range of [0, 1]");
858: PetscCall(SymBroydenRescaleSetFromOptions(B, lsb->rescale, PetscOptionsObject));
859: PetscOptionsHeadEnd();
860: PetscFunctionReturn(PETSC_SUCCESS);
861: }
863: /*@
864: MatLMVMSymBroydenGetPhi - Get the phi parameter for a Broyden class quasi-Newton update matrix
866: Input Parameter:
867: . B - The matrix
869: Output Parameter:
870: . phi - a number defining an update that is an affine combination of the BFGS update (phi = 0) and DFP update (phi = 1)
872: Level: advanced
874: Note:
875: If `B` does not have a constant value of `phi` for all iterations this will
876: return `phi` = `PETSC_DETERMINE` = -1, a negative value that `phi` cannot
877: attain for a valid general Broyden update.
878: This is the case if `B` is a `MATLMVMSYMBADBROYDEN`, where `phi`'s dual value
879: `psi` is constant and `phi` changes from iteration to iteration.
881: .seealso: [](ch_ksp),
882: `MATLMVMSYMBROYDEN`, `MATLMVMSYMBADBROYDEN`,
883: `MATLMVMDFP`, `MATLMVMBFGS`,
884: `MatLMVMSymBroydenSetPhi()`,
885: `MatLMVMSymBadBroydenGetPsi()`, `MatLMVMSymBadBroydenSetPsi()`
886: @*/
887: PetscErrorCode MatLMVMSymBroydenGetPhi(Mat B, PetscReal *phi)
888: {
889: PetscFunctionBegin;
890: *phi = PETSC_DETERMINE;
891: PetscUseMethod(B, "MatLMVMSymBroydenGetPhi_C", (Mat, PetscReal *), (B, phi));
892: PetscFunctionReturn(PETSC_SUCCESS);
893: }
895: static PetscErrorCode MatLMVMSymBroydenGetPhi_SymBrdn(Mat B, PetscReal *phi)
896: {
897: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
898: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
900: PetscFunctionBegin;
901: *phi = lsb->phi_scalar;
902: PetscFunctionReturn(PETSC_SUCCESS);
903: }
905: /*@
906: MatLMVMSymBroydenSetPhi - Get the phi parameter for a Broyden class quasi-Newton update matrix
908: Input Parameters:
909: + B - The matrix
910: - phi - a number defining an update that is a convex combination of the BFGS update (phi = 0) and DFP update (phi = 1)
912: Level: advanced
914: Note:
915: If `B` cannot have a constant value of `phi` for all iterations this will be ignored.
916: This is the case if `B` is a `MATLMVMSYMBADBROYDEN`, where `phi`'s dual value
917: `psi` is constant and `phi` changes from iteration to iteration.
919: .seealso: [](ch_ksp),
920: `MATLMVMSYMBROYDEN`, `MATLMVMSYMBADBROYDEN`,
921: `MATLMVMDFP`, `MATLMVMBFGS`,
922: `MatLMVMSymBroydenGetPhi()`,
923: `MatLMVMSymBadBroydenGetPsi()`, `MatLMVMSymBadBroydenSetPsi()`
924: @*/
925: PetscErrorCode MatLMVMSymBroydenSetPhi(Mat B, PetscReal phi)
926: {
927: PetscFunctionBegin;
928: PetscTryMethod(B, "MatLMVMSymBroydenSetPhi_C", (Mat, PetscReal), (B, phi));
929: PetscFunctionReturn(PETSC_SUCCESS);
930: }
932: static PetscErrorCode MatLMVMSymBroydenSetPhi_SymBrdn(Mat B, PetscReal phi)
933: {
934: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
935: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
937: PetscFunctionBegin;
938: lsb->phi_scalar = phi;
939: PetscFunctionReturn(PETSC_SUCCESS);
940: }
942: /*@
943: MatLMVMSymBadBroydenGetPsi - Get the psi parameter for a Broyden class quasi-Newton update matrix
945: Input Parameter:
946: . B - The matrix
948: Output Parameter:
949: . psi - a number defining an update that is an affine combination of the BFGS update (psi = 1) and DFP update (psi = 0)
951: Level: advanced
953: Note:
954: If B does not have a constant value of `psi` for all iterations this will
955: return `psi` = `PETSC_DETERMINE` = -1, a negative value that `psi` cannot
956: attain for a valid general Broyden update.
957: This is the case if `B` is a `MATLMVMSYMBROYDEN`, where `psi`'s dual value
958: `phi` is constant and `psi` changes from iteration to iteration.
960: .seealso: [](ch_ksp),
961: `MATLMVMSYMBROYDEN`, `MATLMVMSYMBADBROYDEN`,
962: `MATLMVMDFP`, `MATLMVMBFGS`,
963: `MatLMVMSymBadBroydenSetPsi()`,
964: `MatLMVMSymBroydenGetPhi()`, `MatLMVMSymBroydenSetPhi()`
965: @*/
966: PetscErrorCode MatLMVMSymBadBroydenGetPsi(Mat B, PetscReal *psi)
967: {
968: PetscFunctionBegin;
969: *psi = PETSC_DETERMINE;
970: PetscTryMethod(B, "MatLMVMSymBadBroydenGetPsi_C", (Mat, PetscReal *), (B, psi));
971: PetscFunctionReturn(PETSC_SUCCESS);
972: }
974: static PetscErrorCode MatLMVMSymBadBroydenGetPsi_SymBrdn(Mat B, PetscReal *psi)
975: {
976: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
977: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
979: PetscFunctionBegin;
980: *psi = lsb->psi_scalar;
981: PetscFunctionReturn(PETSC_SUCCESS);
982: }
984: /*@
985: MatLMVMSymBadBroydenSetPsi - Get the psi parameter for a Broyden class quasi-Newton update matrix
987: Input Parameters:
988: + B - The matrix
989: - psi - a number defining an update that is a convex combination of the BFGS update (psi = 1) and DFP update (psi = 0)
991: Level: developer
993: Note:
994: If `B` cannot have a constant value of `psi` for all iterations this will
995: be ignored.
996: This is the case if `B` is a `MATLMVMSYMBROYDEN`, where `psi`'s dual value
997: `phi` is constant and `psi` changes from iteration to iteration.
999: .seealso: [](ch_ksp),
1000: `MATLMVMSYMBROYDEN`, `MATLMVMSYMBADBROYDEN`,
1001: `MATLMVMDFP`, `MATLMVMBFGS`,
1002: `MatLMVMSymBadBroydenGetPsi()`,
1003: `MatLMVMSymBroydenGetPhi()`, `MatLMVMSymBroydenSetPhi()`
1004: @*/
1005: PetscErrorCode MatLMVMSymBadBroydenSetPsi(Mat B, PetscReal psi)
1006: {
1007: PetscFunctionBegin;
1008: PetscTryMethod(B, "MatLMVMSymBadBroydenSetPsi_C", (Mat, PetscReal), (B, psi));
1009: PetscFunctionReturn(PETSC_SUCCESS);
1010: }
1012: static PetscErrorCode MatLMVMSymBadBroydenSetPsi_SymBrdn(Mat B, PetscReal psi)
1013: {
1014: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
1015: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
1017: PetscFunctionBegin;
1018: lsb->psi_scalar = psi;
1019: PetscFunctionReturn(PETSC_SUCCESS);
1020: }
1022: static PetscErrorCode MatLMVMSymBroydenSetDelta_SymBrdn(Mat B, PetscScalar delta)
1023: {
1024: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
1025: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
1027: PetscFunctionBegin;
1028: PetscCall(SymBroydenRescaleSetDelta(B, lsb->rescale, PetscAbsReal(PetscRealPart(delta))));
1029: PetscFunctionReturn(PETSC_SUCCESS);
1030: }
1032: PetscErrorCode MatCreate_LMVMSymBrdn(Mat B)
1033: {
1034: Mat_LMVM *lmvm;
1035: Mat_SymBrdn *lsb;
1037: PetscFunctionBegin;
1038: PetscCall(MatCreate_LMVM(B));
1039: PetscCall(PetscObjectChangeTypeName((PetscObject)B, MATLMVMSYMBROYDEN));
1040: PetscCall(MatSetOption(B, MAT_HERMITIAN, PETSC_TRUE));
1041: PetscCall(MatSetOption(B, MAT_SPD, PETSC_TRUE)); // TODO: change to HPD when available
1042: PetscCall(MatSetOption(B, MAT_SPD_ETERNAL, PETSC_TRUE));
1043: B->ops->view = MatView_LMVMSymBrdn;
1044: B->ops->setfromoptions = MatSetFromOptions_LMVMSymBrdn;
1045: B->ops->setup = MatSetUp_LMVMSymBrdn;
1046: B->ops->destroy = MatDestroy_LMVMSymBrdn;
1048: lmvm = (Mat_LMVM *)B->data;
1049: lmvm->ops->reset = MatReset_LMVMSymBrdn;
1050: lmvm->ops->update = MatUpdate_LMVMSymBrdn;
1051: lmvm->ops->copy = MatCopy_LMVMSymBrdn;
1052: lmvm->ops->setmultalgorithm = MatLMVMSetMultAlgorithm_SymBrdn;
1053: lmvm->cache_gradient_products = PETSC_TRUE;
1054: PetscCall(MatLMVMSetMultAlgorithm_SymBrdn(B));
1056: PetscCall(PetscNew(&lsb));
1057: lmvm->ctx = (void *)lsb;
1058: lsb->phi_scalar = 0.125;
1059: lsb->psi_scalar = PETSC_DETERMINE;
1060: lsb->watchdog = 0;
1061: lsb->max_seq_rejects = lmvm->m / 2;
1063: PetscCall(SymBroydenRescaleCreate(&lsb->rescale));
1064: lsb->rescale->theta = lsb->phi_scalar;
1065: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBroydenGetPhi_C", MatLMVMSymBroydenGetPhi_SymBrdn));
1066: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBroydenSetPhi_C", MatLMVMSymBroydenSetPhi_SymBrdn));
1067: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBadBroydenGetPsi_C", MatLMVMSymBadBroydenGetPsi_SymBrdn));
1068: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBadBroydenSetPsi_C", MatLMVMSymBadBroydenSetPsi_SymBrdn));
1069: PetscCall(PetscObjectComposeFunction((PetscObject)B, "MatLMVMSymBroydenSetDelta_C", MatLMVMSymBroydenSetDelta_SymBrdn));
1070: PetscFunctionReturn(PETSC_SUCCESS);
1071: }
1073: /*@
1074: MatLMVMSymBroydenSetDelta - Sets the starting value for the diagonal scaling vector computed
1075: in the SymBrdn approximations (also works for BFGS and DFP).
1077: Input Parameters:
1078: + B - `MATLMVM` matrix
1079: - delta - initial value for diagonal scaling
1081: Level: intermediate
1083: .seealso: [](ch_ksp), `MATLMVMSYMBROYDEN`
1084: @*/
1085: PetscErrorCode MatLMVMSymBroydenSetDelta(Mat B, PetscScalar delta)
1086: {
1087: PetscFunctionBegin;
1089: PetscTryMethod(B, "MatLMVMSymBroydenSetDelta_C", (Mat, PetscScalar), (B, delta));
1090: PetscFunctionReturn(PETSC_SUCCESS);
1091: }
1093: /*@
1094: MatLMVMSymBroydenSetScaleType - Sets the scale type for symmetric Broyden-type updates.
1096: Input Parameters:
1097: + B - the `MATLMVM` matrix
1098: - stype - scale type, see `MatLMVMSymBroydenScaleType`
1100: Options Database Key:
1101: . -mat_lmvm_scale_type <none,scalar,diagonal> - set the scaling type
1103: Level: intermediate
1105: MatLMVMSymBrdnScaleTypes\:
1106: + `MAT_LMVM_SYMBROYDEN_SCALE_NONE` - use whatever initial Hessian is already there (will be the identity if the user does nothing)
1107: . `MAT_LMVM_SYMBROYDEN_SCALE_SCALAR` - use the Shanno scalar as the initial Hessian
1108: . `MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL` - use a diagonalized BFGS update as the initial Hessian
1109: . `MAT_LMVM_SYMBROYDEN_SCALE_USER` - same as `MAT_LMVM_SYMBROYDEN_NONE`
1110: - `MAT_LMVM_SYMBROYDEN_SCALE_DECIDE` - let PETSc decide
1112: .seealso: [](ch_ksp), `MATLMVMSYMBROYDEN`, `MatCreateLMVMSymBroyden()`, `MatLMVMSymBroydenScaleType`
1113: @*/
1114: PetscErrorCode MatLMVMSymBroydenSetScaleType(Mat B, MatLMVMSymBroydenScaleType stype)
1115: {
1116: Mat_LMVM *lmvm = (Mat_LMVM *)B->data;
1117: Mat_SymBrdn *lsb = (Mat_SymBrdn *)lmvm->ctx;
1119: PetscFunctionBegin;
1121: PetscCall(SymBroydenRescaleSetType(lsb->rescale, stype));
1122: PetscFunctionReturn(PETSC_SUCCESS);
1123: }
1125: /*@
1126: MatCreateLMVMSymBroyden - Creates a limited-memory Symmetric Broyden-type matrix used
1127: for approximating Jacobians.
1129: Collective
1131: Input Parameters:
1132: + comm - MPI communicator, set to `PETSC_COMM_SELF`
1133: . n - number of local rows for storage vectors
1134: - N - global size of the storage vectors
1136: Output Parameter:
1137: . B - the matrix
1139: Options Database Keys:
1140: + -mat_lmvm_hist_size - the number of history vectors to keep
1141: . -mat_lmvm_phi - convex ratio between BFGS and DFP components of the update
1142: . -mat_lmvm_scale_type - type of scaling applied to J0 (none, scalar, diagonal)
1143: . -mat_lmvm_mult_algorithm - the algorithm to use for multiplication (recursive, dense, compact_dense)
1144: . -mat_lmvm_cache_J0_products - whether products between the base Jacobian J0 and history vectors should be cached or recomputed
1145: . -mat_lmvm_eps - (developer) numerical zero tolerance for testing when an update should be skipped
1146: . -mat_lmvm_debug - (developer) perform internal debugging checks
1147: . -mat_lmvm_theta - (developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling
1148: . -mat_lmvm_rho - (developer) update limiter for the J0 scaling
1149: . -mat_lmvm_alpha - (developer) coefficient factor for the quadratic subproblem in J0 scaling
1150: . -mat_lmvm_beta - (developer) exponential factor for the diagonal J0 scaling
1151: - -mat_lmvm_sigma_hist - (developer) number of past updates to use in J0 scaling
1153: Level: intermediate
1155: Notes:
1156: It is recommended that one use the `MatCreate()`, `MatSetType()` and/or `MatSetFromOptions()` paradigm instead of this
1157: routine directly.
1159: L-SymBrdn is a convex combination of L-DFP and L-BFGS such that $B = (1 - \phi)B_{\text{BFGS}} + \phi B_{\text{DFP}}$.
1160: The combination factor $\phi$ is restricted to the range $[0, 1]$, where the L-SymBrdn matrix is guaranteed to be
1161: symmetric positive-definite.
1163: To use the L-SymBrdn matrix with other vector types, the matrix must be created using `MatCreate()` and `MatSetType()`,
1164: followed by `MatLMVMAllocate()`. This ensures that the internal storage and work vectors are duplicated from the
1165: correct type of vector.
1167: .seealso: [](ch_ksp), `MatCreate()`, `MATLMVM`, `MATLMVMSYMBROYDEN`, `MatCreateLMVMDFP()`, `MatCreateLMVMSR1()`,
1168: `MatCreateLMVMBFGS()`, `MatCreateLMVMBroyden()`, `MatCreateLMVMBadBroyden()`
1169: @*/
1170: PetscErrorCode MatCreateLMVMSymBroyden(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
1171: {
1172: PetscFunctionBegin;
1173: PetscCall(KSPInitializePackage());
1174: PetscCall(MatCreate(comm, B));
1175: PetscCall(MatSetSizes(*B, n, n, N, N));
1176: PetscCall(MatSetType(*B, MATLMVMSYMBROYDEN));
1177: PetscCall(MatSetUp(*B));
1178: PetscFunctionReturn(PETSC_SUCCESS);
1179: }