Actual source code: pepsolve.c
slepc-3.22.2 2024-12-02
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: PEP routines related to the solution process
13: References:
15: [1] C. Campos and J.E. Roman, "Parallel iterative refinement in
16: polynomial eigenvalue problems", Numer. Linear Algebra Appl.
17: 23(4):730-745, 2016.
18: */
20: #include <slepc/private/pepimpl.h>
21: #include <slepc/private/bvimpl.h>
22: #include <petscdraw.h>
24: static PetscBool cited = PETSC_FALSE;
25: static const char citation[] =
26: "@Article{slepc-pep-refine,\n"
27: " author = \"C. Campos and J. E. Roman\",\n"
28: " title = \"Parallel iterative refinement in polynomial eigenvalue problems\",\n"
29: " journal = \"Numer. Linear Algebra Appl.\",\n"
30: " volume = \"23\",\n"
31: " number = \"4\",\n"
32: " pages = \"730--745\",\n"
33: " year = \"2016,\"\n"
34: " doi = \"https://doi.org/10.1002/nla.2052\"\n"
35: "}\n";
37: PetscErrorCode PEPComputeVectors(PEP pep)
38: {
39: PetscFunctionBegin;
40: PEPCheckSolved(pep,1);
41: if (pep->state==PEP_STATE_SOLVED) PetscTryTypeMethod(pep,computevectors);
42: pep->state = PEP_STATE_EIGENVECTORS;
43: PetscFunctionReturn(PETSC_SUCCESS);
44: }
46: PetscErrorCode PEPExtractVectors(PEP pep)
47: {
48: PetscFunctionBegin;
49: PEPCheckSolved(pep,1);
50: if (pep->state==PEP_STATE_SOLVED) PetscTryTypeMethod(pep,extractvectors);
51: PetscFunctionReturn(PETSC_SUCCESS);
52: }
54: /*@
55: PEPSolve - Solves the polynomial eigensystem.
57: Collective
59: Input Parameter:
60: . pep - eigensolver context obtained from PEPCreate()
62: Options Database Keys:
63: + -pep_view - print information about the solver used
64: . -pep_view_matk - view the coefficient matrix Ak (replace k by an integer from 0 to nmat-1)
65: . -pep_view_vectors - view the computed eigenvectors
66: . -pep_view_values - view the computed eigenvalues
67: . -pep_converged_reason - print reason for convergence, and number of iterations
68: . -pep_error_absolute - print absolute errors of each eigenpair
69: . -pep_error_relative - print relative errors of each eigenpair
70: - -pep_error_backward - print backward errors of each eigenpair
72: Notes:
73: All the command-line options listed above admit an optional argument specifying
74: the viewer type and options. For instance, use '-pep_view_mat0 binary:amatrix.bin'
75: to save the A matrix to a binary file, '-pep_view_values draw' to draw the computed
76: eigenvalues graphically, or '-pep_error_relative :myerr.m:ascii_matlab' to save
77: the errors in a file that can be executed in Matlab.
79: Level: beginner
81: .seealso: PEPCreate(), PEPSetUp(), PEPDestroy(), PEPSetTolerances()
82: @*/
83: PetscErrorCode PEPSolve(PEP pep)
84: {
85: PetscInt i,k;
86: PetscBool flg,islinear;
87: char str[16];
89: PetscFunctionBegin;
91: if (pep->state>=PEP_STATE_SOLVED) PetscFunctionReturn(PETSC_SUCCESS);
92: PetscCall(PetscLogEventBegin(PEP_Solve,pep,0,0,0));
94: /* call setup */
95: PetscCall(PEPSetUp(pep));
96: pep->nconv = 0;
97: pep->its = 0;
98: k = pep->lineariz? pep->ncv: pep->ncv*(pep->nmat-1);
99: for (i=0;i<k;i++) {
100: pep->eigr[i] = 0.0;
101: pep->eigi[i] = 0.0;
102: pep->errest[i] = 0.0;
103: pep->perm[i] = i;
104: }
105: PetscCall(PEPViewFromOptions(pep,NULL,"-pep_view_pre"));
106: PetscCall(RGViewFromOptions(pep->rg,NULL,"-rg_view"));
108: /* Call solver */
109: PetscUseTypeMethod(pep,solve);
110: PetscCheck(pep->reason,PetscObjectComm((PetscObject)pep),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");
111: pep->state = PEP_STATE_SOLVED;
113: /* Only the first nconv columns contain useful information */
114: PetscCall(BVSetActiveColumns(pep->V,0,pep->nconv));
116: PetscCall(PetscObjectTypeCompare((PetscObject)pep,PEPLINEAR,&islinear));
117: if (!islinear) {
118: PetscCall(STPostSolve(pep->st));
119: /* Map eigenvalues back to the original problem */
120: PetscCall(STGetTransform(pep->st,&flg));
121: if (flg) PetscTryTypeMethod(pep,backtransform);
122: }
124: #if !defined(PETSC_USE_COMPLEX)
125: /* reorder conjugate eigenvalues (positive imaginary first) */
126: for (i=0;i<pep->nconv-1;i++) {
127: if (pep->eigi[i] != 0) {
128: if (pep->eigi[i] < 0) {
129: pep->eigi[i] = -pep->eigi[i];
130: pep->eigi[i+1] = -pep->eigi[i+1];
131: /* the next correction only works with eigenvectors */
132: PetscCall(PEPComputeVectors(pep));
133: PetscCall(BVScaleColumn(pep->V,i+1,-1.0));
134: }
135: i++;
136: }
137: }
138: #endif
140: if (pep->refine!=PEP_REFINE_NONE) PetscCall(PetscCitationsRegister(citation,&cited));
142: if (pep->refine==PEP_REFINE_SIMPLE && pep->rits>0 && pep->nconv>0) {
143: PetscCall(PEPComputeVectors(pep));
144: PetscCall(PEPNewtonRefinementSimple(pep,&pep->rits,pep->rtol,pep->nconv));
145: }
147: /* sort eigenvalues according to pep->which parameter */
148: PetscCall(SlepcSortEigenvalues(pep->sc,pep->nconv,pep->eigr,pep->eigi,pep->perm));
149: PetscCall(PetscLogEventEnd(PEP_Solve,pep,0,0,0));
151: /* various viewers */
152: PetscCall(PEPViewFromOptions(pep,NULL,"-pep_view"));
153: PetscCall(PEPConvergedReasonViewFromOptions(pep));
154: PetscCall(PEPErrorViewFromOptions(pep));
155: PetscCall(PEPValuesViewFromOptions(pep));
156: PetscCall(PEPVectorsViewFromOptions(pep));
157: for (i=0;i<pep->nmat;i++) {
158: PetscCall(PetscSNPrintf(str,sizeof(str),"-pep_view_mat%" PetscInt_FMT,i));
159: PetscCall(MatViewFromOptions(pep->A[i],(PetscObject)pep,str));
160: }
162: /* Remove the initial subspace */
163: pep->nini = 0;
164: PetscFunctionReturn(PETSC_SUCCESS);
165: }
167: /*@
168: PEPGetIterationNumber - Gets the current iteration number. If the
169: call to PEPSolve() is complete, then it returns the number of iterations
170: carried out by the solution method.
172: Not Collective
174: Input Parameter:
175: . pep - the polynomial eigensolver context
177: Output Parameter:
178: . its - number of iterations
180: Note:
181: During the i-th iteration this call returns i-1. If PEPSolve() is
182: complete, then parameter "its" contains either the iteration number at
183: which convergence was successfully reached, or failure was detected.
184: Call PEPGetConvergedReason() to determine if the solver converged or
185: failed and why.
187: Level: intermediate
189: .seealso: PEPGetConvergedReason(), PEPSetTolerances()
190: @*/
191: PetscErrorCode PEPGetIterationNumber(PEP pep,PetscInt *its)
192: {
193: PetscFunctionBegin;
195: PetscAssertPointer(its,2);
196: *its = pep->its;
197: PetscFunctionReturn(PETSC_SUCCESS);
198: }
200: /*@
201: PEPGetConverged - Gets the number of converged eigenpairs.
203: Not Collective
205: Input Parameter:
206: . pep - the polynomial eigensolver context
208: Output Parameter:
209: . nconv - number of converged eigenpairs
211: Note:
212: This function should be called after PEPSolve() has finished.
214: Level: beginner
216: .seealso: PEPSetDimensions(), PEPSolve(), PEPGetEigenpair()
217: @*/
218: PetscErrorCode PEPGetConverged(PEP pep,PetscInt *nconv)
219: {
220: PetscFunctionBegin;
222: PetscAssertPointer(nconv,2);
223: PEPCheckSolved(pep,1);
224: *nconv = pep->nconv;
225: PetscFunctionReturn(PETSC_SUCCESS);
226: }
228: /*@
229: PEPGetConvergedReason - Gets the reason why the PEPSolve() iteration was
230: stopped.
232: Not Collective
234: Input Parameter:
235: . pep - the polynomial eigensolver context
237: Output Parameter:
238: . reason - negative value indicates diverged, positive value converged
240: Options Database Key:
241: . -pep_converged_reason - print the reason to a viewer
243: Notes:
244: Possible values for reason are
245: + PEP_CONVERGED_TOL - converged up to tolerance
246: . PEP_CONVERGED_USER - converged due to a user-defined condition
247: . PEP_DIVERGED_ITS - required more than max_it iterations to reach convergence
248: . PEP_DIVERGED_BREAKDOWN - generic breakdown in method
249: - PEP_DIVERGED_SYMMETRY_LOST - pseudo-Lanczos was not able to keep symmetry
251: Can only be called after the call to PEPSolve() is complete.
253: Level: intermediate
255: .seealso: PEPSetTolerances(), PEPSolve(), PEPConvergedReason
256: @*/
257: PetscErrorCode PEPGetConvergedReason(PEP pep,PEPConvergedReason *reason)
258: {
259: PetscFunctionBegin;
261: PetscAssertPointer(reason,2);
262: PEPCheckSolved(pep,1);
263: *reason = pep->reason;
264: PetscFunctionReturn(PETSC_SUCCESS);
265: }
267: /*@
268: PEPGetEigenpair - Gets the i-th solution of the eigenproblem as computed by
269: PEPSolve(). The solution consists in both the eigenvalue and the eigenvector.
271: Collective
273: Input Parameters:
274: + pep - polynomial eigensolver context
275: - i - index of the solution
277: Output Parameters:
278: + eigr - real part of eigenvalue
279: . eigi - imaginary part of eigenvalue
280: . Vr - real part of eigenvector
281: - Vi - imaginary part of eigenvector
283: Notes:
284: It is allowed to pass NULL for Vr and Vi, if the eigenvector is not
285: required. Otherwise, the caller must provide valid Vec objects, i.e.,
286: they must be created by the calling program with e.g. MatCreateVecs().
288: If the eigenvalue is real, then eigi and Vi are set to zero. If PETSc is
289: configured with complex scalars the eigenvalue is stored
290: directly in eigr (eigi is set to zero) and the eigenvector in Vr (Vi is
291: set to zero). In any case, the user can pass NULL in Vr or Vi if one of
292: them is not required.
294: The index i should be a value between 0 and nconv-1 (see PEPGetConverged()).
295: Eigenpairs are indexed according to the ordering criterion established
296: with PEPSetWhichEigenpairs().
298: Level: beginner
300: .seealso: PEPSolve(), PEPGetConverged(), PEPSetWhichEigenpairs()
301: @*/
302: PetscErrorCode PEPGetEigenpair(PEP pep,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)
303: {
304: PetscInt k;
306: PetscFunctionBegin;
311: PEPCheckSolved(pep,1);
312: PetscCheck(i>=0,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
313: PetscCheck(i<pep->nconv,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see PEPGetConverged()");
315: PetscCall(PEPComputeVectors(pep));
316: k = pep->perm[i];
318: /* eigenvalue */
319: #if defined(PETSC_USE_COMPLEX)
320: if (eigr) *eigr = pep->eigr[k];
321: if (eigi) *eigi = 0;
322: #else
323: if (eigr) *eigr = pep->eigr[k];
324: if (eigi) *eigi = pep->eigi[k];
325: #endif
327: /* eigenvector */
328: PetscCall(BV_GetEigenvector(pep->V,k,pep->eigi[k],Vr,Vi));
329: PetscFunctionReturn(PETSC_SUCCESS);
330: }
332: /*@
333: PEPGetErrorEstimate - Returns the error estimate associated to the i-th
334: computed eigenpair.
336: Not Collective
338: Input Parameters:
339: + pep - polynomial eigensolver context
340: - i - index of eigenpair
342: Output Parameter:
343: . errest - the error estimate
345: Notes:
346: This is the error estimate used internally by the eigensolver. The actual
347: error bound can be computed with PEPComputeError(). See also the users
348: manual for details.
350: Level: advanced
352: .seealso: PEPComputeError()
353: @*/
354: PetscErrorCode PEPGetErrorEstimate(PEP pep,PetscInt i,PetscReal *errest)
355: {
356: PetscFunctionBegin;
358: PetscAssertPointer(errest,3);
359: PEPCheckSolved(pep,1);
360: PetscCheck(i>=0,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
361: PetscCheck(i<pep->nconv,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see PEPGetConverged()");
362: *errest = pep->errest[pep->perm[i]];
363: PetscFunctionReturn(PETSC_SUCCESS);
364: }
366: /*
367: PEPComputeResidualNorm_Private - Computes the norm of the residual vector
368: associated with an eigenpair.
370: Input Parameters:
371: kr,ki - eigenvalue
372: xr,xi - eigenvector
373: z - array of 4 work vectors (z[2],z[3] not referenced in complex scalars)
374: */
375: PetscErrorCode PEPComputeResidualNorm_Private(PEP pep,PetscScalar kr,PetscScalar ki,Vec xr,Vec xi,Vec *z,PetscReal *norm)
376: {
377: Mat *A=pep->A;
378: PetscInt i,nmat=pep->nmat;
379: PetscScalar t[20],*vals=t,*ivals=NULL;
380: Vec u,w;
381: #if !defined(PETSC_USE_COMPLEX)
382: Vec ui,wi;
383: PetscReal ni;
384: PetscBool imag;
385: PetscScalar it[20];
386: #endif
388: PetscFunctionBegin;
389: u = z[0]; w = z[1];
390: PetscCall(VecSet(u,0.0));
391: #if !defined(PETSC_USE_COMPLEX)
392: ui = z[2]; wi = z[3];
393: ivals = it;
394: #endif
395: if (nmat>20) {
396: PetscCall(PetscMalloc1(nmat,&vals));
397: #if !defined(PETSC_USE_COMPLEX)
398: PetscCall(PetscMalloc1(nmat,&ivals));
399: #endif
400: }
401: PetscCall(PEPEvaluateBasis(pep,kr,ki,vals,ivals));
402: #if !defined(PETSC_USE_COMPLEX)
403: if (ki == 0 || PetscAbsScalar(ki) < PetscAbsScalar(kr*PETSC_MACHINE_EPSILON))
404: imag = PETSC_FALSE;
405: else {
406: imag = PETSC_TRUE;
407: PetscCall(VecSet(ui,0.0));
408: }
409: #endif
410: for (i=0;i<nmat;i++) {
411: if (vals[i]!=0.0) {
412: PetscCall(MatMult(A[i],xr,w));
413: PetscCall(VecAXPY(u,vals[i],w));
414: }
415: #if !defined(PETSC_USE_COMPLEX)
416: if (imag) {
417: if (ivals[i]!=0 || vals[i]!=0) {
418: PetscCall(MatMult(A[i],xi,wi));
419: if (vals[i]==0) PetscCall(MatMult(A[i],xr,w));
420: }
421: if (ivals[i]!=0) {
422: PetscCall(VecAXPY(u,-ivals[i],wi));
423: PetscCall(VecAXPY(ui,ivals[i],w));
424: }
425: if (vals[i]!=0) PetscCall(VecAXPY(ui,vals[i],wi));
426: }
427: #endif
428: }
429: PetscCall(VecNorm(u,NORM_2,norm));
430: #if !defined(PETSC_USE_COMPLEX)
431: if (imag) {
432: PetscCall(VecNorm(ui,NORM_2,&ni));
433: *norm = SlepcAbsEigenvalue(*norm,ni);
434: }
435: #endif
436: if (nmat>20) {
437: PetscCall(PetscFree(vals));
438: #if !defined(PETSC_USE_COMPLEX)
439: PetscCall(PetscFree(ivals));
440: #endif
441: }
442: PetscFunctionReturn(PETSC_SUCCESS);
443: }
445: /*@
446: PEPComputeError - Computes the error (based on the residual norm) associated
447: with the i-th computed eigenpair.
449: Collective
451: Input Parameters:
452: + pep - the polynomial eigensolver context
453: . i - the solution index
454: - type - the type of error to compute
456: Output Parameter:
457: . error - the error
459: Notes:
460: The error can be computed in various ways, all of them based on the residual
461: norm ||P(l)x||_2 where l is the eigenvalue and x is the eigenvector.
462: See the users guide for additional details.
464: Level: beginner
466: .seealso: PEPErrorType, PEPSolve(), PEPGetErrorEstimate()
467: @*/
468: PetscErrorCode PEPComputeError(PEP pep,PetscInt i,PEPErrorType type,PetscReal *error)
469: {
470: Vec xr,xi,w[4];
471: PetscScalar kr,ki;
472: PetscReal t,z=0.0;
473: PetscInt j;
474: PetscBool flg;
476: PetscFunctionBegin;
480: PetscAssertPointer(error,4);
481: PEPCheckSolved(pep,1);
483: /* allocate work vectors */
484: #if defined(PETSC_USE_COMPLEX)
485: PetscCall(PEPSetWorkVecs(pep,3));
486: xi = NULL;
487: w[2] = NULL;
488: w[3] = NULL;
489: #else
490: PetscCall(PEPSetWorkVecs(pep,6));
491: xi = pep->work[3];
492: w[2] = pep->work[4];
493: w[3] = pep->work[5];
494: #endif
495: xr = pep->work[0];
496: w[0] = pep->work[1];
497: w[1] = pep->work[2];
499: /* compute residual norms */
500: PetscCall(PEPGetEigenpair(pep,i,&kr,&ki,xr,xi));
501: PetscCall(PEPComputeResidualNorm_Private(pep,kr,ki,xr,xi,w,error));
503: /* compute error */
504: switch (type) {
505: case PEP_ERROR_ABSOLUTE:
506: break;
507: case PEP_ERROR_RELATIVE:
508: *error /= SlepcAbsEigenvalue(kr,ki);
509: break;
510: case PEP_ERROR_BACKWARD:
511: /* initialization of matrix norms */
512: if (!pep->nrma[pep->nmat-1]) {
513: for (j=0;j<pep->nmat;j++) {
514: PetscCall(MatHasOperation(pep->A[j],MATOP_NORM,&flg));
515: PetscCheck(flg,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
516: PetscCall(MatNorm(pep->A[j],NORM_INFINITY,&pep->nrma[j]));
517: }
518: }
519: t = SlepcAbsEigenvalue(kr,ki);
520: for (j=pep->nmat-1;j>=0;j--) {
521: z = z*t+pep->nrma[j];
522: }
523: *error /= z;
524: break;
525: default:
526: SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Invalid error type");
527: }
528: PetscFunctionReturn(PETSC_SUCCESS);
529: }