Actual source code: pepsolve.c

slepc-3.14.0 2020-09-30
Report Typos and Errors
  1: /*
  2:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3:    SLEPc - Scalable Library for Eigenvalue Problem Computations
  4:    Copyright (c) 2002-2020, 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:    PEP routines related to the solution process
 12: */

 14: #include <slepc/private/pepimpl.h>
 15: #include <slepc/private/bvimpl.h>
 16: #include <petscdraw.h>

 18: static PetscBool  cited = PETSC_FALSE;
 19: static const char citation[] =
 20:   "@Article{slepc-pep-refine,\n"
 21:   "   author = \"C. Campos and J. E. Roman\",\n"
 22:   "   title = \"Parallel iterative refinement in polynomial eigenvalue problems\",\n"
 23:   "   journal = \"Numer. Linear Algebra Appl.\",\n"
 24:   "   volume = \"23\",\n"
 25:   "   number = \"4\",\n"
 26:   "   pages = \"730--745\",\n"
 27:   "   year = \"2016,\"\n"
 28:   "   doi = \"https://doi.org/10.1002/nla.2052\"\n"
 29:   "}\n";

 31: PetscErrorCode PEPComputeVectors(PEP pep)
 32: {

 36:   PEPCheckSolved(pep,1);
 37:   if (pep->state==PEP_STATE_SOLVED && pep->ops->computevectors) {
 38:     (*pep->ops->computevectors)(pep);
 39:   }
 40:   pep->state = PEP_STATE_EIGENVECTORS;
 41:   return(0);
 42: }

 44: PetscErrorCode PEPExtractVectors(PEP pep)
 45: {

 49:   PEPCheckSolved(pep,1);
 50:   if (pep->state==PEP_STATE_SOLVED && pep->ops->extractvectors) {
 51:     (*pep->ops->extractvectors)(pep);
 52:   }
 53:   return(0);
 54: }

 56: /*@
 57:    PEPSolve - Solves the polynomial eigensystem.

 59:    Collective on pep

 61:    Input Parameter:
 62: .  pep - eigensolver context obtained from PEPCreate()

 64:    Options Database Keys:
 65: +  -pep_view - print information about the solver used
 66: .  -pep_view_matk binary - save any of the coefficient matrices (Ak) to the
 67:                 default binary viewer (replace k by an integer from 0 to nmat-1)
 68: .  -pep_view_vectors binary - save the computed eigenvectors to the default binary viewer
 69: .  -pep_view_values - print computed eigenvalues
 70: .  -pep_converged_reason - print reason for convergence, and number of iterations
 71: .  -pep_error_absolute - print absolute errors of each eigenpair
 72: .  -pep_error_relative - print relative errors of each eigenpair
 73: -  -pep_error_backward - print backward errors of each eigenpair

 75:    Level: beginner

 77: .seealso: PEPCreate(), PEPSetUp(), PEPDestroy(), PEPSetTolerances()
 78: @*/
 79: PetscErrorCode PEPSolve(PEP pep)
 80: {
 82:   PetscInt       i,k;
 83:   PetscBool      flg,islinear;
 84:   char           str[16];

 88:   if (pep->state>=PEP_STATE_SOLVED) return(0);
 89:   PetscLogEventBegin(PEP_Solve,pep,0,0,0);

 91:   /* call setup */
 92:   PEPSetUp(pep);
 93:   pep->nconv = 0;
 94:   pep->its   = 0;
 95:   k = pep->lineariz? pep->ncv: pep->ncv*(pep->nmat-1);
 96:   for (i=0;i<k;i++) {
 97:     pep->eigr[i]   = 0.0;
 98:     pep->eigi[i]   = 0.0;
 99:     pep->errest[i] = 0.0;
100:     pep->perm[i]   = i;
101:   }
102:   PEPViewFromOptions(pep,NULL,"-pep_view_pre");
103:   RGViewFromOptions(pep->rg,NULL,"-rg_view");

105:   /* Call solver */
106:   (*pep->ops->solve)(pep);
107:   if (!pep->reason) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");
108:   pep->state = PEP_STATE_SOLVED;

110:   /* Only the first nconv columns contain useful information */
111:   BVSetActiveColumns(pep->V,0,pep->nconv);

113:   PetscObjectTypeCompare((PetscObject)pep,PEPLINEAR,&islinear);
114:   if (!islinear) {
115:     STPostSolve(pep->st);
116:     /* Map eigenvalues back to the original problem */
117:     STGetTransform(pep->st,&flg);
118:     if (flg && pep->ops->backtransform) {
119:       (*pep->ops->backtransform)(pep);
120:     }
121:   }

123: #if !defined(PETSC_USE_COMPLEX)
124:   /* reorder conjugate eigenvalues (positive imaginary first) */
125:   for (i=0;i<pep->nconv-1;i++) {
126:     if (pep->eigi[i] != 0) {
127:       if (pep->eigi[i] < 0) {
128:         pep->eigi[i] = -pep->eigi[i];
129:         pep->eigi[i+1] = -pep->eigi[i+1];
130:         /* the next correction only works with eigenvectors */
131:         PEPComputeVectors(pep);
132:         BVScaleColumn(pep->V,i+1,-1.0);
133:       }
134:       i++;
135:     }
136:   }
137: #endif

139:   if (pep->refine!=PEP_REFINE_NONE) {
140:     PetscCitationsRegister(citation,&cited);
141:   }

143:   if (pep->refine==PEP_REFINE_SIMPLE && pep->rits>0 && pep->nconv>0) {
144:     PEPComputeVectors(pep);
145:     PEPNewtonRefinementSimple(pep,&pep->rits,pep->rtol,pep->nconv);
146:   }

148:   /* sort eigenvalues according to pep->which parameter */
149:   SlepcSortEigenvalues(pep->sc,pep->nconv,pep->eigr,pep->eigi,pep->perm);
150:   PetscLogEventEnd(PEP_Solve,pep,0,0,0);

152:   /* various viewers */
153:   PEPViewFromOptions(pep,NULL,"-pep_view");
154:   PEPConvergedReasonViewFromOptions(pep);
155:   PEPErrorViewFromOptions(pep);
156:   PEPValuesViewFromOptions(pep);
157:   PEPVectorsViewFromOptions(pep);
158:   for (i=0;i<pep->nmat;i++) {
159:     PetscSNPrintf(str,sizeof(str),"-pep_view_mat%d",(int)i);
160:     MatViewFromOptions(pep->A[i],(PetscObject)pep,str);
161:   }

163:   /* Remove the initial subspace */
164:   pep->nini = 0;
165:   return(0);
166: }

168: /*@
169:    PEPGetIterationNumber - Gets the current iteration number. If the
170:    call to PEPSolve() is complete, then it returns the number of iterations
171:    carried out by the solution method.

173:    Not Collective

175:    Input Parameter:
176: .  pep - the polynomial eigensolver context

178:    Output Parameter:
179: .  its - number of iterations

181:    Note:
182:    During the i-th iteration this call returns i-1. If PEPSolve() is
183:    complete, then parameter "its" contains either the iteration number at
184:    which convergence was successfully reached, or failure was detected.
185:    Call PEPGetConvergedReason() to determine if the solver converged or
186:    failed and why.

188:    Level: intermediate

190: .seealso: PEPGetConvergedReason(), PEPSetTolerances()
191: @*/
192: PetscErrorCode PEPGetIterationNumber(PEP pep,PetscInt *its)
193: {
197:   *its = pep->its;
198:   return(0);
199: }

201: /*@
202:    PEPGetConverged - Gets the number of converged eigenpairs.

204:    Not Collective

206:    Input Parameter:
207: .  pep - the polynomial eigensolver context

209:    Output Parameter:
210: .  nconv - number of converged eigenpairs

212:    Note:
213:    This function should be called after PEPSolve() has finished.

215:    Level: beginner

217: .seealso: PEPSetDimensions(), PEPSolve()
218: @*/
219: PetscErrorCode PEPGetConverged(PEP pep,PetscInt *nconv)
220: {
224:   PEPCheckSolved(pep,1);
225:   *nconv = pep->nconv;
226:   return(0);
227: }

229: /*@
230:    PEPGetConvergedReason - Gets the reason why the PEPSolve() iteration was
231:    stopped.

233:    Not Collective

235:    Input Parameter:
236: .  pep - the polynomial eigensolver context

238:    Output Parameter:
239: .  reason - negative value indicates diverged, positive value converged

241:    Notes:

243:    Possible values for reason are
244: +  PEP_CONVERGED_TOL - converged up to tolerance
245: .  PEP_CONVERGED_USER - converged due to a user-defined condition
246: .  PEP_DIVERGED_ITS - required more than max_it iterations to reach convergence
247: .  PEP_DIVERGED_BREAKDOWN - generic breakdown in method
248: -  PEP_DIVERGED_SYMMETRY_LOST - pseudo-Lanczos was not able to keep symmetry

250:    Can only be called after the call to PEPSolve() is complete.

252:    Level: intermediate

254: .seealso: PEPSetTolerances(), PEPSolve(), PEPConvergedReason
255: @*/
256: PetscErrorCode PEPGetConvergedReason(PEP pep,PEPConvergedReason *reason)
257: {
261:   PEPCheckSolved(pep,1);
262:   *reason = pep->reason;
263:   return(0);
264: }

266: /*@C
267:    PEPGetEigenpair - Gets the i-th solution of the eigenproblem as computed by
268:    PEPSolve(). The solution consists in both the eigenvalue and the eigenvector.

270:    Logically Collective on pep

272:    Input Parameters:
273: +  pep - polynomial eigensolver context
274: -  i   - index of the solution

276:    Output Parameters:
277: +  eigr - real part of eigenvalue
278: .  eigi - imaginary part of eigenvalue
279: .  Vr   - real part of eigenvector
280: -  Vi   - imaginary part of eigenvector

282:    Notes:
283:    It is allowed to pass NULL for Vr and Vi, if the eigenvector is not
284:    required. Otherwise, the caller must provide valid Vec objects, i.e.,
285:    they must be created by the calling program with e.g. MatCreateVecs().

287:    If the eigenvalue is real, then eigi and Vi are set to zero. If PETSc is
288:    configured with complex scalars the eigenvalue is stored
289:    directly in eigr (eigi is set to zero) and the eigenvector in Vr (Vi is
290:    set to zero). In any case, the user can pass NULL in Vr or Vi if one of
291:    them is not required.

293:    The index i should be a value between 0 and nconv-1 (see PEPGetConverged()).
294:    Eigenpairs are indexed according to the ordering criterion established
295:    with PEPSetWhichEigenpairs().

297:    Level: beginner

299: .seealso: PEPSolve(), PEPGetConverged(), PEPSetWhichEigenpairs()
300: @*/
301: PetscErrorCode PEPGetEigenpair(PEP pep,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)
302: {
303:   PetscInt       k;

311:   PEPCheckSolved(pep,1);
312:   if (i<0 || i>=pep->nconv) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");

314:   PEPComputeVectors(pep);
315:   k = pep->perm[i];

317:   /* eigenvalue */
318: #if defined(PETSC_USE_COMPLEX)
319:   if (eigr) *eigr = pep->eigr[k];
320:   if (eigi) *eigi = 0;
321: #else
322:   if (eigr) *eigr = pep->eigr[k];
323:   if (eigi) *eigi = pep->eigi[k];
324: #endif

326:   /* eigenvector */
327:   BV_GetEigenvector(pep->V,k,pep->eigi[k],Vr,Vi);
328:   return(0);
329: }

331: /*@
332:    PEPGetErrorEstimate - Returns the error estimate associated to the i-th
333:    computed eigenpair.

335:    Not Collective

337:    Input Parameter:
338: +  pep - polynomial eigensolver context
339: -  i   - index of eigenpair

341:    Output Parameter:
342: .  errest - the error estimate

344:    Notes:
345:    This is the error estimate used internally by the eigensolver. The actual
346:    error bound can be computed with PEPComputeError(). See also the users
347:    manual for details.

349:    Level: advanced

351: .seealso: PEPComputeError()
352: @*/
353: PetscErrorCode PEPGetErrorEstimate(PEP pep,PetscInt i,PetscReal *errest)
354: {
358:   PEPCheckSolved(pep,1);
359:   if (i<0 || i>=pep->nconv) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");
360:   *errest = pep->errest[pep->perm[i]];
361:   return(0);
362: }

364: /*
365:    PEPComputeResidualNorm_Private - Computes the norm of the residual vector
366:    associated with an eigenpair.

368:    Input Parameters:
369:      kr,ki - eigenvalue
370:      xr,xi - eigenvector
371:      z     - array of 4 work vectors (z[2],z[3] not referenced in complex scalars)
372: */
373: PetscErrorCode PEPComputeResidualNorm_Private(PEP pep,PetscScalar kr,PetscScalar ki,Vec xr,Vec xi,Vec *z,PetscReal *norm)
374: {
376:   Mat            *A=pep->A;
377:   PetscInt       i,nmat=pep->nmat;
378:   PetscScalar    t[20],*vals=t,*ivals=NULL;
379:   Vec            u,w;
380: #if !defined(PETSC_USE_COMPLEX)
381:   Vec            ui,wi;
382:   PetscReal      ni;
383:   PetscBool      imag;
384:   PetscScalar    it[20];
385: #endif

388:   u = z[0]; w = z[1];
389:   VecSet(u,0.0);
390: #if !defined(PETSC_USE_COMPLEX)
391:   ui = z[2]; wi = z[3];
392:   ivals = it;
393: #endif
394:   if (nmat>20) {
395:     PetscMalloc1(nmat,&vals);
396: #if !defined(PETSC_USE_COMPLEX)
397:     PetscMalloc1(nmat,&ivals);
398: #endif
399:   }
400:   PEPEvaluateBasis(pep,kr,ki,vals,ivals);
401: #if !defined(PETSC_USE_COMPLEX)
402:   if (ki == 0 || PetscAbsScalar(ki) < PetscAbsScalar(kr*PETSC_MACHINE_EPSILON))
403:     imag = PETSC_FALSE;
404:   else {
405:     imag = PETSC_TRUE;
406:     VecSet(ui,0.0);
407:   }
408: #endif
409:   for (i=0;i<nmat;i++) {
410:     if (vals[i]!=0.0) {
411:       MatMult(A[i],xr,w);
412:       VecAXPY(u,vals[i],w);
413:     }
414: #if !defined(PETSC_USE_COMPLEX)
415:     if (imag) {
416:       if (ivals[i]!=0 || vals[i]!=0) {
417:         MatMult(A[i],xi,wi);
418:         if (vals[i]==0) {
419:           MatMult(A[i],xr,w);
420:         }
421:       }
422:       if (ivals[i]!=0){
423:         VecAXPY(u,-ivals[i],wi);
424:         VecAXPY(ui,ivals[i],w);
425:       }
426:       if (vals[i]!=0) {
427:         VecAXPY(ui,vals[i],wi);
428:       }
429:     }
430: #endif
431:   }
432:   VecNorm(u,NORM_2,norm);
433: #if !defined(PETSC_USE_COMPLEX)
434:   if (imag) {
435:     VecNorm(ui,NORM_2,&ni);
436:     *norm = SlepcAbsEigenvalue(*norm,ni);
437:   }
438: #endif
439:   if (nmat>20) {
440:     PetscFree(vals);
441: #if !defined(PETSC_USE_COMPLEX)
442:     PetscFree(ivals);
443: #endif
444:   }
445:   return(0);
446: }

448: /*@
449:    PEPComputeError - Computes the error (based on the residual norm) associated
450:    with the i-th computed eigenpair.

452:    Collective on pep

454:    Input Parameter:
455: +  pep  - the polynomial eigensolver context
456: .  i    - the solution index
457: -  type - the type of error to compute

459:    Output Parameter:
460: .  error - the error

462:    Notes:
463:    The error can be computed in various ways, all of them based on the residual
464:    norm ||P(l)x||_2 where l is the eigenvalue and x is the eigenvector.
465:    See the users guide for additional details.

467:    Level: beginner

469: .seealso: PEPErrorType, PEPSolve(), PEPGetErrorEstimate()
470: @*/
471: PetscErrorCode PEPComputeError(PEP pep,PetscInt i,PEPErrorType type,PetscReal *error)
472: {
474:   Vec            xr,xi,w[4];
475:   PetscScalar    kr,ki;
476:   PetscReal      t,z=0.0;
477:   PetscInt       j;
478:   PetscBool      flg;

485:   PEPCheckSolved(pep,1);

487:   /* allocate work vectors */
488: #if defined(PETSC_USE_COMPLEX)
489:   PEPSetWorkVecs(pep,3);
490:   xi   = NULL;
491:   w[2] = NULL;
492:   w[3] = NULL;
493: #else
494:   PEPSetWorkVecs(pep,6);
495:   xi   = pep->work[3];
496:   w[2] = pep->work[4];
497:   w[3] = pep->work[5];
498: #endif
499:   xr   = pep->work[0];
500:   w[0] = pep->work[1];
501:   w[1] = pep->work[2];

503:   /* compute residual norms */
504:   PEPGetEigenpair(pep,i,&kr,&ki,xr,xi);
505:   PEPComputeResidualNorm_Private(pep,kr,ki,xr,xi,w,error);

507:   /* compute error */
508:   switch (type) {
509:     case PEP_ERROR_ABSOLUTE:
510:       break;
511:     case PEP_ERROR_RELATIVE:
512:       *error /= SlepcAbsEigenvalue(kr,ki);
513:       break;
514:     case PEP_ERROR_BACKWARD:
515:       /* initialization of matrix norms */
516:       if (!pep->nrma[pep->nmat-1]) {
517:         for (j=0;j<pep->nmat;j++) {
518:           MatHasOperation(pep->A[j],MATOP_NORM,&flg);
519:           if (!flg) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
520:           MatNorm(pep->A[j],NORM_INFINITY,&pep->nrma[j]);
521:         }
522:       }
523:       t = SlepcAbsEigenvalue(kr,ki);
524:       for (j=pep->nmat-1;j>=0;j--) {
525:         z = z*t+pep->nrma[j];
526:       }
527:       *error /= z;
528:       break;
529:     default:
530:       SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Invalid error type");
531:   }
532:   return(0);
533: }