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(), PEPConvergedReason255: @*/
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: }