Actual source code: subspace.c

slepc-3.23.1 2025-05-01
Report Typos and Errors
  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: "subspace"

 13:    Method: Subspace Iteration

 15:    Algorithm:

 17:        Subspace iteration with Rayleigh-Ritz projection and locking,
 18:        based on the SRRIT implementation.

 20:    References:

 22:        [1] "Subspace Iteration in SLEPc", SLEPc Technical Report STR-3,
 23:            available at https://slepc.upv.es.
 24: */

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

 28: typedef struct {
 29:   PetscBool estimatedrange;     /* the filter range was not set by the user */
 30: } EPS_SUBSPACE;

 32: static PetscErrorCode EPSSetUp_Subspace_Filter(EPS eps)
 33: {
 34:   EPS_SUBSPACE   *ctx = (EPS_SUBSPACE*)eps->data;
 35:   PetscBool      estimaterange=PETSC_TRUE;
 36:   PetscReal      rleft,rright;
 37:   Mat            A;

 39:   PetscFunctionBegin;
 40:   EPSCheckHermitianCondition(eps,PETSC_TRUE," with polynomial filter");
 41:   EPSCheckStandardCondition(eps,PETSC_TRUE," with polynomial filter");
 42:   PetscCheck(eps->intb<PETSC_MAX_REAL || eps->inta>PETSC_MIN_REAL,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"The defined computational interval should have at least one of their sides bounded");
 43:   EPSCheckUnsupportedCondition(eps,EPS_FEATURE_ARBITRARY | EPS_FEATURE_REGION | EPS_FEATURE_EXTRACTION,PETSC_TRUE," with polynomial filter");
 44:   PetscCall(STFilterSetInterval(eps->st,eps->inta,eps->intb));
 45:   if (!ctx->estimatedrange) {
 46:     PetscCall(STFilterGetRange(eps->st,&rleft,&rright));
 47:     estimaterange = (!rleft && !rright)? PETSC_TRUE: PETSC_FALSE;
 48:   }
 49:   if (estimaterange) { /* user did not set a range */
 50:     PetscCall(STGetMatrix(eps->st,0,&A));
 51:     PetscCall(MatEstimateSpectralRange_EPS(A,&rleft,&rright));
 52:     PetscCall(PetscInfo(eps,"Setting eigenvalue range to [%g,%g]\n",(double)rleft,(double)rright));
 53:     PetscCall(STFilterSetRange(eps->st,rleft,rright));
 54:     ctx->estimatedrange = PETSC_TRUE;
 55:   }
 56:   if (eps->ncv==PETSC_DETERMINE && eps->nev==1) eps->nev = 40;  /* user did not provide nev estimation */
 57:   PetscCall(EPSSetDimensions_Default(eps,&eps->nev,&eps->ncv,&eps->mpd));
 58:   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");
 59:   PetscFunctionReturn(PETSC_SUCCESS);
 60: }

 62: static PetscErrorCode EPSSetUp_Subspace(EPS eps)
 63: {
 64:   PetscBool isfilt;

 66:   PetscFunctionBegin;
 67:   EPSCheckDefinite(eps);
 68:   EPSCheckNotStructured(eps);
 69:   if (eps->max_it==PETSC_DETERMINE) eps->max_it = PetscMax(100,2*eps->n/eps->ncv);
 70:   if (!eps->which) PetscCall(EPSSetWhichEigenpairs_Default(eps));
 71:   if (eps->which==EPS_ALL) {
 72:     if (eps->nev==0) eps->nev = 1;
 73:     PetscCall(PetscObjectTypeCompare((PetscObject)eps->st,STFILTER,&isfilt));
 74:     PetscCheck(isfilt,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"Spectrum slicing not supported in this solver");
 75:     PetscCall(EPSSetUp_Subspace_Filter(eps));
 76:   } else {
 77:     PetscCheck(eps->which==EPS_LARGEST_MAGNITUDE || eps->which==EPS_TARGET_MAGNITUDE,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"This solver supports only largest magnitude or target magnitude eigenvalues");
 78:     PetscCall(EPSSetDimensions_Default(eps,&eps->nev,&eps->ncv,&eps->mpd));
 79:   }
 80:   EPSCheckUnsupported(eps,EPS_FEATURE_ARBITRARY | EPS_FEATURE_EXTRACTION | EPS_FEATURE_THRESHOLD | EPS_FEATURE_TWOSIDED);
 81:   PetscCheck(eps->converged==EPSConvergedRelative,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"This solver only supports relative convergence test");

 83:   PetscCall(EPSAllocateSolution(eps,0));
 84:   PetscCall(EPS_SetInnerProduct(eps));
 85:   if (eps->ishermitian) PetscCall(DSSetType(eps->ds,DSHEP));
 86:   else PetscCall(DSSetType(eps->ds,DSNHEP));
 87:   PetscCall(DSAllocate(eps->ds,eps->ncv));
 88:   PetscFunctionReturn(PETSC_SUCCESS);
 89: }

 91: static PetscErrorCode EPSSetUpSort_Subspace(EPS eps)
 92: {
 93:   SlepcSC sc;

 95:   PetscFunctionBegin;
 96:   PetscCall(EPSSetUpSort_Default(eps));
 97:   if (eps->which==EPS_ALL) {
 98:     PetscCall(DSGetSlepcSC(eps->ds,&sc));
 99:     sc->rg            = NULL;
100:     sc->comparison    = SlepcCompareLargestReal;
101:     sc->comparisonctx = NULL;
102:     sc->map           = NULL;
103:     sc->mapobj        = NULL;
104:   }
105:   PetscFunctionReturn(PETSC_SUCCESS);
106: }

108: /*
109:    EPSSubspaceFindGroup - Find a group of nearly equimodular eigenvalues, provided
110:    in arrays wr and wi, according to the tolerance grptol. Also the 2-norms
111:    of the residuals must be passed in (rsd). Arrays are processed from index
112:    l to index m only. The output information is:

114:    ngrp - number of entries of the group
115:    ctr  - (w(l)+w(l+ngrp-1))/2
116:    ae   - average of wr(l),...,wr(l+ngrp-1)
117:    arsd - average of rsd(l),...,rsd(l+ngrp-1)
118: */
119: static PetscErrorCode EPSSubspaceFindGroup(PetscInt l,PetscInt m,PetscScalar *wr,PetscScalar *wi,PetscReal *rsd,PetscReal grptol,PetscInt *ngrp,PetscReal *ctr,PetscReal *ae,PetscReal *arsd)
120: {
121:   PetscInt  i;
122:   PetscReal rmod,rmod1;

124:   PetscFunctionBegin;
125:   *ngrp = 0;
126:   *ctr = 0;
127:   rmod = SlepcAbsEigenvalue(wr[l],wi[l]);

129:   for (i=l;i<m;) {
130:     rmod1 = SlepcAbsEigenvalue(wr[i],wi[i]);
131:     if (PetscAbsReal(rmod-rmod1) > grptol*(rmod+rmod1)) break;
132:     *ctr = (rmod+rmod1)/2.0;
133:     if (wi[i] == 0.0) {
134:       (*ngrp)++;
135:       i++;
136:     } else {
137:       (*ngrp)+=2;
138:       i+=2;
139:     }
140:   }

142:   *ae = 0;
143:   *arsd = 0;
144:   if (*ngrp) {
145:     for (i=l;i<l+*ngrp;i++) {
146:       (*ae) += PetscRealPart(wr[i]);
147:       (*arsd) += rsd[i]*rsd[i];
148:     }
149:     *ae = *ae / *ngrp;
150:     *arsd = PetscSqrtReal(*arsd / *ngrp);
151:   }
152:   PetscFunctionReturn(PETSC_SUCCESS);
153: }

155: /*
156:    EPSSubspaceResidualNorms - Computes the column norms of residual vectors
157:    OP*V(1:n,l:m) - V*T(1:m,l:m), where, on entry, OP*V has been computed and
158:    stored in R. On exit, rsd(l) to rsd(m) contain the computed norms.
159: */
160: static PetscErrorCode EPSSubspaceResidualNorms(BV R,BV V,Mat T,PetscInt l,PetscInt m,PetscScalar *eigi,PetscReal *rsd)
161: {
162:   PetscInt       i;

164:   PetscFunctionBegin;
165:   PetscCall(BVMult(R,-1.0,1.0,V,T));
166:   for (i=l;i<m;i++) PetscCall(BVNormColumnBegin(R,i,NORM_2,rsd+i));
167:   for (i=l;i<m;i++) PetscCall(BVNormColumnEnd(R,i,NORM_2,rsd+i));
168: #if !defined(PETSC_USE_COMPLEX)
169:   for (i=l;i<m-1;i++) {
170:     if (eigi[i]!=0.0) {
171:       rsd[i]   = SlepcAbs(rsd[i],rsd[i+1]);
172:       rsd[i+1] = rsd[i];
173:       i++;
174:     }
175:   }
176: #endif
177:   PetscFunctionReturn(PETSC_SUCCESS);
178: }

180: static PetscErrorCode EPSSolve_Subspace(EPS eps)
181: {
182:   Mat            H,Q,S,T,B;
183:   BV             AV,R;
184:   PetscBool      indef;
185:   PetscInt       i,k,ld,ngrp,nogrp,*itrsd,*itrsdold;
186:   PetscInt       nxtsrr,idsrr,idort,nxtort,nv,ncv = eps->ncv,its,ninside;
187:   PetscReal      arsd,oarsd,ctr,octr,ae,oae,*rsd,*orsd,tcond=1.0,gamma;
188:   PetscScalar    *oeigr,*oeigi;
189:   /* Parameters */
190:   PetscInt       init = 5;        /* Number of initial iterations */
191:   PetscReal      stpfac = 1.5;    /* Max num of iter before next SRR step */
192:   PetscReal      alpha = 1.0;     /* Used to predict convergence of next residual */
193:   PetscReal      beta = 1.1;      /* Used to predict convergence of next residual */
194:   PetscReal      grptol = SLEPC_DEFAULT_TOL;   /* Tolerance for EPSSubspaceFindGroup */
195:   PetscReal      cnvtol = 1e-6;   /* Convergence criterion for cnv */
196:   PetscInt       orttol = 2;      /* Number of decimal digits whose loss
197:                                      can be tolerated in orthogonalization */

199:   PetscFunctionBegin;
200:   its = 0;
201:   PetscCall(PetscMalloc6(ncv,&rsd,ncv,&orsd,ncv,&oeigr,ncv,&oeigi,ncv,&itrsd,ncv,&itrsdold));
202:   PetscCall(DSGetLeadingDimension(eps->ds,&ld));
203:   PetscCall(BVDuplicate(eps->V,&AV));
204:   PetscCall(BVDuplicate(eps->V,&R));
205:   PetscCall(STGetOperator(eps->st,&S));

207:   for (i=0;i<ncv;i++) {
208:     rsd[i] = 0.0;
209:     itrsd[i] = -1;
210:   }

212:   /* Complete the initial basis with random vectors and orthonormalize them */
213:   for (k=eps->nini;k<ncv;k++) {
214:     PetscCall(BVSetRandomColumn(eps->V,k));
215:     PetscCall(BVOrthonormalizeColumn(eps->V,k,PETSC_TRUE,NULL,NULL));
216:   }

218:   while (eps->reason == EPS_CONVERGED_ITERATING) {
219:     eps->its++;
220:     nv = PetscMin(eps->nconv+eps->mpd,ncv);
221:     PetscCall(DSSetDimensions(eps->ds,nv,eps->nconv,0));

223:     for (i=eps->nconv;i<nv;i++) {
224:       oeigr[i] = eps->eigr[i];
225:       oeigi[i] = eps->eigi[i];
226:       orsd[i]  = rsd[i];
227:     }

229:     /* AV(:,idx) = OP * V(:,idx) */
230:     PetscCall(BVSetActiveColumns(eps->V,eps->nconv,nv));
231:     PetscCall(BVSetActiveColumns(AV,eps->nconv,nv));
232:     PetscCall(BVMatMult(eps->V,S,AV));

234:     /* T(:,idx) = V' * AV(:,idx) */
235:     PetscCall(BVSetActiveColumns(eps->V,0,nv));
236:     PetscCall(DSGetMat(eps->ds,DS_MAT_A,&H));
237:     PetscCall(BVDot(AV,eps->V,H));
238:     PetscCall(DSRestoreMat(eps->ds,DS_MAT_A,&H));
239:     PetscCall(DSSetState(eps->ds,DS_STATE_RAW));

241:     /* Solve projected problem */
242:     PetscCall(DSSolve(eps->ds,eps->eigr,eps->eigi));
243:     PetscCall(DSSort(eps->ds,eps->eigr,eps->eigi,NULL,NULL,NULL));
244:     PetscCall(DSSynchronize(eps->ds,eps->eigr,eps->eigi));

246:     /* Update vectors V(:,idx) = V * U(:,idx) */
247:     PetscCall(DSGetMat(eps->ds,DS_MAT_Q,&Q));
248:     PetscCall(BVSetActiveColumns(AV,0,nv));
249:     PetscCall(BVSetActiveColumns(R,0,nv));
250:     PetscCall(BVMultInPlace(eps->V,Q,eps->nconv,nv));
251:     PetscCall(BVMultInPlace(AV,Q,eps->nconv,nv));
252:     PetscCall(DSRestoreMat(eps->ds,DS_MAT_Q,&Q));
253:     PetscCall(BVCopy(AV,R));

255:     /* Convergence check */
256:     PetscCall(DSGetMat(eps->ds,DS_MAT_A,&T));
257:     PetscCall(EPSSubspaceResidualNorms(R,eps->V,T,eps->nconv,nv,eps->eigi,rsd));
258:     PetscCall(DSRestoreMat(eps->ds,DS_MAT_A,&T));

260:     if (eps->which==EPS_ALL && eps->its>1) {   /* adjust eigenvalue count */
261:       ninside = 0;
262:       PetscCall(STFilterGetThreshold(eps->st,&gamma));
263:       for (i=eps->nconv;i<nv;i++) {
264:         if (PetscRealPart(eps->eigr[i]) < gamma) break;
265:         ninside++;
266:       }
267:       eps->nev = eps->nconv+ninside;
268:     }
269:     for (i=eps->nconv;i<nv;i++) {
270:       itrsdold[i] = itrsd[i];
271:       itrsd[i] = its;
272:       eps->errest[i] = rsd[i];
273:     }

275:     for (;;) {
276:       /* Find clusters of computed eigenvalues */
277:       PetscCall(EPSSubspaceFindGroup(eps->nconv,nv,eps->eigr,eps->eigi,eps->errest,grptol,&ngrp,&ctr,&ae,&arsd));
278:       PetscCall(EPSSubspaceFindGroup(eps->nconv,nv,oeigr,oeigi,orsd,grptol,&nogrp,&octr,&oae,&oarsd));
279:       if (ngrp!=nogrp) break;
280:       if (ngrp==0) break;
281:       if (PetscAbsReal(ae-oae)>ctr*cnvtol*(itrsd[eps->nconv]-itrsdold[eps->nconv])) break;
282:       if (arsd>ctr*eps->tol) break;
283:       eps->nconv = eps->nconv + ngrp;
284:       if (eps->nconv>=nv) break;
285:     }

287:     PetscCall(EPSMonitor(eps,eps->its,eps->nconv,eps->eigr,eps->eigi,eps->errest,nv));
288:     PetscCall((*eps->stopping)(eps,eps->its,eps->max_it,eps->nconv,eps->nev,&eps->reason,eps->stoppingctx));
289:     if (eps->reason != EPS_CONVERGED_ITERATING) break;

291:     /* Compute nxtsrr (iteration of next projection step) */
292:     nxtsrr = PetscMin(eps->max_it,PetscMax((PetscInt)PetscFloorReal(stpfac*its),init));

294:     if (ngrp!=nogrp || ngrp==0 || arsd>=oarsd) {
295:       idsrr = nxtsrr - its;
296:     } else {
297:       idsrr = (PetscInt)PetscFloorReal(alpha+beta*(itrsdold[eps->nconv]-itrsd[eps->nconv])*PetscLogReal(arsd/eps->tol)/PetscLogReal(arsd/oarsd));
298:       idsrr = PetscMax(1,idsrr);
299:     }
300:     nxtsrr = PetscMin(nxtsrr,its+idsrr);

302:     /* Compute nxtort (iteration of next orthogonalization step) */
303:     PetscCall(DSCond(eps->ds,&tcond));
304:     idort = PetscMax(1,(PetscInt)PetscFloorReal(orttol/PetscMax(1,PetscLog10Real(tcond))));
305:     nxtort = PetscMin(its+idort,nxtsrr);
306:     PetscCall(PetscInfo(eps,"Updated iteration counts: nxtort=%" PetscInt_FMT ", nxtsrr=%" PetscInt_FMT "\n",nxtort,nxtsrr));

308:     /* V(:,idx) = AV(:,idx) */
309:     PetscCall(BVSetActiveColumns(eps->V,eps->nconv,nv));
310:     PetscCall(BVSetActiveColumns(AV,eps->nconv,nv));
311:     PetscCall(BVCopy(AV,eps->V));
312:     its++;

314:     /* Orthogonalization loop */
315:     do {
316:       PetscCall(BVGetMatrix(eps->V,&B,&indef));
317:       PetscCall(BVSetMatrix(eps->V,NULL,PETSC_FALSE));
318:       while (its<nxtort) {
319:         /* A(:,idx) = OP*V(:,idx) with normalization */
320:         PetscCall(BVMatMult(eps->V,S,AV));
321:         PetscCall(BVCopy(AV,eps->V));
322:         PetscCall(BVNormalize(eps->V,NULL));
323:         its++;
324:       }
325:       PetscCall(BVSetMatrix(eps->V,B,indef));
326:       /* Orthonormalize vectors */
327:       PetscCall(BVOrthogonalize(eps->V,NULL));
328:       nxtort = PetscMin(its+idort,nxtsrr);
329:     } while (its<nxtsrr);
330:   }

332:   PetscCall(PetscFree6(rsd,orsd,oeigr,oeigi,itrsd,itrsdold));
333:   PetscCall(BVDestroy(&AV));
334:   PetscCall(BVDestroy(&R));
335:   PetscCall(STRestoreOperator(eps->st,&S));
336:   PetscCall(DSTruncate(eps->ds,eps->nconv,PETSC_TRUE));
337:   PetscFunctionReturn(PETSC_SUCCESS);
338: }

340: static PetscErrorCode EPSDestroy_Subspace(EPS eps)
341: {
342:   PetscFunctionBegin;
343:   PetscCall(PetscFree(eps->data));
344:   PetscFunctionReturn(PETSC_SUCCESS);
345: }

347: SLEPC_EXTERN PetscErrorCode EPSCreate_Subspace(EPS eps)
348: {
349:   EPS_SUBSPACE *ctx;

351:   PetscFunctionBegin;
352:   PetscCall(PetscNew(&ctx));
353:   eps->data  = (void*)ctx;

355:   eps->useds = PETSC_TRUE;
356:   eps->categ = EPS_CATEGORY_OTHER;

358:   eps->ops->solve          = EPSSolve_Subspace;
359:   eps->ops->setup          = EPSSetUp_Subspace;
360:   eps->ops->setupsort      = EPSSetUpSort_Subspace;
361:   eps->ops->destroy        = EPSDestroy_Subspace;
362:   eps->ops->backtransform  = EPSBackTransform_Default;
363:   eps->ops->computevectors = EPSComputeVectors_Schur;
364:   PetscFunctionReturn(PETSC_SUCCESS);
365: }