Date: 2022-03-04T17:39:20+00:00

Actual source code: diagbrdn.c

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

      3: /*------------------------------------------------------------*/

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

     12:   VecCheckSameSize(F, 2, dX, 3);
     13:   VecCheckMatCompatible(B, dX, 3, F, 2);
     14:   VecPointwiseMult(dX, ldb->invD, F);
     15:   return(0);
     16: }

     18: /*------------------------------------------------------------*/

     20: static PetscErrorCode MatMult_DiagBrdn(Mat B, Vec X, Vec Z)
     21: {
     22:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
     23:   Mat_DiagBrdn      *ldb = (Mat_DiagBrdn*)lmvm->ctx;
     24:   PetscErrorCode    ierr;

     27:   VecCheckSameSize(X, 2, Z, 3);
     28:   VecCheckMatCompatible(B, X, 2, Z, 3);
     29:   VecPointwiseDivide(Z, X, ldb->invD);
     30:   return(0);
     31: }

     33: /*------------------------------------------------------------*/

     35: static PetscErrorCode MatUpdate_DiagBrdn(Mat B, Vec X, Vec F)
     36: {
     37:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
     38:   Mat_DiagBrdn      *ldb = (Mat_DiagBrdn*)lmvm->ctx;
     39:   PetscErrorCode    ierr;
     40:   PetscInt          old_k, i, start;
     41:   PetscScalar       yty, ststmp, curvature, ytDy, stDs, ytDs;
     42:   PetscReal         curvtol, sigma, yy_sum, ss_sum, ys_sum, denom;

     45:   if (!lmvm->m) return(0);
     46:   if (lmvm->prev_set) {
     47:     /* Compute the new (S = X - Xprev) and (Y = F - Fprev) vectors */
     48:     VecAYPX(lmvm->Xprev, -1.0, X);
     49:     VecAYPX(lmvm->Fprev, -1.0, F);
     50:     /* Compute tolerance for accepting the update */
     51:     VecDotBegin(lmvm->Xprev, lmvm->Fprev, &curvature);
     52:     VecDotBegin(lmvm->Xprev, lmvm->Xprev, &ststmp);
     53:     VecDotEnd(lmvm->Xprev, lmvm->Fprev, &curvature);
     54:     VecDotEnd(lmvm->Xprev, lmvm->Xprev, &ststmp);
     55:     if (PetscRealPart(ststmp) < lmvm->eps) {
     56:       curvtol = 0.0;
     57:     } else {
     58:       curvtol = lmvm->eps * PetscRealPart(ststmp);
     59:     }
     60:     /* Test the curvature for the update */
     61:     if (PetscRealPart(curvature) > curvtol) {
     62:       /* Update is good so we accept it */
     63:       old_k = lmvm->k;
     64:       MatUpdateKernel_LMVM(B, lmvm->Xprev, lmvm->Fprev);
     65:       /* If we hit the memory limit, shift the yty and yts arrays */
     66:       if (old_k == lmvm->k) {
     67:         for (i = 0; i <= lmvm->k-1; ++i) {
     68:           ldb->yty[i] = ldb->yty[i+1];
     69:           ldb->yts[i] = ldb->yts[i+1];
     70:           ldb->sts[i] = ldb->sts[i+1];
     71:         }
     72:       }
     73:       /* Accept dot products into the history */
     74:       VecDot(lmvm->Y[lmvm->k], lmvm->Y[lmvm->k], &yty);
     75:       ldb->yty[lmvm->k] = PetscRealPart(yty);
     76:       ldb->yts[lmvm->k] = PetscRealPart(curvature);
     77:       ldb->sts[lmvm->k] = PetscRealPart(ststmp);
     78:       if (ldb->forward) {
     79:         /* We are doing diagonal scaling of the forward Hessian B */
     80:         /*  BFGS = DFP = inv(D); */
     81:         VecCopy(ldb->invD, ldb->invDnew);
     82:         VecReciprocal(ldb->invDnew);

     84:         /*  V = y*y */
     85:         VecPointwiseMult(ldb->V, lmvm->Y[lmvm->k], lmvm->Y[lmvm->k]);

     87:         /*  W = inv(D)*s */
     88:         VecPointwiseMult(ldb->W, ldb->invDnew, lmvm->S[lmvm->k]);
     89:         VecDot(ldb->W, lmvm->S[lmvm->k], &stDs);

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

     94:         if (1.0 != ldb->theta) {
     95:           /*  BFGS portion of the update */
     96:           /*  U = (inv(D)*s)*(inv(D)*s) */
     97:           VecPointwiseMult(ldb->U, ldb->W, ldb->W);

     99:           /*  Assemble */
    100:           VecAXPBY(ldb->BFGS, -1.0/stDs, 0.0, ldb->U);
    101:         }
    102:         if (0.0 != ldb->theta) {
    103:           /*  DFP portion of the update */
    104:           /*  U = inv(D)*s*y */
    105:           VecPointwiseMult(ldb->U, ldb->W, lmvm->Y[lmvm->k]);

    107:           /*  Assemble */
    108:           VecAXPBY(ldb->DFP, stDs/ldb->yts[lmvm->k], 0.0, ldb->V);
    109:           VecAXPY(ldb->DFP, -2.0, ldb->U);
    110:         }

    112:         if (0.0 == ldb->theta) {
    113:           VecAXPY(ldb->invDnew, 1.0, ldb->BFGS);
    114:         } else if (1.0 == ldb->theta) {
    115:           VecAXPY(ldb->invDnew, 1.0/ldb->yts[lmvm->k], ldb->DFP);
    116:         } else {
    117:           /*  Broyden update Dkp1 = Dk + (1-theta)*P + theta*Q + y_i^2/yts*/
    118:           VecAXPBYPCZ(ldb->invDnew, 1.0-ldb->theta, (ldb->theta)/ldb->yts[lmvm->k], 1.0, ldb->BFGS, ldb->DFP);
    119:         }

    121:         VecAXPY(ldb->invDnew, 1.0/ldb->yts[lmvm->k], ldb->V);
    122:         /*  Obtain inverse and ensure positive definite */
    123:         VecReciprocal(ldb->invDnew);
    124:         VecAbs(ldb->invDnew);

    126:       } else {
    127:         /* Inverse Hessian update instead. */
    128:         VecCopy(ldb->invD, ldb->invDnew);

    130:         /*  V = s*s */
    131:         VecPointwiseMult(ldb->V, lmvm->S[lmvm->k], lmvm->S[lmvm->k]);

    133:         /*  W = D*y */
    134:         VecPointwiseMult(ldb->W, ldb->invDnew, lmvm->Y[lmvm->k]);
    135:         VecDot(ldb->W, lmvm->Y[lmvm->k], &ytDy);

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

    140:         if (1.0 != ldb->theta) {
    141:           /*  BFGS portion of the update */
    142:           /*  U = s*Dy */
    143:           VecPointwiseMult(ldb->U, ldb->W, lmvm->S[lmvm->k]);

    145:           /*  Assemble */
    146:           VecAXPBY(ldb->BFGS, ytDy/ldb->yts[lmvm->k], 0.0, ldb->V);
    147:           VecAXPY(ldb->BFGS, -2.0, ldb->U);
    148:         }
    149:         if (0.0 != ldb->theta) {
    150:           /*  DFP portion of the update */

    152:           /*  U = (inv(D)*y)*(inv(D)*y) */
    153:           VecPointwiseMult(ldb->U, ldb->W, ldb->W);

    155:           /*  Assemble */
    156:           VecAXPBY(ldb->DFP, -1.0/ytDy, 0.0, ldb->U);
    157:         }

    159:         if (0.0 == ldb->theta) {
    160:           VecAXPY(ldb->invDnew, 1.0/ldb->yts[lmvm->k], ldb->BFGS);
    161:         } else if (1.0 == ldb->theta) {
    162:           VecAXPY(ldb->invDnew, 1.0, ldb->DFP);
    163:         } else {
    164:           /*  Broyden update U=(1-theta)*P + theta*Q */
    165:           VecAXPBYPCZ(ldb->invDnew, (1.0-ldb->theta)/ldb->yts[lmvm->k], ldb->theta, 1.0, ldb->BFGS, ldb->DFP);
    166:         }
    167:         VecAXPY(ldb->invDnew, 1.0/ldb->yts[lmvm->k], ldb->V);
    168:         /*  Ensure positive definite */
    169:         VecAbs(ldb->invDnew);
    170:       }
    171:       if (ldb->sigma_hist > 0) {
    172:         /*  Start with re-scaling on the newly computed diagonal */
    173:         if (0.5 == ldb->beta) {
    174:           if (1 == PetscMin(lmvm->nupdates, ldb->sigma_hist)) {
    175:             VecPointwiseMult(ldb->V, lmvm->Y[0], ldb->invDnew);
    176:             VecPointwiseDivide(ldb->W, lmvm->S[0], ldb->invDnew);

    178:             VecDotBegin(ldb->V, lmvm->Y[0], &ytDy);
    179:             VecDotBegin(ldb->W, lmvm->S[0], &stDs);
    180:             VecDotEnd(ldb->V, lmvm->Y[0], &ytDy);
    181:             VecDotEnd(ldb->W, lmvm->S[0], &stDs);

    183:             ss_sum = PetscRealPart(stDs);
    184:             yy_sum = PetscRealPart(ytDy);
    185:             ys_sum = ldb->yts[0];
    186:           } else {
    187:             VecCopy(ldb->invDnew, ldb->U);
    188:             VecReciprocal(ldb->U);

    190:             /*  Compute summations for scalar scaling */
    191:             yy_sum = 0;       /*  No safeguard required */
    192:             ys_sum = 0;       /*  No safeguard required */
    193:             ss_sum = 0;       /*  No safeguard required */
    194:             start = PetscMax(0, lmvm->k-ldb->sigma_hist+1);
    195:             for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    196:               VecPointwiseMult(ldb->V, lmvm->Y[i], ldb->U);
    197:               VecPointwiseMult(ldb->W, lmvm->S[i], ldb->U);

    199:               VecDotBegin(ldb->W, lmvm->S[i], &stDs);
    200:               VecDotBegin(ldb->V, lmvm->Y[i], &ytDy);
    201:               VecDotEnd(ldb->W, lmvm->S[i], &stDs);
    202:               VecDotEnd(ldb->V, lmvm->Y[i], &ytDy);

    204:               ss_sum += PetscRealPart(stDs);
    205:               ys_sum += ldb->yts[i];
    206:               yy_sum += PetscRealPart(ytDy);
    207:             }
    208:           }
    209:         } else if (0.0 == ldb->beta) {
    210:           if (1 == PetscMin(lmvm->nupdates, ldb->sigma_hist)) {
    211:             /*  Compute summations for scalar scaling */
    212:             VecPointwiseDivide(ldb->W, lmvm->S[0], ldb->invDnew);

    214:             VecDotBegin(ldb->W, lmvm->Y[0], &ytDs);
    215:             VecDotBegin(ldb->W, ldb->W, &stDs);
    216:             VecDotEnd(ldb->W, lmvm->Y[0], &ytDs);
    217:             VecDotEnd(ldb->W, ldb->W, &stDs);

    219:             ys_sum = PetscRealPart(ytDs);
    220:             ss_sum = PetscRealPart(stDs);
    221:             yy_sum = ldb->yty[0];
    222:           } else {
    223:             VecCopy(ldb->invDnew, ldb->U);
    224:             VecReciprocal(ldb->U);

    226:             /*  Compute summations for scalar scaling */
    227:             yy_sum = 0;       /*  No safeguard required */
    228:             ys_sum = 0;       /*  No safeguard required */
    229:             ss_sum = 0;       /*  No safeguard required */
    230:             start = PetscMax(0, lmvm->k-ldb->sigma_hist+1);
    231:             for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    232:               VecPointwiseMult(ldb->W, lmvm->S[i], ldb->U);

    234:               VecDotBegin(ldb->W, lmvm->Y[i], &ytDs);
    235:               VecDotBegin(ldb->W, ldb->W, &stDs);
    236:               VecDotEnd(ldb->W, lmvm->Y[i], &ytDs);
    237:               VecDotEnd(ldb->W, ldb->W, &stDs);

    239:               ss_sum += PetscRealPart(stDs);
    240:               ys_sum += PetscRealPart(ytDs);
    241:               yy_sum += ldb->yty[i];
    242:             }
    243:           }
    244:         } else if (1.0 == ldb->beta) {
    245:           /*  Compute summations for scalar scaling */
    246:           yy_sum = 0; /*  No safeguard required */
    247:           ys_sum = 0; /*  No safeguard required */
    248:           ss_sum = 0; /*  No safeguard required */
    249:           start = PetscMax(0, lmvm->k-ldb->sigma_hist+1);
    250:           for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    251:             VecPointwiseMult(ldb->V, lmvm->Y[i], ldb->invDnew);

    253:             VecDotBegin(ldb->V, lmvm->S[i], &ytDs);
    254:             VecDotBegin(ldb->V, ldb->V, &ytDy);
    255:             VecDotEnd(ldb->V, lmvm->S[i], &ytDs);
    256:             VecDotEnd(ldb->V, ldb->V, &ytDy);

    258:             yy_sum += PetscRealPart(ytDy);
    259:             ys_sum += PetscRealPart(ytDs);
    260:             ss_sum += ldb->sts[i];
    261:           }
    262:         } else {
    263:           VecCopy(ldb->invDnew, ldb->U);
    264:           VecPow(ldb->U, ldb->beta-1);

    266:           /*  Compute summations for scalar scaling */
    267:           yy_sum = 0; /*  No safeguard required */
    268:           ys_sum = 0; /*  No safeguard required */
    269:           ss_sum = 0; /*  No safeguard required */
    270:           start = PetscMax(0, lmvm->k-ldb->sigma_hist+1);
    271:           for (i = start; i < PetscMin(lmvm->nupdates, ldb->sigma_hist); ++i) {
    272:             VecPointwiseMult(ldb->V, ldb->invDnew, lmvm->Y[i]);
    273:             VecPointwiseMult(ldb->W, ldb->U, lmvm->S[i]);

    275:             VecDotBegin(ldb->V, ldb->W, &ytDs);
    276:             VecDotBegin(ldb->V, ldb->V, &ytDy);
    277:             VecDotBegin(ldb->W, ldb->W, &stDs);
    278:             VecDotEnd(ldb->V, ldb->W, &ytDs);
    279:             VecDotEnd(ldb->V, ldb->V, &ytDy);
    280:             VecDotEnd(ldb->W, ldb->W, &stDs);

    282:             yy_sum += PetscRealPart(ytDy);
    283:             ys_sum += PetscRealPart(ytDs);
    284:             ss_sum += PetscRealPart(stDs);
    285:           }
    286:         }

    288:         if (0.0 == ldb->alpha) {
    289:           /*  Safeguard ys_sum  */
    290:           ys_sum = PetscMax(ldb->tol, ys_sum);

    292:           sigma = ss_sum / ys_sum;
    293:         } else if (1.0 == ldb->alpha) {
    294:           /* yy_sum is never 0; if it were, we'd be at the minimum */
    295:           sigma = ys_sum / yy_sum;
    296:         } else {
    297:           denom = 2.0*ldb->alpha*yy_sum;

    299:           /*  Safeguard denom */
    300:           denom = PetscMax(ldb->tol, denom);

    302:           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;
    303:         }
    304:       } else {
    305:         sigma = 1.0;
    306:       }
    307:       /*  If Q has small values, then Q^(r_beta - 1)
    308:        can have very large values.  Hence, ys_sum
    309:        and ss_sum can be infinity.  In this case,
    310:        sigma can either be not-a-number or infinity. */

    312:       if (PetscIsInfOrNanScalar(sigma)) {
    313:         /*  sigma is not-a-number; skip rescaling */
    314:       } else if (0.0 == sigma) {
    315:         /*  sigma is zero; this is a bad case; skip rescaling */
    316:       } else {
    317:         /*  sigma is positive */
    318:         VecScale(ldb->invDnew, sigma);
    319:       }

    321:       /* Combine the old diagonal and the new diagonal using a convex limiter */
    322:       if (1.0 == ldb->rho) {
    323:         VecCopy(ldb->invDnew, ldb->invD);
    324:       } else if (ldb->rho) {
    325:         VecAXPBY(ldb->invD, 1.0-ldb->rho, ldb->rho, ldb->invDnew);
    326:       }
    327:     } else {
    328:       MatLMVMReset(B, PETSC_FALSE);
    329:     }
    330:     /* End DiagBrdn update */

    332:   }
    333:   /* Save the solution and function to be used in the next update */
    334:   VecCopy(X, lmvm->Xprev);
    335:   VecCopy(F, lmvm->Fprev);
    336:   lmvm->prev_set = PETSC_TRUE;
    337:   return(0);
    338: }

    340: /*------------------------------------------------------------*/

    342: static PetscErrorCode MatCopy_DiagBrdn(Mat B, Mat M, MatStructure str)
    343: {
    344:   Mat_LMVM          *bdata = (Mat_LMVM*)B->data;
    345:   Mat_DiagBrdn      *bctx = (Mat_DiagBrdn*)bdata->ctx;
    346:   Mat_LMVM          *mdata = (Mat_LMVM*)M->data;
    347:   Mat_DiagBrdn      *mctx = (Mat_DiagBrdn*)mdata->ctx;
    348:   PetscErrorCode    ierr;
    349:   PetscInt          i;

    352:   mctx->theta = bctx->theta;
    353:   mctx->alpha = bctx->alpha;
    354:   mctx->beta = bctx->beta;
    355:   mctx->rho = bctx->rho;
    356:   mctx->delta = bctx->delta;
    357:   mctx->delta_min = bctx->delta_min;
    358:   mctx->delta_max = bctx->delta_max;
    359:   mctx->tol = bctx->tol;
    360:   mctx->sigma = bctx->sigma;
    361:   mctx->sigma_hist = bctx->sigma_hist;
    362:   mctx->forward = bctx->forward;
    363:   VecCopy(bctx->invD, mctx->invD);
    364:   for (i=0; i<=bdata->k; ++i) {
    365:     mctx->yty[i] = bctx->yty[i];
    366:     mctx->yts[i] = bctx->yts[i];
    367:     mctx->sts[i] = bctx->sts[i];
    368:   }
    369:   return(0);
    370: }

    372: /*------------------------------------------------------------*/

    374: static PetscErrorCode MatView_DiagBrdn(Mat B, PetscViewer pv)
    375: {
    376:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    377:   Mat_DiagBrdn      *ldb = (Mat_DiagBrdn*)lmvm->ctx;
    378:   PetscErrorCode    ierr;
    379:   PetscBool         isascii;

    382:   PetscObjectTypeCompare((PetscObject)pv,PETSCVIEWERASCII,&isascii);
    383:   if (isascii) {
    384:     PetscViewerASCIIPrintf(pv,"Scale history: %d\n",ldb->sigma_hist);
    385:     PetscViewerASCIIPrintf(pv,"Scale params: alpha=%g, beta=%g, rho=%g\n",(double)ldb->alpha, (double)ldb->beta, (double)ldb->rho);
    386:     PetscViewerASCIIPrintf(pv,"Convex factor: theta=%g\n", (double)ldb->theta);
    387:   }
    388:   MatView_LMVM(B, pv);
    389:   return(0);
    390: }

    392: /*------------------------------------------------------------*/

    394: static PetscErrorCode MatSetFromOptions_DiagBrdn(PetscOptionItems *PetscOptionsObject, Mat B)
    395: {
    396:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    397:   Mat_DiagBrdn       *ldb = (Mat_DiagBrdn*)lmvm->ctx;
    398:   PetscErrorCode    ierr;

    401:   MatSetFromOptions_LMVM(PetscOptionsObject, B);
    402:   PetscOptionsHead(PetscOptionsObject,"Restricted Broyden method for approximating SPD Jacobian actions (MATLMVMDIAGBRDN)");
    403:   PetscOptionsReal("-mat_lmvm_theta","(developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling","",ldb->theta,&ldb->theta,NULL);
    404:   PetscOptionsReal("-mat_lmvm_rho","(developer) update limiter in the J0 scaling","",ldb->rho,&ldb->rho,NULL);
    405:   PetscOptionsReal("-mat_lmvm_tol","(developer) tolerance for bounding rescaling denominator","",ldb->tol,&ldb->tol,NULL);
    406:   PetscOptionsReal("-mat_lmvm_alpha","(developer) convex ratio in the J0 scaling","",ldb->alpha,&ldb->alpha,NULL);
    407:   PetscOptionsBool("-mat_lmvm_forward","Forward -> Update diagonal scaling for B. Else -> diagonal scaling for H.","",ldb->forward,&ldb->forward,NULL);
    408:   PetscOptionsReal("-mat_lmvm_beta","(developer) exponential factor in the diagonal J0 scaling","",ldb->beta,&ldb->beta,NULL);
    409:   PetscOptionsInt("-mat_lmvm_sigma_hist","(developer) number of past updates to use in the default J0 scalar","",ldb->sigma_hist,&ldb->sigma_hist,NULL);
    410:   PetscOptionsTail();
    411:   if ((ldb->theta < 0.0) || (ldb->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]");
    412:   if ((ldb->alpha < 0.0) || (ldb->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]");
    413:   if ((ldb->rho < 0.0) || (ldb->rho > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex update limiter in the J0 scaling cannot be outside the range of [0, 1]");
    414:   if (ldb->sigma_hist < 0) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "J0 scaling history length cannot be negative");
    415:   return(0);
    416: }

    418: /*------------------------------------------------------------*/

    420: static PetscErrorCode MatReset_DiagBrdn(Mat B, PetscBool destructive)
    421: {
    422:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    423:   Mat_DiagBrdn      *ldb = (Mat_DiagBrdn*)lmvm->ctx;
    424:   PetscErrorCode    ierr;

    427:   VecSet(ldb->invD, ldb->delta);
    428:   if (destructive && ldb->allocated) {
    429:     PetscFree3(ldb->yty, ldb->yts, ldb->sts);
    430:     VecDestroy(&ldb->invDnew);
    431:     VecDestroy(&ldb->invD);
    432:     VecDestroy(&ldb->BFGS);
    433:     VecDestroy(&ldb->DFP);
    434:     VecDestroy(&ldb->U);
    435:     VecDestroy(&ldb->V);
    436:     VecDestroy(&ldb->W);
    437:     ldb->allocated = PETSC_FALSE;
    438:   }
    439:   MatReset_LMVM(B, destructive);
    440:   return(0);
    441: }

    443: /*------------------------------------------------------------*/

    445: static PetscErrorCode MatAllocate_DiagBrdn(Mat B, Vec X, Vec F)
    446: {
    447:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    448:   Mat_DiagBrdn      *ldb = (Mat_DiagBrdn*)lmvm->ctx;
    449:   PetscErrorCode    ierr;

    452:   MatAllocate_LMVM(B, X, F);
    453:   if (!ldb->allocated) {
    454:     PetscMalloc3(lmvm->m, &ldb->yty, lmvm->m, &ldb->yts, lmvm->m, &ldb->sts);
    455:     VecDuplicate(lmvm->Xprev, &ldb->invDnew);
    456:     VecDuplicate(lmvm->Xprev, &ldb->invD);
    457:     VecDuplicate(lmvm->Xprev, &ldb->BFGS);
    458:     VecDuplicate(lmvm->Xprev, &ldb->DFP);
    459:     VecDuplicate(lmvm->Xprev, &ldb->U);
    460:     VecDuplicate(lmvm->Xprev, &ldb->V);
    461:     VecDuplicate(lmvm->Xprev, &ldb->W);
    462:     ldb->allocated = PETSC_TRUE;
    463:   }
    464:   return(0);
    465: }

    467: /*------------------------------------------------------------*/

    469: static PetscErrorCode MatDestroy_DiagBrdn(Mat B)
    470: {
    471:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    472:   Mat_DiagBrdn      *ldb = (Mat_DiagBrdn*)lmvm->ctx;
    473:   PetscErrorCode    ierr;

    476:   if (ldb->allocated) {
    477:     PetscFree3(ldb->yty, ldb->yts, ldb->sts);
    478:     VecDestroy(&ldb->invDnew);
    479:     VecDestroy(&ldb->invD);
    480:     VecDestroy(&ldb->BFGS);
    481:     VecDestroy(&ldb->DFP);
    482:     VecDestroy(&ldb->U);
    483:     VecDestroy(&ldb->V);
    484:     VecDestroy(&ldb->W);
    485:     ldb->allocated = PETSC_FALSE;
    486:   }
    487:   PetscFree(lmvm->ctx);
    488:   MatDestroy_LMVM(B);
    489:   return(0);
    490: }

    492: /*------------------------------------------------------------*/

    494: static PetscErrorCode MatSetUp_DiagBrdn(Mat B)
    495: {
    496:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    497:   Mat_DiagBrdn      *ldb = (Mat_DiagBrdn*)lmvm->ctx;
    498:   PetscErrorCode    ierr;

    501:   MatSetUp_LMVM(B);
    502:   if (!ldb->allocated) {
    503:     PetscMalloc3(lmvm->m, &ldb->yty, lmvm->m, &ldb->yts, lmvm->m, &ldb->sts);
    504:     VecDuplicate(lmvm->Xprev, &ldb->invDnew);
    505:     VecDuplicate(lmvm->Xprev, &ldb->invD);
    506:     VecDuplicate(lmvm->Xprev, &ldb->BFGS);
    507:     VecDuplicate(lmvm->Xprev, &ldb->DFP);
    508:     VecDuplicate(lmvm->Xprev, &ldb->U);
    509:     VecDuplicate(lmvm->Xprev, &ldb->V);
    510:     VecDuplicate(lmvm->Xprev, &ldb->W);
    511:     ldb->allocated = PETSC_TRUE;
    512:   }
    513:   return(0);
    514: }

    516: /*------------------------------------------------------------*/

    518: PetscErrorCode MatCreate_LMVMDiagBrdn(Mat B)
    519: {
    520:   Mat_LMVM          *lmvm;
    521:   Mat_DiagBrdn      *ldb;
    522:   PetscErrorCode    ierr;

    525:   MatCreate_LMVM(B);
    526:   PetscObjectChangeTypeName((PetscObject)B, MATLMVMDIAGBROYDEN);
    527:   B->ops->setup = MatSetUp_DiagBrdn;
    528:   B->ops->setfromoptions = MatSetFromOptions_DiagBrdn;
    529:   B->ops->destroy = MatDestroy_DiagBrdn;
    530:   B->ops->solve = MatSolve_DiagBrdn;
    531:   B->ops->view = MatView_DiagBrdn;

    533:   lmvm = (Mat_LMVM*)B->data;
    534:   lmvm->square = PETSC_TRUE;
    535:   lmvm->m = 1;
    536:   lmvm->ops->allocate = MatAllocate_DiagBrdn;
    537:   lmvm->ops->reset = MatReset_DiagBrdn;
    538:   lmvm->ops->mult = MatMult_DiagBrdn;
    539:   lmvm->ops->update = MatUpdate_DiagBrdn;
    540:   lmvm->ops->copy = MatCopy_DiagBrdn;

    542:   PetscNewLog(B, &ldb);
    543:   lmvm->ctx = (void*)ldb;
    544:   ldb->theta = 0.0;
    545:   ldb->alpha = 1.0;
    546:   ldb->rho = 1.0;
    547:   ldb->forward = PETSC_TRUE;
    548:   ldb->beta = 0.5;
    549:   ldb->sigma = 1.0;
    550:   ldb->delta = 1.0;
    551:   ldb->delta_min = 1e-7;
    552:   ldb->delta_max = 100.0;
    553:   ldb->tol = 1e-8;
    554:   ldb->sigma_hist = 1;
    555:   ldb->allocated = PETSC_FALSE;
    556:   return(0);
    557: }

    559: /*------------------------------------------------------------*/

    561: /*@
    562:    MatCreateLMVMDiagBroyden - DiagBrdn creates a symmetric Broyden-type diagonal matrix used
    563:    for approximating Hessians. It consists of a convex combination of DFP and BFGS
    564:    diagonal approximation schemes, such that DiagBrdn = (1-theta)*BFGS + theta*DFP.
    565:    To preserve symmetric positive-definiteness, we restrict theta to be in [0, 1].
    566:    We also ensure positive definiteness by taking the VecAbs() of the final vector.

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

    574:    In order to use the DiagBrdn matrix with other vector types, i.e. doing MatMults
    575:    and MatSolves, the matrix must first be created using MatCreate() and MatSetType(),
    576:    followed by MatLMVMAllocate(). Then it will be available for updating
    577:    (via MatLMVMUpdate) in one's favored solver implementation.
    578:    This allows for MPI compatibility.

    580:    Collective

    582:    Input Parameters:
    583: +  comm - MPI communicator, set to PETSC_COMM_SELF
    584: .  n - number of local rows for storage vectors
    585: -  N - global size of the storage vectors

    587:    Output Parameter:
    588: .  B - the matrix

    590:    It is recommended that one use the MatCreate(), MatSetType() and/or MatSetFromOptions()
    591:    paradigm instead of this routine directly.

    593:    Options Database Keys:
    594: +   -mat_lmvm_theta - (developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling
    595: .   -mat_lmvm_rho - (developer) update limiter for the J0 scaling
    596: .   -mat_lmvm_alpha - (developer) coefficient factor for the quadratic subproblem in J0 scaling
    597: .   -mat_lmvm_beta - (developer) exponential factor for the diagonal J0 scaling
    598: .   -mat_lmvm_sigma_hist - (developer) number of past updates to use in J0 scaling.
    599: .   -mat_lmvm_tol - (developer) tolerance for bounding the denominator of the rescaling away from 0.
    600: -   -mat_lmvm_forward - (developer) whether or not to use the forward or backward Broyden update to the diagonal

    602:    Level: intermediate

    604: .seealso: MatCreate(), MATLMVM, MATLMVMDIAGBRDN, MatCreateLMVMDFP(), MatCreateLMVMSR1(),
    605:           MatCreateLMVMBFGS(), MatCreateLMVMBrdn(), MatCreateLMVMSymBrdn()
    606: @*/
    607: PetscErrorCode MatCreateLMVMDiagBroyden(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
    608: {
    609:   PetscErrorCode    ierr;

    612:   MatCreate(comm, B);
    613:   MatSetSizes(*B, n, n, N, N);
    614:   MatSetType(*B, MATLMVMDIAGBROYDEN);
    615:   MatSetUp(*B);
    616:   return(0);
    617: }