Date: 2024-12-02T08:56:57+00:00

Actual source code: epssolve.c
slepc-3.22.2 2024-12-02
Report Typos and Errors

      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:    EPS routines related to the solution process
     12: */

     14: #include <slepc/private/epsimpl.h>
     15: #include <slepc/private/bvimpl.h>
     16: #include <petscdraw.h>

     18: PetscErrorCode EPSComputeVectors(EPS eps)
     19: {
     20:   PetscFunctionBegin;
     21:   EPSCheckSolved(eps,1);
     22:   if (eps->state==EPS_STATE_SOLVED) PetscTryTypeMethod(eps,computevectors);
     23:   eps->state = EPS_STATE_EIGENVECTORS;
     24:   PetscFunctionReturn(PETSC_SUCCESS);
     25: }

     27: static PetscErrorCode EPSComputeValues(EPS eps)
     28: {
     29:   PetscBool      injective,iscomp,isfilter;
     30:   PetscInt       i,n,aux,nconv0;
     31:   Mat            A,B=NULL,G,Z;

     33:   PetscFunctionBegin;
     34:   switch (eps->categ) {
     35:     case EPS_CATEGORY_KRYLOV:
     36:     case EPS_CATEGORY_OTHER:
     37:       PetscCall(STIsInjective(eps->st,&injective));
     38:       if (injective) {
     39:         /* one-to-one mapping: backtransform eigenvalues */
     40:         PetscUseTypeMethod(eps,backtransform);
     41:       } else {
     42:         /* compute eigenvalues from Rayleigh quotient */
     43:         PetscCall(DSGetDimensions(eps->ds,&n,NULL,NULL,NULL));
     44:         if (!n) break;
     45:         PetscCall(EPSGetOperators(eps,&A,&B));
     46:         PetscCall(BVSetActiveColumns(eps->V,0,n));
     47:         PetscCall(DSGetCompact(eps->ds,&iscomp));
     48:         PetscCall(DSSetCompact(eps->ds,PETSC_FALSE));
     49:         PetscCall(DSGetMat(eps->ds,DS_MAT_A,&G));
     50:         PetscCall(BVMatProject(eps->V,A,eps->V,G));
     51:         PetscCall(DSRestoreMat(eps->ds,DS_MAT_A,&G));
     52:         if (B) {
     53:           PetscCall(DSGetMat(eps->ds,DS_MAT_B,&G));
     54:           PetscCall(BVMatProject(eps->V,B,eps->V,G));
     55:           PetscCall(DSRestoreMat(eps->ds,DS_MAT_B,&G));
     56:         }
     57:         PetscCall(DSSolve(eps->ds,eps->eigr,eps->eigi));
     58:         PetscCall(DSSort(eps->ds,eps->eigr,eps->eigi,NULL,NULL,NULL));
     59:         PetscCall(DSSynchronize(eps->ds,eps->eigr,eps->eigi));
     60:         PetscCall(DSSetCompact(eps->ds,iscomp));
     61:         if (eps->ishermitian && (!eps->isgeneralized || eps->ispositive)) { /* V = V * Z */
     62:           PetscCall(DSVectors(eps->ds,DS_MAT_X,NULL,NULL));
     63:           PetscCall(DSGetMat(eps->ds,DS_MAT_X,&Z));
     64:           PetscCall(BVMultInPlace(eps->V,Z,0,n));
     65:           PetscCall(DSRestoreMat(eps->ds,DS_MAT_X,&Z));
     66:         }
     67:         /* in case of STFILTER discard computed eigenvalues that lie outside the wanted interval */
     68:         PetscCall(PetscObjectTypeCompare((PetscObject)eps->st,STFILTER,&isfilter));
     69:         if (isfilter) {
     70:           nconv0 = eps->nconv;
     71:           for (i=0;i<eps->nconv;i++) {
     72:             if (PetscRealPart(eps->eigr[eps->perm[i]])<eps->inta || PetscRealPart(eps->eigr[eps->perm[i]])>eps->intb) {
     73:               eps->nconv--;
     74:               if (i<eps->nconv) { SlepcSwap(eps->perm[i],eps->perm[eps->nconv],aux); i--; }
     75:             }
     76:           }
     77:           if (nconv0>eps->nconv) PetscCall(PetscInfo(eps,"Discarded %" PetscInt_FMT " computed eigenvalues lying outside the interval\n",nconv0-eps->nconv));
     78:         }
     79:       }
     80:       break;
     81:     case EPS_CATEGORY_PRECOND:
     82:     case EPS_CATEGORY_CONTOUR:
     83:       /* eigenvalues already available as an output of the solver */
     84:       break;
     85:   }
     86:   PetscFunctionReturn(PETSC_SUCCESS);
     87: }

     89: /*@
     90:    EPSSolve - Solves the eigensystem.

     92:    Collective

     94:    Input Parameter:
     95: .  eps - eigensolver context obtained from EPSCreate()

     97:    Options Database Keys:
     98: +  -eps_view - print information about the solver used
     99: .  -eps_view_mat0 - view the first matrix (A)
    100: .  -eps_view_mat1 - view the second matrix (B)
    101: .  -eps_view_vectors - view the computed eigenvectors
    102: .  -eps_view_values - view the computed eigenvalues
    103: .  -eps_converged_reason - print reason for convergence, and number of iterations
    104: .  -eps_error_absolute - print absolute errors of each eigenpair
    105: .  -eps_error_relative - print relative errors of each eigenpair
    106: -  -eps_error_backward - print backward errors of each eigenpair

    108:    Notes:
    109:    All the command-line options listed above admit an optional argument specifying
    110:    the viewer type and options. For instance, use '-eps_view_mat0 binary:amatrix.bin'
    111:    to save the A matrix to a binary file, '-eps_view_values draw' to draw the computed
    112:    eigenvalues graphically, or '-eps_error_relative :myerr.m:ascii_matlab' to save
    113:    the errors in a file that can be executed in Matlab.

    115:    Level: beginner

    117: .seealso: EPSCreate(), EPSSetUp(), EPSDestroy(), EPSSetTolerances()
    118: @*/
    119: PetscErrorCode EPSSolve(EPS eps)
    120: {
    121:   PetscInt       i;
    122:   PetscBool      hasname;
    123:   STMatMode      matmode;
    124:   Mat            A,B;

    126:   PetscFunctionBegin;
    128:   if (eps->state>=EPS_STATE_SOLVED) PetscFunctionReturn(PETSC_SUCCESS);
    129:   PetscCall(PetscLogEventBegin(EPS_Solve,eps,0,0,0));

    131:   /* Call setup */
    132:   PetscCall(EPSSetUp(eps));
    133:   eps->nconv = 0;
    134:   eps->its   = 0;
    135:   for (i=0;i<eps->ncv;i++) {
    136:     eps->eigr[i]   = 0.0;
    137:     eps->eigi[i]   = 0.0;
    138:     eps->errest[i] = 0.0;
    139:     eps->perm[i]   = i;
    140:   }
    141:   PetscCall(EPSViewFromOptions(eps,NULL,"-eps_view_pre"));
    142:   PetscCall(RGViewFromOptions(eps->rg,NULL,"-rg_view"));

    144:   /* Call solver */
    145:   PetscUseTypeMethod(eps,solve);
    146:   PetscCheck(eps->reason,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");
    147:   eps->state = EPS_STATE_SOLVED;

    149:   /* Only the first nconv columns contain useful information (except in CISS) */
    150:   PetscCall(BVSetActiveColumns(eps->V,0,eps->nconv));
    151:   if (eps->twosided) PetscCall(BVSetActiveColumns(eps->W,0,eps->nconv));

    153:   /* If inplace, purify eigenvectors before reverting operator */
    154:   PetscCall(STGetMatMode(eps->st,&matmode));
    155:   if (matmode == ST_MATMODE_INPLACE && eps->ispositive) PetscCall(EPSComputeVectors(eps));
    156:   PetscCall(STPostSolve(eps->st));

    158:   /* Map eigenvalues back to the original problem if appropriate */
    159:   PetscCall(EPSComputeValues(eps));

    161: #if !defined(PETSC_USE_COMPLEX)
    162:   /* Reorder conjugate eigenvalues (positive imaginary first) */
    163:   for (i=0;i<eps->nconv-1;i++) {
    164:     if (eps->eigi[i] != 0) {
    165:       if (eps->eigi[i] < 0) {
    166:         eps->eigi[i] = -eps->eigi[i];
    167:         eps->eigi[i+1] = -eps->eigi[i+1];
    168:         /* the next correction only works with eigenvectors */
    169:         PetscCall(EPSComputeVectors(eps));
    170:         PetscCall(BVScaleColumn(eps->V,i+1,-1.0));
    171:       }
    172:       i++;
    173:     }
    174:   }
    175: #endif

    177:   /* Sort eigenvalues according to eps->which parameter */
    178:   PetscCall(SlepcSortEigenvalues(eps->sc,eps->nconv,eps->eigr,eps->eigi,eps->perm));
    179:   PetscCall(PetscLogEventEnd(EPS_Solve,eps,0,0,0));

    181:   /* Various viewers */
    182:   PetscCall(EPSViewFromOptions(eps,NULL,"-eps_view"));
    183:   PetscCall(EPSConvergedReasonViewFromOptions(eps));
    184:   PetscCall(EPSErrorViewFromOptions(eps));
    185:   PetscCall(EPSValuesViewFromOptions(eps));
    186:   PetscCall(EPSVectorsViewFromOptions(eps));

    188:   PetscCall(PetscOptionsHasName(((PetscObject)eps)->options,((PetscObject)eps)->prefix,"-eps_view_mat0",&hasname));
    189:   if (hasname) {
    190:     PetscCall(EPSGetOperators(eps,&A,NULL));
    191:     PetscCall(MatViewFromOptions(A,(PetscObject)eps,"-eps_view_mat0"));
    192:   }
    193:   if (eps->isgeneralized) {
    194:     PetscCall(PetscOptionsHasName(((PetscObject)eps)->options,((PetscObject)eps)->prefix,"-eps_view_mat1",&hasname));
    195:     if (hasname) {
    196:       PetscCall(EPSGetOperators(eps,NULL,&B));
    197:       PetscCall(MatViewFromOptions(B,(PetscObject)eps,"-eps_view_mat1"));
    198:     }
    199:   }

    201:   /* Remove deflation and initial subspaces */
    202:   if (eps->nds) {
    203:     PetscCall(BVSetNumConstraints(eps->V,0));
    204:     eps->nds = 0;
    205:   }
    206:   eps->nini = 0;
    207:   PetscFunctionReturn(PETSC_SUCCESS);
    208: }

    210: /*@
    211:    EPSGetIterationNumber - Gets the current iteration number. If the
    212:    call to EPSSolve() is complete, then it returns the number of iterations
    213:    carried out by the solution method.

    215:    Not Collective

    217:    Input Parameter:
    218: .  eps - the eigensolver context

    220:    Output Parameter:
    221: .  its - number of iterations

    223:    Note:
    224:    During the i-th iteration this call returns i-1. If EPSSolve() is
    225:    complete, then parameter "its" contains either the iteration number at
    226:    which convergence was successfully reached, or failure was detected.
    227:    Call EPSGetConvergedReason() to determine if the solver converged or
    228:    failed and why.

    230:    Level: intermediate

    232: .seealso: EPSGetConvergedReason(), EPSSetTolerances()
    233: @*/
    234: PetscErrorCode EPSGetIterationNumber(EPS eps,PetscInt *its)
    235: {
    236:   PetscFunctionBegin;
    238:   PetscAssertPointer(its,2);
    239:   *its = eps->its;
    240:   PetscFunctionReturn(PETSC_SUCCESS);
    241: }

    243: /*@
    244:    EPSGetConverged - Gets the number of converged eigenpairs.

    246:    Not Collective

    248:    Input Parameter:
    249: .  eps - the eigensolver context

    251:    Output Parameter:
    252: .  nconv - number of converged eigenpairs

    254:    Note:
    255:    This function should be called after EPSSolve() has finished.

    257:    Level: beginner

    259: .seealso: EPSSetDimensions(), EPSSolve(), EPSGetEigenpair()
    260: @*/
    261: PetscErrorCode EPSGetConverged(EPS eps,PetscInt *nconv)
    262: {
    263:   PetscFunctionBegin;
    265:   PetscAssertPointer(nconv,2);
    266:   EPSCheckSolved(eps,1);
    267:   PetscCall(EPS_GetActualConverged(eps,nconv));
    268:   PetscFunctionReturn(PETSC_SUCCESS);
    269: }

    271: /*@
    272:    EPSGetConvergedReason - Gets the reason why the EPSSolve() iteration was
    273:    stopped.

    275:    Not Collective

    277:    Input Parameter:
    278: .  eps - the eigensolver context

    280:    Output Parameter:
    281: .  reason - negative value indicates diverged, positive value converged

    283:    Options Database Key:
    284: .  -eps_converged_reason - print the reason to a viewer

    286:    Notes:
    287:    Possible values for reason are
    288: +  EPS_CONVERGED_TOL - converged up to tolerance
    289: .  EPS_CONVERGED_USER - converged due to a user-defined condition
    290: .  EPS_DIVERGED_ITS - required more than max_it iterations to reach convergence
    291: .  EPS_DIVERGED_BREAKDOWN - generic breakdown in method
    292: -  EPS_DIVERGED_SYMMETRY_LOST - pseudo-Lanczos was not able to keep symmetry

    294:    Can only be called after the call to EPSSolve() is complete.

    296:    Level: intermediate

    298: .seealso: EPSSetTolerances(), EPSSolve(), EPSConvergedReason
    299: @*/
    300: PetscErrorCode EPSGetConvergedReason(EPS eps,EPSConvergedReason *reason)
    301: {
    302:   PetscFunctionBegin;
    304:   PetscAssertPointer(reason,2);
    305:   EPSCheckSolved(eps,1);
    306:   *reason = eps->reason;
    307:   PetscFunctionReturn(PETSC_SUCCESS);
    308: }

    310: /*@
    311:    EPSGetInvariantSubspace - Gets an orthonormal basis of the computed invariant
    312:    subspace.

    314:    Collective

    316:    Input Parameter:
    317: .  eps - the eigensolver context

    319:    Output Parameter:
    320: .  v - an array of vectors

    322:    Notes:
    323:    This function should be called after EPSSolve() has finished.

    325:    The user should provide in v an array of nconv vectors, where nconv is
    326:    the value returned by EPSGetConverged().

    328:    The first k vectors returned in v span an invariant subspace associated
    329:    with the first k computed eigenvalues (note that this is not true if the
    330:    k-th eigenvalue is complex and matrix A is real; in this case the first
    331:    k+1 vectors should be used). An invariant subspace X of A satisfies Ax
    332:    in X for all x in X (a similar definition applies for generalized
    333:    eigenproblems).

    335:    Level: intermediate

    337: .seealso: EPSGetEigenpair(), EPSGetConverged(), EPSSolve()
    338: @*/
    339: PetscErrorCode EPSGetInvariantSubspace(EPS eps,Vec v[])
    340: {
    341:   PetscInt       i;
    342:   BV             V=eps->V;
    343:   Vec            w;

    345:   PetscFunctionBegin;
    347:   PetscAssertPointer(v,2);
    349:   EPSCheckSolved(eps,1);
    350:   PetscCheck(eps->ishermitian || eps->state!=EPS_STATE_EIGENVECTORS,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONGSTATE,"EPSGetInvariantSubspace must be called before EPSGetEigenpair,EPSGetEigenvector or EPSComputeError");
    351:   if (eps->balance!=EPS_BALANCE_NONE && eps->D) {
    352:     PetscCall(BVDuplicateResize(eps->V,eps->nconv,&V));
    353:     PetscCall(BVSetActiveColumns(eps->V,0,eps->nconv));
    354:     PetscCall(BVCopy(eps->V,V));
    355:     for (i=0;i<eps->nconv;i++) {
    356:       PetscCall(BVGetColumn(V,i,&w));
    357:       PetscCall(VecPointwiseDivide(w,w,eps->D));
    358:       PetscCall(BVRestoreColumn(V,i,&w));
    359:     }
    360:     PetscCall(BVOrthogonalize(V,NULL));
    361:   }
    362:   for (i=0;i<eps->nconv;i++) PetscCall(BVCopyVec(V,i,v[i]));
    363:   if (eps->balance!=EPS_BALANCE_NONE && eps->D) PetscCall(BVDestroy(&V));
    364:   PetscFunctionReturn(PETSC_SUCCESS);
    365: }

    367: /*@
    368:    EPSGetEigenpair - Gets the i-th solution of the eigenproblem as computed by
    369:    EPSSolve(). The solution consists in both the eigenvalue and the eigenvector.

    371:    Collective

    373:    Input Parameters:
    374: +  eps - eigensolver context
    375: -  i   - index of the solution

    377:    Output Parameters:
    378: +  eigr - real part of eigenvalue
    379: .  eigi - imaginary part of eigenvalue
    380: .  Vr   - real part of eigenvector
    381: -  Vi   - imaginary part of eigenvector

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

    388:    If the eigenvalue is real, then eigi and Vi are set to zero. If PETSc is
    389:    configured with complex scalars the eigenvalue is stored
    390:    directly in eigr (eigi is set to zero) and the eigenvector in Vr (Vi is
    391:    set to zero). In both cases, the user can pass NULL in eigi and Vi.

    393:    The index i should be a value between 0 and nconv-1 (see EPSGetConverged()).
    394:    Eigenpairs are indexed according to the ordering criterion established
    395:    with EPSSetWhichEigenpairs().

    397:    The 2-norm of the eigenvector is one unless the problem is generalized
    398:    Hermitian. In this case the eigenvector is normalized with respect to the
    399:    norm defined by the B matrix.

    401:    Level: beginner

    403: .seealso: EPSGetEigenvalue(), EPSGetEigenvector(), EPSGetLeftEigenvector(), EPSSolve(),
    404:           EPSGetConverged(), EPSSetWhichEigenpairs(), EPSGetInvariantSubspace()
    405: @*/
    406: PetscErrorCode EPSGetEigenpair(EPS eps,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)
    407: {
    408:   PetscInt nconv;

    410:   PetscFunctionBegin;
    413:   EPSCheckSolved(eps,1);
    414:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    415:   PetscCall(EPS_GetActualConverged(eps,&nconv));
    416:   PetscCheck(i<nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    417:   PetscCall(EPSGetEigenvalue(eps,i,eigr,eigi));
    418:   if (Vr || Vi) PetscCall(EPSGetEigenvector(eps,i,Vr,Vi));
    419:   PetscFunctionReturn(PETSC_SUCCESS);
    420: }

    422: /*@
    423:    EPSGetEigenvalue - Gets the i-th eigenvalue as computed by EPSSolve().

    425:    Not Collective

    427:    Input Parameters:
    428: +  eps - eigensolver context
    429: -  i   - index of the solution

    431:    Output Parameters:
    432: +  eigr - real part of eigenvalue
    433: -  eigi - imaginary part of eigenvalue

    435:    Notes:
    436:    If the eigenvalue is real, then eigi is set to zero. If PETSc is
    437:    configured with complex scalars the eigenvalue is stored
    438:    directly in eigr (eigi is set to zero).

    440:    The index i should be a value between 0 and nconv-1 (see EPSGetConverged()).
    441:    Eigenpairs are indexed according to the ordering criterion established
    442:    with EPSSetWhichEigenpairs().

    444:    Level: beginner

    446: .seealso: EPSSolve(), EPSGetConverged(), EPSSetWhichEigenpairs(), EPSGetEigenpair()
    447: @*/
    448: PetscErrorCode EPSGetEigenvalue(EPS eps,PetscInt i,PetscScalar *eigr,PetscScalar *eigi)
    449: {
    450:   PetscInt k,nconv;

    452:   PetscFunctionBegin;
    454:   EPSCheckSolved(eps,1);
    455:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    456:   PetscCall(EPS_GetActualConverged(eps,&nconv));
    457:   PetscCheck(i<nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    458:   if (nconv==eps->nconv) {
    459:     k = eps->perm[i];
    460: #if defined(PETSC_USE_COMPLEX)
    461:     if (eigr) *eigr = eps->eigr[k];
    462:     if (eigi) *eigi = 0;
    463: #else
    464:     if (eigr) *eigr = eps->eigr[k];
    465:     if (eigi) *eigi = eps->eigi[k];
    466: #endif
    467:   } else {
    468:     PetscCheck(eps->problem_type==EPS_BSE,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Problem type should be BSE");
    469:     /* BSE problem, even index is +lambda, odd index is -lambda */
    470:     k = eps->perm[i/2];
    471: #if defined(PETSC_USE_COMPLEX)
    472:     if (eigr) *eigr = (i%2)? -eps->eigr[k]: eps->eigr[k];
    473:     if (eigi) *eigi = 0;
    474: #else
    475:     if (eigr) *eigr = (i%2)? -eps->eigr[k]: eps->eigr[k];
    476:     if (eigi) *eigi = eps->eigi[k];
    477: #endif
    478:   }
    479:   PetscFunctionReturn(PETSC_SUCCESS);
    480: }

    482: /*@
    483:    EPSGetEigenvector - Gets the i-th right eigenvector as computed by EPSSolve().

    485:    Collective

    487:    Input Parameters:
    488: +  eps - eigensolver context
    489: -  i   - index of the solution

    491:    Output Parameters:
    492: +  Vr   - real part of eigenvector
    493: -  Vi   - imaginary part of eigenvector

    495:    Notes:
    496:    The caller must provide valid Vec objects, i.e., they must be created
    497:    by the calling program with e.g. MatCreateVecs().

    499:    If the corresponding eigenvalue is real, then Vi is set to zero. If PETSc is
    500:    configured with complex scalars the eigenvector is stored
    501:    directly in Vr (Vi is set to zero). In any case, the user can pass NULL in Vr
    502:    or Vi if one of them is not required.

    504:    The index i should be a value between 0 and nconv-1 (see EPSGetConverged()).
    505:    Eigenpairs are indexed according to the ordering criterion established
    506:    with EPSSetWhichEigenpairs().

    508:    The 2-norm of the eigenvector is one unless the problem is generalized
    509:    Hermitian. In this case the eigenvector is normalized with respect to the
    510:    norm defined by the B matrix.

    512:    Level: beginner

    514: .seealso: EPSSolve(), EPSGetConverged(), EPSSetWhichEigenpairs(), EPSGetEigenpair(), EPSGetLeftEigenvector()
    515: @*/
    516: PetscErrorCode EPSGetEigenvector(EPS eps,PetscInt i,Vec Vr,Vec Vi)
    517: {
    518:   PetscInt nconv;

    520:   PetscFunctionBegin;
    525:   EPSCheckSolved(eps,1);
    526:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    527:   PetscCall(EPS_GetActualConverged(eps,&nconv));
    528:   PetscCheck(i<nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    529:   PetscCall(EPSComputeVectors(eps));
    530:   PetscCall(EPS_GetEigenvector(eps,eps->V,i,Vr,Vi));
    531:   PetscFunctionReturn(PETSC_SUCCESS);
    532: }

    534: /*@
    535:    EPSGetLeftEigenvector - Gets the i-th left eigenvector as computed by EPSSolve().

    537:    Collective

    539:    Input Parameters:
    540: +  eps - eigensolver context
    541: -  i   - index of the solution

    543:    Output Parameters:
    544: +  Wr   - real part of left eigenvector
    545: -  Wi   - imaginary part of left eigenvector

    547:    Notes:
    548:    The caller must provide valid Vec objects, i.e., they must be created
    549:    by the calling program with e.g. MatCreateVecs().

    551:    If the corresponding eigenvalue is real, then Wi is set to zero. If PETSc is
    552:    configured with complex scalars the eigenvector is stored directly in Wr
    553:    (Wi is set to zero). In any case, the user can pass NULL in Wr or Wi if
    554:    one of them is not required.

    556:    The index i should be a value between 0 and nconv-1 (see EPSGetConverged()).
    557:    Eigensolutions are indexed according to the ordering criterion established
    558:    with EPSSetWhichEigenpairs().

    560:    Left eigenvectors are available only if the twosided flag was set, see
    561:    EPSSetTwoSided().

    563:    Level: intermediate

    565: .seealso: EPSGetEigenvector(), EPSGetConverged(), EPSSetWhichEigenpairs(), EPSSetTwoSided()
    566: @*/
    567: PetscErrorCode EPSGetLeftEigenvector(EPS eps,PetscInt i,Vec Wr,Vec Wi)
    568: {
    569:   PetscInt    nconv;
    570:   PetscBool   trivial;
    571:   Mat         H;
    572:   IS          is[2];
    573:   Vec         v;

    575:   PetscFunctionBegin;
    580:   EPSCheckSolved(eps,1);
    581:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    582:   PetscCall(EPS_GetActualConverged(eps,&nconv));
    583:   PetscCheck(i<nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");

    585:   trivial = (eps->problem_type==EPS_HEP || eps->problem_type==EPS_GHEP || eps->problem_type==EPS_BSE)? PETSC_TRUE: PETSC_FALSE;
    586:   if (!trivial) PetscCheck(eps->twosided,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONGSTATE,"Must request left vectors with EPSSetTwoSided");

    588:   PetscCall(EPSComputeVectors(eps));
    589:   if (trivial) {
    590:     PetscCall(EPS_GetEigenvector(eps,eps->V,i,Wr,Wi));
    591:     if (eps->problem_type==EPS_BSE) {   /* change sign of bottom part of the vector */
    592:       PetscCall(STGetMatrix(eps->st,0,&H));
    593:       PetscCall(MatNestGetISs(H,is,NULL));
    594:       if (Wr) {
    595:         PetscCall(VecGetSubVector(Wr,is[1],&v));
    596:         PetscCall(VecScale(v,-1.0));
    597:         PetscCall(VecRestoreSubVector(Wr,is[1],&v));
    598:       }
    599: #if !defined(PETSC_USE_COMPLEX)
    600:       if (Wi) {
    601:         PetscCall(VecGetSubVector(Wi,is[1],&v));
    602:         PetscCall(VecScale(v,-1.0));
    603:         PetscCall(VecRestoreSubVector(Wi,is[1],&v));
    604:       }
    605: #endif
    606:     }
    607:   } else {
    608:     PetscCall(EPS_GetEigenvector(eps,eps->W,i,Wr,Wi));
    609:   }
    610:   PetscFunctionReturn(PETSC_SUCCESS);
    611: }

    613: /*@
    614:    EPSGetErrorEstimate - Returns the error estimate associated to the i-th
    615:    computed eigenpair.

    617:    Not Collective

    619:    Input Parameters:
    620: +  eps - eigensolver context
    621: -  i   - index of eigenpair

    623:    Output Parameter:
    624: .  errest - the error estimate

    626:    Notes:
    627:    This is the error estimate used internally by the eigensolver. The actual
    628:    error bound can be computed with EPSComputeError(). See also the users
    629:    manual for details.

    631:    Level: advanced

    633: .seealso: EPSComputeError()
    634: @*/
    635: PetscErrorCode EPSGetErrorEstimate(EPS eps,PetscInt i,PetscReal *errest)
    636: {
    637:   PetscInt nconv;

    639:   PetscFunctionBegin;
    641:   PetscAssertPointer(errest,3);
    642:   EPSCheckSolved(eps,1);
    643:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    644:   PetscCall(EPS_GetActualConverged(eps,&nconv));
    645:   PetscCheck(i<nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    646:   if (nconv==eps->nconv) {
    647:     *errest = eps->errest[eps->perm[i]];
    648:   } else {
    649:     PetscCheck(eps->problem_type==EPS_BSE,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Problem type should be BSE");
    650:     /* BSE problem, even index is +lambda, odd index is -lambda, assume both have same error */
    651:     *errest = eps->errest[eps->perm[i/2]];
    652:   }
    653:   PetscFunctionReturn(PETSC_SUCCESS);
    654: }

    656: /*
    657:    EPSComputeResidualNorm_Private - Computes the norm of the residual vector
    658:    associated with an eigenpair.

    660:    Input Parameters:
    661:      trans - whether A' must be used instead of A
    662:      kr,ki - eigenvalue
    663:      xr,xi - eigenvector
    664:      z     - three work vectors (the second one not referenced in complex scalars)
    665: */
    666: PetscErrorCode EPSComputeResidualNorm_Private(EPS eps,PetscBool trans,PetscScalar kr,PetscScalar ki,Vec xr,Vec xi,Vec *z,PetscReal *norm)
    667: {
    668:   PetscInt       nmat;
    669:   Mat            A,B;
    670:   Vec            u,w;
    671:   PetscScalar    alpha;
    672: #if !defined(PETSC_USE_COMPLEX)
    673:   Vec            v;
    674:   PetscReal      ni,nr;
    675: #endif
    676:   PetscErrorCode (*matmult)(Mat,Vec,Vec) = trans? MatMultHermitianTranspose: MatMult;

    678:   PetscFunctionBegin;
    679:   u = z[0]; w = z[2];
    680:   PetscCall(STGetNumMatrices(eps->st,&nmat));
    681:   PetscCall(STGetMatrix(eps->st,0,&A));
    682:   if (nmat>1) PetscCall(STGetMatrix(eps->st,1,&B));

    684: #if !defined(PETSC_USE_COMPLEX)
    685:   v = z[1];
    686:   if (ki == 0 || PetscAbsScalar(ki) < PetscAbsScalar(kr*PETSC_MACHINE_EPSILON)) {
    687: #endif
    688:     PetscCall((*matmult)(A,xr,u));                          /* u=A*x */
    689:     if (PetscAbsScalar(kr) > PETSC_MACHINE_EPSILON) {
    690:       if (nmat>1) PetscCall((*matmult)(B,xr,w));
    691:       else PetscCall(VecCopy(xr,w));                        /* w=B*x */
    692:       alpha = trans? -PetscConj(kr): -kr;
    693:       PetscCall(VecAXPY(u,alpha,w));                        /* u=A*x-k*B*x */
    694:     }
    695:     PetscCall(VecNorm(u,NORM_2,norm));
    696: #if !defined(PETSC_USE_COMPLEX)
    697:   } else {
    698:     PetscCall((*matmult)(A,xr,u));                          /* u=A*xr */
    699:     if (SlepcAbsEigenvalue(kr,ki) > PETSC_MACHINE_EPSILON) {
    700:       if (nmat>1) PetscCall((*matmult)(B,xr,v));
    701:       else PetscCall(VecCopy(xr,v));                        /* v=B*xr */
    702:       PetscCall(VecAXPY(u,-kr,v));                          /* u=A*xr-kr*B*xr */
    703:       if (nmat>1) PetscCall((*matmult)(B,xi,w));
    704:       else PetscCall(VecCopy(xi,w));                        /* w=B*xi */
    705:       PetscCall(VecAXPY(u,trans?-ki:ki,w));                 /* u=A*xr-kr*B*xr+ki*B*xi */
    706:     }
    707:     PetscCall(VecNorm(u,NORM_2,&nr));
    708:     PetscCall((*matmult)(A,xi,u));                          /* u=A*xi */
    709:     if (SlepcAbsEigenvalue(kr,ki) > PETSC_MACHINE_EPSILON) {
    710:       PetscCall(VecAXPY(u,-kr,w));                          /* u=A*xi-kr*B*xi */
    711:       PetscCall(VecAXPY(u,trans?ki:-ki,v));                 /* u=A*xi-kr*B*xi-ki*B*xr */
    712:     }
    713:     PetscCall(VecNorm(u,NORM_2,&ni));
    714:     *norm = SlepcAbsEigenvalue(nr,ni);
    715:   }
    716: #endif
    717:   PetscFunctionReturn(PETSC_SUCCESS);
    718: }

    720: /*@
    721:    EPSComputeError - Computes the error (based on the residual norm) associated
    722:    with the i-th computed eigenpair.

    724:    Collective

    726:    Input Parameters:
    727: +  eps  - the eigensolver context
    728: .  i    - the solution index
    729: -  type - the type of error to compute

    731:    Output Parameter:
    732: .  error - the error

    734:    Notes:
    735:    The error can be computed in various ways, all of them based on the residual
    736:    norm ||Ax-kBx||_2 where k is the eigenvalue and x is the eigenvector.

    738:    Level: beginner

    740: .seealso: EPSErrorType, EPSSolve(), EPSGetErrorEstimate()
    741: @*/
    742: PetscErrorCode EPSComputeError(EPS eps,PetscInt i,EPSErrorType type,PetscReal *error)
    743: {
    744:   Mat            A,B;
    745:   Vec            xr,xi,w[3];
    746:   PetscReal      t,vecnorm=1.0,errorl;
    747:   PetscScalar    kr,ki;
    748:   PetscBool      flg;

    750:   PetscFunctionBegin;
    754:   PetscAssertPointer(error,4);
    755:   EPSCheckSolved(eps,1);

    757:   /* allocate work vectors */
    758: #if defined(PETSC_USE_COMPLEX)
    759:   PetscCall(EPSSetWorkVecs(eps,3));
    760:   xi   = NULL;
    761:   w[1] = NULL;
    762: #else
    763:   PetscCall(EPSSetWorkVecs(eps,5));
    764:   xi   = eps->work[3];
    765:   w[1] = eps->work[4];
    766: #endif
    767:   xr   = eps->work[0];
    768:   w[0] = eps->work[1];
    769:   w[2] = eps->work[2];

    771:   /* compute residual norm */
    772:   PetscCall(EPSGetEigenpair(eps,i,&kr,&ki,xr,xi));
    773:   PetscCall(EPSComputeResidualNorm_Private(eps,PETSC_FALSE,kr,ki,xr,xi,w,error));

    775:   /* compute 2-norm of eigenvector */
    776:   if (eps->problem_type==EPS_GHEP) PetscCall(VecNorm(xr,NORM_2,&vecnorm));

    778:   /* if two-sided, compute left residual norm and take the maximum */
    779:   if (eps->twosided) {
    780:     PetscCall(EPSGetLeftEigenvector(eps,i,xr,xi));
    781:     PetscCall(EPSComputeResidualNorm_Private(eps,PETSC_TRUE,kr,ki,xr,xi,w,&errorl));
    782:     *error = PetscMax(*error,errorl);
    783:   }

    785:   /* compute error */
    786:   switch (type) {
    787:     case EPS_ERROR_ABSOLUTE:
    788:       break;
    789:     case EPS_ERROR_RELATIVE:
    790:       *error /= SlepcAbsEigenvalue(kr,ki)*vecnorm;
    791:       break;
    792:     case EPS_ERROR_BACKWARD:
    793:       /* initialization of matrix norms */
    794:       if (!eps->nrma) {
    795:         PetscCall(STGetMatrix(eps->st,0,&A));
    796:         PetscCall(MatHasOperation(A,MATOP_NORM,&flg));
    797:         PetscCheck(flg,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
    798:         PetscCall(MatNorm(A,NORM_INFINITY,&eps->nrma));
    799:       }
    800:       if (eps->isgeneralized) {
    801:         if (!eps->nrmb) {
    802:           PetscCall(STGetMatrix(eps->st,1,&B));
    803:           PetscCall(MatHasOperation(B,MATOP_NORM,&flg));
    804:           PetscCheck(flg,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
    805:           PetscCall(MatNorm(B,NORM_INFINITY,&eps->nrmb));
    806:         }
    807:       } else eps->nrmb = 1.0;
    808:       t = SlepcAbsEigenvalue(kr,ki);
    809:       *error /= (eps->nrma+t*eps->nrmb)*vecnorm;
    810:       break;
    811:     default:
    812:       SETERRQ(PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"Invalid error type");
    813:   }
    814:   PetscFunctionReturn(PETSC_SUCCESS);
    815: }

    817: /*
    818:    EPSGetStartVector - Generate a suitable vector to be used as the starting vector
    819:    for the recurrence that builds the right subspace.

    821:    Collective

    823:    Input Parameters:
    824: +  eps - the eigensolver context
    825: -  i   - iteration number

    827:    Output Parameters:
    828: .  breakdown - flag indicating that a breakdown has occurred

    830:    Notes:
    831:    The start vector is computed from another vector: for the first step (i=0),
    832:    the first initial vector is used (see EPSSetInitialSpace()); otherwise a random
    833:    vector is created. Then this vector is forced to be in the range of OP (only
    834:    for generalized definite problems) and orthonormalized with respect to all
    835:    V-vectors up to i-1. The resulting vector is placed in V[i].

    837:    The flag breakdown is set to true if either i=0 and the vector belongs to the
    838:    deflation space, or i>0 and the vector is linearly dependent with respect
    839:    to the V-vectors.
    840: */
    841: PetscErrorCode EPSGetStartVector(EPS eps,PetscInt i,PetscBool *breakdown)
    842: {
    843:   PetscReal      norm;
    844:   PetscBool      lindep;
    845:   Vec            w,z;

    847:   PetscFunctionBegin;

    851:   /* For the first step, use the first initial vector, otherwise a random one */
    852:   if (i>0 || eps->nini==0) PetscCall(BVSetRandomColumn(eps->V,i));

    854:   /* Force the vector to be in the range of OP for definite generalized problems */
    855:   if (eps->ispositive || (eps->isgeneralized && eps->ishermitian)) {
    856:     PetscCall(BVCreateVec(eps->V,&w));
    857:     PetscCall(BVCopyVec(eps->V,i,w));
    858:     PetscCall(BVGetColumn(eps->V,i,&z));
    859:     PetscCall(STApply(eps->st,w,z));
    860:     PetscCall(BVRestoreColumn(eps->V,i,&z));
    861:     PetscCall(VecDestroy(&w));
    862:   }

    864:   /* Orthonormalize the vector with respect to previous vectors */
    865:   PetscCall(BVOrthogonalizeColumn(eps->V,i,NULL,&norm,&lindep));
    866:   if (breakdown) *breakdown = lindep;
    867:   else if (lindep || norm == 0.0) {
    868:     PetscCheck(i,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Initial vector is zero or belongs to the deflation space");
    869:     PetscCheck(!i,PetscObjectComm((PetscObject)eps),PETSC_ERR_CONV_FAILED,"Unable to generate more start vectors");
    870:   }
    871:   PetscCall(BVScaleColumn(eps->V,i,1.0/norm));
    872:   PetscFunctionReturn(PETSC_SUCCESS);
    873: }

    875: /*
    876:    EPSGetLeftStartVector - Generate a suitable vector to be used as the left starting
    877:    vector for the recurrence that builds the left subspace. See EPSGetStartVector().
    878: */
    879: PetscErrorCode EPSGetLeftStartVector(EPS eps,PetscInt i,PetscBool *breakdown)
    880: {
    881:   PetscReal      norm;
    882:   PetscBool      lindep;

    884:   PetscFunctionBegin;

    888:   /* For the first step, use the first initial vector, otherwise a random one */
    889:   if (i>0 || eps->ninil==0) PetscCall(BVSetRandomColumn(eps->W,i));

    891:   /* Orthonormalize the vector with respect to previous vectors */
    892:   PetscCall(BVOrthogonalizeColumn(eps->W,i,NULL,&norm,&lindep));
    893:   if (breakdown) *breakdown = lindep;
    894:   else if (lindep || norm == 0.0) {
    895:     PetscCheck(i,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Left initial vector is zero");
    896:     SETERRQ(PetscObjectComm((PetscObject)eps),PETSC_ERR_CONV_FAILED,"Unable to generate more left start vectors");
    897:   }
    898:   PetscCall(BVScaleColumn(eps->W,i,1.0/norm));
    899:   PetscFunctionReturn(PETSC_SUCCESS);
    900: }