Date: 2025-03-29T05:58:29+00:00

Actual source code: diagbrdn.c

      1: #include <petscdevice.h>
      2: #include <../src/ksp/ksp/utils/lmvm/diagbrdn/diagbrdn.h>

      4: static PetscErrorCode MatSolve_DiagBrdn(Mat B, Vec F, Vec dX)
      5: {
      6:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
      7:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;

      9:   PetscFunctionBegin;
     10:   PetscCall(VecPointwiseMult(dX, ldb->invD, F));
     11:   PetscFunctionReturn(PETSC_SUCCESS);
     12: }

     14: static PetscErrorCode MatMult_DiagBrdn(Mat B, Vec X, Vec Z)
     15: {
     16:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
     17:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;

     19:   PetscFunctionBegin;
     20:   PetscCall(VecPointwiseDivide(Z, X, ldb->invD));
     21:   PetscFunctionReturn(PETSC_SUCCESS);
     22: }

     24: static PetscErrorCode MatUpdate_DiagBrdn(Mat B, Vec X, Vec F)
     25: {
     26:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
     27:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;
     28:   PetscInt      old_k, i, start;
     29:   PetscScalar   curvature, ytDy, sts, stDs, ytDs;
     30:   PetscReal     curvtol, sigma, yy_sum, ss_sum, ys_sum, denom, ytytmp;
     31:   PetscReal     stDsr, ytDyr;

     33:   PetscFunctionBegin;
     34:   if (!lmvm->m) PetscFunctionReturn(PETSC_SUCCESS);
     35:   if (lmvm->prev_set) {
     36:     /* Compute the new (S = X - Xprev) and (Y = F - Fprev) vectors */
     37:     PetscCall(VecAYPX(lmvm->Xprev, -1.0, X));
     38:     PetscCall(VecAYPX(lmvm->Fprev, -1.0, F));

     40:     /* Test if the updates can be accepted */
     41:     PetscCall(VecDotNorm2(lmvm->Xprev, lmvm->Fprev, &curvature, &ytytmp));
     42:     if (ytytmp < lmvm->eps) curvtol = 0.0;
     43:     else curvtol = lmvm->eps * ytytmp;

     45:     /* Test the curvature for the update */
     46:     if (PetscRealPart(curvature) > curvtol) {
     47:       /* Update is good so we accept it */
     48:       old_k = lmvm->k;
     49:       PetscCall(MatUpdateKernel_LMVM(B, lmvm->Xprev, lmvm->Fprev));
     50:       /* If we hit the memory limit, shift the yty and yts arrays */
     51:       if (old_k == lmvm->k) {
     52:         for (i = 0; i <= lmvm->k - 1; ++i) {
     53:           ldb->yty[i] = ldb->yty[i + 1];
     54:           ldb->yts[i] = ldb->yts[i + 1];
     55:           ldb->sts[i] = ldb->sts[i + 1];
     56:         }
     57:       }
     58:       /* Accept dot products into the history */
     59:       PetscCall(VecDot(lmvm->S[lmvm->k], lmvm->S[lmvm->k], &sts));
     60:       ldb->yty[lmvm->k] = ytytmp;
     61:       ldb->yts[lmvm->k] = PetscRealPart(curvature);
     62:       ldb->sts[lmvm->k] = PetscRealPart(sts);
     63:       if (ldb->forward) {
     64:         /* We are doing diagonal scaling of the forward Hessian B */
     65:         /*  BFGS = DFP = inv(D); */
     66:         PetscCall(VecCopy(ldb->invD, ldb->invDnew));
     67:         PetscCall(VecReciprocal(ldb->invDnew));

     69:         /*  V = y*y */
     70:         PetscCall(VecPointwiseMult(ldb->V, lmvm->Y[lmvm->k], lmvm->Y[lmvm->k]));

     72:         /*  W = inv(D)*s */
     73:         PetscCall(VecPointwiseMult(ldb->W, ldb->invDnew, lmvm->S[lmvm->k]));
     74:         PetscCall(VecDot(ldb->W, lmvm->S[lmvm->k], &stDs));

     76:         /*  Safeguard stDs */
     77:         stDs = PetscMax(PetscRealPart(stDs), ldb->tol);

     79:         if (1.0 != ldb->theta) {
     80:           /*  BFGS portion of the update */
     81:           /*  U = (inv(D)*s)*(inv(D)*s) */
     82:           PetscCall(VecPointwiseMult(ldb->U, ldb->W, ldb->W));

     84:           /*  Assemble */
     85:           PetscCall(VecAXPBY(ldb->BFGS, -1.0 / stDs, 0.0, ldb->U));
     86:         }
     87:         if (0.0 != ldb->theta) {
     88:           /*  DFP portion of the update */
     89:           /*  U = inv(D)*s*y */
     90:           PetscCall(VecPointwiseMult(ldb->U, ldb->W, lmvm->Y[lmvm->k]));

     92:           /*  Assemble */
     93:           PetscCall(VecAXPBY(ldb->DFP, stDs / ldb->yts[lmvm->k], 0.0, ldb->V));
     94:           PetscCall(VecAXPY(ldb->DFP, -2.0, ldb->U));
     95:         }

     97:         if (0.0 == ldb->theta) {
     98:           PetscCall(VecAXPY(ldb->invDnew, 1.0, ldb->BFGS));
     99:         } else if (1.0 == ldb->theta) {
    100:           PetscCall(VecAXPY(ldb->invDnew, 1.0 / ldb->yts[lmvm->k], ldb->DFP));
    101:         } else {
    102:           /*  Broyden update Dkp1 = Dk + (1-theta)*P + theta*Q + y_i^2/yts*/
    103:           PetscCall(VecAXPBYPCZ(ldb->invDnew, 1.0 - ldb->theta, (ldb->theta) / ldb->yts[lmvm->k], 1.0, ldb->BFGS, ldb->DFP));
    104:         }

    106:         PetscCall(VecAXPY(ldb->invDnew, 1.0 / ldb->yts[lmvm->k], ldb->V));
    107:         /*  Obtain inverse and ensure positive definite */
    108:         PetscCall(VecReciprocal(ldb->invDnew));
    109:         PetscCall(VecAbs(ldb->invDnew));

    111:       } else {
    112:         /* Inverse Hessian update instead. */
    113:         PetscCall(VecCopy(ldb->invD, ldb->invDnew));

    115:         /*  V = s*s */
    116:         PetscCall(VecPointwiseMult(ldb->V, lmvm->S[lmvm->k], lmvm->S[lmvm->k]));

    118:         /*  W = D*y */
    119:         PetscCall(VecPointwiseMult(ldb->W, ldb->invDnew, lmvm->Y[lmvm->k]));
    120:         PetscCall(VecDot(ldb->W, lmvm->Y[lmvm->k], &ytDy));

    122:         /*  Safeguard ytDy */
    123:         ytDy = PetscMax(PetscRealPart(ytDy), ldb->tol);

    125:         if (1.0 != ldb->theta) {
    126:           /*  BFGS portion of the update */
    127:           /*  U = s*Dy */
    128:           PetscCall(VecPointwiseMult(ldb->U, ldb->W, lmvm->S[lmvm->k]));

    130:           /*  Assemble */
    131:           PetscCall(VecAXPBY(ldb->BFGS, ytDy / ldb->yts[lmvm->k], 0.0, ldb->V));
    132:           PetscCall(VecAXPY(ldb->BFGS, -2.0, ldb->U));
    133:         }
    134:         if (0.0 != ldb->theta) {
    135:           /*  DFP portion of the update */

    137:           /*  U = (inv(D)*y)*(inv(D)*y) */
    138:           PetscCall(VecPointwiseMult(ldb->U, ldb->W, ldb->W));

    140:           /*  Assemble */
    141:           PetscCall(VecAXPBY(ldb->DFP, -1.0 / ytDy, 0.0, ldb->U));
    142:         }

    144:         if (0.0 == ldb->theta) {
    145:           PetscCall(VecAXPY(ldb->invDnew, 1.0 / ldb->yts[lmvm->k], ldb->BFGS));
    146:         } else if (1.0 == ldb->theta) {
    147:           PetscCall(VecAXPY(ldb->invDnew, 1.0, ldb->DFP));
    148:         } else {
    149:           /*  Broyden update U=(1-theta)*P + theta*Q */
    150:           PetscCall(VecAXPBYPCZ(ldb->invDnew, (1.0 - ldb->theta) / ldb->yts[lmvm->k], ldb->theta, 1.0, ldb->BFGS, ldb->DFP));
    151:         }
    152:         PetscCall(VecAXPY(ldb->invDnew, 1.0 / ldb->yts[lmvm->k], ldb->V));
    153:         /*  Ensure positive definite */
    154:         PetscCall(VecAbs(ldb->invDnew));
    155:       }
    156:       if (ldb->sigma_hist > 0) {
    157:         /*  Start with re-scaling on the newly computed diagonal */
    158:         if (0.5 == ldb->beta) {
    159:           if (1 == PetscMin(lmvm->nupdates, ldb->sigma_hist)) {
    160:             PetscCall(VecPointwiseMult(ldb->V, lmvm->Y[0], ldb->invDnew));
    161:             PetscCall(VecPointwiseDivide(ldb->W, lmvm->S[0], ldb->invDnew));

    163:             PetscCall(VecDot(ldb->V, lmvm->Y[0], &ytDy));
    164:             PetscCall(VecDot(ldb->W, lmvm->S[0], &stDs));

    166:             ss_sum = PetscRealPart(stDs);
    167:             yy_sum = PetscRealPart(ytDy);
    168:             ys_sum = ldb->yts[0];
    169:           } else {
    170:             PetscCall(VecCopy(ldb->invDnew, ldb->U));
    171:             PetscCall(VecReciprocal(ldb->U));

    173:             /*  Compute summations for scalar scaling */
    174:             yy_sum = 0; /*  No safeguard required */
    175:             ys_sum = 0; /*  No safeguard required */
    176:             ss_sum = 0; /*  No safeguard required */
    177:             start  = PetscMax(0, lmvm->k - ldb->sigma_hist + 1);
    178:             for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    179:               PetscCall(VecPointwiseMult(ldb->V, lmvm->Y[i], ldb->U));
    180:               PetscCall(VecPointwiseMult(ldb->W, lmvm->S[i], ldb->U));

    182:               PetscCall(VecDot(ldb->W, lmvm->S[i], &stDs));
    183:               PetscCall(VecDot(ldb->V, lmvm->Y[i], &ytDy));

    185:               ss_sum += PetscRealPart(stDs);
    186:               ys_sum += ldb->yts[i];
    187:               yy_sum += PetscRealPart(ytDy);
    188:             }
    189:           }
    190:         } else if (0.0 == ldb->beta) {
    191:           if (1 == PetscMin(lmvm->nupdates, ldb->sigma_hist)) {
    192:             /*  Compute summations for scalar scaling */
    193:             PetscCall(VecPointwiseDivide(ldb->W, lmvm->S[0], ldb->invDnew));

    195:             PetscCall(VecDotNorm2(lmvm->Y[0], ldb->W, &ytDs, &stDsr));

    197:             ys_sum = PetscRealPart(ytDs);
    198:             ss_sum = stDsr;
    199:             yy_sum = ldb->yty[0];
    200:           } else {
    201:             PetscCall(VecCopy(ldb->invDnew, ldb->U));
    202:             PetscCall(VecReciprocal(ldb->U));

    204:             /*  Compute summations for scalar scaling */
    205:             yy_sum = 0; /*  No safeguard required */
    206:             ys_sum = 0; /*  No safeguard required */
    207:             ss_sum = 0; /*  No safeguard required */
    208:             start  = PetscMax(0, lmvm->k - ldb->sigma_hist + 1);
    209:             for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    210:               PetscCall(VecPointwiseMult(ldb->W, lmvm->S[i], ldb->U));

    212:               PetscCall(VecDotNorm2(lmvm->Y[i], ldb->W, &ytDs, &stDsr));

    214:               ss_sum += stDsr;
    215:               ys_sum += PetscRealPart(ytDs);
    216:               yy_sum += ldb->yty[i];
    217:             }
    218:           }
    219:         } else if (1.0 == ldb->beta) {
    220:           /*  Compute summations for scalar scaling */
    221:           yy_sum = 0; /*  No safeguard required */
    222:           ys_sum = 0; /*  No safeguard required */
    223:           ss_sum = 0; /*  No safeguard required */
    224:           start  = PetscMax(0, lmvm->k - ldb->sigma_hist + 1);
    225:           for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    226:             PetscCall(VecPointwiseMult(ldb->V, lmvm->Y[i], ldb->invDnew));

    228:             PetscCall(VecDotNorm2(lmvm->S[i], ldb->V, &ytDs, &ytDyr));

    230:             yy_sum += ytDyr;
    231:             ys_sum += PetscRealPart(ytDs);
    232:             ss_sum += ldb->sts[i];
    233:           }
    234:         } else {
    235:           PetscCall(VecCopy(ldb->invDnew, ldb->U));
    236:           PetscCall(VecPow(ldb->U, ldb->beta - 1));

    238:           /*  Compute summations for scalar scaling */
    239:           yy_sum = 0; /*  No safeguard required */
    240:           ys_sum = 0; /*  No safeguard required */
    241:           ss_sum = 0; /*  No safeguard required */
    242:           start  = PetscMax(0, lmvm->k - ldb->sigma_hist + 1);
    243:           for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    244:             PetscCall(VecPointwiseMult(ldb->V, ldb->invDnew, lmvm->Y[i]));
    245:             PetscCall(VecPointwiseMult(ldb->W, ldb->U, lmvm->S[i]));

    247:             PetscCall(VecDotNorm2(ldb->W, ldb->V, &ytDs, &ytDyr));
    248:             PetscCall(VecDot(ldb->W, ldb->W, &stDs));

    250:             yy_sum += ytDyr;
    251:             ys_sum += PetscRealPart(ytDs);
    252:             ss_sum += PetscRealPart(stDs);
    253:           }
    254:         }

    256:         if (0.0 == ldb->alpha) {
    257:           /*  Safeguard ys_sum  */
    258:           ys_sum = PetscMax(ldb->tol, ys_sum);

    260:           sigma = ss_sum / ys_sum;
    261:         } else if (1.0 == ldb->alpha) {
    262:           /* yy_sum is never 0; if it were, we'd be at the minimum */
    263:           sigma = ys_sum / yy_sum;
    264:         } else {
    265:           denom = 2.0 * ldb->alpha * yy_sum;

    267:           /*  Safeguard denom */
    268:           denom = PetscMax(ldb->tol, denom);

    270:           sigma = ((2.0 * ldb->alpha - 1) * ys_sum + PetscSqrtReal((2.0 * ldb->alpha - 1) * (2.0 * ldb->alpha - 1) * ys_sum * ys_sum - 4.0 * ldb->alpha * (ldb->alpha - 1) * yy_sum * ss_sum)) / denom;
    271:         }
    272:       } else {
    273:         sigma = 1.0;
    274:       }
    275:       /*  If Q has small values, then Q^(r_beta - 1)
    276:        can have very large values.  Hence, ys_sum
    277:        and ss_sum can be infinity.  In this case,
    278:        sigma can either be not-a-number or infinity. */

    280:       if (PetscIsInfOrNanScalar(sigma)) {
    281:         /*  sigma is not-a-number; skip rescaling */
    282:       } else if (0.0 == sigma) {
    283:         /*  sigma is zero; this is a bad case; skip rescaling */
    284:       } else {
    285:         /*  sigma is positive */
    286:         PetscCall(VecScale(ldb->invDnew, sigma));
    287:       }

    289:       /* Combine the old diagonal and the new diagonal using a convex limiter */
    290:       if (1.0 == ldb->rho) {
    291:         PetscCall(VecCopy(ldb->invDnew, ldb->invD));
    292:       } else if (ldb->rho) PetscCall(VecAXPBY(ldb->invD, 1.0 - ldb->rho, ldb->rho, ldb->invDnew));
    293:     } else {
    294:       PetscCall(MatLMVMReset(B, PETSC_FALSE));
    295:     }
    296:     /* End DiagBrdn update */
    297:   }
    298:   /* Save the solution and function to be used in the next update */
    299:   PetscCall(VecCopy(X, lmvm->Xprev));
    300:   PetscCall(VecCopy(F, lmvm->Fprev));
    301:   lmvm->prev_set = PETSC_TRUE;
    302:   PetscFunctionReturn(PETSC_SUCCESS);
    303: }

    305: static PetscErrorCode MatCopy_DiagBrdn(Mat B, Mat M, MatStructure str)
    306: {
    307:   Mat_LMVM     *bdata = (Mat_LMVM *)B->data;
    308:   Mat_DiagBrdn *bctx  = (Mat_DiagBrdn *)bdata->ctx;
    309:   Mat_LMVM     *mdata = (Mat_LMVM *)M->data;
    310:   Mat_DiagBrdn *mctx  = (Mat_DiagBrdn *)mdata->ctx;
    311:   PetscInt      i;

    313:   PetscFunctionBegin;
    314:   mctx->theta      = bctx->theta;
    315:   mctx->alpha      = bctx->alpha;
    316:   mctx->beta       = bctx->beta;
    317:   mctx->rho        = bctx->rho;
    318:   mctx->delta      = bctx->delta;
    319:   mctx->delta_min  = bctx->delta_min;
    320:   mctx->delta_max  = bctx->delta_max;
    321:   mctx->tol        = bctx->tol;
    322:   mctx->sigma      = bctx->sigma;
    323:   mctx->sigma_hist = bctx->sigma_hist;
    324:   mctx->forward    = bctx->forward;
    325:   PetscCall(VecCopy(bctx->invD, mctx->invD));
    326:   for (i = 0; i <= bdata->k; ++i) {
    327:     mctx->yty[i] = bctx->yty[i];
    328:     mctx->yts[i] = bctx->yts[i];
    329:     mctx->sts[i] = bctx->sts[i];
    330:   }
    331:   PetscFunctionReturn(PETSC_SUCCESS);
    332: }

    334: static PetscErrorCode MatView_DiagBrdn(Mat B, PetscViewer pv)
    335: {
    336:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
    337:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;
    338:   PetscBool     isascii;

    340:   PetscFunctionBegin;
    341:   PetscCall(PetscObjectTypeCompare((PetscObject)pv, PETSCVIEWERASCII, &isascii));
    342:   if (isascii) {
    343:     PetscCall(PetscViewerASCIIPrintf(pv, "Scale history: %" PetscInt_FMT "\n", ldb->sigma_hist));
    344:     PetscCall(PetscViewerASCIIPrintf(pv, "Scale params: alpha=%g, beta=%g, rho=%g\n", (double)ldb->alpha, (double)ldb->beta, (double)ldb->rho));
    345:     PetscCall(PetscViewerASCIIPrintf(pv, "Convex factor: theta=%g\n", (double)ldb->theta));
    346:   }
    347:   PetscCall(MatView_LMVM(B, pv));
    348:   PetscFunctionReturn(PETSC_SUCCESS);
    349: }

    351: static PetscErrorCode MatSetFromOptions_DiagBrdn(Mat B, PetscOptionItems PetscOptionsObject)
    352: {
    353:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
    354:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;

    356:   PetscFunctionBegin;
    357:   PetscCall(MatSetFromOptions_LMVM(B, PetscOptionsObject));
    358:   PetscOptionsHeadBegin(PetscOptionsObject, "Restricted Broyden method for approximating SPD Jacobian actions (MATLMVMDIAGBRDN)");
    359:   PetscCall(PetscOptionsRangeReal("-mat_lmvm_theta", "(developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling", "", ldb->theta, &ldb->theta, NULL, 0.0, 1.0));
    360:   PetscCall(PetscOptionsRangeReal("-mat_lmvm_rho", "(developer) update limiter in the J0 scaling", "", ldb->rho, &ldb->rho, NULL, 0.0, 1.0));
    361:   PetscCall(PetscOptionsReal("-mat_lmvm_tol", "(developer) tolerance for bounding rescaling denominator", "", ldb->tol, &ldb->tol, NULL));
    362:   PetscCall(PetscOptionsRangeReal("-mat_lmvm_alpha", "(developer) convex ratio in the J0 scaling", "", ldb->alpha, &ldb->alpha, NULL, 0.0, 1.0));
    363:   PetscCall(PetscOptionsBool("-mat_lmvm_forward", "Forward -> Update diagonal scaling for B. Else -> diagonal scaling for H.", "", ldb->forward, &ldb->forward, NULL));
    364:   PetscCall(PetscOptionsReal("-mat_lmvm_beta", "(developer) exponential factor in the diagonal J0 scaling", "", ldb->beta, &ldb->beta, NULL));
    365:   PetscCall(PetscOptionsBoundedInt("-mat_lmvm_sigma_hist", "(developer) number of past updates to use in the default J0 scalar", "", ldb->sigma_hist, &ldb->sigma_hist, NULL, 0));
    366:   PetscOptionsHeadEnd();
    367:   PetscFunctionReturn(PETSC_SUCCESS);
    368: }

    370: static PetscErrorCode MatSetUp_DiagBrdn(Mat);
    371: static PetscErrorCode MatReset_DiagBrdn(Mat B, PetscBool destructive)
    372: {
    373:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
    374:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;

    376:   PetscFunctionBegin;
    377:   if (!ldb->allocated) PetscCall(MatSetUp_DiagBrdn(B));
    378:   PetscCall(VecSet(ldb->invD, ldb->delta));
    379:   if (destructive && ldb->allocated) {
    380:     PetscCall(PetscFree3(ldb->yty, ldb->yts, ldb->sts));
    381:     PetscCall(VecDestroy(&ldb->invDnew));
    382:     PetscCall(VecDestroy(&ldb->invD));
    383:     PetscCall(VecDestroy(&ldb->BFGS));
    384:     PetscCall(VecDestroy(&ldb->DFP));
    385:     PetscCall(VecDestroy(&ldb->U));
    386:     PetscCall(VecDestroy(&ldb->V));
    387:     PetscCall(VecDestroy(&ldb->W));
    388:     ldb->allocated = PETSC_FALSE;
    389:   }
    390:   PetscCall(MatReset_LMVM(B, destructive));
    391:   PetscFunctionReturn(PETSC_SUCCESS);
    392: }

    394: static PetscErrorCode MatAllocate_DiagBrdn(Mat B, Vec X, Vec F)
    395: {
    396:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
    397:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;

    399:   PetscFunctionBegin;
    400:   PetscCall(MatAllocate_LMVM(B, X, F));
    401:   if (!ldb->allocated) {
    402:     PetscCall(PetscMalloc3(lmvm->m, &ldb->yty, lmvm->m, &ldb->yts, lmvm->m, &ldb->sts));
    403:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->invDnew));
    404:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->invD));
    405:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->BFGS));
    406:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->DFP));
    407:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->U));
    408:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->V));
    409:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->W));
    410:     ldb->allocated = PETSC_TRUE;
    411:   }
    412:   PetscFunctionReturn(PETSC_SUCCESS);
    413: }

    415: static PetscErrorCode MatDestroy_DiagBrdn(Mat B)
    416: {
    417:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
    418:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;

    420:   PetscFunctionBegin;
    421:   if (ldb->allocated) {
    422:     PetscCall(PetscFree3(ldb->yty, ldb->yts, ldb->sts));
    423:     PetscCall(VecDestroy(&ldb->invDnew));
    424:     PetscCall(VecDestroy(&ldb->invD));
    425:     PetscCall(VecDestroy(&ldb->BFGS));
    426:     PetscCall(VecDestroy(&ldb->DFP));
    427:     PetscCall(VecDestroy(&ldb->U));
    428:     PetscCall(VecDestroy(&ldb->V));
    429:     PetscCall(VecDestroy(&ldb->W));
    430:     ldb->allocated = PETSC_FALSE;
    431:   }
    432:   PetscCall(PetscFree(lmvm->ctx));
    433:   PetscCall(MatDestroy_LMVM(B));
    434:   PetscFunctionReturn(PETSC_SUCCESS);
    435: }

    437: static PetscErrorCode MatSetUp_DiagBrdn(Mat B)
    438: {
    439:   Mat_LMVM     *lmvm = (Mat_LMVM *)B->data;
    440:   Mat_DiagBrdn *ldb  = (Mat_DiagBrdn *)lmvm->ctx;

    442:   PetscFunctionBegin;
    443:   PetscCall(MatSetUp_LMVM(B));
    444:   if (!ldb->allocated) {
    445:     PetscCall(PetscMalloc3(lmvm->m, &ldb->yty, lmvm->m, &ldb->yts, lmvm->m, &ldb->sts));
    446:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->invDnew));
    447:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->invD));
    448:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->BFGS));
    449:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->DFP));
    450:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->U));
    451:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->V));
    452:     PetscCall(VecDuplicate(lmvm->Xprev, &ldb->W));
    453:     PetscCall(VecSet(ldb->invD, ldb->delta));
    454:     ldb->allocated = PETSC_TRUE;
    455:   }
    456:   PetscFunctionReturn(PETSC_SUCCESS);
    457: }

    459: PetscErrorCode MatCreate_LMVMDiagBrdn(Mat B)
    460: {
    461:   Mat_LMVM     *lmvm;
    462:   Mat_DiagBrdn *ldb;

    464:   PetscFunctionBegin;
    465:   PetscCall(MatCreate_LMVM(B));
    466:   PetscCall(PetscObjectChangeTypeName((PetscObject)B, MATLMVMDIAGBROYDEN));
    467:   B->ops->setup          = MatSetUp_DiagBrdn;
    468:   B->ops->setfromoptions = MatSetFromOptions_DiagBrdn;
    469:   B->ops->destroy        = MatDestroy_DiagBrdn;
    470:   B->ops->view           = MatView_DiagBrdn;

    472:   lmvm                = (Mat_LMVM *)B->data;
    473:   lmvm->square        = PETSC_TRUE;
    474:   lmvm->m             = 1;
    475:   lmvm->ops->allocate = MatAllocate_DiagBrdn;
    476:   lmvm->ops->reset    = MatReset_DiagBrdn;
    477:   lmvm->ops->mult     = MatMult_DiagBrdn;
    478:   lmvm->ops->solve    = MatSolve_DiagBrdn;
    479:   lmvm->ops->update   = MatUpdate_DiagBrdn;
    480:   lmvm->ops->copy     = MatCopy_DiagBrdn;

    482:   PetscCall(PetscNew(&ldb));
    483:   lmvm->ctx       = (void *)ldb;
    484:   ldb->theta      = 0.0;
    485:   ldb->alpha      = 1.0;
    486:   ldb->rho        = 1.0;
    487:   ldb->forward    = PETSC_TRUE;
    488:   ldb->beta       = 0.5;
    489:   ldb->sigma      = 1.0;
    490:   ldb->delta      = 1.0;
    491:   ldb->delta_min  = 1e-7;
    492:   ldb->delta_max  = 100.0;
    493:   ldb->tol        = 1e-8;
    494:   ldb->sigma_hist = 1;
    495:   ldb->allocated  = PETSC_FALSE;
    496:   PetscFunctionReturn(PETSC_SUCCESS);
    497: }

    499: /*@
    500:   MatCreateLMVMDiagBroyden - DiagBrdn creates a symmetric Broyden-type diagonal matrix used
    501:   for approximating Hessians.

    503:   Collective

    505:   Input Parameters:
    506: + comm - MPI communicator
    507: . n    - number of local rows for storage vectors
    508: - N    - global size of the storage vectors

    510:   Output Parameter:
    511: . B - the matrix

    513:   Options Database Keys:
    514: + -mat_lmvm_theta      - (developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling
    515: . -mat_lmvm_rho        - (developer) update limiter for the J0 scaling
    516: . -mat_lmvm_alpha      - (developer) coefficient factor for the quadratic subproblem in J0 scaling
    517: . -mat_lmvm_beta       - (developer) exponential factor for the diagonal J0 scaling
    518: . -mat_lmvm_sigma_hist - (developer) number of past updates to use in J0 scaling.
    519: . -mat_lmvm_tol        - (developer) tolerance for bounding the denominator of the rescaling away from 0.
    520: - -mat_lmvm_forward    - (developer) whether or not to use the forward or backward Broyden update to the diagonal

    522:   Level: intermediate

    524:   Notes:
    525:   It is recommended that one use the `MatCreate()`, `MatSetType()` and/or `MatSetFromOptions()`
    526:   paradigm instead of this routine directly.

    528:   It consists of a convex combination of DFP and BFGS
    529:   diagonal approximation schemes, such that DiagBrdn = (1-theta)*BFGS + theta*DFP.
    530:   To preserve symmetric positive-definiteness, we restrict theta to be in [0, 1].
    531:   We also ensure positive definiteness by taking the `VecAbs()` of the final vector.

    533:   There are two ways of approximating the diagonal: using the forward (B) update
    534:   schemes for BFGS and DFP and then taking the inverse, or directly working with
    535:   the inverse (H) update schemes for the BFGS and DFP updates, derived using the
    536:   Sherman-Morrison-Woodbury formula. We have implemented both, controlled by a
    537:   parameter below.

    539:   In order to use the DiagBrdn matrix with other vector types, i.e. doing matrix-vector products
    540:   and matrix solves, the matrix must first be created using `MatCreate()` and `MatSetType()`,
    541:   followed by `MatLMVMAllocate()`. Then it will be available for updating
    542:   (via `MatLMVMUpdate()`) in one's favored solver implementation.

    544: .seealso: [](ch_ksp), `MatCreate()`, `MATLMVM`, `MATLMVMDIAGBRDN`, `MatCreateLMVMDFP()`, `MatCreateLMVMSR1()`,
    545:           `MatCreateLMVMBFGS()`, `MatCreateLMVMBrdn()`, `MatCreateLMVMSymBrdn()`
    546: @*/
    547: PetscErrorCode MatCreateLMVMDiagBroyden(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
    548: {
    549:   PetscFunctionBegin;
    550:   PetscCall(KSPInitializePackage());
    551:   PetscCall(MatCreate(comm, B));
    552:   PetscCall(MatSetSizes(*B, n, n, N, N));
    553:   PetscCall(MatSetType(*B, MATLMVMDIAGBROYDEN));
    554:   PetscCall(MatSetUp(*B));
    555:   PetscFunctionReturn(PETSC_SUCCESS);
    556: }