Date: 2026-03-31T13:23:32+00:00

Actual source code: arnoldi.c

      1: /*
      2:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      3:    SLEPc - Scalable Library for Eigenvalue Problem Computations
      4:    Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain

      6:    This file is part of SLEPc.
      7:    SLEPc is distributed under a 2-clause BSD license (see LICENSE).
      8:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      9: */
     10: /*
     11:    SLEPc eigensolver: "arnoldi"

     13:    Method: Explicitly Restarted Arnoldi

     15:    Algorithm:

     17:        Arnoldi method with explicit restart and deflation.

     19:    References:

     21:        [1] "Arnoldi Methods in SLEPc", SLEPc Technical Report STR-4,
     22:            available at https://slepc.upv.es.
     23: */

     25: #include <slepc/private/epsimpl.h>

     27: typedef struct {
     28:   PetscBool delayed;
     29: } EPS_ARNOLDI;

     31: static PetscErrorCode EPSSetUp_Arnoldi(EPS eps)
     32: {
     33:   PetscFunctionBegin;
     34:   EPSCheckDefinite(eps);
     35:   EPSCheckNotStructured(eps);
     36:   PetscCall(EPSSetDimensions_Default(eps,&eps->nev,&eps->ncv,&eps->mpd));
     37:   PetscCheck(eps->ncv<=eps->nev+eps->mpd,PetscObjectComm((PetscObject)eps),PETSC_ERR_USER_INPUT,"The value of ncv must not be larger than nev+mpd");
     38:   if (eps->max_it==PETSC_DETERMINE) eps->max_it = PetscMax(100,2*eps->n/eps->ncv);
     39:   if (!eps->which) PetscCall(EPSSetWhichEigenpairs_Default(eps));
     40:   PetscCheck(eps->which!=EPS_ALL,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"This solver does not support computing all eigenvalues");
     41:   EPSCheckUnsupported(eps,EPS_FEATURE_ARBITRARY | EPS_FEATURE_THRESHOLD | EPS_FEATURE_TWOSIDED);

     43:   PetscCall(EPSAllocateSolution(eps,1));
     44:   PetscCall(EPS_SetInnerProduct(eps));
     45:   PetscCall(DSSetType(eps->ds,DSNHEP));
     46:   if (eps->extraction==EPS_REFINED || eps->extraction==EPS_REFINED_HARMONIC) PetscCall(DSSetRefined(eps->ds,PETSC_TRUE));
     47:   PetscCall(DSSetExtraRow(eps->ds,PETSC_TRUE));
     48:   PetscCall(DSAllocate(eps->ds,eps->ncv+1));
     49:   PetscFunctionReturn(PETSC_SUCCESS);
     50: }

     52: static PetscErrorCode EPSSolve_Arnoldi(EPS eps)
     53: {
     54:   PetscInt           k,nv,ld;
     55:   Mat                U,Op,H;
     56:   PetscScalar        *Harray;
     57:   PetscReal          beta,gamma=1.0;
     58:   PetscBool          breakdown,harmonic,refined;
     59:   BVOrthogRefineType orthog_ref;
     60:   EPS_ARNOLDI        *arnoldi = (EPS_ARNOLDI*)eps->data;

     62:   PetscFunctionBegin;
     63:   PetscCall(DSGetLeadingDimension(eps->ds,&ld));
     64:   PetscCall(DSGetRefined(eps->ds,&refined));
     65:   harmonic = (eps->extraction==EPS_HARMONIC || eps->extraction==EPS_REFINED_HARMONIC)?PETSC_TRUE:PETSC_FALSE;
     66:   PetscCall(BVGetOrthogonalization(eps->V,NULL,&orthog_ref,NULL,NULL));

     68:   /* Get the starting Arnoldi vector */
     69:   PetscCall(EPSGetStartVector(eps,0,NULL));

     71:   /* Restart loop */
     72:   while (eps->reason == EPS_CONVERGED_ITERATING) {
     73:     eps->its++;

     75:     /* Compute an nv-step Arnoldi factorization */
     76:     nv = PetscMin(eps->nconv+eps->mpd,eps->ncv);
     77:     PetscCall(DSSetDimensions(eps->ds,nv,eps->nconv,0));
     78:     if (!arnoldi->delayed) {
     79:       PetscCall(STGetOperator(eps->st,&Op));
     80:       PetscCall(DSGetMat(eps->ds,DS_MAT_A,&H));
     81:       PetscCall(BVMatArnoldi(eps->V,Op,H,eps->nconv,&nv,&beta,&breakdown));
     82:       PetscCall(DSRestoreMat(eps->ds,DS_MAT_A,&H));
     83:       PetscCall(STRestoreOperator(eps->st,&Op));
     84:     } else if (orthog_ref == BV_ORTHOG_REFINE_NEVER) {
     85:       PetscCall(DSGetArray(eps->ds,DS_MAT_A,&Harray));
     86:       PetscCall(EPSDelayedArnoldi1(eps,Harray,ld,eps->nconv,&nv,&beta,&breakdown));
     87:       PetscCall(DSRestoreArray(eps->ds,DS_MAT_A,&Harray));
     88:     } else {
     89:       PetscCall(DSGetArray(eps->ds,DS_MAT_A,&Harray));
     90:       PetscCall(EPSDelayedArnoldi(eps,Harray,ld,eps->nconv,&nv,&beta,&breakdown));
     91:       PetscCall(DSRestoreArray(eps->ds,DS_MAT_A,&Harray));
     92:     }
     93:     PetscCall(DSSetState(eps->ds,DS_STATE_INTERMEDIATE));
     94:     PetscCall(BVSetActiveColumns(eps->V,eps->nconv,nv));

     96:     /* Compute translation of Krylov decomposition if harmonic extraction used */
     97:     if (harmonic) PetscCall(DSTranslateHarmonic(eps->ds,eps->target,beta,PETSC_FALSE,NULL,&gamma));

     99:     /* Solve projected problem */
    100:     PetscCall(DSSolve(eps->ds,eps->eigr,eps->eigi));
    101:     PetscCall(DSSort(eps->ds,eps->eigr,eps->eigi,NULL,NULL,NULL));
    102:     PetscCall(DSUpdateExtraRow(eps->ds));
    103:     PetscCall(DSSynchronize(eps->ds,eps->eigr,eps->eigi));

    105:     /* Check convergence */
    106:     PetscCall(EPSKrylovConvergence(eps,PETSC_FALSE,eps->nconv,nv-eps->nconv,beta,0.0,gamma,&k));
    107:     if (refined) {
    108:       PetscCall(DSGetMat(eps->ds,DS_MAT_X,&U));
    109:       PetscCall(BVMultInPlace(eps->V,U,eps->nconv,k+1));
    110:       PetscCall(DSRestoreMat(eps->ds,DS_MAT_X,&U));
    111:       PetscCall(BVOrthonormalizeColumn(eps->V,k,PETSC_FALSE,NULL,NULL));
    112:     } else {
    113:       PetscCall(DSGetMat(eps->ds,DS_MAT_Q,&U));
    114:       PetscCall(BVMultInPlace(eps->V,U,eps->nconv,PetscMin(k+1,nv)));
    115:       PetscCall(DSRestoreMat(eps->ds,DS_MAT_Q,&U));
    116:     }
    117:     PetscCall((*eps->stopping)(eps,eps->its,eps->max_it,k,eps->nev,&eps->reason,eps->stoppingctx));
    118:     if (eps->reason == EPS_CONVERGED_ITERATING && breakdown) {
    119:       PetscCall(PetscInfo(eps,"Breakdown in Arnoldi method (it=%" PetscInt_FMT " norm=%g)\n",eps->its,(double)beta));
    120:       PetscCall(EPSGetStartVector(eps,k,&breakdown));
    121:       if (breakdown) {
    122:         eps->reason = EPS_DIVERGED_BREAKDOWN;
    123:         PetscCall(PetscInfo(eps,"Unable to generate more start vectors\n"));
    124:       }
    125:     }
    126:     eps->nconv = k;
    127:     PetscCall(EPSMonitor(eps,eps->its,eps->nconv,eps->eigr,eps->eigi,eps->errest,nv));
    128:   }
    129:   PetscCall(DSTruncate(eps->ds,eps->nconv,PETSC_TRUE));
    130:   PetscFunctionReturn(PETSC_SUCCESS);
    131: }

    133: static PetscErrorCode EPSSetFromOptions_Arnoldi(EPS eps,PetscOptionItems PetscOptionsObject)
    134: {
    135:   PetscBool      set,val;
    136:   EPS_ARNOLDI    *arnoldi = (EPS_ARNOLDI*)eps->data;

    138:   PetscFunctionBegin;
    139:   PetscOptionsHeadBegin(PetscOptionsObject,"EPS Arnoldi Options");

    141:     PetscCall(PetscOptionsBool("-eps_arnoldi_delayed","Use delayed reorthogonalization","EPSArnoldiSetDelayed",arnoldi->delayed,&val,&set));
    142:     if (set) PetscCall(EPSArnoldiSetDelayed(eps,val));

    144:   PetscOptionsHeadEnd();
    145:   PetscFunctionReturn(PETSC_SUCCESS);
    146: }

    148: static PetscErrorCode EPSArnoldiSetDelayed_Arnoldi(EPS eps,PetscBool delayed)
    149: {
    150:   EPS_ARNOLDI *arnoldi = (EPS_ARNOLDI*)eps->data;

    152:   PetscFunctionBegin;
    153:   arnoldi->delayed = delayed;
    154:   PetscFunctionReturn(PETSC_SUCCESS);
    155: }

    157: /*@
    158:    EPSArnoldiSetDelayed - Activates or deactivates delayed reorthogonalization
    159:    in the Arnoldi iteration.

    161:    Logically Collective

    163:    Input Parameters:
    164: +  eps - the linear eigensolver context
    165: -  delayed - boolean flag

    167:    Options Database Key:
    168: .  -eps_arnoldi_delayed (true|false) - activates delayed reorthogonalization in `EPSARNOLDI`

    170:    Note:
    171:    Delayed reorthogonalization is an aggressive optimization for the `EPSARNOLDI`
    172:    eigensolver than may provide better scalability, but sometimes makes the
    173:    solver converge more slowly compared to the default algorithm. The details
    174:    can be found in {cite:p}`Her07`.

    176:    Level: advanced

    178: .seealso: [](ch:eps), `EPSARNOLDI`, `EPSArnoldiGetDelayed()`
    179: @*/
    180: PetscErrorCode EPSArnoldiSetDelayed(EPS eps,PetscBool delayed)
    181: {
    182:   PetscFunctionBegin;
    185:   PetscTryMethod(eps,"EPSArnoldiSetDelayed_C",(EPS,PetscBool),(eps,delayed));
    186:   PetscFunctionReturn(PETSC_SUCCESS);
    187: }

    189: static PetscErrorCode EPSArnoldiGetDelayed_Arnoldi(EPS eps,PetscBool *delayed)
    190: {
    191:   EPS_ARNOLDI *arnoldi = (EPS_ARNOLDI*)eps->data;

    193:   PetscFunctionBegin;
    194:   *delayed = arnoldi->delayed;
    195:   PetscFunctionReturn(PETSC_SUCCESS);
    196: }

    198: /*@
    199:    EPSArnoldiGetDelayed - Gets the type of reorthogonalization used during the Arnoldi
    200:    iteration.

    202:    Not Collective

    204:    Input Parameter:
    205: .  eps - the linear eigensolver context

    207:    Output Parameter:
    208: .  delayed - boolean flag indicating if delayed reorthogonalization has been enabled

    210:    Level: advanced

    212: .seealso: [](ch:eps), `EPSARNOLDI`, `EPSArnoldiSetDelayed()`
    213: @*/
    214: PetscErrorCode EPSArnoldiGetDelayed(EPS eps,PetscBool *delayed)
    215: {
    216:   PetscFunctionBegin;
    218:   PetscAssertPointer(delayed,2);
    219:   PetscUseMethod(eps,"EPSArnoldiGetDelayed_C",(EPS,PetscBool*),(eps,delayed));
    220:   PetscFunctionReturn(PETSC_SUCCESS);
    221: }

    223: static PetscErrorCode EPSDestroy_Arnoldi(EPS eps)
    224: {
    225:   PetscFunctionBegin;
    226:   PetscCall(PetscFree(eps->data));
    227:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSArnoldiSetDelayed_C",NULL));
    228:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSArnoldiGetDelayed_C",NULL));
    229:   PetscFunctionReturn(PETSC_SUCCESS);
    230: }

    232: static PetscErrorCode EPSView_Arnoldi(EPS eps,PetscViewer viewer)
    233: {
    234:   PetscBool      isascii;
    235:   EPS_ARNOLDI    *arnoldi = (EPS_ARNOLDI*)eps->data;

    237:   PetscFunctionBegin;
    238:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
    239:   if (isascii && arnoldi->delayed) PetscCall(PetscViewerASCIIPrintf(viewer,"  using delayed reorthogonalization\n"));
    240:   PetscFunctionReturn(PETSC_SUCCESS);
    241: }

    243: /*MC
    244:    EPSARNOLDI - EPSARNOLDI = "arnoldi" - A basic Krylov method for non-Hermitian
    245:    problems, with explicit restart.

    247:    Notes:
    248:    This solver is very basic and is not recommended in general, since it
    249:    will not be competitive with respect to other solvers.

    251:    The implemented method is Arnoldi with explicit restart and deflation.
    252:    Generally, it is much better to use implicit restart as in `EPSKRYLOVSCHUR`.

    254:    Level: beginner

    256: .seealso: [](ch:eps), `EPS`, `EPSType`, `EPSSetType()`
    257: M*/
    258: SLEPC_EXTERN PetscErrorCode EPSCreate_Arnoldi(EPS eps)
    259: {
    260:   EPS_ARNOLDI    *ctx;

    262:   PetscFunctionBegin;
    263:   PetscCall(PetscNew(&ctx));
    264:   eps->data = (void*)ctx;

    266:   eps->useds = PETSC_TRUE;

    268:   eps->ops->solve          = EPSSolve_Arnoldi;
    269:   eps->ops->setup          = EPSSetUp_Arnoldi;
    270:   eps->ops->setupsort      = EPSSetUpSort_Default;
    271:   eps->ops->setfromoptions = EPSSetFromOptions_Arnoldi;
    272:   eps->ops->destroy        = EPSDestroy_Arnoldi;
    273:   eps->ops->view           = EPSView_Arnoldi;
    274:   eps->ops->backtransform  = EPSBackTransform_Default;
    275:   eps->ops->computevectors = EPSComputeVectors_Schur;

    277:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSArnoldiSetDelayed_C",EPSArnoldiSetDelayed_Arnoldi));
    278:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSArnoldiGetDelayed_C",EPSArnoldiGetDelayed_Arnoldi));
    279:   PetscFunctionReturn(PETSC_SUCCESS);
    280: }