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: BVSetActiveColumns(pep->V,0,pep->nconv);
72: if (pep->ops->extractvectors) {
73: (*pep->ops->extractvectors)(pep);
74: }
75: break;
76: default: 77: break;
78: }
79: return(0);
80: }
84: /*@
85: PEPSolve - Solves the polynomial eigensystem.
87: Collective on PEP 89: Input Parameter:
90: . pep - eigensolver context obtained from PEPCreate()
92: Options Database Keys:
93: + -pep_view - print information about the solver used
94: . -pep_view_matk binary - save any of the coefficient matrices (Ak) to the
95: default binary viewer (replace k by an integer from 0 to nmat-1)
96: . -pep_view_vectors binary - save the computed eigenvectors to the default binary viewer
97: . -pep_view_values - print computed eigenvalues
98: . -pep_converged_reason - print reason for convergence, and number of iterations
99: . -pep_error_absolute - print absolute errors of each eigenpair
100: . -pep_error_relative - print relative errors of each eigenpair
101: - -pep_error_backward - print backward errors of each eigenpair
103: Level: beginner
105: .seealso: PEPCreate(), PEPSetUp(), PEPDestroy(), PEPSetTolerances()
106: @*/
107: PetscErrorCode PEPSolve(PEP pep)108: {
110: PetscInt i,k;
111: PetscBool flg,islinear;
112: #define OPTLEN 16113: char str[OPTLEN];
117: if (pep->state>=PEP_STATE_SOLVED) return(0);
118: PetscLogEventBegin(PEP_Solve,pep,0,0,0);
120: /* call setup */
121: PEPSetUp(pep);
122: pep->nconv = 0;
123: pep->its = 0;
124: k = pep->lineariz? pep->ncv: pep->ncv*(pep->nmat-1);
125: for (i=0;i<k;i++) {
126: pep->eigr[i] = 0.0;
127: pep->eigi[i] = 0.0;
128: pep->errest[i] = 0.0;
129: pep->perm[i] = i;
130: }
131: PEPViewFromOptions(pep,NULL,"-pep_view_pre");
133: (*pep->ops->solve)(pep);
134: 135: if (!pep->reason) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");
137: PetscObjectTypeCompare((PetscObject)pep,PEPLINEAR,&islinear);
138: if (!islinear) {
139: STPostSolve(pep->st);
140: /* Map eigenvalues back to the original problem */
141: STGetTransform(pep->st,&flg);
142: if (flg && pep->ops->backtransform) {
143: (*pep->ops->backtransform)(pep);
144: }
145: }
147: pep->state = PEP_STATE_SOLVED;
149: #if !defined(PETSC_USE_COMPLEX)
150: /* reorder conjugate eigenvalues (positive imaginary first) */
151: for (i=0;i<pep->nconv-1;i++) {
152: if (pep->eigi[i] != 0) {
153: if (pep->eigi[i] < 0) {
154: pep->eigi[i] = -pep->eigi[i];
155: pep->eigi[i+1] = -pep->eigi[i+1];
156: /* the next correction only works with eigenvectors */
157: PEPComputeVectors(pep);
158: BVScaleColumn(pep->V,i+1,-1.0);
159: }
160: i++;
161: }
162: }
163: #endif
165: if (pep->refine!=PEP_REFINE_NONE) {
166: PetscCitationsRegister(citation,&cited);
167: }
169: if (pep->refine==PEP_REFINE_SIMPLE && pep->rits>0 && pep->nconv>0) {
170: PEPComputeVectors(pep);
171: PEPNewtonRefinementSimple(pep,&pep->rits,pep->rtol,pep->nconv);
172: }
174: /* sort eigenvalues according to pep->which parameter */
175: SlepcSortEigenvalues(pep->sc,pep->nconv,pep->eigr,pep->eigi,pep->perm);
176: PetscLogEventEnd(PEP_Solve,pep,0,0,0);
178: /* various viewers */
179: PEPViewFromOptions(pep,NULL,"-pep_view");
180: PEPReasonViewFromOptions(pep);
181: PEPErrorViewFromOptions(pep);
182: PEPValuesViewFromOptions(pep);
183: PEPVectorsViewFromOptions(pep);
184: for (i=0;i<pep->nmat;i++) {
185: PetscSNPrintf(str,OPTLEN,"-pep_view_mat%d",(int)i);
186: MatViewFromOptions(pep->A[i],(PetscObject)pep,str);
187: }
189: /* Remove the initial subspace */
190: pep->nini = 0;
191: return(0);
192: }
196: /*@
197: PEPGetIterationNumber - Gets the current iteration number. If the
198: call to PEPSolve() is complete, then it returns the number of iterations
199: carried out by the solution method.
201: Not Collective
203: Input Parameter:
204: . pep - the polynomial eigensolver context
206: Output Parameter:
207: . its - number of iterations
209: Level: intermediate
211: Note:
212: During the i-th iteration this call returns i-1. If PEPSolve() is
213: complete, then parameter "its" contains either the iteration number at
214: which convergence was successfully reached, or failure was detected.
215: Call PEPGetConvergedReason() to determine if the solver converged or
216: failed and why.
218: .seealso: PEPGetConvergedReason(), PEPSetTolerances()
219: @*/
220: PetscErrorCode PEPGetIterationNumber(PEP pep,PetscInt *its)221: {
225: *its = pep->its;
226: return(0);
227: }
231: /*@
232: PEPGetConverged - Gets the number of converged eigenpairs.
234: Not Collective
236: Input Parameter:
237: . pep - the polynomial eigensolver context
239: Output Parameter:
240: . nconv - number of converged eigenpairs
242: Note:
243: This function should be called after PEPSolve() has finished.
245: Level: beginner
247: .seealso: PEPSetDimensions(), PEPSolve()
248: @*/
249: PetscErrorCode PEPGetConverged(PEP pep,PetscInt *nconv)250: {
254: PEPCheckSolved(pep,1);
255: *nconv = pep->nconv;
256: return(0);
257: }
261: /*@
262: PEPGetConvergedReason - Gets the reason why the PEPSolve() iteration was
263: stopped.
265: Not Collective
267: Input Parameter:
268: . pep - the polynomial eigensolver context
270: Output Parameter:
271: . reason - negative value indicates diverged, positive value converged
273: Notes:
275: Possible values for reason are
276: + PEP_CONVERGED_TOL - converged up to tolerance
277: . PEP_CONVERGED_USER - converged due to a user-defined condition
278: . PEP_DIVERGED_ITS - required more than max_it iterations to reach convergence
279: . PEP_DIVERGED_BREAKDOWN - generic breakdown in method
280: - PEP_DIVERGED_SYMMETRY_LOST - pseudo-Lanczos was not able to keep symmetry
282: Can only be called after the call to PEPSolve() is complete.
284: Level: intermediate
286: .seealso: PEPSetTolerances(), PEPSolve(), PEPConvergedReason287: @*/
288: PetscErrorCode PEPGetConvergedReason(PEP pep,PEPConvergedReason *reason)289: {
293: PEPCheckSolved(pep,1);
294: *reason = pep->reason;
295: return(0);
296: }
300: /*@
301: PEPGetEigenpair - Gets the i-th solution of the eigenproblem as computed by
302: PEPSolve(). The solution consists in both the eigenvalue and the eigenvector.
304: Logically Collective on EPS306: Input Parameters:
307: + pep - polynomial eigensolver context
308: - i - index of the solution
310: Output Parameters:
311: + eigr - real part of eigenvalue
312: . eigi - imaginary part of eigenvalue
313: . Vr - real part of eigenvector
314: - Vi - imaginary part of eigenvector
316: Notes:
317: It is allowed to pass NULL for Vr and Vi, if the eigenvector is not
318: required. Otherwise, the caller must provide valid Vec objects, i.e.,
319: they must be created by the calling program with e.g. MatCreateVecs().
321: If the eigenvalue is real, then eigi and Vi are set to zero. If PETSc is
322: configured with complex scalars the eigenvalue is stored
323: directly in eigr (eigi is set to zero) and the eigenvector in Vr (Vi is
324: set to zero). In both cases, the user can pass NULL in eigi and Vi.
326: The index i should be a value between 0 and nconv-1 (see PEPGetConverged()).
327: Eigenpairs are indexed according to the ordering criterion established
328: with PEPSetWhichEigenpairs().
330: Level: beginner
332: .seealso: PEPSolve(), PEPGetConverged(), PEPSetWhichEigenpairs()
333: @*/
334: PetscErrorCode PEPGetEigenpair(PEP pep,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)335: {
336: PetscInt k;
344: PEPCheckSolved(pep,1);
345: if (i<0 || i>=pep->nconv) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");
347: PEPComputeVectors(pep);
348: k = pep->perm[i];
350: /* eigenvalue */
351: #if defined(PETSC_USE_COMPLEX)
352: if (eigr) *eigr = pep->eigr[k];
353: if (eigi) *eigi = 0;
354: #else
355: if (eigr) *eigr = pep->eigr[k];
356: if (eigi) *eigi = pep->eigi[k];
357: #endif
359: /* eigenvector */
360: #if defined(PETSC_USE_COMPLEX)
361: if (Vr) { BVCopyVec(pep->V,k,Vr); }
362: if (Vi) { VecSet(Vi,0.0); }
363: #else
364: if (pep->eigi[k]>0) { /* first value of conjugate pair */
365: if (Vr) { BVCopyVec(pep->V,k,Vr); }
366: if (Vi) { BVCopyVec(pep->V,k+1,Vi); }
367: } else if (pep->eigi[k]<0) { /* second value of conjugate pair */
368: if (Vr) { BVCopyVec(pep->V,k-1,Vr); }
369: if (Vi) {
370: BVCopyVec(pep->V,k,Vi);
371: VecScale(Vi,-1.0);
372: }
373: } else { /* real eigenvalue */
374: if (Vr) { BVCopyVec(pep->V,k,Vr); }
375: if (Vi) { VecSet(Vi,0.0); }
376: }
377: #endif
378: return(0);
379: }
383: /*@
384: PEPGetErrorEstimate - Returns the error estimate associated to the i-th
385: computed eigenpair.
387: Not Collective
389: Input Parameter:
390: + pep - polynomial eigensolver context
391: - i - index of eigenpair
393: Output Parameter:
394: . errest - the error estimate
396: Notes:
397: This is the error estimate used internally by the eigensolver. The actual
398: error bound can be computed with PEPComputeError(). See also the users
399: manual for details.
401: Level: advanced
403: .seealso: PEPComputeError()
404: @*/
405: PetscErrorCode PEPGetErrorEstimate(PEP pep,PetscInt i,PetscReal *errest)406: {
410: PEPCheckSolved(pep,1);
411: if (i<0 || i>=pep->nconv) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");
412: if (errest) *errest = pep->errest[pep->perm[i]];
413: return(0);
414: }
418: /*
419: PEPComputeResidualNorm_Private - Computes the norm of the residual vector
420: associated with an eigenpair.
422: Input Parameters:
423: kr,ki - eigenvalue
424: xr,xi - eigenvector
425: z - array of 4 work vectors (z[2],z[3] not referenced in complex scalars)
426: */
427: PetscErrorCode PEPComputeResidualNorm_Private(PEP pep,PetscScalar kr,PetscScalar ki,Vec xr,Vec xi,Vec *z,PetscReal *norm)428: {
430: Mat *A=pep->A;
431: PetscInt i,nmat=pep->nmat;
432: PetscScalar t[20],*vals=t,*ivals=NULL;
433: Vec u,w;
434: #if !defined(PETSC_USE_COMPLEX)
435: Vec ui,wi;
436: PetscReal ni;
437: PetscBool imag;
438: PetscScalar it[20];
439: #endif
442: u = z[0]; w = z[1];
443: VecSet(u,0.0);
444: #if !defined(PETSC_USE_COMPLEX)
445: ui = z[2]; wi = z[3];
446: ivals = it;
447: #endif
448: if (nmat>20) {
449: PetscMalloc1(nmat,&vals);
450: #if !defined(PETSC_USE_COMPLEX)
451: PetscMalloc1(nmat,&ivals);
452: #endif
453: }
454: PEPEvaluateBasis(pep,kr,ki,vals,ivals);
455: #if !defined(PETSC_USE_COMPLEX)
456: if (ki == 0 || PetscAbsScalar(ki) < PetscAbsScalar(kr*PETSC_MACHINE_EPSILON))
457: imag = PETSC_FALSE;
458: else {
459: imag = PETSC_TRUE;
460: VecSet(ui,0.0);
461: }
462: #endif
463: for (i=0;i<nmat;i++) {
464: if (vals[i]!=0.0) {
465: MatMult(A[i],xr,w);
466: VecAXPY(u,vals[i],w);
467: }
468: #if !defined(PETSC_USE_COMPLEX)
469: if (imag) {
470: if (ivals[i]!=0 || vals[i]!=0) {
471: MatMult(A[i],xi,wi);
472: if (vals[i]==0) {
473: MatMult(A[i],xr,w);
474: }
475: }
476: if (ivals[i]!=0){
477: VecAXPY(u,-ivals[i],wi);
478: VecAXPY(ui,ivals[i],w);
479: }
480: if (vals[i]!=0) {
481: VecAXPY(ui,vals[i],wi);
482: }
483: }
484: #endif
485: }
486: VecNorm(u,NORM_2,norm);
487: #if !defined(PETSC_USE_COMPLEX)
488: if (imag) {
489: VecNorm(ui,NORM_2,&ni);
490: *norm = SlepcAbsEigenvalue(*norm,ni);
491: }
492: #endif
493: if (nmat>20) {
494: PetscFree(vals);
495: #if !defined(PETSC_USE_COMPLEX)
496: PetscFree(ivals);
497: #endif
498: }
499: return(0);
500: }
504: /*@
505: PEPComputeError - Computes the error (based on the residual norm) associated
506: with the i-th computed eigenpair.
508: Collective on PEP510: Input Parameter:
511: + pep - the polynomial eigensolver context
512: . i - the solution index
513: - type - the type of error to compute
515: Output Parameter:
516: . error - the error
518: Notes:
519: The error can be computed in various ways, all of them based on the residual
520: norm ||P(l)x||_2 where l is the eigenvalue and x is the eigenvector.
521: See the users guide for additional details.
523: Level: beginner
525: .seealso: PEPErrorType, PEPSolve(), PEPGetErrorEstimate()
526: @*/
527: PetscErrorCode PEPComputeError(PEP pep,PetscInt i,PEPErrorType type,PetscReal *error)528: {
530: Vec xr,xi,w[4];
531: PetscScalar kr,ki;
532: PetscReal t,z=0.0;
533: PetscInt j;
534: PetscBool flg;
541: PEPCheckSolved(pep,1);
543: /* allocate work vectors */
544: #if defined(PETSC_USE_COMPLEX)
545: PEPSetWorkVecs(pep,3);
546: xi = NULL;
547: w[2] = NULL;
548: w[3] = NULL;
549: #else
550: PEPSetWorkVecs(pep,6);
551: xi = pep->work[3];
552: w[2] = pep->work[4];
553: w[3] = pep->work[5];
554: #endif
555: xr = pep->work[0];
556: w[0] = pep->work[1];
557: w[1] = pep->work[2];
559: /* compute residual norms */
560: PEPGetEigenpair(pep,i,&kr,&ki,xr,xi);
561: PEPComputeResidualNorm_Private(pep,kr,ki,xr,xi,w,error);
563: /* compute error */
564: switch (type) {
565: case PEP_ERROR_ABSOLUTE:
566: break;
567: case PEP_ERROR_RELATIVE:
568: *error /= SlepcAbsEigenvalue(kr,ki);
569: break;
570: case PEP_ERROR_BACKWARD:
571: /* initialization of matrix norms */
572: if (!pep->nrma[pep->nmat-1]) {
573: for (j=0;j<pep->nmat;j++) {
574: MatHasOperation(pep->A[j],MATOP_NORM,&flg);
575: if (!flg) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
576: MatNorm(pep->A[j],NORM_INFINITY,&pep->nrma[j]);
577: }
578: }
579: t = SlepcAbsEigenvalue(kr,ki);
580: for (j=pep->nmat-1;j>=0;j--) {
581: z = z*t+pep->nrma[j];
582: }
583: *error /= z;
584: break;
585: default:586: SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Invalid error type");
587: }
588: return(0);
589: }