Actual source code: pepsolve.c

  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 eigenproblem.

 57:    Collective

 59:    Input Parameter:
 60: .  pep - the polynomial eigensolver context

 62:    Options Database Keys:
 63: +  -pep_view             - print information about the solver once the solve is complete
 64: .  -pep_view_pre         - print information about the solver before the solve starts
 65: .  -pep_view_matk        - view the coefficient matrix $A_k$ (replace `k` by an integer from 0 to `nmat`-1)
 66: .  -pep_view_vectors     - view the computed eigenvectors
 67: .  -pep_view_values      - view the computed eigenvalues
 68: .  -pep_converged_reason - print reason for convergence/divergence, and number of iterations
 69: .  -pep_error_absolute   - print absolute errors of each eigenpair
 70: .  -pep_error_relative   - print relative errors of each eigenpair
 71: -  -pep_error_backward   - print backward errors of each eigenpair

 73:    Notes:
 74:    The problem matrices are specified with `PEPSetOperators()`.

 76:    `PEPSolve()` will return without generating an error regardless of whether
 77:    all requested solutions were computed or not. Call `PEPGetConverged()` to get the
 78:    actual number of computed solutions, and `PEPGetConvergedReason()` to determine if
 79:    the solver converged or failed and why.

 81:    All the command-line options listed above admit an optional argument specifying
 82:    the viewer type and options. For instance, use `-pep_view_mat0 binary:matrix0.bin`
 83:    to save the $A_0$ matrix to a binary file, `-pep_view_values draw` to draw the computed
 84:    eigenvalues graphically, or `-pep_error_relative :myerr.m:ascii_matlab` to save
 85:    the errors in a file that can be executed in Matlab.

 87:    Level: beginner

 89: .seealso: [](ch:pep), `PEPCreate()`, `PEPSetUp()`, `PEPDestroy()`, `PEPSetTolerances()`, `PEPGetConverged()`, `PEPGetConvergedReason()`
 90: @*/
 91: PetscErrorCode PEPSolve(PEP pep)
 92: {
 93:   PetscInt       i,k;
 94:   PetscBool      flg,islinear;
 95:   char           str[16];

 97:   PetscFunctionBegin;
 99:   if (pep->state>=PEP_STATE_SOLVED) PetscFunctionReturn(PETSC_SUCCESS);
100:   PetscCall(PetscLogEventBegin(PEP_Solve,pep,0,0,0));

102:   /* call setup */
103:   PetscCall(PEPSetUp(pep));
104:   pep->nconv = 0;
105:   pep->its   = 0;
106:   k = pep->lineariz? pep->ncv: pep->ncv*(pep->nmat-1);
107:   for (i=0;i<k;i++) {
108:     pep->eigr[i]   = 0.0;
109:     pep->eigi[i]   = 0.0;
110:     pep->errest[i] = 0.0;
111:     pep->perm[i]   = i;
112:   }
113:   PetscCall(PEPViewFromOptions(pep,NULL,"-pep_view_pre"));
114:   PetscCall(RGViewFromOptions(pep->rg,NULL,"-rg_view"));

116:   /* Call solver */
117:   PetscUseTypeMethod(pep,solve);
118:   PetscCheck(pep->reason,PetscObjectComm((PetscObject)pep),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");
119:   pep->state = PEP_STATE_SOLVED;

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

124:   PetscCall(PetscObjectTypeCompare((PetscObject)pep,PEPLINEAR,&islinear));
125:   if (!islinear) {
126:     PetscCall(STPostSolve(pep->st));
127:     /* Map eigenvalues back to the original problem */
128:     PetscCall(STGetTransform(pep->st,&flg));
129:     if (flg) PetscTryTypeMethod(pep,backtransform);
130:   }

132: #if !defined(PETSC_USE_COMPLEX)
133:   /* reorder conjugate eigenvalues (positive imaginary first) */
134:   for (i=0;i<pep->nconv-1;i++) {
135:     if (pep->eigi[i] != 0) {
136:       if (pep->eigi[i] < 0) {
137:         pep->eigi[i] = -pep->eigi[i];
138:         pep->eigi[i+1] = -pep->eigi[i+1];
139:         /* the next correction only works with eigenvectors */
140:         PetscCall(PEPComputeVectors(pep));
141:         PetscCall(BVScaleColumn(pep->V,i+1,-1.0));
142:       }
143:       i++;
144:     }
145:   }
146: #endif

148:   if (pep->refine!=PEP_REFINE_NONE) PetscCall(PetscCitationsRegister(citation,&cited));

150:   if (pep->refine==PEP_REFINE_SIMPLE && pep->rits>0 && pep->nconv>0) {
151:     PetscCall(PEPComputeVectors(pep));
152:     PetscCall(PEPNewtonRefinementSimple(pep,&pep->rits,pep->rtol,pep->nconv));
153:   }

155:   /* sort eigenvalues according to pep->which parameter */
156:   PetscCall(SlepcSortEigenvalues(pep->sc,pep->nconv,pep->eigr,pep->eigi,pep->perm));
157:   PetscCall(PetscLogEventEnd(PEP_Solve,pep,0,0,0));

159:   /* various viewers */
160:   PetscCall(PEPViewFromOptions(pep,NULL,"-pep_view"));
161:   PetscCall(PEPConvergedReasonViewFromOptions(pep));
162:   PetscCall(PEPErrorViewFromOptions(pep));
163:   PetscCall(PEPValuesViewFromOptions(pep));
164:   PetscCall(PEPVectorsViewFromOptions(pep));
165:   for (i=0;i<pep->nmat;i++) {
166:     PetscCall(PetscSNPrintf(str,sizeof(str),"-pep_view_mat%" PetscInt_FMT,i));
167:     PetscCall(MatViewFromOptions(pep->A[i],(PetscObject)pep,str));
168:   }

170:   /* Remove the initial subspace */
171:   pep->nini = 0;
172:   PetscFunctionReturn(PETSC_SUCCESS);
173: }

175: /*@
176:    PEPGetIterationNumber - Gets the current iteration number. If the
177:    call to `PEPSolve()` is complete, then it returns the number of iterations
178:    carried out by the solution method.

180:    Not Collective

182:    Input Parameter:
183: .  pep - the polynomial eigensolver context

185:    Output Parameter:
186: .  its - number of iterations

188:    Note:
189:    During the $i$-th iteration this call returns $i-1$. If `PEPSolve()` is
190:    complete, then parameter `its` contains either the iteration number at
191:    which convergence was successfully reached, or failure was detected.
192:    Call `PEPGetConvergedReason()` to determine if the solver converged or
193:    failed and why.

195:    Level: intermediate

197: .seealso: [](ch:pep), `PEPGetConvergedReason()`, `PEPSetTolerances()`
198: @*/
199: PetscErrorCode PEPGetIterationNumber(PEP pep,PetscInt *its)
200: {
201:   PetscFunctionBegin;
203:   PetscAssertPointer(its,2);
204:   *its = pep->its;
205:   PetscFunctionReturn(PETSC_SUCCESS);
206: }

208: /*@
209:    PEPGetConverged - Gets the number of converged eigenpairs.

211:    Not Collective

213:    Input Parameter:
214: .  pep - the polynomial eigensolver context

216:    Output Parameter:
217: .  nconv - number of converged eigenpairs

219:    Notes:
220:    This function should be called after `PEPSolve()` has finished.

222:    The value `nconv` may be different from the number of requested solutions
223:    `nev`, but not larger than `ncv`, see `PEPSetDimensions()`.

225:    Level: beginner

227: .seealso: [](ch:pep), `PEPSetDimensions()`, `PEPSolve()`, `PEPGetEigenpair()`
228: @*/
229: PetscErrorCode PEPGetConverged(PEP pep,PetscInt *nconv)
230: {
231:   PetscFunctionBegin;
233:   PetscAssertPointer(nconv,2);
234:   PEPCheckSolved(pep,1);
235:   *nconv = pep->nconv;
236:   PetscFunctionReturn(PETSC_SUCCESS);
237: }

239: /*@
240:    PEPGetConvergedReason - Gets the reason why the `PEPSolve()` iteration was
241:    stopped.

243:    Not Collective

245:    Input Parameter:
246: .  pep - the polynomial eigensolver context

248:    Output Parameter:
249: .  reason - negative value indicates diverged, positive value converged, see
250:    `PEPConvergedReason` for the possible values

252:    Options Database Key:
253: .  -pep_converged_reason - print reason for convergence/divergence, and number of iterations

255:    Note:
256:    If this routine is called before or doing the `PEPSolve()` the value of
257:    `PEP_CONVERGED_ITERATING` is returned.

259:    Level: intermediate

261: .seealso: [](ch:pep), `PEPSetTolerances()`, `PEPSolve()`, `PEPConvergedReason`
262: @*/
263: PetscErrorCode PEPGetConvergedReason(PEP pep,PEPConvergedReason *reason)
264: {
265:   PetscFunctionBegin;
267:   PetscAssertPointer(reason,2);
268:   PEPCheckSolved(pep,1);
269:   *reason = pep->reason;
270:   PetscFunctionReturn(PETSC_SUCCESS);
271: }

273: /*@
274:    PEPGetEigenpair - Gets the `i`-th solution of the eigenproblem as computed by
275:    `PEPSolve()`. The solution consists in both the eigenvalue and the eigenvector.

277:    Collective

279:    Input Parameters:
280: +  pep - the polynomial eigensolver context
281: -  i   - index of the solution

283:    Output Parameters:
284: +  eigr - real part of eigenvalue
285: .  eigi - imaginary part of eigenvalue
286: .  Vr   - real part of eigenvector
287: -  Vi   - imaginary part of eigenvector

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

294:    If the eigenvalue is real, then `eigi` and `Vi` are set to zero. If PETSc is
295:    configured with complex scalars the eigenvalue is stored
296:    directly in `eigr` (`eigi` is set to zero) and the eigenvector in `Vr` (`Vi` is
297:    set to zero). In any case, the user can pass `NULL` in `Vr` or `Vi` if one of
298:    them is not required.

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

304:    The eigenvector is normalized to have unit norm.

306:    Level: beginner

308: .seealso: [](ch:pep), `PEPSolve()`, `PEPGetConverged()`, `PEPSetWhichEigenpairs()`
309: @*/
310: PetscErrorCode PEPGetEigenpair(PEP pep,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)
311: {
312:   PetscInt       k;

314:   PetscFunctionBegin;
319:   PEPCheckSolved(pep,1);
320:   PetscCheck(i>=0,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
321:   PetscCheck(i<pep->nconv,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see PEPGetConverged()");

323:   PetscCall(PEPComputeVectors(pep));
324:   k = pep->perm[i];

326:   /* eigenvalue */
327: #if defined(PETSC_USE_COMPLEX)
328:   if (eigr) *eigr = pep->eigr[k];
329:   if (eigi) *eigi = 0;
330: #else
331:   if (eigr) *eigr = pep->eigr[k];
332:   if (eigi) *eigi = pep->eigi[k];
333: #endif

335:   /* eigenvector */
336:   PetscCall(BV_GetEigenvector(pep->V,k,pep->eigi[k],Vr,Vi));
337:   PetscFunctionReturn(PETSC_SUCCESS);
338: }

340: /*@
341:    PEPGetErrorEstimate - Returns the error estimate associated to the `i`-th
342:    computed eigenpair.

344:    Not Collective

346:    Input Parameters:
347: +  pep - the polynomial eigensolver context
348: -  i   - index of eigenpair

350:    Output Parameter:
351: .  errest - the error estimate

353:    Note:
354:    This is the error estimate used internally by the eigensolver. The actual
355:    error bound can be computed with `PEPComputeError()`.

357:    Level: advanced

359: .seealso: [](ch:pep), `PEPComputeError()`
360: @*/
361: PetscErrorCode PEPGetErrorEstimate(PEP pep,PetscInt i,PetscReal *errest)
362: {
363:   PetscFunctionBegin;
365:   PetscAssertPointer(errest,3);
366:   PEPCheckSolved(pep,1);
367:   PetscCheck(i>=0,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
368:   PetscCheck(i<pep->nconv,PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see PEPGetConverged()");
369:   *errest = pep->errest[pep->perm[i]];
370:   PetscFunctionReturn(PETSC_SUCCESS);
371: }

373: /*
374:    PEPComputeResidualNorm_Private - Computes the norm of the residual vector
375:    associated with an eigenpair.

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

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

452: /*@
453:    PEPComputeError - Computes the error (based on the residual norm) associated
454:    with the `i`-th computed eigenpair.

456:    Collective

458:    Input Parameters:
459: +  pep  - the polynomial eigensolver context
460: .  i    - the solution index
461: -  type - the type of error to compute, see `PEPErrorType`

463:    Output Parameter:
464: .  error - the error

466:    Note:
467:    The error can be computed in various ways, all of them based on the residual
468:    norm $\|P(\lambda)x\|_2$ where $(\lambda,x)$ is the approximate eigenpair.

470:    Level: beginner

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

482:   PetscFunctionBegin;
486:   PetscAssertPointer(error,4);
487:   PEPCheckSolved(pep,1);

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

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

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