Actual source code: symbrdn.c
petsc-3.10.5 2019-03-28
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: /*------------------------------------------------------------*/

      6: /*
      7:   The solution method below is the matrix-free implementation of
      8:   Equation 8.6a in Dennis and More "Quasi-Newton Methods, Motivation
      9:   and Theory" (https://epubs.siam.org/doi/abs/10.1137/1019005).
     10:
     11:   Q[i] = (B_i)^{-1}*S[i] terms are computed ahead of time whenever
     12:   the matrix is updated with a new (S[i], Y[i]) pair. This allows
     13:   repeated calls of MatSolve without incurring redundant computation.
     14:
     15:   dX <- J0^{-1} * F
     16:
     17:   for i=0,1,2,...,k
     18:     # Q[i] = (B_i)^T{-1} Y[i]
     19:
     20:     rho = 1.0 / (Y[i]^T S[i])
     21:     alpha = rho * (S[i]^T F)
     22:     zeta = 1.0 / (Y[i]^T Q[i])
     23:     gamma = zeta * (Y[i]^T dX)
     24:
     25:     dX <- dX - (gamma * Q[i]) + (alpha * Y[i])
     26:     W <- (rho * S[i]) - (zeta * Q[i])
     27:     dX <- dX + (psi[i] * (Y[i]^T Q[i]) * (W^T F) * W)
     28:   end
     29: */
     30: static PetscErrorCode MatSolve_LMVMSymBrdn(Mat B, Vec F, Vec dX)
     31: {
     32:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
     33:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
     34:   PetscErrorCode    ierr;
     35:   PetscInt          i, j;
     36:   PetscReal         numer;
     37:   PetscScalar       sjtpi, yjtsi, wtsi, yjtqi, sjtyi, wtyi, ytx, stf, wtf, stp, ytq;
     38:
     40:   /* Efficient shortcuts for pure BFGS and pure DFP configurations */
     41:   if (lsb->phi == 0.0) {
     42:     MatSolve_LMVMBFGS(B, F, dX);
     43:     return(0);
     44:   }
     45:   if (lsb->phi == 1.0) {
     46:     MatSolve_LMVMDFP(B, F, dX);
     47:     return(0);
     48:   }
     49:
     50:   VecCheckSameSize(F, 2, dX, 3);
     51:   VecCheckMatCompatible(B, dX, 3, F, 2);
     52:
     53:   if (lsb->needP) {
     54:     /* Start the loop for (P[k] = (B_k) * S[k]) */
     55:     for (i = 0; i <= lmvm->k; ++i) {
     56:       MatSymBrdnApplyJ0Fwd(B, lmvm->S[i], lsb->P[i]);
     57:       for (j = 0; j <= i-1; ++j) {
     58:         /* Compute the necessary dot products */
     59:         VecDotBegin(lmvm->S[j], lsb->P[i], &sjtpi);
     60:         VecDotBegin(lmvm->Y[j], lmvm->S[i], &yjtsi);
     61:         VecDotEnd(lmvm->S[j], lsb->P[i], &sjtpi);
     62:         VecDotEnd(lmvm->Y[j], lmvm->S[i], &yjtsi);
     63:         /* Compute the pure BFGS component of the forward product */
     64:         VecAXPBYPCZ(lsb->P[i], -PetscRealPart(sjtpi)/lsb->stp[j], PetscRealPart(yjtsi)/lsb->yts[j], 1.0, lsb->P[j], lmvm->Y[j]);
     65:         /* Tack on the convexly scaled extras to the forward product */
     66:         if (lsb->phi > 0.0) {
     67:           VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->stp[j], 0.0, lmvm->Y[j], lsb->P[j]);
     68:           VecDot(lsb->work, lmvm->S[i], &wtsi);
     69:           VecAXPY(lsb->P[i], lsb->phi*lsb->stp[j]*PetscRealPart(wtsi), lsb->work);
     70:         }
     71:       }
     72:       VecDot(lmvm->S[i], lsb->P[i], &stp);
     73:       lsb->stp[i] = PetscRealPart(stp);
     74:     }
     75:     lsb->needP = PETSC_FALSE;
     76:   }
     77:   if (lsb->needQ) {
     78:     /* Start the loop for (Q[k] = (B_k)^{-1} * Y[k]) */
     79:     for (i = 0; i <= lmvm->k; ++i) {
     80:       MatSymBrdnApplyJ0Inv(B, lmvm->Y[i], lsb->Q[i]);
     81:       for (j = 0; j <= i-1; ++j) {
     82:         /* Compute the necessary dot products */
     83:         VecDotBegin(lmvm->Y[j], lsb->Q[i], &yjtqi);
     84:         VecDotBegin(lmvm->S[j], lmvm->Y[i], &sjtyi);
     85:         VecDotEnd(lmvm->Y[j], lsb->Q[i], &yjtqi);
     86:         VecDotEnd(lmvm->S[j], lmvm->Y[i], &sjtyi);
     87:         /* Compute the pure DFP component of the inverse application*/
     88:         VecAXPBYPCZ(lsb->Q[i], -PetscRealPart(yjtqi)/lsb->ytq[j], PetscRealPart(sjtyi)/lsb->yts[j], 1.0, lsb->Q[j], lmvm->S[j]);
     89:         /* Tack on the convexly scaled extras to the inverse application*/
     90:         if (lsb->psi[j] > 0.0) {
     91:           VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->ytq[j], 0.0, lmvm->S[j], lsb->Q[j]);
     92:           VecDot(lsb->work, lmvm->Y[i], &wtyi);
     93:           VecAXPY(lsb->Q[i], lsb->psi[j]*lsb->ytq[j]*PetscRealPart(wtyi), lsb->work);
     94:         }
     95:       }
     96:       VecDot(lmvm->Y[i], lsb->Q[i], &ytq);
     97:       lsb->ytq[i] = PetscRealPart(ytq);
     98:       if (lsb->phi == 1.0) {
     99:         lsb->psi[i] = 0.0;
    100:       } else if (lsb->phi == 0.0) {
    101:         lsb->psi[i] = 1.0;
    102:       } else {
    103:         numer = (1.0 - lsb->phi)*lsb->yts[i]*lsb->yts[i];
    104:         lsb->psi[i] = numer / (numer + (lsb->phi*lsb->ytq[i]*lsb->stp[i]));
    105:       }
    106:     }
    107:     lsb->needQ = PETSC_FALSE;
    108:   }
    109:
    110:   /* Start the outer iterations for ((B^{-1}) * dX) */
    111:   MatSymBrdnApplyJ0Inv(B, F, dX);
    112:   for (i = 0; i <= lmvm->k; ++i) {
    113:     /* Compute the necessary dot products -- store yTs and yTp for inner iterations later */
    114:     VecDotBegin(lmvm->Y[i], dX, &ytx);
    115:     VecDotBegin(lmvm->S[i], F, &stf);
    116:     VecDotEnd(lmvm->Y[i], dX, &ytx);
    117:     VecDotEnd(lmvm->S[i], F, &stf);
    118:     /* Compute the pure DFP component */
    119:     VecAXPBYPCZ(dX, -PetscRealPart(ytx)/lsb->ytq[i], PetscRealPart(stf)/lsb->yts[i], 1.0, lsb->Q[i], lmvm->S[i]);
    120:     /* Tack on the convexly scaled extras */
    121:     VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[i], -1.0/lsb->ytq[i], 0.0, lmvm->S[i], lsb->Q[i]);
    122:     VecDot(lsb->work, F, &wtf);
    123:     VecAXPY(dX, lsb->psi[i]*lsb->ytq[i]*PetscRealPart(wtf), lsb->work);
    124:   }

    126:   return(0);
    127: }

    129: /*------------------------------------------------------------*/

    131: /*
    132:   The forward-product below is the matrix-free implementation of
    133:   Equation 16 in Dennis and Wolkowicz "Sizing and Least Change Secant
    134:   Methods" (http://www.caam.rice.edu/caam/trs/90/TR90-05.pdf).
    135:
    136:   P[i] = (B_i)*S[i] terms are computed ahead of time whenever
    137:   the matrix is updated with a new (S[i], Y[i]) pair. This allows
    138:   repeated calls of MatMult inside KSP solvers without unnecessarily
    139:   recomputing P[i] terms in expensive nested-loops.
    140:
    141:   Z <- J0 * X
    142:
    143:   for i=0,1,2,...,k
    144:     # P[i] = (B_k) * S[i]
    145:
    146:     rho = 1.0 / (Y[i]^T S[i])
    147:     alpha = rho * (Y[i]^T F)
    148:     zeta = 1.0 / (S[i]^T P[i])
    149:     gamma = zeta * (S[i]^T dX)
    150:
    151:     dX <- dX - (gamma * P[i]) + (alpha * S[i])
    152:     W <- (rho * Y[i]) - (zeta * P[i])
    153:     dX <- dX + (phi * (S[i]^T P[i]) * (W^T F) * W)
    154:   end
    155: */
    156: static PetscErrorCode MatMult_LMVMSymBrdn(Mat B, Vec X, Vec Z)
    157: {
    158:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    159:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    160:   PetscErrorCode    ierr;
    161:   PetscInt          i, j;
    162:   PetscScalar         sjtpi, yjtsi, wtsi, stz, ytx, wtx, stp;
    163:
    164:
    166:   /* Efficient shortcuts for pure BFGS and pure DFP configurations */
    167:   if (lsb->phi == 0.0) {
    168:     MatMult_LMVMBFGS(B, X, Z);
    169:     return(0);
    170:   }
    171:   if (lsb->phi == 1.0) {
    172:     MatMult_LMVMDFP(B, X, Z);
    173:     return(0);
    174:   }
    175:
    176:   VecCheckSameSize(X, 2, Z, 3);
    177:   VecCheckMatCompatible(B, X, 2, Z, 3);
    178:
    179:   if (lsb->needP) {
    180:     /* Start the loop for (P[k] = (B_k) * S[k]) */
    181:     for (i = 0; i <= lmvm->k; ++i) {
    182:       MatSymBrdnApplyJ0Fwd(B, lmvm->S[i], lsb->P[i]);
    183:       for (j = 0; j <= i-1; ++j) {
    184:         /* Compute the necessary dot products */
    185:         VecDotBegin(lmvm->S[j], lsb->P[i], &sjtpi);
    186:         VecDotBegin(lmvm->Y[j], lmvm->S[i], &yjtsi);
    187:         VecDotEnd(lmvm->S[j], lsb->P[i], &sjtpi);
    188:         VecDotEnd(lmvm->Y[j], lmvm->S[i], &yjtsi);
    189:         /* Compute the pure BFGS component of the forward product */
    190:         VecAXPBYPCZ(lsb->P[i], -PetscRealPart(sjtpi)/lsb->stp[j], PetscRealPart(yjtsi)/lsb->yts[j], 1.0, lsb->P[j], lmvm->Y[j]);
    191:         /* Tack on the convexly scaled extras to the forward product */
    192:         if (lsb->phi > 0.0) {
    193:           VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[j], -1.0/lsb->stp[j], 0.0, lmvm->Y[j], lsb->P[j]);
    194:           VecDot(lsb->work, lmvm->S[i], &wtsi);
    195:           VecAXPY(lsb->P[i], lsb->phi*lsb->stp[j]*PetscRealPart(wtsi), lsb->work);
    196:         }
    197:       }
    198:       VecDot(lmvm->S[i], lsb->P[i], &stp);
    199:       lsb->stp[i] = PetscRealPart(stp);
    200:     }
    201:     lsb->needP = PETSC_FALSE;
    202:   }
    203:
    204:   /* Start the outer iterations for (B * X) */
    205:   MatSymBrdnApplyJ0Fwd(B, X, Z);
    206:   for (i = 0; i <= lmvm->k; ++i) {
    207:     /* Compute the necessary dot products */
    208:     VecDotBegin(lmvm->S[i], Z, &stz);
    209:     VecDotBegin(lmvm->Y[i], X, &ytx);
    210:     VecDotEnd(lmvm->S[i], Z, &stz);
    211:     VecDotEnd(lmvm->Y[i], X, &ytx);
    212:     /* Compute the pure BFGS component */
    213:     VecAXPBYPCZ(Z, -PetscRealPart(stz)/lsb->stp[i], PetscRealPart(ytx)/lsb->yts[i], 1.0, lsb->P[i], lmvm->Y[i]);
    214:     /* Tack on the convexly scaled extras */
    215:     VecAXPBYPCZ(lsb->work, 1.0/lsb->yts[i], -1.0/lsb->stp[i], 0.0, lmvm->Y[i], lsb->P[i]);
    216:     VecDot(lsb->work, X, &wtx);
    217:     VecAXPY(Z, lsb->phi*lsb->stp[i]*PetscRealPart(wtx), lsb->work);
    218:   }
    219:   return(0);
    220: }

    222: /*------------------------------------------------------------*/

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

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

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

    316: /*------------------------------------------------------------*/

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

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

    363: /*------------------------------------------------------------*/

    365: static PetscErrorCode MatReset_LMVMSymBrdn(Mat B, PetscBool destructive)
    366: {
    367:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    368:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    369:   Mat_LMVM          *dbase;
    370:   Mat_DiagBrdn      *dctx;
    371:   PetscErrorCode    ierr;
    372:
    374:   lsb->watchdog = 0;
    375:   lsb->needP = lsb->needQ = PETSC_TRUE;
    376:   if (lsb->allocated) {
    377:     if (destructive) {
    378:       VecDestroy(&lsb->work);
    379:       PetscFree6(lsb->stp, lsb->ytq, lsb->yts, lsb->yty, lsb->sts, lsb->psi);
    380:       VecDestroyVecs(lmvm->m, &lsb->P);
    381:       VecDestroyVecs(lmvm->m, &lsb->Q);
    382:       switch (lsb->scale_type) {
    383:       case SYMBRDN_SCALE_DIAG:
    384:         MatLMVMReset(lsb->D, PETSC_TRUE);
    385:         break;
    386:       default:
    387:         break;
    388:       }
    389:       lsb->allocated = PETSC_FALSE;
    390:     } else {
    391:       PetscMemzero(lsb->psi, lmvm->m);
    392:       switch (lsb->scale_type) {
    393:       case SYMBRDN_SCALE_SCALAR:
    394:         lsb->sigma = lsb->delta;
    395:         break;
    396:       case SYMBRDN_SCALE_DIAG:
    397:         MatLMVMReset(lsb->D, PETSC_FALSE);
    398:         dbase = (Mat_LMVM*)lsb->D->data;
    399:         dctx = (Mat_DiagBrdn*)dbase->ctx;
    400:         VecSet(dctx->invD, lsb->delta);
    401:         break;
    402:       case SYMBRDN_SCALE_NONE:
    403:         lsb->sigma = 1.0;
    404:         break;
    405:       default:
    406:         break;
    407:       }
    408:     }
    409:   }
    410:   MatReset_LMVM(B, destructive);
    411:   return(0);
    412: }

    414: /*------------------------------------------------------------*/

    416: static PetscErrorCode MatAllocate_LMVMSymBrdn(Mat B, Vec X, Vec F)
    417: {
    418:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    419:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    420:   PetscErrorCode    ierr;
    421:
    423:   MatAllocate_LMVM(B, X, F);
    424:   if (!lsb->allocated) {
    425:     VecDuplicate(X, &lsb->work);
    426:     PetscMalloc6(lmvm->m, &lsb->stp, lmvm->m, &lsb->ytq, lmvm->m, &lsb->yts, lmvm->m, &lsb->yty, lmvm->m, &lsb->sts, lmvm->m, &lsb->psi);
    427:     PetscMemzero(lsb->psi, lmvm->m);
    428:     if (lmvm->m > 0) {
    429:       VecDuplicateVecs(X, lmvm->m, &lsb->P);
    430:       VecDuplicateVecs(X, lmvm->m, &lsb->Q);
    431:     }
    432:     switch (lsb->scale_type) {
    433:     case SYMBRDN_SCALE_DIAG:
    434:       MatLMVMAllocate(lsb->D, X, F);
    435:       break;
    436:     default:
    437:       break;
    438:     }
    439:     lsb->allocated = PETSC_TRUE;
    440:   }
    441:   return(0);
    442: }

    444: /*------------------------------------------------------------*/

    446: static PetscErrorCode MatDestroy_LMVMSymBrdn(Mat B)
    447: {
    448:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    449:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    450:   PetscErrorCode    ierr;

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

    466: /*------------------------------------------------------------*/

    468: static PetscErrorCode MatSetUp_LMVMSymBrdn(Mat B)
    469: {
    470:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    471:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    472:   PetscErrorCode    ierr;
    473:   PetscInt          n, N;
    474:
    476:   MatSetUp_LMVM(B);
    477:   if (!lsb->allocated) {
    478:     VecDuplicate(lmvm->Xprev, &lsb->work);
    479:     PetscMalloc6(lmvm->m, &lsb->stp, lmvm->m, &lsb->ytq, lmvm->m, &lsb->yts, lmvm->m, &lsb->yty, lmvm->m, &lsb->sts, lmvm->m, &lsb->psi);
    480:     PetscMemzero(lsb->psi, lmvm->m);
    481:     if (lmvm->m > 0) {
    482:       VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lsb->P);
    483:       VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lsb->Q);
    484:     }
    485:     switch (lsb->scale_type) {
    486:     case SYMBRDN_SCALE_DIAG:
    487:       MatGetLocalSize(B, &n, &n);
    488:       MatGetSize(B, &N, &N);
    489:       MatSetSizes(lsb->D, n, n, N, N);
    490:       MatSetUp(lsb->D);
    491:       break;
    492:     default:
    493:       break;
    494:     }
    495:     lsb->allocated = PETSC_TRUE;
    496:   }
    497:   return(0);
    498: }

    500: /*------------------------------------------------------------*/

    502: PetscErrorCode MatView_LMVMSymBrdn(Mat B, PetscViewer pv)
    503: {
    504:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    505:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    506:   PetscErrorCode    ierr;
    507:   PetscBool         isascii;

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

    524: /*------------------------------------------------------------*/

    526: PetscErrorCode MatSetFromOptions_LMVMSymBrdn(PetscOptionItems *PetscOptionsObject, Mat B)
    527: {
    528:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    529:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    530:   Mat_LMVM          *dbase;
    531:   Mat_DiagBrdn      *dctx;
    532:   PetscErrorCode    ierr;

    535:   MatSetFromOptions_LMVM(PetscOptionsObject, B);
    536:   PetscOptionsHead(PetscOptionsObject,"Restricted Broyden method for approximating SPD Jacobian actions (MATLMVMSYMBRDN)");
    537:   PetscOptionsEList("-mat_lmvm_scale_type", "(developer) scaling type applied to J0", "", Scale_Table, SYMBRDN_SCALE_SIZE, Scale_Table[lsb->scale_type], &lsb->scale_type,NULL);
    538:   PetscOptionsReal("-mat_lmvm_phi","(developer) convex ratio between BFGS and DFP components of the update","",lsb->phi,&lsb->phi,NULL);
    539:   PetscOptionsReal("-mat_lmvm_theta","(developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling","",lsb->theta,&lsb->theta,NULL);
    540:   PetscOptionsReal("-mat_lmvm_rho","(developer) update limiter in the J0 scaling","",lsb->rho,&lsb->rho,NULL);
    541:   PetscOptionsReal("-mat_lmvm_alpha","(developer) convex ratio in the J0 scaling","",lsb->alpha,&lsb->alpha,NULL);
    542:   PetscOptionsReal("-mat_lmvm_beta","(developer) exponential factor in the diagonal J0 scaling","",lsb->beta,&lsb->beta,NULL);
    543:   PetscOptionsInt("-mat_lmvm_sigma_hist","(developer) number of past updates to use in the default J0 scalar","",lsb->sigma_hist,&lsb->sigma_hist,NULL);
    544:   PetscOptionsTail();
    545:   if ((lsb->phi < 0.0) || (lsb->phi > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio for the update formula cannot be outside the range of [0, 1]");
    546:   if ((lsb->theta < 0.0) || (lsb->theta > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio for the diagonal J0 scale cannot be outside the range of [0, 1]");
    547:   if ((lsb->alpha < 0.0) || (lsb->alpha > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "convex ratio in the J0 scaling cannot be outside the range of [0, 1]");
    548:   if ((lsb->rho < 0.0) || (lsb->rho > 1.0)) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "update limiter in the J0 scaling cannot be outside the range of [0, 1]");
    549:   if (lsb->sigma_hist < 0) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_OUTOFRANGE, "J0 scaling history length cannot be negative");
    550:   if (lsb->scale_type == SYMBRDN_SCALE_DIAG) {
    551:     MatSetFromOptions(lsb->D);
    552:     dbase = (Mat_LMVM*)lsb->D->data;
    553:     dctx = (Mat_DiagBrdn*)dbase->ctx;
    554:     dctx->delta_min  = lsb->delta_min;
    555:     dctx->delta_max  = lsb->delta_max;
    556:     dctx->theta      = lsb->theta;
    557:     dctx->rho        = lsb->rho;
    558:     dctx->alpha      = lsb->alpha;
    559:     dctx->beta       = lsb->beta;
    560:     dctx->sigma_hist = lsb->sigma_hist;
    561:     dctx->forward    = PETSC_TRUE;
    562:   }
    563:   return(0);
    564: }

    566: /*------------------------------------------------------------*/

    568: PetscErrorCode MatCreate_LMVMSymBrdn(Mat B)
    569: {
    570:   Mat_LMVM          *lmvm;
    571:   Mat_SymBrdn       *lsb;
    572:   PetscErrorCode    ierr;

    575:   MatCreate_LMVM(B);
    576:   PetscObjectChangeTypeName((PetscObject)B, MATLMVMSYMBRDN);
    577:   MatSetOption(B, MAT_SPD, PETSC_TRUE);
    578:   B->ops->view = MatView_LMVMSymBrdn;
    579:   B->ops->setfromoptions = MatSetFromOptions_LMVMSymBrdn;
    580:   B->ops->setup = MatSetUp_LMVMSymBrdn;
    581:   B->ops->destroy = MatDestroy_LMVMSymBrdn;
    582:   B->ops->solve = MatSolve_LMVMSymBrdn;
    583:
    584:   lmvm = (Mat_LMVM*)B->data;
    585:   lmvm->square = PETSC_TRUE;
    586:   lmvm->ops->allocate = MatAllocate_LMVMSymBrdn;
    587:   lmvm->ops->reset = MatReset_LMVMSymBrdn;
    588:   lmvm->ops->update = MatUpdate_LMVMSymBrdn;
    589:   lmvm->ops->mult = MatMult_LMVMSymBrdn;
    590:   lmvm->ops->copy = MatCopy_LMVMSymBrdn;
    591:
    592:   PetscNewLog(B, &lsb);
    593:   lmvm->ctx = (void*)lsb;
    594:   lsb->allocated       = PETSC_FALSE;
    595:   lsb->needP           = lsb->needQ = PETSC_TRUE;
    596:   lsb->phi             = 0.125;
    597:   lsb->theta           = 0.125;
    598:   lsb->alpha           = 1.0;
    599:   lsb->rho             = 1.0;
    600:   lsb->beta            = 0.5;
    601:   lsb->sigma           = 1.0;
    602:   lsb->delta           = 1.0;
    603:   lsb->delta_min       = 1e-7;
    604:   lsb->delta_max       = 100.0;
    605:   lsb->sigma_hist      = 1;
    606:   lsb->scale_type      = SYMBRDN_SCALE_DIAG;
    607:   lsb->watchdog        = 0;
    608:   lsb->max_seq_rejects = lmvm->m/2;
    609:
    610:   MatCreate(PetscObjectComm((PetscObject)B), &lsb->D);
    611:   MatSetType(lsb->D, MATLMVMDIAGBRDN);
    612:   MatSetOptionsPrefix(lsb->D, "J0_");
    613:   return(0);
    614: }

    616: /*------------------------------------------------------------*/

    618: /*@
    619:    MatSymBrdnSetDelta - Sets the starting value for the diagonal scaling vector computed
    620:    in the SymBrdn approximations (also works for BFGS and DFP).
    621:
    622:    Input Parameters:
    623: +  B - LMVM matrix
    624: -  delta - initial value for diagonal scaling

    626:    Level: intermediate
    627: @*/

    629: PetscErrorCode MatSymBrdnSetDelta(Mat B, PetscScalar delta)
    630: {
    631:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    632:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    633:   PetscErrorCode    ierr;
    634:   PetscBool         is_bfgs, is_dfp, is_symbrdn, is_symbadbrdn;
    635:
    637:   PetscObjectTypeCompare((PetscObject)B, MATLMVMBFGS, &is_bfgs);
    638:   PetscObjectTypeCompare((PetscObject)B, MATLMVMDFP, &is_dfp);
    639:   PetscObjectTypeCompare((PetscObject)B, MATLMVMSYMBRDN, &is_symbrdn);
    640:   PetscObjectTypeCompare((PetscObject)B, MATLMVMSYMBADBRDN, &is_symbadbrdn);
    641:   if (!is_bfgs && !is_dfp && !is_symbrdn && !is_symbadbrdn) SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_INCOMP, "diagonal scaling is only available for DFP, BFGS and SymBrdn matrices");
    642:   lsb->delta = PetscAbsReal(PetscRealPart(delta));
    643:   lsb->delta = PetscMin(lsb->delta, lsb->delta_max);
    644:   lsb->delta = PetscMax(lsb->delta, lsb->delta_min);
    645:   return(0);
    646: }

    648: /*------------------------------------------------------------*/

    650: /*@
    651:    MatCreateLMVMSymBrdn - Creates a limited-memory Symmetric Broyden-type matrix used
    652:    for approximating Jacobians. L-SymBrdn is a convex combination of L-DFP and
    653:    L-BFGS such that SymBrdn = (1 - phi)*BFGS + phi*DFP. The combination factor
    654:    phi is restricted to the range [0, 1], where the L-SymBrdn matrix is guaranteed
    655:    to be symmetric positive-definite.
    656:
    657:    The provided local and global sizes must match the solution and function vectors
    658:    used with MatLMVMUpdate() and MatSolve(). The resulting L-SymBrdn matrix will have
    659:    storage vectors allocated with VecCreateSeq() in serial and VecCreateMPI() in
    660:    parallel. To use the L-SymBrdn matrix with other vector types, the matrix must be
    661:    created using MatCreate() and MatSetType(), followed by MatLMVMAllocate().
    662:    This ensures that the internal storage and work vectors are duplicated from the
    663:    correct type of vector.

    665:    Collective on MPI_Comm

    667:    Input Parameters:
    668: +  comm - MPI communicator, set to PETSC_COMM_SELF
    669: .  n - number of local rows for storage vectors
    670: -  N - global size of the storage vectors

    672:    Output Parameter:
    673: .  B - the matrix

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

    678:    Options Database Keys:
    679: .   -mat_lmvm_num_vecs - maximum number of correction vectors (i.e.: updates) stored
    680: .   -mat_lmvm_phi - (developer) convex ratio between BFGS and DFP components of the update
    681: .   -mat_lmvm_scale_type - (developer) type of scaling applied to J0 (none, scalar, diagonal)
    682: .   -mat_lmvm_theta - (developer) convex ratio between BFGS and DFP components of the diagonal J0 scaling
    683: .   -mat_lmvm_rho - (developer) update limiter for the J0 scaling
    684: .   -mat_lmvm_alpha - (developer) coefficient factor for the quadratic subproblem in J0 scaling
    685: .   -mat_lmvm_beta - (developer) exponential factor for the diagonal J0 scaling
    686: .   -mat_lmvm_sigma_hist - (developer) number of past updates to use in J0 scaling

    688:    Level: intermediate

    690: .seealso: MatCreate(), MATLMVM, MATLMVMSYMBRDN, MatCreateLMVMDFP(), MatCreateLMVMSR1(),
    691:           MatCreateLMVMBFGS(), MatCreateLMVMBrdn(), MatCreateLMVMBadBrdn()
    692: @*/
    693: PetscErrorCode MatCreateLMVMSymBrdn(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
    694: {
    695:   PetscErrorCode    ierr;
    696:
    698:   MatCreate(comm, B);
    699:   MatSetSizes(*B, n, n, N, N);
    700:   MatSetType(*B, MATLMVMSYMBRDN);
    701:   MatSetUp(*B);
    702:   return(0);
    703: }

    705: /*------------------------------------------------------------*/

    707: PetscErrorCode MatSymBrdnApplyJ0Fwd(Mat B, Vec X, Vec Z)
    708: {
    709:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    710:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    711:   PetscErrorCode    ierr;
    712:
    714:   if (lmvm->J0 || lmvm->user_pc || lmvm->user_ksp || lmvm->user_scale) {
    715:     MatLMVMApplyJ0Fwd(B, X, Z);
    716:   } else {
    717:     switch (lsb->scale_type) {
    718:     case SYMBRDN_SCALE_SCALAR:
    719:       VecCopy(X, Z);
    720:       VecScale(Z, 1.0/lsb->sigma);
    721:       break;
    722:     case SYMBRDN_SCALE_DIAG:
    723:       MatMult(lsb->D, X, Z);
    724:       break;
    725:     case SYMBRDN_SCALE_NONE:
    726:     default:
    727:       VecCopy(X, Z);
    728:       break;
    729:     }
    730:   }
    731:   return(0);
    732: }

    734: /*------------------------------------------------------------*/

    736: PetscErrorCode MatSymBrdnApplyJ0Inv(Mat B, Vec F, Vec dX)
    737: {
    738:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    739:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    740:   PetscErrorCode    ierr;
    741:
    743:   if (lmvm->J0 || lmvm->user_pc || lmvm->user_ksp || lmvm->user_scale) {
    744:     MatLMVMApplyJ0Inv(B, F, dX);
    745:   } else {
    746:     switch (lsb->scale_type) {
    747:     case SYMBRDN_SCALE_SCALAR:
    748:       VecCopy(F, dX);
    749:       VecScale(dX, lsb->sigma);
    750:       break;
    751:     case SYMBRDN_SCALE_DIAG:
    752:       MatSolve(lsb->D, F, dX);
    753:       break;
    754:     case SYMBRDN_SCALE_NONE:
    755:     default:
    756:       VecCopy(F, dX);
    757:       break;
    758:     }
    759:   }
    760:   return(0);
    761: }

    763: /*------------------------------------------------------------*/

    765: PetscErrorCode MatSymBrdnComputeJ0Scalar(Mat B)
    766: {
    767:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    768:   Mat_SymBrdn       *lsb = (Mat_SymBrdn*)lmvm->ctx;
    769:   PetscInt          i, start;
    770:   PetscReal         a, b, c, sig1, sig2, signew;
    771:
    773:   if (lsb->sigma_hist == 0) {
    774:     signew = 1.0;
    775:   } else {
    776:     start = PetscMax(0, lmvm->k-lsb->sigma_hist+1);
    777:     signew = 0.0;
    778:     if (lsb->alpha == 1.0) {
    779:       for (i = start; i <= lmvm->k; ++i) {
    780:         signew += lsb->yts[i]/lsb->yty[i];
    781:       }
    782:     } else if (lsb->alpha == 0.5) {
    783:       for (i = start; i <= lmvm->k; ++i) {
    784:         signew += lsb->sts[i]/lsb->yty[i];
    785:       }
    786:       signew = PetscSqrtReal(signew);
    787:     } else if (lsb->alpha == 0.0) {
    788:       for (i = start; i <= lmvm->k; ++i) {
    789:         signew += lsb->sts[i]/lsb->yts[i];
    790:       }
    791:     } else {
    792:       /* compute coefficients of the quadratic */
    793:       a = b = c = 0.0;
    794:       for (i = start; i <= lmvm->k; ++i) {
    795:         a += lsb->yty[i];
    796:         b += lsb->yts[i];
    797:         c += lsb->sts[i];
    798:       }
    799:       a *= lsb->alpha;
    800:       b *= -(2.0*lsb->alpha - 1.0);
    801:       c *= lsb->alpha - 1.0;
    802:       /* use quadratic formula to find roots */
    803:       sig1 = (-b + PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
    804:       sig2 = (-b - PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
    805:       /* accept the positive root as the scalar */
    806:       if (sig1 > 0.0) {
    807:         signew = sig1;
    808:       } else if (sig2 > 0.0) {
    809:         signew = sig2;
    810:       } else {
    811:         SETERRQ(PetscObjectComm((PetscObject)B), PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
    812:       }
    813:     }
    814:   }
    815:   lsb->sigma = lsb->rho*signew + (1.0 - lsb->rho)*lsb->sigma;
    816:   return(0);
    817: }