Date: 2026-05-01T10:02:04+00:00

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.
     86:    See `PetscObjectViewFromOptions()` for more details.

     88:    Level: beginner

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

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

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

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

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

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

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

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

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

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

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

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

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

    181:    Not Collective

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

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

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

    196:    Level: intermediate

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

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

    212:    Not Collective

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

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

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

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

    226:    Level: beginner

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

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

    244:    Not Collective

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

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

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

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

    260:    Level: intermediate

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

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

    278:    Collective

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

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

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

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

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

    305:    The eigenvector is normalized to have unit norm.

    307:    Level: beginner

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

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

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

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

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

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

    345:    Not Collective

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

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

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

    358:    Level: advanced

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

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

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

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

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

    457:    Collective

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

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

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

    471:    Level: beginner

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

    483:   PetscFunctionBegin;
    487:   PetscAssertPointer(error,4);
    488:   PEPCheckSolved(pep,1);

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

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

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