Actual source code: brdn.c
petsc-3.10.5 2019-03-28
Report Typos and Errors

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

      3: /*
      4:   Limited-memory "good" Broyden's method for approximating the inverse of
      5:   a Jacobian.
      6: */

      8: typedef struct {
      9:   Vec *P, *Q;
     10:   PetscBool allocated, needP, needQ;
     11:   PetscReal *sts, *stq;
     12: } Mat_Brdn;

     14: /*------------------------------------------------------------*/

     16: /*
     17:   The solution method is the matrix-free implementation of the inverse Hessian
     18:   representation in page 312 of Griewank "Broyden Updating, The Good and The Bad!"
     19:   (http://www.emis.ams.org/journals/DMJDMV/vol-ismp/45_griewank-andreas-broyden.pdf).
     20:
     21:   Q[i] = (B_i)^{-1}*S[i] terms are computed ahead of time whenever
     22:   the matrix is updated with a new (S[i], Y[i]) pair. This allows
     23:   repeated calls of MatSolve without incurring redundant computation.
     24:
     25:   dX <- J0^{-1} * F
     26:
     27:   for i=0,1,2,...,k
     28:     # Q[i] = (B_i)^{-1} * Y[i]
     29:     tau = (S[i]^T dX) / (S[i]^T Q[i])
     30:     dX <- dX + (tau * (S[i] - Q[i]))
     31:   end
     32:  */

     34: static PetscErrorCode MatSolve_LMVMBrdn(Mat B, Vec F, Vec dX)
     35: {
     36:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
     37:   Mat_Brdn          *lbrdn = (Mat_Brdn*)lmvm->ctx;
     38:   PetscErrorCode    ierr;
     39:   PetscInt          i, j;
     40:   PetscScalar       sjtqi, stx, stq;
     41:
     43:   VecCheckSameSize(F, 2, dX, 3);
     44:   VecCheckMatCompatible(B, dX, 3, F, 2);
     45:
     46:   if (lbrdn->needQ) {
     47:     /* Pre-compute (Q[i] = (B_i)^{-1} * Y[i]) */
     48:     for (i = 0; i <= lmvm->k; ++i) {
     49:       MatLMVMApplyJ0Inv(B, lmvm->Y[i], lbrdn->Q[i]);
     50:       for (j = 0; j <= i-1; ++j) {
     51:         VecDot(lmvm->S[j], lbrdn->Q[i], &sjtqi);
     52:         VecAXPBYPCZ(lbrdn->Q[i], PetscRealPart(sjtqi)/lbrdn->stq[j], -PetscRealPart(sjtqi)/lbrdn->stq[j], 1.0, lmvm->S[j], lbrdn->Q[j]);
     53:       }
     54:       VecDot(lmvm->S[i], lbrdn->Q[i], &stq);
     55:       lbrdn->stq[i] = PetscRealPart(stq);
     56:     }
     57:     lbrdn->needQ = PETSC_FALSE;
     58:   }
     59:
     60:   MatLMVMApplyJ0Inv(B, F, dX);
     61:   for (i = 0; i <= lmvm->k-1; ++i) {
     62:     VecDot(lmvm->S[i], dX, &stx);
     63:     VecAXPBYPCZ(dX, PetscRealPart(stx)/lbrdn->stq[i], -PetscRealPart(stx)/lbrdn->stq[i], 1.0, lmvm->S[i], lbrdn->Q[i]);
     64:   }
     65:   return(0);
     66: }

     68: /*------------------------------------------------------------*/

     70: /*
     71:   The forward product is the matrix-free implementation of Equation 2 in
     72:   page 302 of Griewank "Broyden Updating, The Good and The Bad!"
     73:   (http://www.emis.ams.org/journals/DMJDMV/vol-ismp/45_griewank-andreas-broyden.pdf).
     74:
     75:   P[i] = (B_i)*S[i] terms are computed ahead of time whenever
     76:   the matrix is updated with a new (S[i], Y[i]) pair. This allows
     77:   repeated calls of MatMult inside KSP solvers without unnecessarily
     78:   recomputing P[i] terms in expensive nested-loops.
     79:
     80:   Z <- J0 * X
     81:
     82:   for i=0,1,2,...,k
     83:     # P[i] = B_i * S[i]
     84:     tau = (S[i]^T X) / (S[i]^T S[i])
     85:     dX <- dX + (tau * (Y[i] - P[i]))
     86:   end
     87:  */

     89: static PetscErrorCode MatMult_LMVMBrdn(Mat B, Vec X, Vec Z)
     90: {
     91:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
     92:   Mat_Brdn          *lbrdn = (Mat_Brdn*)lmvm->ctx;
     93:   PetscErrorCode    ierr;
     94:   PetscInt          i, j;
     95:   PetscScalar       sjtsi, stx;
     96:
     98:   VecCheckSameSize(X, 2, Z, 3);
     99:   VecCheckMatCompatible(B, X, 2, Z, 3);
    100:
    101:   if (lbrdn->needP) {
    102:     /* Pre-compute (P[i] = (B_i) * S[i]) */
    103:     for (i = 0; i <= lmvm->k; ++i) {
    104:       MatLMVMApplyJ0Fwd(B, lmvm->S[i], lbrdn->P[i]);
    105:       for (j = 0; j <= i-1; ++j) {
    106:         VecDot(lmvm->S[j], lmvm->S[i], &sjtsi);
    107:         VecAXPBYPCZ(lbrdn->P[i], PetscRealPart(sjtsi)/lbrdn->sts[j], -PetscRealPart(sjtsi)/lbrdn->sts[j], 1.0, lmvm->Y[j], lbrdn->P[j]);
    108:       }
    109:     }
    110:     lbrdn->needP = PETSC_FALSE;
    111:   }
    112:
    113:   MatLMVMApplyJ0Fwd(B, X, Z);
    114:   for (i = 0; i <= lmvm->k-1; ++i) {
    115:     VecDot(lmvm->S[i], X, &stx);
    116:     VecAXPBYPCZ(Z, PetscRealPart(stx)/lbrdn->sts[i], -PetscRealPart(stx)/lbrdn->sts[i], 1.0, lmvm->Y[i], lbrdn->P[i]);
    117:   }
    118:   return(0);
    119: }

    121: /*------------------------------------------------------------*/

    123: static PetscErrorCode MatUpdate_LMVMBrdn(Mat B, Vec X, Vec F)
    124: {
    125:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    126:   Mat_Brdn          *lbrdn = (Mat_Brdn*)lmvm->ctx;
    127:   PetscErrorCode    ierr;
    128:   PetscInt          old_k, i;
    129:   PetscScalar       sts;

    132:   if (!lmvm->m) return(0);
    133:   if (lmvm->prev_set) {
    134:     /* Compute the new (S = X - Xprev) and (Y = F - Fprev) vectors */
    135:     VecAYPX(lmvm->Xprev, -1.0, X);
    136:     VecAYPX(lmvm->Fprev, -1.0, F);
    137:     /* Accept the update */
    138:     lbrdn->needP = lbrdn->needQ = PETSC_TRUE;
    139:     old_k = lmvm->k;
    140:     MatUpdateKernel_LMVM(B, lmvm->Xprev, lmvm->Fprev);
    141:     /* If we hit the memory limit, shift the sts array */
    142:     if (old_k == lmvm->k) {
    143:       for (i = 0; i <= lmvm->k-1; ++i) {
    144:         lbrdn->sts[i] = lbrdn->sts[i+1];
    145:       }
    146:     }
    147:     VecDot(lmvm->S[lmvm->k], lmvm->S[lmvm->k], &sts);
    148:     lbrdn->sts[lmvm->k] = PetscRealPart(sts);
    149:   }
    150:   /* Save the solution and function to be used in the next update */
    151:   VecCopy(X, lmvm->Xprev);
    152:   VecCopy(F, lmvm->Fprev);
    153:   lmvm->prev_set = PETSC_TRUE;
    154:   return(0);
    155: }

    157: /*------------------------------------------------------------*/

    159: static PetscErrorCode MatCopy_LMVMBrdn(Mat B, Mat M, MatStructure str)
    160: {
    161:   Mat_LMVM          *bdata = (Mat_LMVM*)B->data;
    162:   Mat_Brdn          *bctx = (Mat_Brdn*)bdata->ctx;
    163:   Mat_LMVM          *mdata = (Mat_LMVM*)M->data;
    164:   Mat_Brdn          *mctx = (Mat_Brdn*)mdata->ctx;
    165:   PetscErrorCode    ierr;
    166:   PetscInt          i;

    169:   mctx->needP = bctx->needP;
    170:   mctx->needQ = bctx->needQ;
    171:   for (i=0; i<=bdata->k; ++i) {
    172:     mctx->sts[i] = bctx->sts[i];
    173:     mctx->stq[i] = bctx->stq[i];
    174:     VecCopy(bctx->P[i], mctx->P[i]);
    175:     VecCopy(bctx->Q[i], mctx->Q[i]);
    176:   }
    177:   return(0);
    178: }

    180: /*------------------------------------------------------------*/

    182: static PetscErrorCode MatReset_LMVMBrdn(Mat B, PetscBool destructive)
    183: {
    184:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    185:   Mat_Brdn          *lbrdn = (Mat_Brdn*)lmvm->ctx;
    186:   PetscErrorCode    ierr;
    187:
    189:   lbrdn->needP = lbrdn->needQ = PETSC_TRUE;
    190:   if (destructive && lbrdn->allocated) {
    191:     PetscFree2(lbrdn->sts, lbrdn->stq);
    192:     VecDestroyVecs(lmvm->m, &lbrdn->P);
    193:     VecDestroyVecs(lmvm->m, &lbrdn->Q);
    194:     lbrdn->allocated = PETSC_FALSE;
    195:   }
    196:   MatReset_LMVM(B, destructive);
    197:   return(0);
    198: }

    200: /*------------------------------------------------------------*/

    202: static PetscErrorCode MatAllocate_LMVMBrdn(Mat B, Vec X, Vec F)
    203: {
    204:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    205:   Mat_Brdn          *lbrdn = (Mat_Brdn*)lmvm->ctx;
    206:   PetscErrorCode    ierr;
    207:
    209:   MatAllocate_LMVM(B, X, F);
    210:   if (!lbrdn->allocated) {
    211:     PetscMalloc2(lmvm->m, &lbrdn->sts, lmvm->m, &lbrdn->stq);
    212:     if (lmvm->m > 0) {
    213:       VecDuplicateVecs(X, lmvm->m, &lbrdn->P);
    214:       VecDuplicateVecs(X, lmvm->m, &lbrdn->Q);
    215:     }
    216:     lbrdn->allocated = PETSC_TRUE;
    217:   }
    218:   return(0);
    219: }

    221: /*------------------------------------------------------------*/

    223: static PetscErrorCode MatDestroy_LMVMBrdn(Mat B)
    224: {
    225:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    226:   Mat_Brdn          *lbrdn = (Mat_Brdn*)lmvm->ctx;
    227:   PetscErrorCode    ierr;

    230:   if (lbrdn->allocated) {
    231:     PetscFree2(lbrdn->sts, lbrdn->stq);
    232:     VecDestroyVecs(lmvm->m, &lbrdn->P);
    233:     VecDestroyVecs(lmvm->m, &lbrdn->Q);
    234:     lbrdn->allocated = PETSC_FALSE;
    235:   }
    236:   PetscFree(lmvm->ctx);
    237:   MatDestroy_LMVM(B);
    238:   return(0);
    239: }

    241: /*------------------------------------------------------------*/

    243: static PetscErrorCode MatSetUp_LMVMBrdn(Mat B)
    244: {
    245:   Mat_LMVM          *lmvm = (Mat_LMVM*)B->data;
    246:   Mat_Brdn          *lbrdn = (Mat_Brdn*)lmvm->ctx;
    247:   PetscErrorCode    ierr;
    248:
    250:   MatSetUp_LMVM(B);
    251:   if (!lbrdn->allocated) {
    252:     PetscMalloc2(lmvm->m, &lbrdn->sts, lmvm->m, &lbrdn->stq);
    253:     if (lmvm->m > 0) {
    254:       VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lbrdn->P);
    255:       VecDuplicateVecs(lmvm->Xprev, lmvm->m, &lbrdn->Q);
    256:     }
    257:     lbrdn->allocated = PETSC_TRUE;
    258:   }
    259:   return(0);
    260: }

    262: /*------------------------------------------------------------*/

    264: PetscErrorCode MatCreate_LMVMBrdn(Mat B)
    265: {
    266:   Mat_LMVM          *lmvm;
    267:   Mat_Brdn          *lbrdn;
    268:   PetscErrorCode    ierr;

    271:   MatCreate_LMVM(B);
    272:   PetscObjectChangeTypeName((PetscObject)B, MATLMVMBRDN);
    273:   B->ops->setup = MatSetUp_LMVMBrdn;
    274:   B->ops->destroy = MatDestroy_LMVMBrdn;
    275:   B->ops->solve = MatSolve_LMVMBrdn;

    277:   lmvm = (Mat_LMVM*)B->data;
    278:   lmvm->square = PETSC_TRUE;
    279:   lmvm->ops->allocate = MatAllocate_LMVMBrdn;
    280:   lmvm->ops->reset = MatReset_LMVMBrdn;
    281:   lmvm->ops->mult = MatMult_LMVMBrdn;
    282:   lmvm->ops->update = MatUpdate_LMVMBrdn;
    283:   lmvm->ops->copy = MatCopy_LMVMBrdn;

    285:   PetscNewLog(B, &lbrdn);
    286:   lmvm->ctx = (void*)lbrdn;
    287:   lbrdn->allocated = PETSC_FALSE;
    288:   lbrdn->needP = lbrdn->needQ = PETSC_TRUE;
    289:   return(0);
    290: }

    292: /*------------------------------------------------------------*/

    294: /*@
    295:    MatCreateLMVMBrdn - Creates a limited-memory "good" Broyden-type approximation
    296:    matrix used for a Jacobian. L-Brdn is not guaranteed to be symmetric or
    297:    positive-definite.
    298:
    299:    The provided local and global sizes must match the solution and function vectors
    300:    used with MatLMVMUpdate() and MatSolve(). The resulting L-Brdn matrix will have
    301:    storage vectors allocated with VecCreateSeq() in serial and VecCreateMPI() in
    302:    parallel. To use the L-Brdn matrix with other vector types, the matrix must be
    303:    created using MatCreate() and MatSetType(), followed by MatLMVMAllocate().
    304:    This ensures that the internal storage and work vectors are duplicated from the
    305:    correct type of vector.

    307:    Collective on MPI_Comm

    309:    Input Parameters:
    310: +  comm - MPI communicator, set to PETSC_COMM_SELF
    311: .  n - number of local rows for storage vectors
    312: -  N - global size of the storage vectors

    314:    Output Parameter:
    315: .  B - the matrix

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

    320:    Options Database Keys:
    321: .   -mat_lmvm_num_vecs - maximum number of correction vectors (i.e.: updates) stored

    323:    Level: intermediate

    325: .seealso: MatCreate(), MATLMVM, MATLMVMBRDN, MatCreateLMVMDFP(), MatCreateLMVMSR1(),
    326:          MatCreateLMVMBFGS(), MatCreateLMVMBadBrdn(), MatCreateLMVMSymBrdn()
    327: @*/
    328: PetscErrorCode MatCreateLMVMBrdn(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
    329: {
    330:   PetscErrorCode    ierr;
    331:
    333:   MatCreate(comm, B);
    334:   MatSetSizes(*B, n, n, N, N);
    335:   MatSetType(*B, MATLMVMBRDN);
    336:   MatSetUp(*B);
    337:   return(0);
    338: }