Date: 2026-03-31T05:46:16+00:00

Actual source code: badbrdn.c

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

      3: // Bad Broyden is dual to Broyden: MatSolve routines are dual to Broyden MatMult routines

      5: static PetscErrorCode MatSolve_LMVMBadBrdn_Recursive(Mat B, Vec F, Vec dX)
      6: {
      7:   PetscFunctionBegin;
      8:   PetscCall(BroydenKernel_Recursive(B, MATLMVM_MODE_DUAL, F, dX));
      9:   PetscFunctionReturn(PETSC_SUCCESS);
     10: }

     12: static PetscErrorCode MatSolve_LMVMBadBrdn_CompactDense(Mat B, Vec F, Vec dX)
     13: {
     14:   PetscFunctionBegin;
     15:   PetscCall(BroydenKernel_CompactDense(B, MATLMVM_MODE_DUAL, F, dX));
     16:   PetscFunctionReturn(PETSC_SUCCESS);
     17: }

     19: PETSC_UNUSED static PetscErrorCode MatSolve_LMVMBadBrdn_Dense(Mat B, Vec F, Vec dX)
     20: {
     21:   PetscFunctionBegin;
     22:   PetscCall(BroydenKernel_Dense(B, MATLMVM_MODE_DUAL, F, dX));
     23:   PetscFunctionReturn(PETSC_SUCCESS);
     24: }

     26: static PetscErrorCode MatSolveHermitianTranspose_LMVMBadBrdn_Recursive(Mat B, Vec F, Vec dX)
     27: {
     28:   PetscFunctionBegin;
     29:   PetscCall(BroydenKernelHermitianTranspose_Recursive(B, MATLMVM_MODE_DUAL, F, dX));
     30:   PetscFunctionReturn(PETSC_SUCCESS);
     31: }

     33: static PetscErrorCode MatSolveHermitianTranspose_LMVMBadBrdn_CompactDense(Mat B, Vec F, Vec dX)
     34: {
     35:   PetscFunctionBegin;
     36:   PetscCall(BroydenKernelHermitianTranspose_CompactDense(B, MATLMVM_MODE_DUAL, F, dX));
     37:   PetscFunctionReturn(PETSC_SUCCESS);
     38: }

     40: PETSC_UNUSED static PetscErrorCode MatSolveHermitianTranspose_LMVMBadBrdn_Dense(Mat B, Vec F, Vec dX)
     41: {
     42:   PetscFunctionBegin;
     43:   PetscCall(BroydenKernelHermitianTranspose_Dense(B, MATLMVM_MODE_DUAL, F, dX));
     44:   PetscFunctionReturn(PETSC_SUCCESS);
     45: }

     47: /*
     48:    The bad Broyden kernel can be written as

     50:      B_{k+1} x = B_k x + (y_k - B_k s_k)^T * (y_k^T B_k s_k)^{-1} y_k^T B_k x
     51:                = (I + (y_k - B_k s_k)^T (y_k^T B_k s_k)^{-1} y_k^T) (B_k x)
     52:                  \______________________ _________________________/
     53:                                         V
     54:                                recursive rank-1 update

     56:    When the basis (y_k - B_k s_k) and the products (y_k^T B_k s_k) have been computed, the product can be computed by
     57:    application of rank-1 updates from oldest to newest
     58:  */

     60: static PetscErrorCode BadBroydenKernel_Recursive_Inner(Mat B, MatLMVMMode mode, PetscInt oldest, PetscInt next, Vec B0X)
     61: {
     62:   Mat_LMVM        *lmvm        = (Mat_LMVM *)B->data;
     63:   Mat_Brdn        *lbrdn       = (Mat_Brdn *)lmvm->ctx;
     64:   MatLMVMBasisType Y_t         = LMVMModeMap(LMBASIS_Y, mode);
     65:   LMBasis          Y_minus_BkS = lbrdn->basis[LMVMModeMap(BROYDEN_BASIS_Y_MINUS_BKS, mode)];
     66:   LMBasis          Y;
     67:   LMProducts       YtBkS = lbrdn->products[LMVMModeMap(BROYDEN_PRODUCTS_YTBKS, mode)];

     69:   PetscFunctionBegin;
     70:   PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
     71:   // These cannot be parallelized, notice the data dependence
     72:   for (PetscInt i = oldest; i < next; i++) {
     73:     Vec         y_i, yimbisi;
     74:     PetscScalar yitbix;
     75:     PetscScalar yitbisi;

     77:     PetscCall(LMBasisGetVecRead(Y, i, &y_i));
     78:     PetscCall(VecDot(B0X, y_i, &yitbix));
     79:     PetscCall(LMBasisRestoreVecRead(Y, i, &y_i));
     80:     PetscCall(LMProductsGetDiagonalValue(YtBkS, i, &yitbisi));
     81:     PetscCall(LMBasisGetVecRead(Y_minus_BkS, i, &yimbisi));
     82:     PetscCall(VecAXPY(B0X, yitbix / yitbisi, yimbisi));
     83:     PetscCall(LMBasisRestoreVecRead(Y_minus_BkS, i, &yimbisi));
     84:   }
     85:   PetscFunctionReturn(PETSC_SUCCESS);
     86: }

     88: /*
     89:    Compute the basis vectors (y_k - B_k s_k) and dot products (y_k^T B_k s_k) recursively
     90:  */

     92: static PetscErrorCode BadBroydenRecursiveBasisUpdate(Mat B, MatLMVMMode mode)
     93: {
     94:   Mat_LMVM           *lmvm  = (Mat_LMVM *)B->data;
     95:   Mat_Brdn           *lbrdn = (Mat_Brdn *)lmvm->ctx;
     96:   MatLMVMBasisType    Y_t   = LMVMModeMap(LMBASIS_Y, mode);
     97:   MatLMVMBasisType    B0S_t = LMVMModeMap(LMBASIS_B0S, mode);
     98:   LMBasis             Y_minus_BkS;
     99:   LMProducts          YtBkS;
    100:   BroydenBasisType    Y_minus_BkS_t = LMVMModeMap(BROYDEN_BASIS_Y_MINUS_BKS, mode);
    101:   BroydenProductsType YtBkS_t       = LMVMModeMap(BROYDEN_PRODUCTS_YTBKS, mode);
    102:   PetscInt            oldest, next;
    103:   PetscInt            products_oldest;
    104:   LMBasis             Y;
    105:   PetscInt            start;

    107:   PetscFunctionBegin;
    108:   if (!lbrdn->basis[Y_minus_BkS_t]) PetscCall(LMBasisCreate(Y_t == LMBASIS_Y ? lmvm->Fprev : lmvm->Xprev, lmvm->m, &lbrdn->basis[Y_minus_BkS_t]));
    109:   Y_minus_BkS = lbrdn->basis[Y_minus_BkS_t];
    110:   if (!lbrdn->products[YtBkS_t]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_DIAGONAL, &lbrdn->products[YtBkS_t]));
    111:   YtBkS = lbrdn->products[YtBkS_t];
    112:   PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
    113:   PetscCall(MatLMVMGetRange(B, &oldest, &next));
    114:   // invalidate computed values if J0 has changed
    115:   PetscCall(LMProductsPrepare(YtBkS, lmvm->J0, oldest, next));
    116:   products_oldest = PetscMax(0, YtBkS->k - lmvm->m);
    117:   if (oldest > products_oldest) {
    118:     // recursion is starting from a different starting index, it must be recomputed
    119:     YtBkS->k = oldest;
    120:   }
    121:   Y_minus_BkS->k = start = YtBkS->k;
    122:   // recompute each column vector in Y_minus_BkS, and the product y_k^T B_k s_k, in order
    123:   for (PetscInt j = start; j < next; j++) {
    124:     Vec         p_j, y_j, B0s_j;
    125:     PetscScalar yjtbjsj, alpha;

    127:     PetscCall(LMBasisGetWorkVec(Y_minus_BkS, &p_j));

    129:     // p_j starts as B_0 * s_j
    130:     PetscCall(MatLMVMBasisGetVecRead(B, B0S_t, j, &B0s_j, &alpha));
    131:     PetscCall(VecAXPBY(p_j, alpha, 0.0, B0s_j));
    132:     PetscCall(MatLMVMBasisRestoreVecRead(B, B0S_t, j, &B0s_j, &alpha));

    134:     /* Use the matmult kernel to compute p_j = B_j * p_j
    135:        (if j == oldest, p_j is already correct) */
    136:     if (j > oldest) PetscCall(BadBroydenKernel_Recursive_Inner(B, mode, oldest, j, p_j));
    137:     PetscCall(LMBasisGetVecRead(Y, j, &y_j));
    138:     PetscCall(VecDot(p_j, y_j, &yjtbjsj));
    139:     PetscCall(LMProductsInsertNextDiagonalValue(YtBkS, j, yjtbjsj));
    140:     PetscCall(VecAYPX(p_j, -1.0, y_j));
    141:     PetscCall(LMBasisRestoreVecRead(Y, j, &y_j));
    142:     PetscCall(LMBasisSetNextVec(Y_minus_BkS, p_j));
    143:     PetscCall(LMBasisRestoreWorkVec(Y_minus_BkS, &p_j));
    144:   }
    145:   PetscFunctionReturn(PETSC_SUCCESS);
    146: }

    148: PETSC_INTERN PetscErrorCode BadBroydenKernel_Recursive(Mat B, MatLMVMMode mode, Vec X, Vec Y)
    149: {
    150:   PetscInt oldest, next;

    152:   PetscFunctionBegin;
    153:   PetscCall(MatLMVMApplyJ0Mode(mode)(B, X, Y));
    154:   PetscCall(MatLMVMGetRange(B, &oldest, &next));
    155:   if (next > oldest) {
    156:     PetscCall(BadBroydenRecursiveBasisUpdate(B, mode));
    157:     PetscCall(BadBroydenKernel_Recursive_Inner(B, mode, oldest, next, Y));
    158:   }
    159:   PetscFunctionReturn(PETSC_SUCCESS);
    160: }

    162: /*
    163:    The adjoint of the recursive bad Broyden kernel is

    165:      B_{k+1}^T x = B_k^T (I + y_k (s_k^T B_k^T y_k)^{-1} (y_k - B_k s_k)^T) x
    166:                          \____________________ ___________________________/
    167:                                             V
    168:                                 recursive rank-1 update

    170:     which can be computed by application of rank-1 updates from newest to oldest
    171:  */

    173: static PetscErrorCode BadBroydenKernelHermitianTranspose_Recursive_Inner(Mat B, MatLMVMMode mode, PetscInt oldest, PetscInt next, Vec X)
    174: {
    175:   MatLMVMBasisType    Y_t           = LMVMModeMap(LMBASIS_Y, mode);
    176:   BroydenBasisType    Y_minus_BkS_t = LMVMModeMap(BROYDEN_BASIS_Y_MINUS_BKS, mode);
    177:   BroydenProductsType YtBkS_t       = LMVMModeMap(BROYDEN_PRODUCTS_YTBKS, mode);
    178:   Mat_LMVM           *lmvm          = (Mat_LMVM *)B->data;
    179:   Mat_Brdn           *lbrdn         = (Mat_Brdn *)lmvm->ctx;
    180:   LMBasis             Y_minus_BkS   = lbrdn->basis[Y_minus_BkS_t];
    181:   LMBasis             Y;
    182:   LMProducts          YtBkS = lbrdn->products[YtBkS_t];

    184:   PetscFunctionBegin;
    185:   PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
    186:   // These cannot be parallelized, notice the data dependence
    187:   for (PetscInt i = next - 1; i >= oldest; i--) {
    188:     Vec         yimbisi, y_i;
    189:     PetscScalar yimbisitx;
    190:     PetscScalar yitbisi;

    192:     PetscCall(LMBasisGetVecRead(Y_minus_BkS, i, &yimbisi));
    193:     PetscCall(VecDot(X, yimbisi, &yimbisitx));
    194:     PetscCall(LMBasisRestoreVecRead(Y_minus_BkS, i, &yimbisi));
    195:     PetscCall(LMProductsGetDiagonalValue(YtBkS, i, &yitbisi));
    196:     PetscCall(LMBasisGetVecRead(Y, i, &y_i));
    197:     PetscCall(VecAXPY(X, yimbisitx / PetscConj(yitbisi), y_i));
    198:     PetscCall(LMBasisRestoreVecRead(Y, i, &y_i));
    199:   }
    200:   PetscFunctionReturn(PETSC_SUCCESS);
    201: }

    203: PETSC_INTERN PetscErrorCode BadBroydenKernelHermitianTranspose_Recursive(Mat B, MatLMVMMode mode, Vec X, Vec BX)
    204: {
    205:   MatLMVMBasisType Y_t = LMVMModeMap(LMBASIS_Y, mode);
    206:   PetscInt         oldest, next;
    207:   Vec              G = X;
    208:   LMBasis          Y;

    210:   PetscFunctionBegin;
    211:   PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
    212:   PetscCall(MatLMVMGetRange(B, &oldest, &next));
    213:   if (next > oldest) {
    214:     PetscCall(LMBasisGetWorkVec(Y, &G));
    215:     PetscCall(VecCopy(X, G));
    216:     PetscCall(BadBroydenRecursiveBasisUpdate(B, mode));
    217:     PetscCall(BadBroydenKernelHermitianTranspose_Recursive_Inner(B, mode, oldest, next, G));
    218:   }
    219:   PetscCall(MatLMVMApplyJ0HermitianTransposeMode(mode)(B, G, BX));
    220:   if (next > oldest) PetscCall(LMBasisRestoreWorkVec(Y, &G));
    221:   PetscFunctionReturn(PETSC_SUCCESS);
    222: }

    224: /*
    225:    The bad Broyden kernel can be written as

    227:      B_k = B_0 + (Y - B_0 S) (Y^H B_0 S - stril(Y^H Y))^{-1} Y^H B_0
    228:          = (I + (Y - B_0 S) (Y^H B_0 S - stril(Y^H Y))^{-1} Y^H) B_0

    230:    where stril is the strictly lower triangular component.  We compute and factorize
    231:    the small matrix in order to apply a single rank m update
    232:  */

    234: static PetscErrorCode BadBroydenCompactProductsUpdate(Mat B, MatLMVMMode mode)
    235: {
    236:   MatLMVMBasisType    Y_t               = LMVMModeMap(LMBASIS_Y, mode);
    237:   MatLMVMBasisType    B0S_t             = LMVMModeMap(LMBASIS_B0S, mode);
    238:   BroydenProductsType YtB0S_minus_YtY_t = LMVMModeMap(BROYDEN_PRODUCTS_YTB0S_MINUS_YTY, mode);
    239:   Mat_LMVM           *lmvm              = (Mat_LMVM *)B->data;
    240:   Mat_Brdn           *lbrdn             = (Mat_Brdn *)lmvm->ctx;
    241:   LMProducts          YtB0S, YtY, YtB0S_minus_YtY;
    242:   LMBasis             Y, B0S;
    243:   PetscScalar         alpha;
    244:   PetscInt            oldest, k, next;
    245:   PetscBool           local_is_nonempty;
    246:   Mat                 ytbsmyty_local;

    248:   PetscFunctionBegin;
    249:   if (!lbrdn->products[YtB0S_minus_YtY_t]) PetscCall(MatLMVMCreateProducts(B, LMBLOCK_FULL, &lbrdn->products[YtB0S_minus_YtY_t]));
    250:   YtB0S_minus_YtY = lbrdn->products[YtB0S_minus_YtY_t];
    251:   PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
    252:   PetscCall(MatLMVMGetUpdatedBasis(B, B0S_t, &B0S, &B0S_t, &alpha));
    253:   PetscCall(MatLMVMGetRange(B, &oldest, &next));
    254:   // invalidate computed values if J0 has changed
    255:   PetscCall(LMProductsPrepare(YtB0S_minus_YtY, lmvm->J0, oldest, next));
    256:   PetscCall(LMProductsGetLocalMatrix(YtB0S_minus_YtY, &ytbsmyty_local, &k, &local_is_nonempty));
    257:   if (k < next) {
    258:     Mat yty_local, ytbs_local;

    260:     PetscCall(MatLMVMGetUpdatedProducts(B, Y_t, B0S_t, LMBLOCK_FULL, &YtB0S));
    261:     PetscCall(MatLMVMGetUpdatedProducts(B, Y_t, Y_t, LMBLOCK_STRICT_UPPER_TRIANGLE, &YtY));
    262:     PetscCall(LMProductsGetLocalMatrix(YtB0S, &ytbs_local, NULL, NULL));
    263:     PetscCall(LMProductsGetLocalMatrix(YtY, &yty_local, NULL, NULL));
    264:     if (local_is_nonempty) {
    265:       PetscCall(MatSetUnfactored(ytbsmyty_local));
    266:       PetscCall(MatCopy(yty_local, ytbsmyty_local, SAME_NONZERO_PATTERN));
    267:       PetscCall(MatTranspose(ytbsmyty_local, MAT_INPLACE_MATRIX, &ytbsmyty_local));
    268:       if (PetscDefined(USE_COMPLEX)) PetscCall(MatConjugate(ytbsmyty_local));
    269:       PetscCall(MatScale(ytbsmyty_local, -1.0));
    270:       PetscCall(MatAXPY(ytbsmyty_local, alpha, ytbs_local, SAME_NONZERO_PATTERN));
    271:       PetscCall(LMProductsOnesOnUnusedDiagonal(ytbsmyty_local, oldest, next));
    272:       PetscCall(MatLUFactor(ytbsmyty_local, NULL, NULL, NULL));
    273:     }
    274:     PetscCall(LMProductsRestoreLocalMatrix(YtY, &yty_local, NULL));
    275:     PetscCall(LMProductsRestoreLocalMatrix(YtB0S, &ytbs_local, NULL));
    276:   }
    277:   PetscCall(LMProductsRestoreLocalMatrix(YtB0S_minus_YtY, &ytbsmyty_local, &next));
    278:   PetscFunctionReturn(PETSC_SUCCESS);
    279: }

    281: PETSC_INTERN PetscErrorCode BadBroydenKernel_CompactDense(Mat B, MatLMVMMode mode, Vec X, Vec BX)
    282: {
    283:   PetscInt oldest, next;

    285:   PetscFunctionBegin;
    286:   PetscCall(MatLMVMApplyJ0Mode(mode)(B, X, BX));
    287:   PetscCall(MatLMVMGetRange(B, &oldest, &next));
    288:   if (next > oldest) {
    289:     Mat_LMVM           *lmvm              = (Mat_LMVM *)B->data;
    290:     Mat_Brdn           *lbrdn             = (Mat_Brdn *)lmvm->ctx;
    291:     MatLMVMBasisType    Y_t               = LMVMModeMap(LMBASIS_Y, mode);
    292:     MatLMVMBasisType    Y_minus_B0S_t     = LMVMModeMap(LMBASIS_Y_MINUS_B0S, mode);
    293:     BroydenProductsType YtB0S_minus_YtY_t = LMVMModeMap(BROYDEN_PRODUCTS_YTB0S_MINUS_YTY, mode);
    294:     LMProducts          YtB0S_minus_YtY;
    295:     LMBasis             Y;
    296:     Vec                 YtB0X, v;

    298:     PetscCall(BadBroydenCompactProductsUpdate(B, mode));
    299:     PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
    300:     YtB0S_minus_YtY = lbrdn->products[YtB0S_minus_YtY_t];
    301:     PetscCall(MatLMVMGetWorkRow(B, &YtB0X));
    302:     PetscCall(MatLMVMGetWorkRow(B, &v));
    303:     PetscCall(LMBasisGEMVH(Y, oldest, next, 1.0, BX, 0.0, YtB0X));
    304:     PetscCall(LMProductsSolve(YtB0S_minus_YtY, oldest, next, YtB0X, v, /* ^H */ PETSC_FALSE));
    305:     PetscCall(MatLMVMBasisGEMV(B, Y_minus_B0S_t, oldest, next, 1.0, v, 1.0, BX));
    306:     PetscCall(MatLMVMRestoreWorkRow(B, &v));
    307:     PetscCall(MatLMVMRestoreWorkRow(B, &YtB0X));
    308:   }
    309:   PetscFunctionReturn(PETSC_SUCCESS);
    310: }

    312: /*
    313:    The adjoint of the above formula for the bad Broyden kernel is

    315:      B_k^H = B_0^H + B_0^H Y (Y^H B_0 S - stril(Y^H Y))^{-H} (Y - B_0 S)^H
    316:            = B_0^H (I + Y (Y^H B_0 S - stril(Y^H Y))^{-H} (Y - B_0 S)^H)
    317:  */

    319: PETSC_INTERN PetscErrorCode BadBroydenKernelHermitianTranspose_CompactDense(Mat B, MatLMVMMode mode, Vec X, Vec BHX)
    320: {
    321:   MatLMVMBasisType Y_t = LMVMModeMap(LMBASIS_Y, mode);
    322:   PetscInt         oldest, next;
    323:   Vec              G = X;
    324:   LMBasis          Y = NULL;

    326:   PetscFunctionBegin;
    327:   PetscCall(MatLMVMGetRange(B, &oldest, &next));
    328:   if (next > oldest) {
    329:     Mat_LMVM           *lmvm              = (Mat_LMVM *)B->data;
    330:     Mat_Brdn           *lbrdn             = (Mat_Brdn *)lmvm->ctx;
    331:     MatLMVMBasisType    Y_minus_B0S_t     = LMVMModeMap(LMBASIS_Y_MINUS_B0S, mode);
    332:     BroydenProductsType YtB0S_minus_YtY_t = LMVMModeMap(BROYDEN_PRODUCTS_YTB0S_MINUS_YTY, mode);
    333:     LMProducts          YtB0S_minus_YtY;
    334:     Vec                 YmB0StG, v;

    336:     PetscCall(MatLMVMGetUpdatedBasis(B, Y_t, &Y, NULL, NULL));
    337:     PetscCall(LMBasisGetWorkVec(Y, &G));
    338:     PetscCall(VecCopy(X, G));
    339:     PetscCall(BadBroydenCompactProductsUpdate(B, mode));
    340:     YtB0S_minus_YtY = lbrdn->products[YtB0S_minus_YtY_t];
    341:     PetscCall(MatLMVMGetWorkRow(B, &YmB0StG));
    342:     PetscCall(MatLMVMGetWorkRow(B, &v));
    343:     PetscCall(MatLMVMBasisGEMVH(B, Y_minus_B0S_t, oldest, next, 1.0, G, 0.0, YmB0StG));
    344:     PetscCall(LMProductsSolve(YtB0S_minus_YtY, oldest, next, YmB0StG, v, /* ^H */ PETSC_TRUE));
    345:     PetscCall(LMBasisGEMV(Y, oldest, next, 1.0, v, 1.0, G));
    346:     PetscCall(MatLMVMRestoreWorkRow(B, &v));
    347:     PetscCall(MatLMVMRestoreWorkRow(B, &YmB0StG));
    348:   }
    349:   PetscCall(MatLMVMApplyJ0HermitianTransposeMode(mode)(B, G, BHX));
    350:   if (next > oldest) PetscCall(LMBasisRestoreWorkVec(Y, &G));
    351:   PetscFunctionReturn(PETSC_SUCCESS);
    352: }

    354: static PetscErrorCode MatMult_LMVMBadBrdn_Recursive(Mat B, Vec F, Vec dX)
    355: {
    356:   PetscFunctionBegin;
    357:   PetscCall(BadBroydenKernel_Recursive(B, MATLMVM_MODE_PRIMAL, F, dX));
    358:   PetscFunctionReturn(PETSC_SUCCESS);
    359: }

    361: static PetscErrorCode MatMult_LMVMBadBrdn_CompactDense(Mat B, Vec F, Vec dX)
    362: {
    363:   PetscFunctionBegin;
    364:   PetscCall(BadBroydenKernel_CompactDense(B, MATLMVM_MODE_PRIMAL, F, dX));
    365:   PetscFunctionReturn(PETSC_SUCCESS);
    366: }

    368: static PetscErrorCode MatMultHermitianTranspose_LMVMBadBrdn_Recursive(Mat B, Vec F, Vec dX)
    369: {
    370:   PetscFunctionBegin;
    371:   PetscCall(BadBroydenKernelHermitianTranspose_Recursive(B, MATLMVM_MODE_PRIMAL, F, dX));
    372:   PetscFunctionReturn(PETSC_SUCCESS);
    373: }

    375: static PetscErrorCode MatMultHermitianTranspose_LMVMBadBrdn_CompactDense(Mat B, Vec F, Vec dX)
    376: {
    377:   PetscFunctionBegin;
    378:   PetscCall(BadBroydenKernelHermitianTranspose_CompactDense(B, MATLMVM_MODE_PRIMAL, F, dX));
    379:   PetscFunctionReturn(PETSC_SUCCESS);
    380: }

    382: static PetscErrorCode MatLMVMSetMultAlgorithm_BadBrdn(Mat B)
    383: {
    384:   Mat_LMVM *lmvm = (Mat_LMVM *)B->data;

    386:   PetscFunctionBegin;
    387:   switch (lmvm->mult_alg) {
    388:   case MAT_LMVM_MULT_RECURSIVE:
    389:     lmvm->ops->mult    = MatMult_LMVMBadBrdn_Recursive;
    390:     lmvm->ops->multht  = MatMultHermitianTranspose_LMVMBadBrdn_Recursive;
    391:     lmvm->ops->solve   = MatSolve_LMVMBadBrdn_Recursive;
    392:     lmvm->ops->solveht = MatSolveHermitianTranspose_LMVMBadBrdn_Recursive;
    393:     break;
    394:   case MAT_LMVM_MULT_DENSE:
    395:     lmvm->ops->mult    = MatMult_LMVMBadBrdn_CompactDense;
    396:     lmvm->ops->multht  = MatMultHermitianTranspose_LMVMBadBrdn_CompactDense;
    397:     lmvm->ops->solve   = MatSolve_LMVMBadBrdn_Dense;
    398:     lmvm->ops->solveht = MatSolveHermitianTranspose_LMVMBadBrdn_Dense;
    399:     break;
    400:   case MAT_LMVM_MULT_COMPACT_DENSE:
    401:     lmvm->ops->mult    = MatMult_LMVMBadBrdn_CompactDense;
    402:     lmvm->ops->multht  = MatMultHermitianTranspose_LMVMBadBrdn_CompactDense;
    403:     lmvm->ops->solve   = MatSolve_LMVMBadBrdn_CompactDense;
    404:     lmvm->ops->solveht = MatSolveHermitianTranspose_LMVMBadBrdn_CompactDense;
    405:     break;
    406:   }
    407:   PetscFunctionReturn(PETSC_SUCCESS);
    408: }

    410: PetscErrorCode MatCreate_LMVMBadBrdn(Mat B)
    411: {
    412:   Mat_LMVM *lmvm;

    414:   PetscFunctionBegin;
    415:   PetscCall(MatCreate_LMVMBrdn(B));
    416:   PetscCall(PetscObjectChangeTypeName((PetscObject)B, MATLMVMBADBROYDEN));
    417:   lmvm                          = (Mat_LMVM *)B->data;
    418:   lmvm->ops->setmultalgorithm   = MatLMVMSetMultAlgorithm_BadBrdn;
    419:   lmvm->cache_gradient_products = PETSC_TRUE;
    420:   PetscCall(MatLMVMSetMultAlgorithm_BadBrdn(B));
    421:   PetscFunctionReturn(PETSC_SUCCESS);
    422: }

    424: /*@
    425:   MatCreateLMVMBadBroyden - Creates a limited-memory modified (aka "bad") Broyden-type
    426:   approximation matrix used for a Jacobian. L-BadBrdn is not guaranteed to be
    427:   symmetric or positive-definite.

    429:   To use the L-BadBrdn matrix with other vector types, the matrix must be
    430:   created using `MatCreate()` and `MatSetType()`, followed by `MatLMVMAllocate()`.
    431:   This ensures that the internal storage and work vectors are duplicated from the
    432:   correct type of vector.

    434:   Collective

    436:   Input Parameters:
    437: + comm - MPI communicator
    438: . n    - number of local rows for storage vectors
    439: - N    - global size of the storage vectors

    441:   Output Parameter:
    442: . B - the matrix

    444:   Options Database Keys:
    445: + -mat_lmvm_hist_size         - the number of history vectors to keep
    446: . -mat_lmvm_mult_algorithm    - the algorithm to use for multiplication (recursive, dense, compact_dense)
    447: . -mat_lmvm_cache_J0_products - whether products between the base Jacobian J0 and history vectors should be cached or recomputed
    448: - -mat_lmvm_debug             - (developer) perform internal debugging checks

    450:   Level: intermediate

    452:   Note:
    453:   It is recommended that one use the `MatCreate()`, `MatSetType()` and/or `MatSetFromOptions()`
    454:   paradigm instead of this routine directly.

    456: .seealso: [](ch_ksp), `MatCreate()`, `MATLMVM`, `MATLMVMBADBRDN`, `MatCreateLMVMDFP()`, `MatCreateLMVMSR1()`,
    457:           `MatCreateLMVMBFGS()`, `MatCreateLMVMBroyden()`, `MatCreateLMVMSymBroyden()`
    458: @*/
    459: PetscErrorCode MatCreateLMVMBadBroyden(MPI_Comm comm, PetscInt n, PetscInt N, Mat *B)
    460: {
    461:   PetscFunctionBegin;
    462:   PetscCall(KSPInitializePackage());
    463:   PetscCall(MatCreate(comm, B));
    464:   PetscCall(MatSetSizes(*B, n, n, N, N));
    465:   PetscCall(MatSetType(*B, MATLMVMBADBROYDEN));
    466:   PetscCall(MatSetUp(*B));
    467:   PetscFunctionReturn(PETSC_SUCCESS);
    468: }