Date: 2021-03-03T22:27:38+00:00

Actual source code: symbrdn.c
petsc-3.14.5 2021-03-03
Report Typos and Errors

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

      4: const char *const MatLMVMSymBroydenScaleTypes[] = {"NONE","SCALAR","DIAGONAL","USER","MatLMVMSymBrdnScaleType","MAT_LMVM_SYMBROYDEN_SCALING_",NULL};

      6: /*------------------------------------------------------------*/

      8: /*
      9:   The solution method below is the matrix-free implementation of
     10:   Equation 8.6a in Dennis and More "Quasi-Newton Methods, Motivation
     11:   and Theory" (https://epubs.siam.org/doi/abs/10.1137/1019005).

     13:   Q[i] = (B_i)^{-1}*S[i] terms are computed ahead of time whenever
     14:   the matrix is updated with a new (S[i], Y[i]) pair. This allows
     15:   repeated calls of MatSolve without incurring redundant computation.

     17:   dX <- J0^{-1} * F

     19:   for i=0,1,2,...,k
     20:     # Q[i] = (B_i)^T{-1} Y[i]

     22:     rho = 1.0 / (Y[i]^T S[i])
     23:     alpha = rho * (S[i]^T F)
     24:     zeta = 1.0 / (Y[i]^T Q[i])
     25:     gamma = zeta * (Y[i]^T dX)

     27:     dX <- dX - (gamma * Q[i]) + (alpha * Y[i])
     28:     W <- (rho * S[i]) - (zeta * Q[i])
     29:     dX <- dX + (psi[i] * (Y[i]^T Q[i]) * (W^T F) * W)
     30:   end
     31: */
     32: static PetscErrorCode MatSolve_LMVMSymBrdn(Mat B, Vec F, Vec dX)
     33: {
     34:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
     35:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
     36:   PetscErrorCode    ierr;
     37:   PetscInt          i, j;
     38:   PetscReal         numer;
     39:   PetscScalar       sjtpi, yjtsi, wtsi, yjtqi, sjtyi, wtyi, ytx, stf, wtf, stp, ytq;

     42:   /* Efficient shortcuts for pure BFGS and pure DFP configurations */
     43:   if (lsb->phi == 0.0) {
     44:     MatSolve_LMVMBFGS(B, F, dX);
     45:     return(0);
     46:   }
     47:   if (lsb->phi == 1.0) {
     48:     MatSolve_LMVMDFP(B, F, dX);
     49:     return(0);
     50:   }

     52:   VecCheckSameSize(F, 2, dX, 3);
     53:   VecCheckMatCompatible(B, dX, 3, F, 2);

     55:   if (lsb->needP) {
     56:     /* Start the loop for (P[k] = (B_k) * S[k]) */
     57:     for (i = 0; i <= lmvm->k; ++i) {
     58:       MatSymBrdnApplyJ0Fwd(B, lmvm->S[i], lsb->P[i]);
     59:       for (j = 0; j <= i-1; ++j) {
     60:         /* Compute the necessary dot products */
     61:         VecDotBegin(lmvm->S[j], lsb->P[i], &sjtpi);
     62:         VecDotBegin(lmvm->Y[j], lmvm->S[i], &yjtsi);
     63:         VecDotEnd(lmvm->S[j], lsb->P[i], &sjtpi);
     64:         VecDotEnd(lmvm->Y[j], lmvm->S[i], &yjtsi);
     65:         /* Compute the pure BFGS component of the forward product */
     66:         VecAXPBYPCZ(lsb->P[i], -PetscRealPart(sjtpi)/lsb->stp[j], PetscRealPart(yjtsi)/lsb->yts[j], 1.0, lsb->P[j], lmvm->Y[j]);
     67:         /* Tack on the convexly scaled extras to the forward product */
     68:         if (lsb->phi > 0.0) {
     69:           VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->stp[j], 0.0, lmvm->Y[j], lsb->P[j]);
     70:           VecDot(lsb->work, lmvm->S[i], &wtsi);
     71:           VecAXPY(lsb->P[i], lsb->phi*lsb->stp[j]*PetscRealPart(wtsi), lsb->work);
     72:         }
     73:       }
     74:       VecDot(lmvm->S[i], lsb->P[i], &stp);
     75:       lsb->stp[i] = PetscRealPart(stp);
     76:     }
     77:     lsb->needP = PETSC_FALSE;
     78:   }
     79:   if (lsb->needQ) {
     80:     /* Start the loop for (Q[k] = (B_k)^{-1} * Y[k]) */
     81:     for (i = 0; i <= lmvm->k; ++i) {
     82:       MatSymBrdnApplyJ0Inv(B, lmvm->Y[i], lsb->Q[i]);
     83:       for (j = 0; j <= i-1; ++j) {
     84:         /* Compute the necessary dot products */
     85:         VecDotBegin(lmvm->Y[j], lsb->Q[i], &yjtqi);
     86:         VecDotBegin(lmvm->S[j], lmvm->Y[i], &sjtyi);
     87:         VecDotEnd(lmvm->Y[j], lsb->Q[i], &yjtqi);
     88:         VecDotEnd(lmvm->S[j], lmvm->Y[i], &sjtyi);
     89:         /* Compute the pure DFP component of the inverse application*/
     90:         VecAXPBYPCZ(lsb->Q[i], -PetscRealPart(yjtqi)/lsb->ytq[j], PetscRealPart(sjtyi)/lsb->yts[j], 1.0, lsb->Q[j], lmvm->S[j]);
     91:         /* Tack on the convexly scaled extras to the inverse application*/
     92:         if (lsb->psi[j] > 0.0) {
     93:           VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->ytq[j], 0.0, lmvm->S[j], lsb->Q[j]);
     94:           VecDot(lsb->work, lmvm->Y[i], &wtyi);
     95:           VecAXPY(lsb->Q[i], lsb->psi[j]*lsb->ytq[j]*PetscRealPart(wtyi), lsb->work);
     96:         }
     97:       }
     98:       VecDot(lmvm->Y[i], lsb->Q[i], &ytq);
     99:       lsb->ytq[i] = PetscRealPart(ytq);
    100:       if (lsb->phi == 1.0) {
    101:         lsb->psi[i] = 0.0;
    102:       } else if (lsb->phi == 0.0) {
    103:         lsb->psi[i] = 1.0;
    104:       } else {
    105:         numer = (1.0 - lsb->phi)*lsb->yts[i]*lsb->yts[i];
    106:         lsb->psi[i] = numer / (numer + (lsb->phi*lsb->ytq[i]*lsb->stp[i]));
    107:       }
    108:     }
    109:     lsb->needQ = PETSC_FALSE;
    110:   }

    112:   /* Start the outer iterations for ((B^{-1}) * dX) */
    113:   MatSymBrdnApplyJ0Inv(B, F, dX);
    114:   for (i = 0; i <= lmvm->k; ++i) {
    115:     /* Compute the necessary dot products -- store yTs and yTp for inner iterations later */
    116:     VecDotBegin(lmvm->Y[i], dX, &ytx);
    117:     VecDotBegin(lmvm->S[i], F, &stf);
    118:     VecDotEnd(lmvm->Y[i], dX, &ytx);
    119:     VecDotEnd(lmvm->S[i], F, &stf);
    120:     /* Compute the pure DFP component */
    121:     VecAXPBYPCZ(dX, -PetscRealPart(ytx)/lsb->ytq[i], PetscRealPart(stf)/lsb->yts[i], 1.0, lsb->Q[i], lmvm->S[i]);
    122:     /* Tack on the convexly scaled extras */
    123:     VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[i], -1.0/lsb->ytq[i], 0.0, lmvm->S[i], lsb->Q[i]);
    124:     VecDot(lsb->work, F, &wtf);
    125:     VecAXPY(dX, lsb->psi[i]*lsb->ytq[i]*PetscRealPart(wtf), lsb->work);
    126:   }

    128:   return(0);
    129: }

    131: /*------------------------------------------------------------*/

    133: /*
    134:   The forward-product below is the matrix-free implementation of
    135:   Equation 16 in Dennis and Wolkowicz "Sizing and Least Change Secant
    136:   Methods" (http://www.caam.rice.edu/caam/trs/90/TR90-05.pdf).

    138:   P[i] = (B_i)*S[i] terms are computed ahead of time whenever
    139:   the matrix is updated with a new (S[i], Y[i]) pair. This allows
    140:   repeated calls of MatMult inside KSP solvers without unnecessarily
    141:   recomputing P[i] terms in expensive nested-loops.

    143:   Z <- J0 * X

    145:   for i=0,1,2,...,k
    146:     # P[i] = (B_k) * S[i]

    148:     rho = 1.0 / (Y[i]^T S[i])
    149:     alpha = rho * (Y[i]^T F)
    150:     zeta = 1.0 / (S[i]^T P[i])
    151:     gamma = zeta * (S[i]^T dX)

    153:     dX <- dX - (gamma * P[i]) + (alpha * S[i])
    154:     W <- (rho * Y[i]) - (zeta * P[i])
    155:     dX <- dX + (phi * (S[i]^T P[i]) * (W^T F) * W)
    156:   end
    157: */
    158: static PetscErrorCode MatMult_LMVMSymBrdn(Mat B, Vec X, Vec Z)
    159: {
    160:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    161:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    162:   PetscErrorCode    ierr;
    163:   PetscInt          i, j;
    164:   PetscScalar         sjtpi, yjtsi, wtsi, stz, ytx, wtx, stp;

    168:   /* Efficient shortcuts for pure BFGS and pure DFP configurations */
    169:   if (lsb->phi == 0.0) {
    170:     MatMult_LMVMBFGS(B, X, Z);
    171:     return(0);
    172:   }
    173:   if (lsb->phi == 1.0) {
    174:     MatMult_LMVMDFP(B, X, Z);
    175:     return(0);
    176:   }

    178:   VecCheckSameSize(X, 2, Z, 3);
    179:   VecCheckMatCompatible(B, X, 2, Z, 3);

    181:   if (lsb->needP) {
    182:     /* Start the loop for (P[k] = (B_k) * S[k]) */
    183:     for (i = 0; i <= lmvm->k; ++i) {
    184:       MatSymBrdnApplyJ0Fwd(B, lmvm->S[i], lsb->P[i]);
    185:       for (j = 0; j <= i-1; ++j) {
    186:         /* Compute the necessary dot products */
    187:         VecDotBegin(lmvm->S[j], lsb->P[i], &sjtpi);
    188:         VecDotBegin(lmvm->Y[j], lmvm->S[i], &yjtsi);
    189:         VecDotEnd(lmvm->S[j], lsb->P[i], &sjtpi);
    190:         VecDotEnd(lmvm->Y[j], lmvm->S[i], &yjtsi);
    191:         /* Compute the pure BFGS component of the forward product */
    192:         VecAXPBYPCZ(lsb->P[i], -PetscRealPart(sjtpi)/lsb->stp[j], PetscRealPart(yjtsi)/lsb->yts[j], 1.0, lsb->P[j], lmvm->Y[j]);
    193:         /* Tack on the convexly scaled extras to the forward product */
    194:         if (lsb->phi > 0.0) {
    195:           VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->stp[j], 0.0, lmvm->Y[j], lsb->P[j]);
    196:           VecDot(lsb->work, lmvm->S[i], &wtsi);
    197:           VecAXPY(lsb->P[i], lsb->phi*lsb->stp[j]*PetscRealPart(wtsi), lsb->work);
    198:         }
    199:       }
    200:       VecDot(lmvm->S[i], lsb->P[i], &stp);
    201:       lsb->stp[i] = PetscRealPart(stp);
    202:     }
    203:     lsb->needP = PETSC_FALSE;
    204:   }

    206:   /* Start the outer iterations for (B * X) */
    207:   MatSymBrdnApplyJ0Fwd(B, X, Z);
    208:   for (i = 0; i <= lmvm->k; ++i) {
    209:     /* Compute the necessary dot products */
    210:     VecDotBegin(lmvm->S[i], Z, &stz);
    211:     VecDotBegin(lmvm->Y[i], X, &ytx);
    212:     VecDotEnd(lmvm->S[i], Z, &stz);
    213:     VecDotEnd(lmvm->Y[i], X, &ytx);
    214:     /* Compute the pure BFGS component */
    215:     VecAXPBYPCZ(Z, -PetscRealPart(stz)/lsb->stp[i], PetscRealPart(ytx)/lsb->yts[i], 1.0, lsb->P[i], lmvm->Y[i]);
    216:     /* Tack on the convexly scaled extras */
    217:     VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[i], -1.0/lsb->stp[i], 0.0, lmvm->Y[i], lsb->P[i]);
    218:     VecDot(lsb->work, X, &wtx);
    219:     VecAXPY(Z, lsb->phi*lsb->stp[i]*PetscRealPart(wtx), lsb->work);
    220:   }
    221:   return(0);
    222: }

    224: /*------------------------------------------------------------*/

    226: static PetscErrorCode MatUpdate_LMVMSymBrdn(Mat B, Vec X, Vec F)
    227: {
    228:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    229:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    230:   Mat_LMVM          *dbase;
    231:   Mat_DiagBrdn      *dctx;
    232:   PetscErrorCode    ierr;
    233:   PetscInt          old_k, i;
    234:   PetscReal         curvtol;
    235:   PetscScalar       curvature, ytytmp, ststmp;

    238:   if (!lmvm->m) return(0);
    239:   if (lmvm->prev_set) {
    240:     /* Compute the new (S = X - Xprev) and (Y = F - Fprev) vectors */
    241:     VecAYPX(lmvm->Xprev, -1.0, X);
    242:     VecAYPX(lmvm->Fprev, -1.0, F);
    243:     /* Test if the updates can be accepted */
    244:     VecDotBegin(lmvm->Xprev, lmvm->Fprev, &curvature);
    245:     VecDotBegin(lmvm->Xprev, lmvm->Xprev, &ststmp);
    246:     VecDotEnd(lmvm->Xprev, lmvm->Fprev, &curvature);
    247:     VecDotEnd(lmvm->Xprev, lmvm->Xprev, &ststmp);
    248:     if (PetscRealPart(ststmp) < lmvm->eps) {
    249:       curvtol = 0.0;
    250:     } else {
    251:       curvtol = lmvm->eps * PetscRealPart(ststmp);
    252:     }
    253:     if (PetscRealPart(curvature) > curvtol) {
    254:       /* Update is good, accept it */
    255:       lsb->watchdog = 0;
    256:       lsb->needP = lsb->needQ = PETSC_TRUE;
    257:       old_k = lmvm->k;
    258:       MatUpdateKernel_LMVM(B, lmvm->Xprev, lmvm->Fprev);
    259:       /* If we hit the memory limit, shift the yts, yty and sts arrays */
    260:       if (old_k == lmvm->k) {
    261:         for (i = 0; i <= lmvm->k-1; ++i) {
    262:           lsb->yts[i] = lsb->yts[i+1];
    263:           lsb->yty[i] = lsb->yty[i+1];
    264:           lsb->sts[i] = lsb->sts[i+1];
    265:         }
    266:       }
    267:       /* Update history of useful scalars */
    268:       VecDot(lmvm->Y[lmvm->k], lmvm->Y[lmvm->k], &ytytmp);
    269:       lsb->yts[lmvm->k] = PetscRealPart(curvature);
    270:       lsb->yty[lmvm->k] = PetscRealPart(ytytmp);
    271:       lsb->sts[lmvm->k] = PetscRealPart(ststmp);
    272:       /* Compute the scalar scale if necessary */
    273:       if (lsb->scale_type == MAT_LMVM_SYMBROYDEN_SCALE_SCALAR) {
    274:         MatSymBrdnComputeJ0Scalar(B);
    275:       }
    276:     } else {
    277:       /* Update is bad, skip it */
    278:       ++lmvm->nrejects;
    279:       ++lsb->watchdog;
    280:     }
    281:   } else {
    282:     switch (lsb->scale_type) {
    283:     case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    284:       dbase = (Mat_LMVM*)lsb->D->data;
    285:       dctx = (Mat_DiagBrdn*)dbase->ctx;
    286:       VecSet(dctx->invD, lsb->delta);
    287:       break;
    288:     case MAT_LMVM_SYMBROYDEN_SCALE_SCALAR:
    289:       lsb->sigma = lsb->delta;
    290:       break;
    291:     case MAT_LMVM_SYMBROYDEN_SCALE_NONE:
    292:       lsb->sigma = 1.0;
    293:       break;
    294:     default:
    295:       break;
    296:     }
    297:   }

    299:   /* Update the scaling */
    300:   if (lsb->scale_type == MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL) {
    301:     MatLMVMUpdate(lsb->D, X, F);
    302:   }

    304:   if (lsb->watchdog > lsb->max_seq_rejects) {
    305:     MatLMVMReset(B, PETSC_FALSE);
    306:     if (lsb->scale_type == MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL) {
    307:       MatLMVMReset(lsb->D, PETSC_FALSE);
    308:     }
    309:   }

    311:   /* Save the solution and function to be used in the next update */
    312:   VecCopy(X, lmvm->Xprev);
    313:   VecCopy(F, lmvm->Fprev);
    314:   lmvm->prev_set = PETSC_TRUE;
    315:   return(0);
    316: }

    318: /*------------------------------------------------------------*/

    320: static PetscErrorCode MatCopy_LMVMSymBrdn(Mat B, Mat M, MatStructure str)
    321: {
    322:   Mat_LMVM          *bdata = (Mat_LMVM*)B->data;
    323:   Mat_SymBrdn       *blsb = (Mat_SymBrdn*)bdata->ctx;
    324:   Mat_LMVM          *mdata = (Mat_LMVM*)M->data;
    325:   Mat_SymBrdn       *mlsb = (Mat_SymBrdn*)mdata->ctx;
    326:   PetscErrorCode    ierr;
    327:   PetscInt          i;

    330:   mlsb->phi = blsb->phi;
    331:   mlsb->needP = blsb->needP;
    332:   mlsb->needQ = blsb->needQ;
    333:   for (i=0; i<=bdata->k; ++i) {
    334:     mlsb->stp[i] = blsb->stp[i];
    335:     mlsb->ytq[i] = blsb->ytq[i];
    336:     mlsb->yts[i] = blsb->yts[i];
    337:     mlsb->psi[i] = blsb->psi[i];
    338:     VecCopy(blsb->P[i], mlsb->P[i]);
    339:     VecCopy(blsb->Q[i], mlsb->Q[i]);
    340:   }
    341:   mlsb->scale_type      = blsb->scale_type;
    342:   mlsb->alpha           = blsb->alpha;
    343:   mlsb->beta            = blsb->beta;
    344:   mlsb->rho             = blsb->rho;
    345:   mlsb->delta           = blsb->delta;
    346:   mlsb->sigma_hist      = blsb->sigma_hist;
    347:   mlsb->watchdog        = blsb->watchdog;
    348:   mlsb->max_seq_rejects = blsb->max_seq_rejects;
    349:   switch (blsb->scale_type) {
    350:   case MAT_LMVM_SYMBROYDEN_SCALE_SCALAR:
    351:     mlsb->sigma = blsb->sigma;
    352:     break;
    353:   case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    354:     MatCopy(blsb->D, mlsb->D, SAME_NONZERO_PATTERN);
    355:     break;
    356:   case MAT_LMVM_SYMBROYDEN_SCALE_NONE:
    357:     mlsb->sigma = 1.0;
    358:     break;
    359:   default:
    360:     break;
    361:   }
    362:   return(0);
    363: }

    365: /*------------------------------------------------------------*/

    367: static PetscErrorCode MatReset_LMVMSymBrdn(Mat B, PetscBool destructive)
    368: {
    369:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    370:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    371:   Mat_LMVM          *dbase;
    372:   Mat_DiagBrdn      *dctx;
    373:   PetscErrorCode    ierr;

    376:   lsb->watchdog = 0;
    377:   lsb->needP = lsb->needQ = PETSC_TRUE;
    378:   if (lsb->allocated) {
    379:     if (destructive) {
    380:       VecDestroy(&lsb->work);
    381:       PetscFree5(lsb->stp, lsb->ytq, lsb->yts, lsb->yty, lsb->sts);
    382:       PetscFree(lsb->psi);
    383:       VecDestroyVecs(lmvm->m, &lsb->P);
    384:       VecDestroyVecs(lmvm->m, &lsb->Q);
    385:       switch (lsb->scale_type) {
    386:       case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    387:         MatLMVMReset(lsb->D, PETSC_TRUE);
    388:         break;
    389:       default:
    390:         break;
    391:       }
    392:       lsb->allocated = PETSC_FALSE;
    393:     } else {
    394:       PetscMemzero(lsb->psi, lmvm->m);
    395:       switch (lsb->scale_type) {
    396:       case MAT_LMVM_SYMBROYDEN_SCALE_SCALAR:
    397:         lsb->sigma = lsb->delta;
    398:         break;
    399:       case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    400:         MatLMVMReset(lsb->D, PETSC_FALSE);
    401:         dbase = (Mat_LMVM*)lsb->D->data;
    402:         dctx = (Mat_DiagBrdn*)dbase->ctx;
    403:         VecSet(dctx->invD, lsb->delta);
    404:         break;
    405:       case MAT_LMVM_SYMBROYDEN_SCALE_NONE:
    406:         lsb->sigma = 1.0;
    407:         break;
    408:       default:
    409:         break;
    410:       }
    411:     }
    412:   }
    413:   MatReset_LMVM(B, destructive);
    414:   return(0);
    415: }

    417: /*------------------------------------------------------------*/

    419: static PetscErrorCode MatAllocate_LMVMSymBrdn(Mat B, Vec X, Vec F)
    420: {
    421:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    422:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    423:   PetscErrorCode    ierr;

    426:   MatAllocate_LMVM(B, X, F);
    427:   if (!lsb->allocated) {
    428:     VecDuplicate(X, &lsb->work);
    429:     if (lmvm->m > 0) {
    430:       PetscMalloc5(lmvm->m,&lsb->stp,lmvm->m,&lsb->ytq,lmvm->m,&lsb->yts,lmvm->m,&lsb->yty,lmvm->m,&lsb->sts);
    431:       PetscCalloc1(lmvm->m,&lsb->psi);
    432:       VecDuplicateVecs(X, lmvm->m, &lsb->P);
    433:       VecDuplicateVecs(X, lmvm->m, &lsb->Q);
    434:     }
    435:     switch (lsb->scale_type) {
    436:     case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    437:       MatLMVMAllocate(lsb->D, X, F);
    438:       break;
    439:     default:
    440:       break;
    441:     }
    442:     lsb->allocated = PETSC_TRUE;
    443:   }
    444:   return(0);
    445: }

    447: /*------------------------------------------------------------*/

    449: static PetscErrorCode MatDestroy_LMVMSymBrdn(Mat B)
    450: {
    451:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    452:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    453:   PetscErrorCode    ierr;

    456:   if (lsb->allocated) {
    457:     VecDestroy(&lsb->work);
    458:     PetscFree5(lsb->stp, lsb->ytq, lsb->yts, lsb->yty, lsb->sts);
    459:     PetscFree(lsb->psi);
    460:     VecDestroyVecs(lmvm->m, &lsb->P);
    461:     VecDestroyVecs(lmvm->m, &lsb->Q);
    462:     lsb->allocated = PETSC_FALSE;
    463:   }
    464:   MatDestroy(&lsb->D);
    465:   PetscFree(lmvm->ctx);
    466:   MatDestroy_LMVM(B);
    467:   return(0);
    468: }

    470: /*------------------------------------------------------------*/

    472: static PetscErrorCode MatSetUp_LMVMSymBrdn(Mat B)
    473: {
    474:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    475:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    476:   PetscErrorCode    ierr;
    477:   PetscInt          n, N;

    480:   MatSetUp_LMVM(B);
    481:   if (!lsb->allocated) {
    482:     VecDuplicate(lmvm->Xprev, &lsb->work);
    483:     if (lmvm->m > 0) {
    484:       PetscMalloc5(lmvm->m,&lsb->stp,lmvm->m,&lsb->ytq,lmvm->m,&lsb->yts,lmvm->m,&lsb->yty,lmvm->m,&lsb->sts);
    485:       PetscCalloc1(lmvm->m,&lsb->psi);
    486:       VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lsb->P);
    487:       VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lsb->Q);
    488:     }
    489:     switch (lsb->scale_type) {
    490:     case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    491:       MatGetLocalSize(B, &n, &n);
    492:       MatGetSize(B, &N, &N);
    493:       MatSetSizes(lsb->D, n, n, N, N);
    494:       MatSetUp(lsb->D);
    495:       break;
    496:     default:
    497:       break;
    498:     }
    499:     lsb->allocated = PETSC_TRUE;
    500:   }
    501:   return(0);
    502: }

    504: /*------------------------------------------------------------*/

    506: PetscErrorCode MatView_LMVMSymBrdn(Mat B, PetscViewer pv)
    507: {
    508:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    509:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    510:   PetscErrorCode    ierr;
    511:   PetscBool         isascii;

    514:   PetscObjectTypeCompare((PetscObject)pv,PETSCVIEWERASCII,&isascii);
    515:   if (isascii) {
    516:     PetscViewerASCIIPrintf(pv,"Scale type: %s\n",MatLMVMSymBroydenScaleTypes[lsb->scale_type]);
    517:     PetscViewerASCIIPrintf(pv,"Scale history: %d\n",lsb->sigma_hist);
    518:     PetscViewerASCIIPrintf(pv,"Scale params: alpha=%g, beta=%g, rho=%g\n",(double)lsb->alpha, (double)lsb->beta, (double)lsb->rho);
    519:     PetscViewerASCIIPrintf(pv,"Convex factors: phi=%g, theta=%g\n",(double)lsb->phi, (double)lsb->theta);
    520:   }
    521:   MatView_LMVM(B, pv);
    522:   if (lsb->scale_type == MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL) {
    523:     MatView(lsb->D, pv);
    524:   }
    525:   return(0);
    526: }

    528: /*------------------------------------------------------------*/

    530: PetscErrorCode MatSetFromOptions_LMVMSymBrdn(PetscOptionItems *PetscOptionsObject, Mat B)
    531: {
    532:   Mat_LMVM                     *lmvm = (Mat_LMVM*)B->data;
    533:   Mat_SymBrdn                  *lsb = (Mat_SymBrdn*)lmvm->ctx;
    534:   PetscErrorCode               ierr;

    537:   MatSetFromOptions_LMVM(PetscOptionsObject, B);
    538:   PetscOptionsHead(PetscOptionsObject,"Restricted/Symmetric Broyden method for approximating SPD Jacobian actions (MATLMVMSYMBRDN)");
    539:   PetscOptionsReal("-mat_lmvm_phi","(developer) convex ratio between BFGS and DFP components of the update","",lsb->phi,&lsb->phi,NULL);
    540:   if ((lsb->phi < 0.0) || (lsb->phi > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio for the update formula cannot be outside the range of [0, 1]");
    541:   MatSetFromOptions_LMVMSymBrdn_Private(PetscOptionsObject, B);
    542:   PetscOptionsTail();
    543:   return(0);
    544: }

    546: PetscErrorCode MatSetFromOptions_LMVMSymBrdn_Private(PetscOptionItems *PetscOptionsObject, Mat B)
    547: {
    548:   Mat_LMVM                     *lmvm = (Mat_LMVM*)B->data;
    549:   Mat_SymBrdn                  *lsb = (Mat_SymBrdn*)lmvm->ctx;
    550:   Mat_LMVM                     *dbase;
    551:   Mat_DiagBrdn                 *dctx;
    552:   MatLMVMSymBroydenScaleType   stype = lsb->scale_type;
    553:   PetscBool                    flg;
    554:   PetscErrorCode               ierr;

    557:   PetscOptionsReal("-mat_lmvm_beta","(developer) exponential factor in the diagonal J0 scaling","",lsb->beta,&lsb->beta,NULL);
    558:   PetscOptionsReal("-mat_lmvm_theta","(developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling","",lsb->theta,&lsb->theta,NULL);
    559:   if ((lsb->theta < 0.0) || (lsb->theta > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio for the diagonal J0 scale cannot be outside the range of [0, 1]");
    560:   PetscOptionsReal("-mat_lmvm_rho","(developer) update limiter in the J0 scaling","",lsb->rho,&lsb->rho,NULL);
    561:   if ((lsb->rho < 0.0) || (lsb->rho > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "update limiter in the J0 scaling cannot be outside the range of [0, 1]");
    562:   PetscOptionsReal("-mat_lmvm_alpha","(developer) convex ratio in the J0 scaling","",lsb->alpha,&lsb->alpha,NULL);
    563:   if ((lsb->alpha < 0.0) || (lsb->alpha > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio in the J0 scaling cannot be outside the range of [0, 1]");
    564:   PetscOptionsBoundedInt("-mat_lmvm_sigma_hist","(developer) number of past updates to use in the default J0 scalar","",lsb->sigma_hist,&lsb->sigma_hist,NULL,1);
    565:   PetscOptionsEnum("-mat_lmvm_scale_type", "(developer) scaling type applied to J0","MatLMVMSymBrdnScaleType",MatLMVMSymBroydenScaleTypes,(PetscEnum)stype,(PetscEnum*)&stype,&flg);
    566:   if (flg) MatLMVMSymBroydenSetScaleType(B, stype);
    567:   if (lsb->scale_type == MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL) {
    568:     MatSetFromOptions(lsb->D);
    569:     dbase = (Mat_LMVM*)lsb->D->data;
    570:     dctx = (Mat_DiagBrdn*)dbase->ctx;
    571:     dctx->delta_min  = lsb->delta_min;
    572:     dctx->delta_max  = lsb->delta_max;
    573:     dctx->theta      = lsb->theta;
    574:     dctx->rho        = lsb->rho;
    575:     dctx->alpha      = lsb->alpha;
    576:     dctx->beta       = lsb->beta;
    577:     dctx->sigma_hist = lsb->sigma_hist;
    578:   }
    579:   return(0);
    580: }

    582: /*------------------------------------------------------------*/

    584: PetscErrorCode MatCreate_LMVMSymBrdn(Mat B)
    585: {
    586:   Mat_LMVM          *lmvm;
    587:   Mat_SymBrdn       *lsb;
    588:   PetscErrorCode    ierr;

    591:   MatCreate_LMVM(B);
    592:   PetscObjectChangeTypeName((PetscObject)B, MATLMVMSYMBROYDEN);
    593:   MatSetOption(B, MAT_SPD, PETSC_TRUE);
    594:   B->ops->view = MatView_LMVMSymBrdn;
    595:   B->ops->setfromoptions = MatSetFromOptions_LMVMSymBrdn;
    596:   B->ops->setup = MatSetUp_LMVMSymBrdn;
    597:   B->ops->destroy = MatDestroy_LMVMSymBrdn;
    598:   B->ops->solve = MatSolve_LMVMSymBrdn;

    600:   lmvm = (Mat_LMVM*)B->data;
    601:   lmvm->square = PETSC_TRUE;
    602:   lmvm->ops->allocate = MatAllocate_LMVMSymBrdn;
    603:   lmvm->ops->reset = MatReset_LMVMSymBrdn;
    604:   lmvm->ops->update = MatUpdate_LMVMSymBrdn;
    605:   lmvm->ops->mult = MatMult_LMVMSymBrdn;
    606:   lmvm->ops->copy = MatCopy_LMVMSymBrdn;

    608:   PetscNewLog(B, &lsb);
    609:   lmvm->ctx = (void*)lsb;
    610:   lsb->allocated       = PETSC_FALSE;
    611:   lsb->needP           = lsb->needQ = PETSC_TRUE;
    612:   lsb->phi             = 0.125;
    613:   lsb->theta           = 0.125;
    614:   lsb->alpha           = 1.0;
    615:   lsb->rho             = 1.0;
    616:   lsb->beta            = 0.5;
    617:   lsb->sigma           = 1.0;
    618:   lsb->delta           = 1.0;
    619:   lsb->delta_min       = 1e-7;
    620:   lsb->delta_max       = 100.0;
    621:   lsb->sigma_hist      = 1;
    622:   lsb->scale_type      = MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL;
    623:   lsb->watchdog        = 0;
    624:   lsb->max_seq_rejects = lmvm->m/2;

    626:   MatCreate(PetscObjectComm((PetscObject)B), &lsb->D);
    627:   MatSetType(lsb->D, MATLMVMDIAGBROYDEN);
    628:   MatSetOptionsPrefix(lsb->D, "J0_");
    629:   return(0);
    630: }

    632: /*------------------------------------------------------------*/

    634: /*@
    635:    MatLMVMSymBroydenSetDelta - Sets the starting value for the diagonal scaling vector computed
    636:    in the SymBrdn approximations (also works for BFGS and DFP).

    638:    Input Parameters:
    639: +  B - LMVM matrix
    640: -  delta - initial value for diagonal scaling

    642:    Level: intermediate
    643: @*/

    645: PetscErrorCode MatLMVMSymBroydenSetDelta(Mat B, PetscScalar delta)
    646: {
    647:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    648:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    649:   PetscErrorCode    ierr;
    650:   PetscBool         is_bfgs, is_dfp, is_symbrdn, is_symbadbrdn;

    653:   PetscObjectTypeCompare((PetscObject)B, MATLMVMBFGS, &is_bfgs);
    654:   PetscObjectTypeCompare((PetscObject)B, MATLMVMDFP, &is_dfp);
    655:   PetscObjectTypeCompare((PetscObject)B, MATLMVMSYMBROYDEN, &is_symbrdn);
    656:   PetscObjectTypeCompare((PetscObject)B, MATLMVMSYMBADBROYDEN, &is_symbadbrdn);
    657:   if (!is_bfgs && !is_dfp && !is_symbrdn && !is_symbadbrdn) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_INCOMP, "diagonal scaling is only available for DFP, BFGS and SymBrdn matrices");
    658:   lsb->delta = PetscAbsReal(PetscRealPart(delta));
    659:   lsb->delta = PetscMin(lsb->delta, lsb->delta_max);
    660:   lsb->delta = PetscMax(lsb->delta, lsb->delta_min);
    661:   return(0);
    662: }

    664: /*------------------------------------------------------------*/

    666: /*@
    667:     MatLMVMSymBroydenSetScaleType - Sets the scale type for symmetric Broyden-type updates.

    669:     Input Parameters:
    670: +   snes - the iterative context
    671: -   rtype - restart type

    673:     Options Database:
    674: .   -mat_lmvm_scale_type <none,scalar,diagonal> - set the scaling type

    676:     Level: intermediate

    678:     MatLMVMSymBrdnScaleTypes:
    679: +   MAT_LMVM_SYMBROYDEN_SCALE_NONE - initial Hessian is the identity matrix
    680: .   MAT_LMVM_SYMBROYDEN_SCALE_SCALAR - use the Shanno scalar as the initial Hessian
    681: -   MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL - use a diagonalized BFGS update as the initial Hessian

    683: .seealso: MATLMVMSYMBROYDEN, MatCreateLMVMSymBroyden()
    684: @*/
    685: PetscErrorCode MatLMVMSymBroydenSetScaleType(Mat B, MatLMVMSymBroydenScaleType stype)
    686: {
    687:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    688:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;

    692:   lsb->scale_type = stype;
    693:   return(0);
    694: }

    696: /*------------------------------------------------------------*/

    698: /*@
    699:    MatCreateLMVMSymBroyden - Creates a limited-memory Symmetric Broyden-type matrix used
    700:    for approximating Jacobians. L-SymBrdn is a convex combination of L-DFP and
    701:    L-BFGS such that SymBrdn = (1 - phi)*BFGS + phi*DFP. The combination factor
    702:    phi is restricted to the range [0, 1], where the L-SymBrdn matrix is guaranteed
    703:    to be symmetric positive-definite.

    705:    The provided local and global sizes must match the solution and function vectors
    706:    used with MatLMVMUpdate() and MatSolve(). The resulting L-SymBrdn matrix will have
    707:    storage vectors allocated with VecCreateSeq() in serial and VecCreateMPI() in
    708:    parallel. To use the L-SymBrdn matrix with other vector types, the matrix must be
    709:    created using MatCreate() and MatSetType(), followed by MatLMVMAllocate().
    710:    This ensures that the internal storage and work vectors are duplicated from the
    711:    correct type of vector.

    713:    Collective

    715:    Input Parameters:
    716: +  comm - MPI communicator, set to PETSC_COMM_SELF
    717: .  n - number of local rows for storage vectors
    718: -  N - global size of the storage vectors

    720:    Output Parameter:
    721: .  B - the matrix

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

    726:    Options Database Keys:
    727: +   -mat_lmvm_num_vecs - maximum number of correction vectors (i.e.: updates) stored
    728: .   -mat_lmvm_phi - (developer) convex ratio between BFGS and DFP components of the update
    729: .   -mat_lmvm_scale_type - (developer) type of scaling applied to J0 (none, scalar, diagonal)
    730: .   -mat_lmvm_theta - (developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling
    731: .   -mat_lmvm_rho - (developer) update limiter for the J0 scaling
    732: .   -mat_lmvm_alpha - (developer) coefficient factor for the quadratic subproblem in J0 scaling
    733: .   -mat_lmvm_beta - (developer) exponential factor for the diagonal J0 scaling
    734: -   -mat_lmvm_sigma_hist - (developer) number of past updates to use in J0 scaling

    736:    Level: intermediate

    738: .seealso: MatCreate(), MATLMVM, MATLMVMSYMBROYDEN, MatCreateLMVMDFP(), MatCreateLMVMSR1(),
    739:           MatCreateLMVMBFGS(), MatCreateLMVMBrdn(), MatCreateLMVMBadBrdn()
    740: @*/
    741: PetscErrorCode MatCreateLMVMSymBroyden(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
    742: {
    743:   PetscErrorCode    ierr;

    746:   MatCreate(comm, B);
    747:   MatSetSizes(*B, n, n, N, N);
    748:   MatSetType(*B, MATLMVMSYMBROYDEN);
    749:   MatSetUp(*B);
    750:   return(0);
    751: }

    753: /*------------------------------------------------------------*/

    755: PetscErrorCode MatSymBrdnApplyJ0Fwd(Mat B, Vec X, Vec Z)
    756: {
    757:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    758:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    759:   PetscErrorCode    ierr;

    762:   if (lmvm->J0 || lmvm->user_pc || lmvm->user_ksp || lmvm->user_scale) {
    763:     lsb->scale_type = MAT_LMVM_SYMBROYDEN_SCALE_USER;
    764:     MatLMVMApplyJ0Fwd(B, X, Z);
    765:   } else {
    766:     switch (lsb->scale_type) {
    767:     case MAT_LMVM_SYMBROYDEN_SCALE_SCALAR:
    768:       VecCopy(X, Z);
    769:       VecScale(Z, 1.0/lsb->sigma);
    770:       break;
    771:     case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    772:       MatMult(lsb->D, X, Z);
    773:       break;
    774:     case MAT_LMVM_SYMBROYDEN_SCALE_NONE:
    775:     default:
    776:       VecCopy(X, Z);
    777:       break;
    778:     }
    779:   }
    780:   return(0);
    781: }

    783: /*------------------------------------------------------------*/

    785: PetscErrorCode MatSymBrdnApplyJ0Inv(Mat B, Vec F, Vec dX)
    786: {
    787:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    788:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    789:   PetscErrorCode    ierr;

    792:   if (lmvm->J0 || lmvm->user_pc || lmvm->user_ksp || lmvm->user_scale) {
    793:     lsb->scale_type = MAT_LMVM_SYMBROYDEN_SCALE_USER;
    794:     MatLMVMApplyJ0Inv(B, F, dX);
    795:   } else {
    796:     switch (lsb->scale_type) {
    797:     case MAT_LMVM_SYMBROYDEN_SCALE_SCALAR:
    798:       VecCopy(F, dX);
    799:       VecScale(dX, lsb->sigma);
    800:       break;
    801:     case MAT_LMVM_SYMBROYDEN_SCALE_DIAGONAL:
    802:       MatSolve(lsb->D, F, dX);
    803:       break;
    804:     case MAT_LMVM_SYMBROYDEN_SCALE_NONE:
    805:     default:
    806:       VecCopy(F, dX);
    807:       break;
    808:     }
    809:   }
    810:   return(0);
    811: }

    813: /*------------------------------------------------------------*/

    815: PetscErrorCode MatSymBrdnComputeJ0Scalar(Mat B)
    816: {
    817:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    818:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    819:   PetscInt          i, start;
    820:   PetscReal         a, b, c, sig1, sig2, signew;

    823:   if (lsb->sigma_hist == 0) {
    824:     signew = 1.0;
    825:   } else {
    826:     start = PetscMax(0, lmvm->k-lsb->sigma_hist+1);
    827:     signew = 0.0;
    828:     if (lsb->alpha == 1.0) {
    829:       for (i = start; i <= lmvm->k; ++i) {
    830:         signew += lsb->yts[i]/lsb->yty[i];
    831:       }
    832:     } else if (lsb->alpha == 0.5) {
    833:       for (i = start; i <= lmvm->k; ++i) {
    834:         signew += lsb->sts[i]/lsb->yty[i];
    835:       }
    836:       signew = PetscSqrtReal(signew);
    837:     } else if (lsb->alpha == 0.0) {
    838:       for (i = start; i <= lmvm->k; ++i) {
    839:         signew += lsb->sts[i]/lsb->yts[i];
    840:       }
    841:     } else {
    842:       /* compute coefficients of the quadratic */
    843:       a = b = c = 0.0;
    844:       for (i = start; i <= lmvm->k; ++i) {
    845:         a += lsb->yty[i];
    846:         b += lsb->yts[i];
    847:         c += lsb->sts[i];
    848:       }
    849:       a *= lsb->alpha;
    850:       b *= -(2.0*lsb->alpha - 1.0);
    851:       c *= lsb->alpha - 1.0;
    852:       /* use quadratic formula to find roots */
    853:       sig1 = (-b + PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
    854:       sig2 = (-b - PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
    855:       /* accept the positive root as the scalar */
    856:       if (sig1 > 0.0) {
    857:         signew = sig1;
    858:       } else if (sig2 > 0.0) {
    859:         signew = sig2;
    860:       } else {
    861:         SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
    862:       }
    863:     }
    864:   }
    865:   lsb->sigma = lsb->rho*signew + (1.0 - lsb->rho)*lsb->sigma;
    866:   return(0);
    867: }