Date: 2022-03-31T04:48:11+00:00

Actual source code: agmres.c

      2: #define PETSCKSP_DLL

      4: #include <../src/ksp/ksp/impls/gmres/agmres/agmresimpl.h>

      6: #define AGMRES_DEFAULT_MAXK 30
      7: #define AGMRES_DELTA_DIRECTIONS 10
      8: static PetscErrorCode KSPAGMRESBuildSoln(KSP,PetscInt);
      9: static PetscErrorCode KSPAGMRESBuildBasis(KSP);
     10: static PetscErrorCode KSPAGMRESBuildHessenberg(KSP);

     12: PetscLogEvent KSP_AGMRESComputeDeflationData, KSP_AGMRESBuildBasis, KSP_AGMRESComputeShifts, KSP_AGMRESRoddec;

     14: extern PetscErrorCode KSPSetUp_DGMRES(KSP);
     15: extern PetscErrorCode KSPBuildSolution_DGMRES(KSP,Vec,Vec*);
     16: extern PetscErrorCode KSPSolve_DGMRES(KSP);
     17: extern PetscErrorCode KSPDGMRESComputeDeflationData_DGMRES(KSP,PetscInt*);
     18: extern PetscErrorCode KSPDGMRESComputeSchurForm_DGMRES(KSP,PetscInt*);
     19: extern PetscErrorCode KSPDGMRESApplyDeflation_DGMRES(KSP,Vec,Vec);
     20: extern PetscErrorCode KSPDestroy_DGMRES(KSP);
     21: extern PetscErrorCode KSPSetFromOptions_DGMRES(PetscOptionItems *,KSP);
     22: extern PetscErrorCode KSPDGMRESSetEigen_DGMRES(KSP,PetscInt);
     23: /*
     24:    This function allocates  data for the Newton basis GMRES implementation.
     25:    Note that most data are allocated in KSPSetUp_DGMRES and KSPSetUp_GMRES, including the space for the basis vectors, the various Hessenberg matrices and the Givens rotations coefficients

     27: */
     28: static PetscErrorCode KSPSetUp_AGMRES(KSP ksp)
     29: {
     30:   PetscInt        hes;
     31:   PetscInt        nloc;
     32:   KSP_AGMRES      *agmres = (KSP_AGMRES*)ksp->data;
     33:   PetscInt        neig    = agmres->neig;
     34:   const PetscInt  max_k   = agmres->max_k;
     35:   PetscInt        N       = MAXKSPSIZE;
     36:   PetscInt        lwork   = PetscMax(8 * N + 16, 4 * neig * (N - neig));

     39:   N     = MAXKSPSIZE;
     40:   /* Preallocate space during the call to KSPSetup_GMRES for the Krylov basis */
     41:   agmres->q_preallocate = PETSC_TRUE; /* No allocation on the fly */
     42:   /* Preallocate space to compute later the eigenvalues in GMRES */
     43:   ksp->calc_sings = PETSC_TRUE;
     44:   agmres->max_k   = N; /* Set the augmented size to be allocated in KSPSetup_GMRES */
     45:   KSPSetUp_DGMRES(ksp);
     46:   agmres->max_k   = max_k;
     47:   hes             = (N + 1) * (N + 1);

     49:   /* Data for the Newton basis GMRES */
     50:   PetscCalloc4(max_k,&agmres->Rshift,max_k,&agmres->Ishift,hes,&agmres->Rloc,(N+1)*4,&agmres->wbufptr);
     51:   PetscMalloc3(N+1,&agmres->tau,lwork,&agmres->work,N+1,&agmres->nrs);
     52:   PetscCalloc4(N+1,&agmres->Scale,N+1,&agmres->sgn,N+1,&agmres->tloc,N+1,&agmres->temp);

     54:   /* Allocate space for the vectors in the orthogonalized basis*/
     55:   VecGetLocalSize(agmres->vecs[0], &nloc);
     56:   PetscMalloc1(nloc*(N+1), &agmres->Qloc);

     58:   /* Init the ring of processors for the roddec orthogonalization */
     59:   KSPAGMRESRoddecInitNeighboor(ksp);

     61:   if (agmres->neig < 1) return 0;

     63:   /* Allocate space for the deflation */
     64:   PetscMalloc1(N, &agmres->select);
     65:   VecDuplicateVecs(VEC_V(0), N, &agmres->TmpU);
     66:   PetscMalloc2(N*N, &agmres->MatEigL, N*N, &agmres->MatEigR);
     67:   /*  PetscMalloc6(N*N, &agmres->Q, N*N, &agmres->Z, N, &agmres->wr, N, &agmres->wi, N, &agmres->beta, N, &agmres->modul); */
     68:   PetscMalloc3(N*N, &agmres->Q, N*N, &agmres->Z, N, &agmres->beta);
     69:   PetscMalloc2((N+1),&agmres->perm,(2*neig*N),&agmres->iwork);
     70:   return 0;
     71: }

     73: /*
     74:     Returns the current solution from the private data structure of AGMRES back to ptr.
     75: */
     76: static PetscErrorCode KSPBuildSolution_AGMRES(KSP ksp,Vec ptr, Vec *result)
     77: {
     78:   KSP_AGMRES     *agmres = (KSP_AGMRES*)ksp->data;

     80:   if (!ptr) {
     81:     if (!agmres->sol_temp) {
     82:       VecDuplicate(ksp->vec_sol,&agmres->sol_temp);
     83:       VecCopy(ksp->vec_sol,agmres->sol_temp);
     84:       PetscLogObjectParent((PetscObject)ksp,(PetscObject)agmres->sol_temp);
     85:     }
     86:     ptr = agmres->sol_temp;
     87:   } else {
     88:     VecCopy(ksp->vec_sol, ptr);
     89:   }
     90:   if (result) *result = ptr;
     91:   return 0;
     92: }

     94: /* Computes the shifts  needed to generate stable basis vectors (through the Newton polynomials)
     95:    At input, the operators (matrix and preconditioners) are used to create a new GMRES KSP.
     96:    One cycle of GMRES with the Arnoldi process is performed and the eigenvalues of the induced Hessenberg matrix (the Ritz values) are computed.
     97:    NOTE: This function is not currently used; the next function is rather used when  the eigenvectors are needed next to augment the basis
     98: */
     99: PetscErrorCode KSPComputeShifts_GMRES(KSP ksp)
    100: {
    101:   KSP_AGMRES      *agmres = (KSP_AGMRES*)(ksp->data);
    102:   KSP             kspgmres;
    103:   Mat             Amat, Pmat;
    104:   const PetscInt  max_k = agmres->max_k;
    105:   PC              pc;
    106:   PetscInt        m;
    107:   PetscScalar     *Rshift, *Ishift;
    108:   PetscBool       flg;

    110:   /* Perform one cycle of classical GMRES (with the Arnoldi process) to get the Hessenberg matrix
    111:    We assume here that the ksp is AGMRES and that the operators for the
    112:    linear system have been set in this ksp */
    113:   KSPCreate(PetscObjectComm((PetscObject)ksp), &kspgmres);
    114:   if (!ksp->pc) KSPGetPC(ksp,&ksp->pc);
    115:   PCGetOperators(ksp->pc, &Amat, &Pmat);
    116:   KSPSetOperators(kspgmres, Amat, Pmat);
    117:   KSPSetFromOptions(kspgmres);
    118:   PetscOptionsHasName(NULL,((PetscObject)ksp)->prefix, "-ksp_view", &flg);
    119:   if (flg) PetscOptionsClearValue(NULL,"-ksp_view");
    120:   KSPSetType(kspgmres, KSPGMRES);
    121:   KSPGMRESSetRestart(kspgmres, max_k);
    122:   KSPGetPC(ksp, &pc);
    123:   KSPSetPC(kspgmres, pc);
    124:   /* Copy common options */
    125:   kspgmres->pc_side = ksp->pc_side;
    126:   /* Setup KSP context */
    127:   KSPSetComputeEigenvalues(kspgmres, PETSC_TRUE);
    128:   KSPSetUp(kspgmres);

    130:   kspgmres->max_it = max_k; /* Restrict the maximum number of iterations to one cycle of GMRES */
    131:   kspgmres->rtol   = ksp->rtol;

    133:   KSPSolve(kspgmres, ksp->vec_rhs, ksp->vec_sol);

    135:   ksp->guess_zero = PETSC_FALSE;
    136:   ksp->rnorm      = kspgmres->rnorm;
    137:   ksp->its        = kspgmres->its;
    138:   if (kspgmres->reason == KSP_CONVERGED_RTOL) {
    139:     ksp->reason = KSP_CONVERGED_RTOL;
    140:     return 0;
    141:   } else ksp->reason = KSP_CONVERGED_ITERATING;
    142:   /* Now, compute the Shifts values */
    143:   PetscMalloc2(max_k,&Rshift,max_k,&Ishift);
    144:   KSPComputeEigenvalues(kspgmres, max_k, Rshift, Ishift, &m);
    146:   else {
    147:     KSPAGMRESLejaOrdering(Rshift, Ishift, agmres->Rshift, agmres->Ishift, max_k);

    149:     agmres->HasShifts = PETSC_TRUE;
    150:   }
    151:   /* Restore KSP view options */
    152:   if (flg) PetscOptionsSetValue(NULL,"-ksp_view", "");
    153:   return 0;
    154: }

    156: /* Computes the shift values (Ritz values) needed to generate stable basis vectors
    157:    One cycle of DGMRES is performed to find the eigenvalues. The same data structures are used since AGMRES extends DGMRES
    158:    Note that when the basis is  to be augmented, then this function computes the harmonic Ritz vectors from this first cycle.
    159:    Input :
    160:     - The operators (matrix, preconditioners and right hand side) are  normally required.
    161:     - max_k : the size of the (non augmented) basis.
    162:     - neig: The number of eigenvectors to augment, if deflation is needed
    163:    Output :
    164:     - The shifts as complex pair of arrays in wr and wi (size max_k).
    165:     - The harmonic Ritz vectors (agmres->U) if deflation is needed.
    166: */
    167: static PetscErrorCode KSPComputeShifts_DGMRES(KSP ksp)
    168: {
    169:   KSP_AGMRES     *agmres = (KSP_AGMRES*)(ksp->data);
    170:   PetscInt       max_k   = agmres->max_k; /* size of the (non augmented) Krylov subspace */
    171:   PetscInt       Neig    = 0;
    172:   const PetscInt max_it  = ksp->max_it;
    173:   PetscBool      flg;

    175:   /* Perform one cycle of dgmres to find the eigenvalues and compute the first approximations of the eigenvectors */

    177:   PetscLogEventBegin(KSP_AGMRESComputeShifts, ksp, 0,0,0);
    178:   /* Send the size of the augmented basis to DGMRES */
    179:   ksp->max_it             = max_k; /* set this to have DGMRES performing only one cycle */
    180:   ksp->ops->buildsolution = KSPBuildSolution_DGMRES;
    181:   KSPSolve_DGMRES(ksp);
    182:   ksp->guess_zero         = PETSC_FALSE;
    183:   if (ksp->reason == KSP_CONVERGED_RTOL) {
    184:     PetscLogEventEnd(KSP_AGMRESComputeShifts, ksp, 0,0,0);
    185:     return 0;
    186:   } else ksp->reason = KSP_CONVERGED_ITERATING;

    188:   if ((agmres->r == 0) && (agmres->neig > 0)) {  /* Compute the eigenvalues for the shifts and the eigenvectors (to augment the Newton basis) */
    189:     agmres->HasSchur = PETSC_FALSE;
    190:     KSPDGMRESComputeDeflationData_DGMRES(ksp, &Neig);
    191:     Neig             = max_k;
    192:   } else { /* From DGMRES, compute only the eigenvalues needed as Shifts for the Newton Basis */
    193:     KSPDGMRESComputeSchurForm_DGMRES(ksp, &Neig);
    194:   }

    196:   /* It may happen that the Ritz values from one cycle of GMRES are not accurate enough to provide a good stability. In this case, another cycle of GMRES is performed.  The two sets of values thus generated are sorted and the most accurate are kept as shifts */
    197:   PetscOptionsHasName(NULL,NULL, "-ksp_agmres_ImproveShifts", &flg);
    198:   if (!flg) {
    199:     KSPAGMRESLejaOrdering(agmres->wr, agmres->wi, agmres->Rshift, agmres->Ishift, max_k);
    200:   } else { /* Perform another cycle of DGMRES to find another set of eigenvalues */
    201:     PetscInt    i;
    202:     PetscScalar *wr, *wi,*Rshift, *Ishift;
    203:     PetscMalloc4(2*max_k, &wr, 2*max_k, &wi, 2*max_k, &Rshift, 2*max_k, &Ishift);
    204:     for (i = 0; i < max_k; i++) {
    205:       wr[i] = agmres->wr[i];
    206:       wi[i] = agmres->wi[i];
    207:     }

    209:     KSPSolve_DGMRES(ksp);

    211:     ksp->guess_zero = PETSC_FALSE;
    212:     if (ksp->reason == KSP_CONVERGED_RTOL) return 0;
    213:     else ksp->reason = KSP_CONVERGED_ITERATING;
    214:     if (agmres->neig > 0) { /* Compute the eigenvalues for the shifts) and the eigenvectors (to augment the Newton basis */
    215:       agmres->HasSchur = PETSC_FALSE;

    217:       KSPDGMRESComputeDeflationData_DGMRES(ksp, &Neig);
    218:       Neig = max_k;
    219:     } else { /* From DGMRES, compute only the eigenvalues needed as Shifts for the Newton Basis */
    220:       KSPDGMRESComputeSchurForm_DGMRES(ksp, &Neig);
    221:     }
    222:     for (i = 0; i < max_k; i++) {
    223:       wr[max_k+i] = agmres->wr[i];
    224:       wi[max_k+i] = agmres->wi[i];
    225:     }
    226:     KSPAGMRESLejaOrdering(wr, wi, Rshift, Ishift, 2*max_k);
    227:     for (i = 0; i< max_k; i++) {
    228:       agmres->Rshift[i] = Rshift[i];
    229:       agmres->Ishift[i] = Ishift[i];
    230:     }
    231:     PetscFree(Rshift);
    232:     PetscFree(wr);
    233:     PetscFree(Ishift);
    234:     PetscFree(wi);
    235:   }
    236:   agmres->HasShifts = PETSC_TRUE;
    237:   ksp->max_it       = max_it;
    238:   PetscLogEventEnd(KSP_AGMRESComputeShifts, ksp, 0,0,0);
    239:   return 0;
    240: }

    242: /*
    243:    Generate the basis vectors from the Newton polynomials with shifts and scaling factors
    244:    The scaling factors are computed to obtain unit vectors. Note that this step can be avoided with the preprocessing option KSP_AGMRES_NONORM.
    245:    Inputs :
    246:     - Operators (Matrix and preconditioners and the first basis vector in VEC_V(0)
    247:     - Shifts values in agmres->Rshift and agmres->Ishift.
    248:    Output :
    249:     - agmres->vecs or VEC_V : basis vectors
    250:     - agmres->Scale : Scaling factors (equal to 1 if no scaling is done)
    251: */
    252: static PetscErrorCode KSPAGMRESBuildBasis(KSP ksp)
    253: {
    254:   KSP_AGMRES     *agmres = (KSP_AGMRES*)ksp->data;
    255:   PetscReal      *Rshift = agmres->Rshift;
    256:   PetscReal      *Ishift = agmres->Ishift;
    257:   PetscReal      *Scale  = agmres->Scale;
    258:   const PetscInt max_k   = agmres->max_k;
    259:   PetscInt       KspSize = KSPSIZE;  /* if max_k == KspSizen then the basis should not be augmented */
    260:   PetscInt       j       = 1;

    262:   PetscLogEventBegin(KSP_AGMRESBuildBasis, ksp, 0,0,0);
    263:   Scale[0] = 1.0;
    264:   while (j <= max_k) {
    265:     if (Ishift[j-1] == 0) {
    266:       if ((ksp->pc_side == PC_LEFT) && agmres->r && agmres->DeflPrecond) {
    267:         /* Apply the precond-matrix operators */
    268:         KSP_PCApplyBAorAB(ksp, VEC_V(j-1), VEC_TMP, VEC_TMP_MATOP);
    269:         /* Then apply deflation as a preconditioner */
    270:         KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_TMP, VEC_V(j));
    271:       } else if ((ksp->pc_side == PC_RIGHT) && agmres->r && agmres->DeflPrecond) {
    272:         KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_V(j-1), VEC_TMP);
    273:         KSP_PCApplyBAorAB(ksp, VEC_TMP, VEC_V(j), VEC_TMP_MATOP);
    274:       } else {
    275:         KSP_PCApplyBAorAB(ksp, VEC_V(j-1), VEC_V(j), VEC_TMP_MATOP);
    276:       }
    277:       VecAXPY(VEC_V(j), -Rshift[j-1], VEC_V(j-1));
    278: #if defined(KSP_AGMRES_NONORM)
    279:       Scale[j] = 1.0;
    280: #else
    281:       VecScale(VEC_V(j), Scale[j-1]); /* This step can be postponed until all vectors are built */
    282:       VecNorm(VEC_V(j), NORM_2, &(Scale[j]));
    283:       Scale[j] = 1.0/Scale[j];
    284: #endif

    286:       agmres->matvecs += 1;
    287:       j++;
    288:     } else {
    289:       if ((ksp->pc_side == PC_LEFT) && agmres->r && agmres->DeflPrecond) {
    290:         /* Apply the precond-matrix operators */
    291:         KSP_PCApplyBAorAB(ksp, VEC_V(j-1), VEC_TMP, VEC_TMP_MATOP);
    292:         /* Then apply deflation as a preconditioner */
    293:         KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_TMP, VEC_V(j));
    294:       } else if ((ksp->pc_side == PC_RIGHT) && agmres->r && agmres->DeflPrecond) {
    295:         KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_V(j-1), VEC_TMP);
    296:         KSP_PCApplyBAorAB(ksp, VEC_TMP, VEC_V(j), VEC_TMP_MATOP);
    297:       } else {
    298:         KSP_PCApplyBAorAB(ksp, VEC_V(j-1), VEC_V(j), VEC_TMP_MATOP);
    299:       }
    300:       VecAXPY(VEC_V(j), -Rshift[j-1], VEC_V(j-1));
    301: #if defined(KSP_AGMRES_NONORM)
    302:       Scale[j] = 1.0;
    303: #else
    304:       VecScale(VEC_V(j), Scale[j-1]);
    305:       VecNorm(VEC_V(j), NORM_2, &(Scale[j]));
    306:       Scale[j] = 1.0/Scale[j];
    307: #endif
    308:       agmres->matvecs += 1;
    309:       j++;
    310:       if ((ksp->pc_side == PC_LEFT) && agmres->r && agmres->DeflPrecond) {
    311:         /* Apply the precond-matrix operators */
    312:         KSP_PCApplyBAorAB(ksp, VEC_V(j-1), VEC_TMP, VEC_TMP_MATOP);
    313:         /* Then apply deflation as a preconditioner */
    314:         KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_TMP, VEC_V(j));
    315:       } else if ((ksp->pc_side == PC_RIGHT) && agmres->r && agmres->DeflPrecond) {
    316:         KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_V(j-1), VEC_TMP);
    317:         KSP_PCApplyBAorAB(ksp, VEC_TMP, VEC_V(j), VEC_TMP_MATOP);
    318:       } else {
    319:         KSP_PCApplyBAorAB(ksp, VEC_V(j-1), VEC_V(j), VEC_TMP_MATOP);
    320:       }
    321:       VecAXPY(VEC_V(j), -Rshift[j-2], VEC_V(j-1));
    322:       VecAXPY(VEC_V(j), Scale[j-2]*Ishift[j-2]*Ishift[j-2], VEC_V(j-2));
    323: #if defined(KSP_AGMRES_NONORM)
    324:       Scale[j] = 1.0;
    325: #else
    326:       VecNorm(VEC_V(j), NORM_2, &(Scale[j]));
    327:       Scale[j] = 1.0/Scale[j];
    328: #endif
    329:       agmres->matvecs += 1;
    330:       j++;
    331:     }
    332:   }
    333:   /* Augment the subspace with the eigenvectors*/
    334:   while (j <= KspSize) {
    335:     KSP_PCApplyBAorAB(ksp, agmres->U[j - max_k - 1], VEC_V(j), VEC_TMP_MATOP);
    336: #if defined(KSP_AGMRES_NONORM)
    337:     Scale[j] = 1.0;
    338: #else
    339:     VecScale(VEC_V(j), Scale[j-1]);
    340:     VecNorm(VEC_V(j), NORM_2, &(Scale[j]));
    341:     Scale[j] = 1.0/Scale[j];
    342: #endif
    343:     agmres->matvecs += 1;
    344:     j++;
    345:   }
    346:   PetscLogEventEnd(KSP_AGMRESBuildBasis, ksp, 0,0,0);
    347:   return 0;
    348: }

    350: /*
    351:   Form the Hessenberg matrix for the Arnoldi-like relation.
    352:    Inputs :
    353:    - Shifts values in agmres->Rshift and agmres->Ishift
    354:    - RLoc : Triangular matrix from the RODDEC orthogonalization
    355:    Outputs :
    356:    - H = agmres->hh_origin : The Hessenberg matrix.

    358:    NOTE: Note that the computed Hessenberg matrix is not mathematically equivalent to that in the real Arnoldi process (in KSP GMRES). If it is needed, it can be explicitly  formed as H <-- H * RLoc^-1.
    359:  */
    360: static PetscErrorCode KSPAGMRESBuildHessenberg(KSP ksp)
    361: {
    362:   KSP_AGMRES     *agmres = (KSP_AGMRES*)ksp->data;
    363:   PetscScalar    *Rshift = agmres->Rshift;
    364:   PetscScalar    *Ishift = agmres->Ishift;
    365:   PetscScalar    *Scale  = agmres->Scale;
    366:   PetscInt       i       = 0, j = 0;
    367:   const PetscInt max_k   = agmres->max_k;
    368:   PetscInt       KspSize = KSPSIZE;
    369:   PetscInt       N       = MAXKSPSIZE+1;

    371:   PetscArrayzero(agmres->hh_origin, (N+1)*N);
    372:   while (j < max_k) {
    373:     /* Real shifts */
    374:     if (Ishift[j] == 0) {
    375:       for (i = 0; i <= j; i++) {
    376:         *H(i,j) = *RLOC(i,j+1)/Scale[j]  + (Rshift[j] * *RLOC(i,j));
    377:       }
    378:       *H(j+1,j) = *RLOC(j+1,j+1)/Scale[j];
    379:       j++;
    380:     } else if (Ishift[j] > 0) {
    381:       for (i = 0; i <= j; i++) {
    382:         *H(i,j) = *RLOC(i,j+1)/Scale[j] +  Rshift[j] * *RLOC(i, j);
    383:       }
    384:       *H(j+1,j) = *RLOC(j+1, j+1)/Scale[j];
    385:       j++;
    386:       for (i = 0; i <= j; i++) {
    387:         *H(i,j) = (*RLOC(i,j+1) + Rshift[j-1] * *RLOC(i,j) - Scale[j-1] * Ishift[j-1]*Ishift[j-1]* *RLOC(i,j-1));
    388:       }
    389:       *H(j,j) = (*RLOC(j,j+1) + Rshift[j-1] * *RLOC(j,j));
    390:       *H(j+1,j) = *RLOC(j+1,j+1);
    391:       j++;
    392:     } else SETERRQ(PetscObjectComm((PetscObject)ksp), PETSC_ERR_ORDER, "BAD ORDERING OF THE SHIFTS VALUES IN THE NEWTON BASIS");
    393:   }
    394:   for (j = max_k; j< KspSize; j++) { /* take into account the norm of the augmented vectors */
    395:     for (i = 0; i <= j+1; i++) *H(i,j) = *RLOC(i, j+1)/Scale[j];
    396:   }
    397:   return 0;
    398: }

    400: /*
    401:   Form the new approximate solution from the least-square problem
    402: */
    403: static PetscErrorCode KSPAGMRESBuildSoln(KSP ksp,PetscInt it)
    404: {
    405:   KSP_AGMRES     *agmres = (KSP_AGMRES*)ksp->data;
    406:   const PetscInt max_k = agmres->max_k;       /* Size of the non-augmented Krylov basis */
    407:   PetscInt       i, j;
    408:   PetscInt       r = agmres->r;           /* current number of augmented eigenvectors */
    409:   PetscBLASInt   KspSize;
    410:   PetscBLASInt   lC;
    411:   PetscBLASInt   N;
    412:   PetscBLASInt   ldH = it + 1;
    413:   PetscBLASInt   lwork;
    414:   PetscBLASInt   info, nrhs = 1;

    416:   PetscBLASIntCast(KSPSIZE,&KspSize);
    417:   PetscBLASIntCast(4 * (KspSize+1),&lwork);
    418:   PetscBLASIntCast(KspSize+1,&lC);
    419:   PetscBLASIntCast(MAXKSPSIZE + 1,&N);
    420:   PetscBLASIntCast(N + 1,&ldH);
    421:   /* Save a copy of the Hessenberg matrix */
    422:   for (j = 0; j < N-1; j++) {
    423:     for (i = 0; i < N; i++) {
    424:       *HS(i,j) = *H(i,j);
    425:     }
    426:   }
    427:   /* QR factorize the Hessenberg matrix */
    428:   PetscStackCallBLAS("LAPACKgeqrf",LAPACKgeqrf_(&lC, &KspSize, agmres->hh_origin, &ldH, agmres->tau, agmres->work, &lwork, &info));
    430:   /* Update the right hand side of the least square problem */
    431:   PetscArrayzero(agmres->nrs, N);

    433:   agmres->nrs[0] = ksp->rnorm;
    434:   PetscStackCallBLAS("LAPACKormqr",LAPACKormqr_("L", "T", &lC, &nrhs, &KspSize, agmres->hh_origin, &ldH, agmres->tau, agmres->nrs, &N, agmres->work, &lwork, &info));
    436:   ksp->rnorm = PetscAbsScalar(agmres->nrs[KspSize]);
    437:   /* solve the least-square problem */
    438:   PetscStackCallBLAS("LAPACKtrtrs",LAPACKtrtrs_("U", "N", "N", &KspSize, &nrhs, agmres->hh_origin, &ldH, agmres->nrs, &N, &info));
    440:   /* Accumulate the correction to the solution of the preconditioned problem in VEC_TMP */
    441:   VecZeroEntries(VEC_TMP);
    442:   VecMAXPY(VEC_TMP, max_k, agmres->nrs, &VEC_V(0));
    443:   if (!agmres->DeflPrecond) VecMAXPY(VEC_TMP, r, &agmres->nrs[max_k], agmres->U);

    445:   if ((ksp->pc_side == PC_RIGHT) && agmres->r && agmres->DeflPrecond) {
    446:     KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_TMP, VEC_TMP_MATOP);
    447:     VecCopy(VEC_TMP_MATOP, VEC_TMP);
    448:   }
    449:   KSPUnwindPreconditioner(ksp, VEC_TMP, VEC_TMP_MATOP);
    450:   /* add the solution to the previous one */
    451:   VecAXPY(ksp->vec_sol, 1.0, VEC_TMP);
    452:   return 0;
    453: }

    455: /*
    456:    Run  one cycle of the Newton-basis gmres, possibly augmented with eigenvectors.

    458:    Return residual history if requested.
    459:    Input :
    460:    - The vector VEC_V(0) is the initia residual
    461:    Output :
    462:     - the solution vector is in agmres->vec_sol
    463:    - itcount : number of inner iterations
    464:     - res : the new residual norm
    465:  .
    466:  NOTE: Unlike GMRES where the residual norm is available at each (inner) iteration,  here it is available at the end of the cycle.
    467: */
    468: static PetscErrorCode KSPAGMRESCycle(PetscInt *itcount,KSP ksp)
    469: {
    470:   KSP_AGMRES     *agmres = (KSP_AGMRES*)(ksp->data);
    471:   PetscReal      res;
    472:   PetscInt       KspSize = KSPSIZE;

    474:   /* check for the convergence */
    475:   res = ksp->rnorm; /* Norm of the initial residual vector */
    476:   if (!res) {
    477:     if (itcount) *itcount = 0;
    478:     ksp->reason = KSP_CONVERGED_ATOL;
    479:     PetscInfo(ksp,"Converged due to zero residual norm on entry\n");
    480:     return 0;
    481:   }
    482:   (*ksp->converged)(ksp,ksp->its,res,&ksp->reason,ksp->cnvP);
    483:   /* Build the Krylov basis with Newton polynomials */
    484:   KSPAGMRESBuildBasis(ksp);
    485:   /* QR Factorize the basis with RODDEC */
    486:   KSPAGMRESRoddec(ksp, KspSize+1);

    488:   /* Recover a (partial) Hessenberg matrix for the Arnoldi-like relation */
    489:   KSPAGMRESBuildHessenberg(ksp);
    490:   /* Solve the least square problem and unwind the preconditioner */
    491:   KSPAGMRESBuildSoln(ksp, KspSize);

    493:   res        = ksp->rnorm;
    494:   ksp->its  += KspSize;
    495:   agmres->it = KspSize-1;
    496:   /*  Test for the convergence */
    497:   (*ksp->converged)(ksp,ksp->its,res,&ksp->reason,ksp->cnvP);
    498:   KSPLogResidualHistory(ksp,res);
    499:   KSPMonitor(ksp,ksp->its,res);

    501:   *itcount = KspSize;
    502:   return 0;
    503: }

    505: static PetscErrorCode KSPSolve_AGMRES(KSP ksp)
    506: {
    507:   PetscInt       its;
    508:   KSP_AGMRES     *agmres    = (KSP_AGMRES*)ksp->data;
    509:   PetscBool      guess_zero = ksp->guess_zero;
    510:   PetscReal      res_old, res;
    511:   PetscInt       test;

    513:   PetscObjectSAWsTakeAccess((PetscObject)ksp);
    514:   ksp->its = 0;
    515:   PetscObjectSAWsGrantAccess((PetscObject)ksp);

    517:   if (!agmres->HasShifts) { /* Compute Shifts for the Newton basis */
    518:     KSPComputeShifts_DGMRES(ksp);
    519:   }
    520:   /* NOTE: At this step, the initial guess is not equal to zero since one cycle of the classical GMRES is performed to compute the shifts */
    521:   (*ksp->converged)(ksp,0,ksp->rnorm,&ksp->reason,ksp->cnvP);
    522:   while (!ksp->reason) {
    523:     KSPInitialResidual(ksp,ksp->vec_sol,VEC_TMP,VEC_TMP_MATOP,VEC_V(0),ksp->vec_rhs);
    524:     if ((ksp->pc_side == PC_LEFT) && agmres->r && agmres->DeflPrecond) {
    525:       KSPDGMRESApplyDeflation_DGMRES(ksp, VEC_V(0), VEC_TMP);
    526:       VecCopy(VEC_TMP, VEC_V(0));

    528:       agmres->matvecs += 1;
    529:     }
    530:     VecNormalize(VEC_V(0),&(ksp->rnorm));
    531:     KSPCheckNorm(ksp,ksp->rnorm);
    532:     res_old = ksp->rnorm; /* Record the residual norm to test if deflation is needed */

    534:     ksp->ops->buildsolution = KSPBuildSolution_AGMRES;

    536:     KSPAGMRESCycle(&its,ksp);
    537:     if (ksp->its >= ksp->max_it) {
    538:       if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
    539:       break;
    540:     }
    541:     /* compute the eigenvectors to augment the subspace : use an adaptive strategy */
    542:     res = ksp->rnorm;
    543:     if (!ksp->reason && agmres->neig > 0) {
    544:       test = agmres->max_k * PetscLogReal(ksp->rtol/res) / PetscLogReal(res/res_old); /* estimate the remaining number of steps */
    545:       if ((test > agmres->smv*(ksp->max_it-ksp->its)) || agmres->force) {
    546:         if (!agmres->force && ((test > agmres->bgv*(ksp->max_it-ksp->its)) && ((agmres->r + 1) < agmres->max_neig))) {
    547:           agmres->neig += 1; /* Augment the number of eigenvalues to deflate if the convergence is too slow */
    548:         }
    549:         KSPDGMRESComputeDeflationData_DGMRES(ksp,&agmres->neig);
    550:       }
    551:     }
    552:     ksp->guess_zero = PETSC_FALSE; /* every future call to KSPInitialResidual() will have nonzero guess */
    553:   }
    554:   ksp->guess_zero = guess_zero; /* restore if user has provided nonzero initial guess */
    555:   return 0;
    556: }

    558: static PetscErrorCode KSPDestroy_AGMRES(KSP ksp)
    559: {
    560:   KSP_AGMRES     *agmres = (KSP_AGMRES*)ksp->data;

    562:   PetscFree(agmres->hh_origin);

    564:   PetscFree(agmres->Qloc);
    565:   PetscFree4(agmres->Rshift,agmres->Ishift,agmres->Rloc,agmres->wbufptr);
    566:   PetscFree3(agmres->tau,agmres->work,agmres->nrs);
    567:   PetscFree4(agmres->Scale,agmres->sgn,agmres->tloc,agmres->temp);

    569:   PetscFree(agmres->select);
    570:   PetscFree(agmres->wr);
    571:   PetscFree(agmres->wi);
    572:   if (agmres->neig) {
    573:     VecDestroyVecs(MAXKSPSIZE,&agmres->TmpU);
    574:     PetscFree(agmres->perm);
    575:     PetscFree(agmres->MatEigL);
    576:     PetscFree(agmres->MatEigR);
    577:     PetscFree(agmres->Q);
    578:     PetscFree(agmres->Z);
    579:     PetscFree(agmres->beta);
    580:   }
    581:   KSPDestroy_DGMRES(ksp);
    582:   return 0;
    583: }

    585: static PetscErrorCode KSPView_AGMRES(KSP ksp,PetscViewer viewer)
    586: {
    587:   KSP_AGMRES     *agmres = (KSP_AGMRES*)ksp->data;
    588:   const char     *cstr   = "RODDEC ORTHOGONOLIZATION";
    589:   char           ritzvec[25];
    590:   PetscBool      iascii,isstring;
    591: #if defined(KSP_AGMRES_NONORM)
    592:   const char *Nstr = "SCALING FACTORS : NO";
    593: #else
    594:   const char *Nstr = "SCALING FACTORS : YES";
    595: #endif

    597:   PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
    598:   PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERSTRING,&isstring);

    600:   if (iascii) {
    601:     PetscViewerASCIIPrintf(viewer, " restart=%d using %s\n", agmres->max_k, cstr);
    602:     PetscViewerASCIIPrintf(viewer, " %s\n", Nstr);
    603:     PetscViewerASCIIPrintf(viewer, " Number of matvecs : %D\n", agmres->matvecs);
    604:     if (agmres->force) PetscViewerASCIIPrintf (viewer, " Adaptive strategy is used: FALSE\n");
    605:     else PetscViewerASCIIPrintf(viewer, " Adaptive strategy is used: TRUE\n");
    606:     if (agmres->DeflPrecond) {
    607:       PetscViewerASCIIPrintf(viewer, " STRATEGY OF DEFLATION: PRECONDITIONER \n");
    608:       PetscViewerASCIIPrintf(viewer, "  Frequency of extracted eigenvalues = %D\n", agmres->neig);
    609:       PetscViewerASCIIPrintf(viewer, "  Total number of extracted eigenvalues = %D\n", agmres->r);
    610:       PetscViewerASCIIPrintf(viewer, "  Maximum number of eigenvalues set to be extracted = %D\n", agmres->max_neig);
    611:     } else {
    612:       if (agmres->ritz) sprintf(ritzvec, "Ritz vectors");
    613:       else sprintf(ritzvec, "Harmonic Ritz vectors");
    614:       PetscViewerASCIIPrintf(viewer, " STRATEGY OF DEFLATION: AUGMENT\n");
    615:       PetscViewerASCIIPrintf(viewer," augmented vectors  %d at frequency %d with %s\n", agmres->r, agmres->neig, ritzvec);
    616:     }
    617:     PetscViewerASCIIPrintf(viewer, " Minimum relaxation parameter for the adaptive strategy(smv)  = %g\n", agmres->smv);
    618:     PetscViewerASCIIPrintf(viewer, " Maximum relaxation parameter for the adaptive strategy(bgv)  = %g\n", agmres->bgv);
    619:   } else if (isstring) {
    620:     PetscViewerStringSPrintf(viewer,"%s restart %D",cstr,agmres->max_k);
    621:   }
    622:   return 0;
    623: }

    625: static PetscErrorCode KSPSetFromOptions_AGMRES(PetscOptionItems *PetscOptionsObject,KSP ksp)
    626: {
    627:   PetscInt       neig;
    628:   KSP_AGMRES     *agmres = (KSP_AGMRES*)ksp->data;
    629:   PetscBool      flg;

    631:   KSPSetFromOptions_DGMRES(PetscOptionsObject,ksp);  /* Set common options from DGMRES and GMRES */
    632:   PetscOptionsHead(PetscOptionsObject,"KSP AGMRES Options");
    633:   PetscOptionsInt("-ksp_agmres_eigen", "Number of eigenvalues to deflate", "KSPDGMRESSetEigen", agmres->neig, &neig, &flg);
    634:   if (flg) {
    635:     KSPDGMRESSetEigen_DGMRES(ksp, neig);
    636:     agmres->r = 0;
    637:   } else agmres->neig = 0;
    638:   PetscOptionsInt("-ksp_agmres_maxeigen", "Maximum number of eigenvalues to deflate", "KSPDGMRESSetMaxEigen", agmres->max_neig, &neig, &flg);
    639:   if (flg) agmres->max_neig = neig+EIG_OFFSET;
    640:   else agmres->max_neig = agmres->neig+EIG_OFFSET;
    641:   PetscOptionsBool("-ksp_agmres_DeflPrecond", "Determine if the deflation should be applied as a preconditioner -- similar to KSP DGMRES", "KSPGMRESDeflPrecond",agmres->DeflPrecond,&agmres->DeflPrecond,NULL);
    642:   PetscOptionsBool("-ksp_agmres_ritz", "Compute the Ritz vectors instead of the Harmonic Ritz vectors ", "KSPGMRESHarmonic",agmres->ritz,&agmres->ritz ,&flg);
    643:   PetscOptionsReal("-ksp_agmres_MinRatio", "Relaxation parameter in the adaptive strategy; smallest multiple of the remaining number of steps allowed", "KSPGMRESSetMinRatio", agmres->smv, &agmres->smv, NULL);
    644:   PetscOptionsReal("-ksp_agmres_MaxRatio", "Relaxation parameter in the adaptive strategy; Largest multiple of the remaining number of steps allowed", "KSPGMRESSetMaxRatio",agmres->bgv,&agmres->bgv, &flg);
    645:   PetscOptionsTail();
    646:   return 0;
    647: }

    649: /*MC
    650:  KSPAGMRES - Newton basis GMRES implementation with adaptive augmented eigenvectors

    652: The techniques used are best described in [1]. The contribution of this work is that it combines many of the previous work to reduce the amount of MPI messages and improve the robustness of the global approach by using deflation techniques. It has been successfully applied to a class of real and industrial problems. Please see [1] for numerical experiments and [2] for a description of these problems.
    653: There are  many ongoing work that aim at avoiding (or minimizing) the communication in Krylov subspace methods. This code can be used as an experimental framework to combine several techniques in the particular case of GMRES. For instance, the computation of the shifts can be improved with techniques described in [3]. The orthogonalization technique can be replaced by TSQR [4]. The generation of the basis can be done using s-steps approaches[5].

    655:  Options Database Keys:
    656:  +   -ksp_gmres_restart <restart> -  the number of Krylov directions
    657:  .   -ksp_gmres_krylov_monitor - plot the Krylov space generated
    658:  .   -ksp_agmres_eigen <neig> - Number of eigenvalues to deflate (Number of vectors to augment)
    659:  .   -ksp_agmres_maxeigen <max_neig> - Maximum number of eigenvalues to deflate
    660:  .   -ksp_agmres_MinRatio <1> - Relaxation parameter in the adaptive strategy; smallest multiple of the remaining number of steps allowed
    661:  .   -ksp_agmres_MaxRatio <1> - Relaxation parameter in the adaptive strategy; Largest multiple of the remaining number of steps allowed
    662:  .   -ksp_agmres_DeflPrecond - Apply deflation as a preconditioner, this is similar to DGMRES but it rather builds a Newton basis.  This is an experimental option.
    663:  -   -ksp_dgmres_force <0, 1> - Force the deflation at each restart.

    665:  Level: beginner

    667:  Notes:
    668:     Left and right preconditioning are supported, but not symmetric preconditioning. Complex arithmetic is not supported

    670:  Developer Notes:
    671:     This object is subclassed off of KSPDGMRES

    673:  Contributed by Desire NUENTSA WAKAM, INRIA <desire.nuentsa_wakam@inria.fr>
    674:  Inputs from Guy Atenekeng <atenekeng@yahoo.com> and R.B. Sidje <roger.b.sidje@ua.edu>

    676:  References :
    677:  +   [1] D. Nuentsa Wakam and J. Erhel, Parallelism and robustness in GMRES with the Newton basis and the deflated restarting. Research report INRIA RR-7787, November 2011,https://hal.inria.fr/inria-00638247/en,  in revision for ETNA.
    678:  .  [2] D. NUENTSA WAKAM and F. PACULL, Memory Efficient Hybrid Algebraic Solvers for Linear Systems Arising from Compressible Flows, Computers and Fluids, In Press, http://dx.doi.org/10.1016/j.compfluid.2012.03.023
    679:  .  [3] B. Philippe and L. Reichel, On the generation of Krylov subspace bases, Applied Numerical
    680: Mathematics, 62(9), pp. 1171-1186, 2012
    681:  .  [4] J. Demmel, L. Grigori, M. F. Hoemmen, and J. Langou, Communication-optimal parallel and sequential QR and LU factorizations, SIAM journal on Scientific Computing, 34(1), A206-A239, 2012
    682:  .  [5] M. Mohiyuddin, M. Hoemmen, J. Demmel, and K. Yelick, Minimizing communication in sparse matrix solvers, in SC '09: Proceedings of the Conference on High Performance Computing Networking, Storage and Analysis, New York, NY, USA, 2009, ACM, pp. 1154-1171.
    683:  .    Sidje, Roger B. Alternatives for parallel Krylov subspace basis computation. Numer. Linear Algebra Appl. 4 (1997), no. 4, 305-331

    685:  .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPDGMRES, KSPPGMRES,
    686:  KSPGMRESSetRestart(), KSPGMRESSetHapTol(), KSPGMRESSetPreAllocateVectors(), KSPGMRESSetOrthogonalization(), KSPGMRESGetOrthogonalization(),
    687:  KSPGMRESClassicalGramSchmidtOrthogonalization(), KSPGMRESModifiedGramSchmidtOrthogonalization(),
    688:  KSPGMRESCGSRefinementType, KSPGMRESSetCGSRefinementType(), KSPGMRESGetCGSRefinementType(), KSPGMRESMonitorKrylov(), KSPSetPCSide()
    689:  M*/

    691: PETSC_EXTERN PetscErrorCode KSPCreate_AGMRES(KSP ksp)
    692: {
    693:   KSP_AGMRES     *agmres;

    695:   PetscNewLog(ksp,&agmres);
    696:   ksp->data = (void*)agmres;

    698:   KSPSetSupportedNorm(ksp,KSP_NORM_PRECONDITIONED,PC_LEFT,3);
    699:   KSPSetSupportedNorm(ksp,KSP_NORM_UNPRECONDITIONED,PC_RIGHT,2);
    700:   ksp->ops->buildsolution                = KSPBuildSolution_AGMRES;
    701:   ksp->ops->setup                        = KSPSetUp_AGMRES;
    702:   ksp->ops->solve                        = KSPSolve_AGMRES;
    703:   ksp->ops->destroy                      = KSPDestroy_AGMRES;
    704:   ksp->ops->view                         = KSPView_AGMRES;
    705:   ksp->ops->setfromoptions               = KSPSetFromOptions_AGMRES;
    706:   ksp->guess_zero                        = PETSC_TRUE;
    707:   ksp->ops->computeextremesingularvalues = KSPComputeExtremeSingularValues_GMRES;
    708:   ksp->ops->computeeigenvalues           = KSPComputeEigenvalues_GMRES;

    710:   PetscObjectComposeFunction((PetscObject) ksp,"KSPGMRESSetPreAllocateVectors_C",KSPGMRESSetPreAllocateVectors_GMRES);
    711:   PetscObjectComposeFunction((PetscObject) ksp,"KSPGMRESSetOrthogonalization_C",KSPGMRESSetOrthogonalization_GMRES);
    712:   PetscObjectComposeFunction((PetscObject) ksp,"KSPGMRESSetRestart_C",KSPGMRESSetRestart_GMRES);
    713:   PetscObjectComposeFunction((PetscObject) ksp,"KSPGMRESSetHapTol_C",KSPGMRESSetHapTol_GMRES);
    714:   PetscObjectComposeFunction((PetscObject) ksp,"KSPGMRESSetCGSRefinementType_C",KSPGMRESSetCGSRefinementType_GMRES);
    715:   /* -- New functions defined in DGMRES -- */
    716:   PetscObjectComposeFunction((PetscObject) ksp, "KSPDGMRESSetEigen_C",KSPDGMRESSetEigen_DGMRES);
    717:   PetscObjectComposeFunction((PetscObject) ksp, "KSPDGMRESComputeSchurForm_C",KSPDGMRESComputeSchurForm_DGMRES);
    718:   PetscObjectComposeFunction((PetscObject) ksp, "KSPDGMRESComputeDeflationData_C",KSPDGMRESComputeDeflationData_DGMRES);
    719:   PetscObjectComposeFunction((PetscObject) ksp, "KSPDGMRESApplyDeflation_C",KSPDGMRESApplyDeflation_DGMRES);

    721:   PetscLogEventRegister("AGMRESCompDefl",   KSP_CLASSID, &KSP_AGMRESComputeDeflationData);
    722:   PetscLogEventRegister("AGMRESBuildBasis", KSP_CLASSID, &KSP_AGMRESBuildBasis);
    723:   PetscLogEventRegister("AGMRESCompShifts", KSP_CLASSID, &KSP_AGMRESComputeShifts);
    724:   PetscLogEventRegister("AGMRESOrthog",     KSP_CLASSID, &KSP_AGMRESRoddec);

    726:   agmres->haptol         = 1.0e-30;
    727:   agmres->q_preallocate  = 0;
    728:   agmres->delta_allocate = AGMRES_DELTA_DIRECTIONS;
    729:   agmres->orthog         = KSPGMRESClassicalGramSchmidtOrthogonalization;
    730:   agmres->nrs            = NULL;
    731:   agmres->sol_temp       = NULL;
    732:   agmres->max_k          = AGMRES_DEFAULT_MAXK;
    733:   agmres->Rsvd           = NULL;
    734:   agmres->cgstype        = KSP_GMRES_CGS_REFINE_NEVER;
    735:   agmres->orthogwork     = NULL;

    737:   /* Default values for the deflation */
    738:   agmres->r           = 0;
    739:   agmres->neig        = 0;
    740:   agmres->max_neig    = 0;
    741:   agmres->lambdaN     = 0.0;
    742:   agmres->smv         = SMV;
    743:   agmres->bgv         = 1;
    744:   agmres->force       = PETSC_FALSE;
    745:   agmres->matvecs     = 0;
    746:   agmres->improve     = PETSC_FALSE;
    747:   agmres->HasShifts   = PETSC_FALSE;
    748:   agmres->r           = 0;
    749:   agmres->HasSchur    = PETSC_FALSE;
    750:   agmres->DeflPrecond = PETSC_FALSE;
    751:   PetscObjectGetNewTag((PetscObject)ksp,&agmres->tag);
    752:   return 0;
    753: }