1: /*
2: PEP routines related to the solution process.
4: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5: SLEPc - Scalable Library for Eigenvalue Problem Computations
6: Copyright (c) 2002-2016, Universitat Politecnica de Valencia, Spain
8: This file is part of SLEPc.
10: SLEPc is free software: you can redistribute it and/or modify it under the
11: terms of version 3 of the GNU Lesser General Public License as published by
12: the Free Software Foundation.
14: SLEPc is distributed in the hope that it will be useful, but WITHOUT ANY
15: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16: FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17: more details.
19: You should have received a copy of the GNU Lesser General Public License
20: along with SLEPc. If not, see <http://www.gnu.org/licenses/>.
21: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
22: */
24: #include <slepc/private/pepimpl.h> /*I "slepcpep.h" I*/
25: #include <petscdraw.h>
27: static PetscBool cited = PETSC_FALSE;
28: static const char citation[] =
29: "@Article{slepc-pep-refine,\n"
30: " author = \"C. Campos and J. E. Roman\",\n"
31: " title = \"Parallel iterative refinement in polynomial eigenvalue problems\",\n"
32: " journal = \"Numer. Linear Algebra Appl.\",\n"
33: " volume = \"to appear\",\n"
34: " number = \"\",\n"
35: " pages = \"\",\n"
36: " year = \"2016,\"\n"
37: " doi = \"http://dx.doi.org/10.1002/nla.2052\"\n"
38: "}\n";
42: PetscErrorCode PEPComputeVectors(PEP pep) 43: {
47: PEPCheckSolved(pep,1);
48: switch (pep->state) {
49: case PEP_STATE_SOLVED:
50: if (pep->ops->computevectors) {
51: (*pep->ops->computevectors)(pep);
52: }
53: break;
54: default: 55: break;
56: }
57: pep->state = PEP_STATE_EIGENVECTORS;
58: return(0);
59: }
63: PetscErrorCode PEPExtractVectors(PEP pep) 64: {
68: PEPCheckSolved(pep,1);
69: switch (pep->state) {
70: case PEP_STATE_SOLVED:
71: if (pep->ops->extractvectors) {
72: (*pep->ops->extractvectors)(pep);
73: }
74: break;
75: default: 76: break;
77: }
78: return(0);
79: }
83: /*@
84: PEPSolve - Solves the polynomial eigensystem.
86: Collective on PEP 88: Input Parameter:
89: . pep - eigensolver context obtained from PEPCreate()
91: Options Database Keys:
92: + -pep_view - print information about the solver used
93: . -pep_view_matk binary - save any of the coefficient matrices (Ak) to the
94: default binary viewer (replace k by an integer from 0 to nmat-1)
95: . -pep_view_vectors binary - save the computed eigenvectors to the default binary viewer
96: . -pep_view_values - print computed eigenvalues
97: . -pep_converged_reason - print reason for convergence, and number of iterations
98: . -pep_error_absolute - print absolute errors of each eigenpair
99: . -pep_error_relative - print relative errors of each eigenpair
100: - -pep_error_backward - print backward errors of each eigenpair
102: Level: beginner
104: .seealso: PEPCreate(), PEPSetUp(), PEPDestroy(), PEPSetTolerances()
105: @*/
106: PetscErrorCode PEPSolve(PEP pep)107: {
109: PetscInt i,k;
110: PetscBool flg,islinear;
111: #define OPTLEN 16112: char str[OPTLEN];
116: if (pep->state>=PEP_STATE_SOLVED) return(0);
117: PetscLogEventBegin(PEP_Solve,pep,0,0,0);
119: /* call setup */
120: PEPSetUp(pep);
121: pep->nconv = 0;
122: pep->its = 0;
123: k = pep->lineariz? pep->ncv: pep->ncv*(pep->nmat-1);
124: for (i=0;i<k;i++) {
125: pep->eigr[i] = 0.0;
126: pep->eigi[i] = 0.0;
127: pep->errest[i] = 0.0;
128: pep->perm[i] = i;
129: }
130: PEPViewFromOptions(pep,NULL,"-pep_view_pre");
132: (*pep->ops->solve)(pep);
133: 134: if (!pep->reason) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");
136: PetscObjectTypeCompare((PetscObject)pep,PEPLINEAR,&islinear);
137: if (!islinear) {
138: STPostSolve(pep->st);
139: /* Map eigenvalues back to the original problem */
140: STGetTransform(pep->st,&flg);
141: if (flg && pep->ops->backtransform) {
142: (*pep->ops->backtransform)(pep);
143: }
144: }
146: pep->state = PEP_STATE_SOLVED;
148: #if !defined(PETSC_USE_COMPLEX)
149: /* reorder conjugate eigenvalues (positive imaginary first) */
150: for (i=0;i<pep->nconv-1;i++) {
151: if (pep->eigi[i] != 0) {
152: if (pep->eigi[i] < 0) {
153: pep->eigi[i] = -pep->eigi[i];
154: pep->eigi[i+1] = -pep->eigi[i+1];
155: /* the next correction only works with eigenvectors */
156: PEPComputeVectors(pep);
157: BVScaleColumn(pep->V,i+1,-1.0);
158: }
159: i++;
160: }
161: }
162: #endif
164: if (pep->refine!=PEP_REFINE_NONE) {
165: PetscCitationsRegister(citation,&cited);
166: }
168: if (pep->refine==PEP_REFINE_SIMPLE && pep->rits>0 && pep->nconv>0) {
169: PEPComputeVectors(pep);
170: PEPNewtonRefinementSimple(pep,&pep->rits,pep->rtol,pep->nconv);
171: }
173: /* sort eigenvalues according to pep->which parameter */
174: SlepcSortEigenvalues(pep->sc,pep->nconv,pep->eigr,pep->eigi,pep->perm);
175: PetscLogEventEnd(PEP_Solve,pep,0,0,0);
177: /* various viewers */
178: PEPViewFromOptions(pep,NULL,"-pep_view");
179: PEPReasonViewFromOptions(pep);
180: PEPErrorViewFromOptions(pep);
181: PEPValuesViewFromOptions(pep);
182: PEPVectorsViewFromOptions(pep);
183: for (i=0;i<pep->nmat;i++) {
184: PetscSNPrintf(str,OPTLEN,"-pep_view_mat%d",(int)i);
185: MatViewFromOptions(pep->A[i],(PetscObject)pep,str);
186: }
188: /* Remove the initial subspace */
189: pep->nini = 0;
190: return(0);
191: }
195: /*@
196: PEPGetIterationNumber - Gets the current iteration number. If the
197: call to PEPSolve() is complete, then it returns the number of iterations
198: carried out by the solution method.
200: Not Collective
202: Input Parameter:
203: . pep - the polynomial eigensolver context
205: Output Parameter:
206: . its - number of iterations
208: Level: intermediate
210: Note:
211: During the i-th iteration this call returns i-1. If PEPSolve() is
212: complete, then parameter "its" contains either the iteration number at
213: which convergence was successfully reached, or failure was detected.
214: Call PEPGetConvergedReason() to determine if the solver converged or
215: failed and why.
217: .seealso: PEPGetConvergedReason(), PEPSetTolerances()
218: @*/
219: PetscErrorCode PEPGetIterationNumber(PEP pep,PetscInt *its)220: {
224: *its = pep->its;
225: return(0);
226: }
230: /*@
231: PEPGetConverged - Gets the number of converged eigenpairs.
233: Not Collective
235: Input Parameter:
236: . pep - the polynomial eigensolver context
238: Output Parameter:
239: . nconv - number of converged eigenpairs
241: Note:
242: This function should be called after PEPSolve() has finished.
244: Level: beginner
246: .seealso: PEPSetDimensions(), PEPSolve()
247: @*/
248: PetscErrorCode PEPGetConverged(PEP pep,PetscInt *nconv)249: {
253: PEPCheckSolved(pep,1);
254: *nconv = pep->nconv;
255: return(0);
256: }
260: /*@
261: PEPGetConvergedReason - Gets the reason why the PEPSolve() iteration was
262: stopped.
264: Not Collective
266: Input Parameter:
267: . pep - the polynomial eigensolver context
269: Output Parameter:
270: . reason - negative value indicates diverged, positive value converged
272: Notes:
274: Possible values for reason are
275: + PEP_CONVERGED_TOL - converged up to tolerance
276: . PEP_CONVERGED_USER - converged due to a user-defined condition
277: . PEP_DIVERGED_ITS - required more than max_it iterations to reach convergence
278: . PEP_DIVERGED_BREAKDOWN - generic breakdown in method
279: - PEP_DIVERGED_SYMMETRY_LOST - pseudo-Lanczos was not able to keep symmetry
281: Can only be called after the call to PEPSolve() is complete.
283: Level: intermediate
285: .seealso: PEPSetTolerances(), PEPSolve(), PEPConvergedReason286: @*/
287: PetscErrorCode PEPGetConvergedReason(PEP pep,PEPConvergedReason *reason)288: {
292: PEPCheckSolved(pep,1);
293: *reason = pep->reason;
294: return(0);
295: }
299: /*@
300: PEPGetEigenpair - Gets the i-th solution of the eigenproblem as computed by
301: PEPSolve(). The solution consists in both the eigenvalue and the eigenvector.
303: Logically Collective on EPS305: Input Parameters:
306: + pep - polynomial eigensolver context
307: - i - index of the solution
309: Output Parameters:
310: + eigr - real part of eigenvalue
311: . eigi - imaginary part of eigenvalue
312: . Vr - real part of eigenvector
313: - Vi - imaginary part of eigenvector
315: Notes:
316: It is allowed to pass NULL for Vr and Vi, if the eigenvector is not
317: required. Otherwise, the caller must provide valid Vec objects, i.e.,
318: they must be created by the calling program with e.g. MatCreateVecs().
320: If the eigenvalue is real, then eigi and Vi are set to zero. If PETSc is
321: configured with complex scalars the eigenvalue is stored
322: directly in eigr (eigi is set to zero) and the eigenvector in Vr (Vi is
323: set to zero). In both cases, the user can pass NULL in eigi and Vi.
325: The index i should be a value between 0 and nconv-1 (see PEPGetConverged()).
326: Eigenpairs are indexed according to the ordering criterion established
327: with PEPSetWhichEigenpairs().
329: Level: beginner
331: .seealso: PEPSolve(), PEPGetConverged(), PEPSetWhichEigenpairs()
332: @*/
333: PetscErrorCode PEPGetEigenpair(PEP pep,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)334: {
335: PetscInt k;
343: PEPCheckSolved(pep,1);
344: if (i<0 || i>=pep->nconv) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");
346: PEPComputeVectors(pep);
347: k = pep->perm[i];
349: /* eigenvalue */
350: #if defined(PETSC_USE_COMPLEX)
351: if (eigr) *eigr = pep->eigr[k];
352: if (eigi) *eigi = 0;
353: #else
354: if (eigr) *eigr = pep->eigr[k];
355: if (eigi) *eigi = pep->eigi[k];
356: #endif
358: /* eigenvector */
359: #if defined(PETSC_USE_COMPLEX)
360: if (Vr) { BVCopyVec(pep->V,k,Vr); }
361: if (Vi) { VecSet(Vi,0.0); }
362: #else
363: if (pep->eigi[k]>0) { /* first value of conjugate pair */
364: if (Vr) { BVCopyVec(pep->V,k,Vr); }
365: if (Vi) { BVCopyVec(pep->V,k+1,Vi); }
366: } else if (pep->eigi[k]<0) { /* second value of conjugate pair */
367: if (Vr) { BVCopyVec(pep->V,k-1,Vr); }
368: if (Vi) {
369: BVCopyVec(pep->V,k,Vi);
370: VecScale(Vi,-1.0);
371: }
372: } else { /* real eigenvalue */
373: if (Vr) { BVCopyVec(pep->V,k,Vr); }
374: if (Vi) { VecSet(Vi,0.0); }
375: }
376: #endif
377: return(0);
378: }
382: /*@
383: PEPGetErrorEstimate - Returns the error estimate associated to the i-th
384: computed eigenpair.
386: Not Collective
388: Input Parameter:
389: + pep - polynomial eigensolver context
390: - i - index of eigenpair
392: Output Parameter:
393: . errest - the error estimate
395: Notes:
396: This is the error estimate used internally by the eigensolver. The actual
397: error bound can be computed with PEPComputeError(). See also the users
398: manual for details.
400: Level: advanced
402: .seealso: PEPComputeError()
403: @*/
404: PetscErrorCode PEPGetErrorEstimate(PEP pep,PetscInt i,PetscReal *errest)405: {
409: PEPCheckSolved(pep,1);
410: if (i<0 || i>=pep->nconv) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");
411: if (errest) *errest = pep->errest[pep->perm[i]];
412: return(0);
413: }
417: /*
418: PEPComputeResidualNorm_Private - Computes the norm of the residual vector
419: associated with an eigenpair.
421: Input Parameters:
422: kr,ki - eigenvalue
423: xr,xi - eigenvector
424: z - array of 4 work vectors (z[2],z[3] not referenced in complex scalars)
425: */
426: PetscErrorCode PEPComputeResidualNorm_Private(PEP pep,PetscScalar kr,PetscScalar ki,Vec xr,Vec xi,Vec *z,PetscReal *norm)427: {
429: Mat *A=pep->A;
430: PetscInt i,nmat=pep->nmat;
431: PetscScalar t[20],*vals=t,*ivals=NULL;
432: Vec u,w;
433: #if !defined(PETSC_USE_COMPLEX)
434: Vec ui,wi;
435: PetscReal ni;
436: PetscBool imag;
437: PetscScalar it[20];
438: #endif
441: u = z[0]; w = z[1];
442: VecSet(u,0.0);
443: #if !defined(PETSC_USE_COMPLEX)
444: ui = z[2]; wi = z[3];
445: ivals = it;
446: #endif
447: if (nmat>20) {
448: PetscMalloc1(nmat,&vals);
449: #if !defined(PETSC_USE_COMPLEX)
450: PetscMalloc1(nmat,&ivals);
451: #endif
452: }
453: PEPEvaluateBasis(pep,kr,ki,vals,ivals);
454: #if !defined(PETSC_USE_COMPLEX)
455: if (ki == 0 || PetscAbsScalar(ki) < PetscAbsScalar(kr*PETSC_MACHINE_EPSILON))
456: imag = PETSC_FALSE;
457: else {
458: imag = PETSC_TRUE;
459: VecSet(ui,0.0);
460: }
461: #endif
462: for (i=0;i<nmat;i++) {
463: if (vals[i]!=0.0) {
464: MatMult(A[i],xr,w);
465: VecAXPY(u,vals[i],w);
466: }
467: #if !defined(PETSC_USE_COMPLEX)
468: if (imag) {
469: if (ivals[i]!=0 || vals[i]!=0) {
470: MatMult(A[i],xi,wi);
471: if (vals[i]==0) {
472: MatMult(A[i],xr,w);
473: }
474: }
475: if (ivals[i]!=0){
476: VecAXPY(u,-ivals[i],wi);
477: VecAXPY(ui,ivals[i],w);
478: }
479: if (vals[i]!=0) {
480: VecAXPY(ui,vals[i],wi);
481: }
482: }
483: #endif
484: }
485: VecNorm(u,NORM_2,norm);
486: #if !defined(PETSC_USE_COMPLEX)
487: if (imag) {
488: VecNorm(ui,NORM_2,&ni);
489: *norm = SlepcAbsEigenvalue(*norm,ni);
490: }
491: #endif
492: if (nmat>20) {
493: PetscFree(vals);
494: #if !defined(PETSC_USE_COMPLEX)
495: PetscFree(ivals);
496: #endif
497: }
498: return(0);
499: }
503: /*@
504: PEPComputeError - Computes the error (based on the residual norm) associated
505: with the i-th computed eigenpair.
507: Collective on PEP509: Input Parameter:
510: + pep - the polynomial eigensolver context
511: . i - the solution index
512: - type - the type of error to compute
514: Output Parameter:
515: . error - the error
517: Notes:
518: The error can be computed in various ways, all of them based on the residual
519: norm ||P(l)x||_2 where l is the eigenvalue and x is the eigenvector.
520: See the users guide for additional details.
522: Level: beginner
524: .seealso: PEPErrorType, PEPSolve(), PEPGetErrorEstimate()
525: @*/
526: PetscErrorCode PEPComputeError(PEP pep,PetscInt i,PEPErrorType type,PetscReal *error)527: {
529: Vec xr,xi,w[4];
530: PetscScalar kr,ki;
531: PetscReal t,z=0.0;
532: PetscInt j;
533: PetscBool flg;
540: PEPCheckSolved(pep,1);
542: /* allocate work vectors */
543: #if defined(PETSC_USE_COMPLEX)
544: PEPSetWorkVecs(pep,3);
545: xi = NULL;
546: w[2] = NULL;
547: w[3] = NULL;
548: #else
549: PEPSetWorkVecs(pep,6);
550: xi = pep->work[3];
551: w[2] = pep->work[4];
552: w[3] = pep->work[5];
553: #endif
554: xr = pep->work[0];
555: w[0] = pep->work[1];
556: w[1] = pep->work[2];
558: /* compute residual norms */
559: PEPGetEigenpair(pep,i,&kr,&ki,xr,xi);
560: PEPComputeResidualNorm_Private(pep,kr,ki,xr,xi,w,error);
562: /* compute error */
563: switch (type) {
564: case PEP_ERROR_ABSOLUTE:
565: break;
566: case PEP_ERROR_RELATIVE:
567: *error /= SlepcAbsEigenvalue(kr,ki);
568: break;
569: case PEP_ERROR_BACKWARD:
570: /* initialization of matrix norms */
571: if (!pep->nrma[pep->nmat-1]) {
572: for (j=0;j<pep->nmat;j++) {
573: MatHasOperation(pep->A[j],MATOP_NORM,&flg);
574: if (!flg) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
575: MatNorm(pep->A[j],NORM_INFINITY,&pep->nrma[j]);
576: }
577: }
578: t = SlepcAbsEigenvalue(kr,ki);
579: for (j=pep->nmat-1;j>=0;j--) {
580: z = z*t+pep->nrma[j];
581: }
582: *error /= z;
583: break;
584: default:585: SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Invalid error type");
586: }
587: return(0);
588: }