Actual source code: pipeprcg.c

      1: #include <petsc/private/kspimpl.h>

      3: typedef struct KSP_CG_PIPE_PR_s KSP_CG_PIPE_PR;
      4: struct KSP_CG_PIPE_PR_s {
      5:   PetscBool rc_w_q; /* flag to determine whether w_k should be recomputed with A r_k */
      6: };

      8: /*
      9:      KSPSetUp_PIPEPRCG - Sets up the workspace needed by the PIPEPRCG method.

     11:       This is called once, usually automatically by KSPSolve() or KSPSetUp()
     12:      but can be called directly by KSPSetUp()
     13: */
     14: static PetscErrorCode KSPSetUp_PIPEPRCG(KSP ksp)
     15: {
     16:   PetscFunctionBegin;
     17:   /* get work vectors needed by PIPEPRCG */
     18:   PetscCall(KSPSetWorkVecs(ksp, 9));
     19:   PetscFunctionReturn(PETSC_SUCCESS);
     20: }

     22: static PetscErrorCode KSPSetFromOptions_PIPEPRCG(KSP ksp, PetscOptionItems PetscOptionsObject)
     23: {
     24:   KSP_CG_PIPE_PR *prcg = (KSP_CG_PIPE_PR *)ksp->data;
     25:   PetscBool       flag = PETSC_FALSE;

     27:   PetscFunctionBegin;
     28:   PetscOptionsHeadBegin(PetscOptionsObject, "KSP PIPEPRCG options");
     29:   PetscCall(PetscOptionsBool("-recompute_w", "-recompute w_k with Ar_k? (default = True)", "", prcg->rc_w_q, &prcg->rc_w_q, &flag));
     30:   if (!flag) prcg->rc_w_q = PETSC_TRUE;
     31:   PetscOptionsHeadEnd();
     32:   PetscFunctionReturn(PETSC_SUCCESS);
     33: }

     35: /*
     36:  KSPSolve_PIPEPRCG - This routine actually applies the pipelined predict and recompute conjugate gradient method
     37: */
     38: static PetscErrorCode KSPSolve_PIPEPRCG(KSP ksp)
     39: {
     40:   PetscInt        i;
     41:   KSP_CG_PIPE_PR *prcg  = (KSP_CG_PIPE_PR *)ksp->data;
     42:   PetscScalar     alpha = 0.0, beta = 0.0, nu = 0.0, nu_old = 0.0, mudelgam[3], *mu_p, *delta_p, *gamma_p;
     43:   PetscReal       dp = 0.0;
     44:   Vec             X, B, R, RT, W, WT, P, S, ST, U, UT, PRTST[3];
     45:   Mat             Amat, Pmat;
     46:   PetscBool       diagonalscale, rc_w_q = prcg->rc_w_q;

     48:   /* note that these are pointers to entries of muldelgam, different than nu */
     49:   mu_p    = &mudelgam[0];
     50:   delta_p = &mudelgam[1];
     51:   gamma_p = &mudelgam[2];

     53:   PetscFunctionBegin;
     54:   PetscCall(PCGetDiagonalScale(ksp->pc, &diagonalscale));
     55:   PetscCheck(!diagonalscale, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ((PetscObject)ksp)->type_name);

     57:   X  = ksp->vec_sol;
     58:   B  = ksp->vec_rhs;
     59:   R  = ksp->work[0];
     60:   RT = ksp->work[1];
     61:   W  = ksp->work[2];
     62:   WT = ksp->work[3];
     63:   P  = ksp->work[4];
     64:   S  = ksp->work[5];
     65:   ST = ksp->work[6];
     66:   U  = ksp->work[7];
     67:   UT = ksp->work[8];

     69:   PetscCall(PCGetOperators(ksp->pc, &Amat, &Pmat));

     71:   /* initialize */
     72:   ksp->its = 0;
     73:   if (!ksp->guess_zero) {
     74:     PetscCall(KSP_MatMult(ksp, Amat, X, R)); /*   r <- b - Ax  */
     75:     PetscCall(VecAYPX(R, -1.0, B));
     76:   } else {
     77:     PetscCall(VecCopy(B, R)); /*   r <- b       */
     78:   }

     80:   PetscCall(KSP_PCApply(ksp, R, RT));       /*   rt <- Br     */
     81:   PetscCall(KSP_MatMult(ksp, Amat, RT, W)); /*   w <- A rt    */
     82:   PetscCall(KSP_PCApply(ksp, W, WT));       /*   wt <- B w    */

     84:   PetscCall(VecCopy(RT, P));  /*   p <- rt      */
     85:   PetscCall(VecCopy(W, S));   /*   p <- rt      */
     86:   PetscCall(VecCopy(WT, ST)); /*   p <- rt      */

     88:   PetscCall(KSP_MatMult(ksp, Amat, ST, U)); /*   u <- Ast     */
     89:   PetscCall(KSP_PCApply(ksp, U, UT));       /*   ut <- Bu     */

     91:   PetscCall(VecDotBegin(RT, R, &nu));
     92:   PetscCall(VecDotBegin(P, S, mu_p));
     93:   PetscCall(VecDotBegin(ST, S, gamma_p));

     95:   PetscCall(VecDotEnd(RT, R, &nu));     /*   nu    <- (rt,r)  */
     96:   PetscCall(VecDotEnd(P, S, mu_p));     /*   mu    <- (p,s)   */
     97:   PetscCall(VecDotEnd(ST, S, gamma_p)); /*   gamma <- (st,s)  */
     98:   *delta_p = *mu_p;

    100:   i = 0;
    101:   do {
    102:     /* Compute appropriate norm */
    103:     switch (ksp->normtype) {
    104:     case KSP_NORM_PRECONDITIONED:
    105:       PetscCall(VecNormBegin(RT, NORM_2, &dp));
    106:       PetscCall(PetscCommSplitReductionBegin(PetscObjectComm((PetscObject)RT)));
    107:       PetscCall(VecNormEnd(RT, NORM_2, &dp));
    108:       break;
    109:     case KSP_NORM_UNPRECONDITIONED:
    110:       PetscCall(VecNormBegin(R, NORM_2, &dp));
    111:       PetscCall(PetscCommSplitReductionBegin(PetscObjectComm((PetscObject)R)));
    112:       PetscCall(VecNormEnd(R, NORM_2, &dp));
    113:       break;
    114:     case KSP_NORM_NATURAL:
    115:       dp = PetscSqrtReal(PetscAbsScalar(nu));
    116:       break;
    117:     case KSP_NORM_NONE:
    118:       dp = 0.0;
    119:       break;
    120:     default:
    121:       SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "%s", KSPNormTypes[ksp->normtype]);
    122:     }

    124:     ksp->rnorm = dp;
    125:     PetscCall(KSPLogResidualHistory(ksp, dp));
    126:     PetscCall(KSPMonitor(ksp, i, dp));
    127:     PetscCall((*ksp->converged)(ksp, i, dp, &ksp->reason, ksp->cnvP));
    128:     if (ksp->reason) PetscFunctionReturn(PETSC_SUCCESS);

    130:     /* update scalars */
    131:     alpha  = nu / *mu_p;
    132:     nu_old = nu;
    133:     nu     = nu_old - 2. * alpha * (*delta_p) + (alpha * alpha) * (*gamma_p);
    134:     beta   = nu / nu_old;

    136:     /* update vectors */
    137:     PetscCall(VecAXPY(X, alpha, P));    /*   x  <- x  + alpha * p   */
    138:     PetscCall(VecAXPY(R, -alpha, S));   /*   r  <- r  - alpha * s   */
    139:     PetscCall(VecAXPY(RT, -alpha, ST)); /*   rt <- rt - alpha * st  */
    140:     PetscCall(VecAXPY(W, -alpha, U));   /*   w  <- w  - alpha * u   */
    141:     PetscCall(VecAXPY(WT, -alpha, UT)); /*   wt <- wt - alpha * ut  */
    142:     PetscCall(VecAYPX(P, beta, RT));    /*   p  <- rt + beta  * p   */
    143:     PetscCall(VecAYPX(S, beta, W));     /*   s  <- w  + beta  * s   */
    144:     PetscCall(VecAYPX(ST, beta, WT));   /*   st <- wt + beta  * st  */

    146:     PetscCall(VecDotBegin(RT, R, &nu));

    148:     PRTST[0] = P;
    149:     PRTST[1] = RT;
    150:     PRTST[2] = ST;

    152:     PetscCall(VecMDotBegin(S, 3, PRTST, mudelgam));

    154:     PetscCall(PetscCommSplitReductionBegin(PetscObjectComm((PetscObject)R)));

    156:     PetscCall(KSP_MatMult(ksp, Amat, ST, U)); /*   u  <- A st             */
    157:     PetscCall(KSP_PCApply(ksp, U, UT));       /*   ut <- B u              */

    159:     /* predict-and-recompute */
    160:     /* ideally this is combined with the previous matvec; i.e. equivalent of MDot */
    161:     if (rc_w_q) {
    162:       PetscCall(KSP_MatMult(ksp, Amat, RT, W)); /*   w  <- A rt             */
    163:       PetscCall(KSP_PCApply(ksp, W, WT));       /*   wt <- B w              */
    164:     }

    166:     PetscCall(VecDotEnd(RT, R, &nu));
    167:     PetscCall(VecMDotEnd(S, 3, PRTST, mudelgam));

    169:     i++;
    170:     ksp->its = i;

    172:   } while (i <= ksp->max_it);
    173:   if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
    174:   PetscFunctionReturn(PETSC_SUCCESS);
    175: }

    177: /*MC
    178:    KSPPIPEPRCG - Pipelined predict-and-recompute conjugate gradient Krylov method {cite}`chen2020predict`. [](sec_pipelineksp)

    180:    Options Database Key:
    181: .  -ksp_pipeprcg_recompute_w - recompute the $w_k$ with $Ar_k$, default is true

    183:    Level: intermediate

    185:    Notes:
    186:    This method has only a single non-blocking reduction per iteration, compared to 2 blocking for standard `KSPCG`.
    187:    The non-blocking reduction is overlapped by the matrix-vector product and preconditioner application.

    189:    MPI configuration may be necessary for reductions to make asynchronous progress, which is important for performance of pipelined methods.
    190:    See [](doc_faq_pipelined)

    192:    Contributed by:
    193:    Tyler Chen, University of Washington, Applied Mathematics Department

    195:    Acknowledgments:
    196:    This material is based upon work supported by the National Science Foundation Graduate Research Fellowship Program under Grant No. DGE-1762114.
    197:    Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author and do not necessarily
    198:    reflect the views of the National Science Foundation.

    200: .seealso: [](ch_ksp), [](doc_faq_pipelined), [](sec_pipelineksp), `KSPCreate()`, `KSPSetType()`, `KSPCG`, `KSPPIPECG`, `KSPPIPECR`, `KSPGROPPCG`, `KSPPGMRES`, `KSPCGUseSingleReduction()`
    201: M*/
    202: PETSC_EXTERN PetscErrorCode KSPCreate_PIPEPRCG(KSP ksp)
    203: {
    204:   KSP_CG_PIPE_PR *prcg = NULL;
    205:   PetscBool       cite = PETSC_FALSE;

    207:   PetscFunctionBegin;
    208:   PetscCall(PetscCitationsRegister("@article{predict_and_recompute_cg,\n  author = {Tyler Chen and Erin C. Carson},\n  title = {Predict-and-recompute conjugate gradient variants},\n  journal = {},\n  year = {2020},\n  eprint = {1905.01549},\n  "
    209:                                    "archivePrefix = {arXiv},\n  primaryClass = {cs.NA}\n}",
    210:                                    &cite));

    212:   PetscCall(PetscNew(&prcg));
    213:   ksp->data = (void *)prcg;

    215:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_LEFT, 2));
    216:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_PRECONDITIONED, PC_LEFT, 2));
    217:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NATURAL, PC_LEFT, 2));
    218:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_LEFT, 1));

    220:   ksp->ops->setup          = KSPSetUp_PIPEPRCG;
    221:   ksp->ops->solve          = KSPSolve_PIPEPRCG;
    222:   ksp->ops->destroy        = KSPDestroyDefault;
    223:   ksp->ops->view           = NULL;
    224:   ksp->ops->setfromoptions = KSPSetFromOptions_PIPEPRCG;
    225:   ksp->ops->buildsolution  = KSPBuildSolutionDefault;
    226:   ksp->ops->buildresidual  = KSPBuildResidualDefault;
    227:   PetscFunctionReturn(PETSC_SUCCESS);
    228: }