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

Actual source code: chase.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:    This file implements a wrapper to eigensolvers in ChASE.
     12: */

     14: #include <slepc/private/epsimpl.h>
     15: #include <petsc/private/petscscalapack.h>

     17: #if !defined(PETSC_USE_COMPLEX)
     18: #if defined(PETSC_USE_REAL_SINGLE)
     19: /* s */
     20: #define CHASEchase_init_blockcyclic pschase_init_blockcyclic_
     21: #define CHASEchase                  pschase_
     22: #define CHASEchase_finalize         pschase_finalize_
     23: #elif defined(PETSC_USE_REAL_DOUBLE)
     24: /* d */
     25: #define CHASEchase_init_blockcyclic pdchase_init_blockcyclic_
     26: #define CHASEchase                  pdchase_
     27: #define CHASEchase_finalize         pdchase_finalize_
     28: #endif
     29: #else
     30: #if defined(PETSC_USE_REAL_SINGLE)
     31: /* c */
     32: #define CHASEchase_init_blockcyclic pcchase_init_blockcyclic_
     33: #define CHASEchase                  pcchase_
     34: #define CHASEchase_finalize         pcchase_finalize_
     35: #elif defined(PETSC_USE_REAL_DOUBLE)
     36: /* z */
     37: #define CHASEchase_init_blockcyclic pzchase_init_blockcyclic_
     38: #define CHASEchase                  pzchase_
     39: #define CHASEchase_finalize         pzchase_finalize_
     40: #endif
     41: #endif

     43: SLEPC_EXTERN void CHASEchase_init_blockcyclic(PetscInt*,PetscInt*,PetscInt*,PetscInt*,PetscInt*,PetscScalar*,PetscInt*,PetscScalar*,PetscReal*,PetscInt*,PetscInt*,char*,PetscInt*,PetscInt*,MPI_Comm*,PetscInt*);
     44: SLEPC_EXTERN void CHASEchase(PetscInt*,PetscReal*,char*,char*,char*);
     45: SLEPC_EXTERN void CHASEchase_finalize(PetscInt*);

     47: typedef struct {
     48:   Mat       As;    /* converted matrix */
     49:   PetscInt  nex;   /* extra searching space size */
     50:   PetscInt  deg;   /* initial degree of Chebyshev polynomial filter */
     51:   PetscBool opt;   /* internal optimization of polynomial degree */
     52: } EPS_ChASE;

     54: static PetscErrorCode EPSSetUp_ChASE(EPS eps)
     55: {
     56:   EPS_ChASE      *ctx = (EPS_ChASE*)eps->data;
     57:   Mat            A;
     58:   PetscBool      isshift;
     59:   PetscScalar    shift;

     61:   PetscFunctionBegin;
     62:   EPSCheckStandard(eps);
     63:   EPSCheckHermitian(eps);
     64:   EPSCheckNotStructured(eps);
     65:   PetscCall(PetscObjectTypeCompare((PetscObject)eps->st,STSHIFT,&isshift));
     66:   PetscCheck(isshift,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"This solver does not support spectral transformations");
     67:   if (eps->nev==0) eps->nev = 1;
     68:   if (eps->ncv==PETSC_DETERMINE) eps->ncv = PetscMin(eps->n,PetscMax(2*eps->nev,eps->nev+15));
     69:   else PetscCheck(eps->ncv>=eps->nev+1 || (eps->ncv==eps->nev && eps->ncv==eps->n),PetscObjectComm((PetscObject)eps),PETSC_ERR_USER_INPUT,"The value of ncv must be at least nev+1");
     70:   if (eps->mpd!=PETSC_DETERMINE) PetscCall(PetscInfo(eps,"Warning: parameter mpd ignored\n"));
     71:   if (eps->max_it==PETSC_DETERMINE) eps->max_it = 1;
     72:   if (!eps->which) eps->which = EPS_SMALLEST_REAL;
     73:   PetscCheck(eps->which==EPS_SMALLEST_REAL,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"This solver only supports computation of leftmost eigenvalues");
     74:   EPSCheckUnsupported(eps,EPS_FEATURE_BALANCE | EPS_FEATURE_ARBITRARY | EPS_FEATURE_REGION);
     75:   EPSCheckIgnored(eps,EPS_FEATURE_EXTRACTION | EPS_FEATURE_CONVERGENCE | EPS_FEATURE_STOPPING);
     76:   PetscCall(EPSAllocateSolution(eps,0));

     78:   ctx->nex = eps->ncv-eps->nev;
     79:   if (ctx->deg==PETSC_DEFAULT) ctx->deg = 10;

     81:   /* convert matrices */
     82:   PetscCall(MatDestroy(&ctx->As));
     83:   PetscCall(STGetMatrix(eps->st,0,&A));
     84:   PetscCall(MatConvert(A,MATSCALAPACK,MAT_INITIAL_MATRIX,&ctx->As));
     85:   PetscCall(STGetShift(eps->st,&shift));
     86:   if (shift != 0.0) PetscCall(MatShift(ctx->As,-shift));
     87:   PetscFunctionReturn(PETSC_SUCCESS);
     88: }

     90: static PetscErrorCode EPSSolve_ChASE(EPS eps)
     91: {
     92:   EPS_ChASE      *ctx = (EPS_ChASE*)eps->data;
     93:   Mat            As = ctx->As,Vs,V;
     94:   Mat_ScaLAPACK  *a = (Mat_ScaLAPACK*)As->data,*v;
     95:   MPI_Comm       comm;
     96:   PetscReal      *w = eps->errest;  /* used to store real eigenvalues */
     97:   PetscInt       i,init=0,flg=1;
     98:   char           grid_major='R',mode='X',opt=ctx->opt?'S':'X',qr='C';

    100:   PetscFunctionBegin;
    101:   PetscCheck(a->grid->npcol==1,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"Current implementation of the ChASE interface only supports process grids with one column; use -mat_scalapack_grid_height <p> where <p> is the number of processes");
    102:   PetscCall(BVGetMat(eps->V,&V));
    103:   PetscCall(MatConvert(V,MATSCALAPACK,MAT_INITIAL_MATRIX,&Vs));
    104:   PetscCall(BVRestoreMat(eps->V,&V));
    105:   v = (Mat_ScaLAPACK*)Vs->data;

    107:   /* initialization */
    108:   PetscCall(PetscObjectGetComm((PetscObject)As,&comm));
    109:   CHASEchase_init_blockcyclic(&a->N,&eps->nev,&ctx->nex,&a->mb,&a->nb,a->loc,&a->lld,v->loc,w,&a->grid->nprow,&a->grid->npcol,&grid_major,&a->rsrc,&a->csrc,&comm,&init);
    110:   PetscCheck(init==1,PetscObjectComm((PetscObject)eps),PETSC_ERR_LIB,"Problem initializing ChASE");

    112:   /* solve */
    113:   CHASEchase(&ctx->deg,&eps->tol,&mode,&opt,&qr);
    114:   CHASEchase_finalize(&flg);
    115:   PetscCheck(flg==0,PetscObjectComm((PetscObject)eps),PETSC_ERR_LIB,"Problem solving with ChASE");

    117:   for (i=0;i<eps->ncv;i++) {
    118:     eps->eigr[i]   = eps->errest[i];
    119:     eps->errest[i] = eps->tol;
    120:   }

    122:   PetscCall(BVGetMat(eps->V,&V));
    123:   PetscCall(MatConvert(Vs,MATDENSE,MAT_REUSE_MATRIX,&V));
    124:   PetscCall(BVRestoreMat(eps->V,&V));
    125:   PetscCall(MatDestroy(&Vs));

    127:   eps->nconv  = eps->nev;
    128:   eps->its    = 1;
    129:   eps->reason = EPS_CONVERGED_TOL;
    130:   PetscFunctionReturn(PETSC_SUCCESS);
    131: }

    133: static PetscErrorCode EPSCHASESetDegree_ChASE(EPS eps,PetscInt deg,PetscBool opt)
    134: {
    135:   EPS_ChASE *ctx = (EPS_ChASE*)eps->data;

    137:   PetscFunctionBegin;
    138:   if (deg==PETSC_DEFAULT || deg==PETSC_DECIDE) {
    139:     ctx->deg   = PETSC_DEFAULT;
    140:     eps->state = EPS_STATE_INITIAL;
    141:   } else {
    142:     PetscCheck(deg>0,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"The degree must be >0");
    143:     ctx->deg = deg;
    144:   }
    145:   ctx->opt = opt;
    146:   PetscFunctionReturn(PETSC_SUCCESS);
    147: }

    149: /*@
    150:    EPSCHASESetDegree - Sets the degree of the Chebyshev polynomial filter in the ChASE solver.

    152:    Logically Collective

    154:    Input Parameters:
    155: +  eps - the linear eigensolver context
    156: .  deg - initial degree of Chebyshev polynomial filter
    157: -  opt - internal optimization of polynomial degree

    159:    Options Database Keys:
    160: +  -eps_chase_degree deg              - set the initial degree
    161: -  -eps_chase_degree_opt (true|false) - toggle the optimization

    163:    Note:
    164:    See the documentation of ChASE {cite:p}`Win19` for details.

    166:    Level: advanced

    168: .seealso: [](ch:eps), `EPSCHASE`, `EPSCHASEGetDegree()`
    169: @*/
    170: PetscErrorCode EPSCHASESetDegree(EPS eps,PetscInt deg,PetscBool opt)
    171: {
    172:   PetscFunctionBegin;
    176:   PetscTryMethod(eps,"EPSCHASESetDegree_C",(EPS,PetscInt,PetscBool),(eps,deg,opt));
    177:   PetscFunctionReturn(PETSC_SUCCESS);
    178: }

    180: static PetscErrorCode EPSCHASEGetDegree_ChASE(EPS eps,PetscInt *deg,PetscBool *opt)
    181: {
    182:   EPS_ChASE *ctx = (EPS_ChASE*)eps->data;

    184:   PetscFunctionBegin;
    185:   if (deg) *deg = ctx->deg;
    186:   if (opt) *opt = ctx->opt;
    187:   PetscFunctionReturn(PETSC_SUCCESS);
    188: }

    190: /*@
    191:    EPSCHASEGetDegree - Gets the degree of the Chebyshev polynomial filter used in the ChASE solver.

    193:    Not Collective

    195:    Input Parameter:
    196: .  eps - the linear eigensolver context

    198:    Output Parameters:
    199: +  deg - initial degree of Chebyshev polynomial filter
    200: -  opt - internal optimization of polynomial degree

    202:    Level: advanced

    204: .seealso: [](ch:eps), `EPSCHASE`, `EPSCHASESetDegree()`
    205: @*/
    206: PetscErrorCode EPSCHASEGetDegree(EPS eps,PetscInt *deg,PetscBool *opt)
    207: {
    208:   PetscFunctionBegin;
    210:   PetscUseMethod(eps,"EPSCHASEGetDegree_C",(EPS,PetscInt*,PetscBool*),(eps,deg,opt));
    211:   PetscFunctionReturn(PETSC_SUCCESS);
    212: }

    214: static PetscErrorCode EPSSetFromOptions_ChASE(EPS eps,PetscOptionItems PetscOptionsObject)
    215: {
    216:   EPS_ChASE   *ctx = (EPS_ChASE*)eps->data;
    217:   PetscBool   flg1,flg2;
    218:   PetscInt    deg;
    219:   PetscBool   opt;

    221:   PetscFunctionBegin;
    222:   PetscOptionsHeadBegin(PetscOptionsObject,"EPS ChASE Options");

    224:     deg = ctx->deg;
    225:     PetscCall(PetscOptionsInt("-eps_chase_degree","Initial degree of Chebyshev polynomial filter","EPSCHASESetDegree",deg,&deg,&flg1));
    226:     opt = ctx->opt;
    227:     PetscCall(PetscOptionsBool("-eps_chase_degree_opt","Internal optimization of polynomial degree","EPSCHASESetDegree",opt,&opt,&flg2));
    228:     if (flg1 || flg2) PetscCall(EPSCHASESetDegree(eps,deg,opt));

    230:   PetscOptionsHeadEnd();
    231:   PetscFunctionReturn(PETSC_SUCCESS);
    232: }

    234: static PetscErrorCode EPSDestroy_ChASE(EPS eps)
    235: {
    236:   PetscFunctionBegin;
    237:   PetscCall(PetscFree(eps->data));
    238:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSCHASESetDegree_C",NULL));
    239:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSCHASEGetDegree_C",NULL));
    240:   PetscFunctionReturn(PETSC_SUCCESS);
    241: }

    243: static PetscErrorCode EPSReset_ChASE(EPS eps)
    244: {
    245:   EPS_ChASE   *ctx = (EPS_ChASE*)eps->data;

    247:   PetscFunctionBegin;
    248:   PetscCall(MatDestroy(&ctx->As));
    249:   PetscFunctionReturn(PETSC_SUCCESS);
    250: }

    252: static PetscErrorCode EPSView_ChASE(EPS eps,PetscViewer viewer)
    253: {
    254:   EPS_ChASE   *ctx = (EPS_ChASE*)eps->data;
    255:   PetscBool   isascii;

    257:   PetscFunctionBegin;
    258:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
    259:   if (isascii) {
    260:     PetscCall(PetscViewerASCIIPrintf(viewer,"  initial degree of Chebyshev polynomial filter: %" PetscInt_FMT "\n",ctx->deg));
    261:     if (ctx->opt) PetscCall(PetscViewerASCIIPrintf(viewer,"  internal optimization of polynomial degree enabled\n"));
    262:   }
    263:   PetscFunctionReturn(PETSC_SUCCESS);
    264: }

    266: /*MC
    267:    EPSCHASE - EPSCHASE = "chase" - A wrapper to CHASE {cite:p}`Win19`.

    269:    Notes:
    270:    The Chebyshev Accelerated Subspace Iteration Eigensolver (ChASE) is
    271:    expected to be particularly efficient when computing many eigenvalues
    272:    at the lower end of the spectrum, in sequences of eigenproblems.

    274:    The current implementation of the interface in SLEPc is restricted
    275:    to the case where the matrix has a block cyclic distribution, so
    276:    SLEPc will redistribute the matrix to ScaLAPACK format.

    278:    Level: beginner

    280: .seealso: [](ch:eps), `EPS`, `EPSType`, `EPSSetType()`
    281: M*/
    282: SLEPC_EXTERN PetscErrorCode EPSCreate_ChASE(EPS eps)
    283: {
    284:   EPS_ChASE   *ctx;

    286:   PetscFunctionBegin;
    287:   PetscCall(PetscNew(&ctx));
    288:   eps->data = (void*)ctx;
    289:   ctx->deg  = PETSC_DEFAULT;
    290:   ctx->opt  = PETSC_TRUE;

    292:   eps->categ = EPS_CATEGORY_OTHER;

    294:   eps->ops->solve          = EPSSolve_ChASE;
    295:   eps->ops->setup          = EPSSetUp_ChASE;
    296:   eps->ops->setupsort      = EPSSetUpSort_Basic;
    297:   eps->ops->setfromoptions = EPSSetFromOptions_ChASE;
    298:   eps->ops->destroy        = EPSDestroy_ChASE;
    299:   eps->ops->reset          = EPSReset_ChASE;
    300:   eps->ops->view           = EPSView_ChASE;
    301:   eps->ops->backtransform  = EPSBackTransform_Default;
    302:   eps->ops->setdefaultst   = EPSSetDefaultST_NoFactor;

    304:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSCHASESetDegree_C",EPSCHASESetDegree_ChASE));
    305:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSCHASEGetDegree_C",EPSCHASEGetDegree_ChASE));
    306:   PetscFunctionReturn(PETSC_SUCCESS);
    307: }