Date: 2023-09-05T10:17:53+00:00

Actual source code: epssolve.c
slepc-3.19.2 2023-09-05
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: #define SWAP(a,b,t) do {t=a;a=b;b=t;} while (0)

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

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

     91: /*@
     92:    EPSSolve - Solves the eigensystem.

     94:    Collective

     96:    Input Parameter:
     97: .  eps - eigensolver context obtained from EPSCreate()

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

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

    117:    Level: beginner

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

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

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

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

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

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

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

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

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

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

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

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

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

    217:    Not Collective

    219:    Input Parameter:
    220: .  eps - the eigensolver context

    222:    Output Parameter:
    223: .  its - number of iterations

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

    232:    Level: intermediate

    234: .seealso: EPSGetConvergedReason(), EPSSetTolerances()
    235: @*/
    236: PetscErrorCode EPSGetIterationNumber(EPS eps,PetscInt *its)
    237: {
    238:   PetscFunctionBegin;
    241:   *its = eps->its;
    242:   PetscFunctionReturn(PETSC_SUCCESS);
    243: }

    245: /*@
    246:    EPSGetConverged - Gets the number of converged eigenpairs.

    248:    Not Collective

    250:    Input Parameter:
    251: .  eps - the eigensolver context

    253:    Output Parameter:
    254: .  nconv - number of converged eigenpairs

    256:    Note:
    257:    This function should be called after EPSSolve() has finished.

    259:    Level: beginner

    261: .seealso: EPSSetDimensions(), EPSSolve(), EPSGetEigenpair()
    262: @*/
    263: PetscErrorCode EPSGetConverged(EPS eps,PetscInt *nconv)
    264: {
    265:   PetscFunctionBegin;
    268:   EPSCheckSolved(eps,1);
    269:   *nconv = eps->nconv;
    270:   PetscFunctionReturn(PETSC_SUCCESS);
    271: }

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

    277:    Not Collective

    279:    Input Parameter:
    280: .  eps - the eigensolver context

    282:    Output Parameter:
    283: .  reason - negative value indicates diverged, positive value converged

    285:    Options Database Key:
    286: .  -eps_converged_reason - print the reason to a viewer

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

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

    298:    Level: intermediate

    300: .seealso: EPSSetTolerances(), EPSSolve(), EPSConvergedReason
    301: @*/
    302: PetscErrorCode EPSGetConvergedReason(EPS eps,EPSConvergedReason *reason)
    303: {
    304:   PetscFunctionBegin;
    307:   EPSCheckSolved(eps,1);
    308:   *reason = eps->reason;
    309:   PetscFunctionReturn(PETSC_SUCCESS);
    310: }

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

    316:    Collective

    318:    Input Parameter:
    319: .  eps - the eigensolver context

    321:    Output Parameter:
    322: .  v - an array of vectors

    324:    Notes:
    325:    This function should be called after EPSSolve() has finished.

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

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

    337:    Level: intermediate

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

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

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

    373:    Collective

    375:    Input Parameters:
    376: +  eps - eigensolver context
    377: -  i   - index of the solution

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

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

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

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

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

    403:    Level: beginner

    405: .seealso: EPSGetEigenvalue(), EPSGetEigenvector(), EPSGetLeftEigenvector(), EPSSolve(),
    406:           EPSGetConverged(), EPSSetWhichEigenpairs(), EPSGetInvariantSubspace()
    407: @*/
    408: PetscErrorCode EPSGetEigenpair(EPS eps,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)
    409: {
    410:   PetscFunctionBegin;
    413:   EPSCheckSolved(eps,1);
    414:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    415:   PetscCheck(i<eps->nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    416:   PetscCall(EPSGetEigenvalue(eps,i,eigr,eigi));
    417:   if (Vr || Vi) PetscCall(EPSGetEigenvector(eps,i,Vr,Vi));
    418:   PetscFunctionReturn(PETSC_SUCCESS);
    419: }

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

    424:    Not Collective

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

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

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

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

    443:    Level: beginner

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

    451:   PetscFunctionBegin;
    453:   EPSCheckSolved(eps,1);
    454:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    455:   PetscCheck(i<eps->nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    456:   k = eps->perm[i];
    457: #if defined(PETSC_USE_COMPLEX)
    458:   if (eigr) *eigr = eps->eigr[k];
    459:   if (eigi) *eigi = 0;
    460: #else
    461:   if (eigr) *eigr = eps->eigr[k];
    462:   if (eigi) *eigi = eps->eigi[k];
    463: #endif
    464:   PetscFunctionReturn(PETSC_SUCCESS);
    465: }

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

    470:    Collective

    472:    Input Parameters:
    473: +  eps - eigensolver context
    474: -  i   - index of the solution

    476:    Output Parameters:
    477: +  Vr   - real part of eigenvector
    478: -  Vi   - imaginary part of eigenvector

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

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

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

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

    497:    Level: beginner

    499: .seealso: EPSSolve(), EPSGetConverged(), EPSSetWhichEigenpairs(), EPSGetEigenpair(), EPSGetLeftEigenvector()
    500: @*/
    501: PetscErrorCode EPSGetEigenvector(EPS eps,PetscInt i,Vec Vr,Vec Vi)
    502: {
    503:   PetscInt       k;

    505:   PetscFunctionBegin;
    510:   EPSCheckSolved(eps,1);
    511:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    512:   PetscCheck(i<eps->nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    513:   PetscCall(EPSComputeVectors(eps));
    514:   k = eps->perm[i];
    515:   PetscCall(BV_GetEigenvector(eps->V,k,eps->eigi[k],Vr,Vi));
    516:   PetscFunctionReturn(PETSC_SUCCESS);
    517: }

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

    522:    Collective

    524:    Input Parameters:
    525: +  eps - eigensolver context
    526: -  i   - index of the solution

    528:    Output Parameters:
    529: +  Wr   - real part of left eigenvector
    530: -  Wi   - imaginary part of left eigenvector

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

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

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

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

    548:    Level: intermediate

    550: .seealso: EPSGetEigenvector(), EPSGetConverged(), EPSSetWhichEigenpairs(), EPSSetTwoSided()
    551: @*/
    552: PetscErrorCode EPSGetLeftEigenvector(EPS eps,PetscInt i,Vec Wr,Vec Wi)
    553: {
    554:   PetscInt       k;

    556:   PetscFunctionBegin;
    561:   EPSCheckSolved(eps,1);
    562:   PetscCheck(eps->twosided,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONGSTATE,"Must request left vectors with EPSSetTwoSided");
    563:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    564:   PetscCheck(i<eps->nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    565:   PetscCall(EPSComputeVectors(eps));
    566:   k = eps->perm[i];
    567:   PetscCall(BV_GetEigenvector(eps->W,k,eps->eigi[k],Wr,Wi));
    568:   PetscFunctionReturn(PETSC_SUCCESS);
    569: }

    571: /*@
    572:    EPSGetErrorEstimate - Returns the error estimate associated to the i-th
    573:    computed eigenpair.

    575:    Not Collective

    577:    Input Parameters:
    578: +  eps - eigensolver context
    579: -  i   - index of eigenpair

    581:    Output Parameter:
    582: .  errest - the error estimate

    584:    Notes:
    585:    This is the error estimate used internally by the eigensolver. The actual
    586:    error bound can be computed with EPSComputeError(). See also the users
    587:    manual for details.

    589:    Level: advanced

    591: .seealso: EPSComputeError()
    592: @*/
    593: PetscErrorCode EPSGetErrorEstimate(EPS eps,PetscInt i,PetscReal *errest)
    594: {
    595:   PetscFunctionBegin;
    598:   EPSCheckSolved(eps,1);
    599:   PetscCheck(i>=0,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index cannot be negative");
    600:   PetscCheck(i<eps->nconv,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The index can be nconv-1 at most, see EPSGetConverged()");
    601:   *errest = eps->errest[eps->perm[i]];
    602:   PetscFunctionReturn(PETSC_SUCCESS);
    603: }

    605: /*
    606:    EPSComputeResidualNorm_Private - Computes the norm of the residual vector
    607:    associated with an eigenpair.

    609:    Input Parameters:
    610:      trans - whether A' must be used instead of A
    611:      kr,ki - eigenvalue
    612:      xr,xi - eigenvector
    613:      z     - three work vectors (the second one not referenced in complex scalars)
    614: */
    615: PetscErrorCode EPSComputeResidualNorm_Private(EPS eps,PetscBool trans,PetscScalar kr,PetscScalar ki,Vec xr,Vec xi,Vec *z,PetscReal *norm)
    616: {
    617:   PetscInt       nmat;
    618:   Mat            A,B;
    619:   Vec            u,w;
    620:   PetscScalar    alpha;
    621: #if !defined(PETSC_USE_COMPLEX)
    622:   Vec            v;
    623:   PetscReal      ni,nr;
    624: #endif
    625:   PetscErrorCode (*matmult)(Mat,Vec,Vec) = trans? MatMultHermitianTranspose: MatMult;

    627:   PetscFunctionBegin;
    628:   u = z[0]; w = z[2];
    629:   PetscCall(STGetNumMatrices(eps->st,&nmat));
    630:   PetscCall(STGetMatrix(eps->st,0,&A));
    631:   if (nmat>1) PetscCall(STGetMatrix(eps->st,1,&B));

    633: #if !defined(PETSC_USE_COMPLEX)
    634:   v = z[1];
    635:   if (ki == 0 || PetscAbsScalar(ki) < PetscAbsScalar(kr*PETSC_MACHINE_EPSILON)) {
    636: #endif
    637:     PetscCall((*matmult)(A,xr,u));                          /* u=A*x */
    638:     if (PetscAbsScalar(kr) > PETSC_MACHINE_EPSILON) {
    639:       if (nmat>1) PetscCall((*matmult)(B,xr,w));
    640:       else PetscCall(VecCopy(xr,w));                        /* w=B*x */
    641:       alpha = trans? -PetscConj(kr): -kr;
    642:       PetscCall(VecAXPY(u,alpha,w));                        /* u=A*x-k*B*x */
    643:     }
    644:     PetscCall(VecNorm(u,NORM_2,norm));
    645: #if !defined(PETSC_USE_COMPLEX)
    646:   } else {
    647:     PetscCall((*matmult)(A,xr,u));                          /* u=A*xr */
    648:     if (SlepcAbsEigenvalue(kr,ki) > PETSC_MACHINE_EPSILON) {
    649:       if (nmat>1) PetscCall((*matmult)(B,xr,v));
    650:       else PetscCall(VecCopy(xr,v));                        /* v=B*xr */
    651:       PetscCall(VecAXPY(u,-kr,v));                          /* u=A*xr-kr*B*xr */
    652:       if (nmat>1) PetscCall((*matmult)(B,xi,w));
    653:       else PetscCall(VecCopy(xi,w));                        /* w=B*xi */
    654:       PetscCall(VecAXPY(u,trans?-ki:ki,w));                 /* u=A*xr-kr*B*xr+ki*B*xi */
    655:     }
    656:     PetscCall(VecNorm(u,NORM_2,&nr));
    657:     PetscCall((*matmult)(A,xi,u));                          /* u=A*xi */
    658:     if (SlepcAbsEigenvalue(kr,ki) > PETSC_MACHINE_EPSILON) {
    659:       PetscCall(VecAXPY(u,-kr,w));                          /* u=A*xi-kr*B*xi */
    660:       PetscCall(VecAXPY(u,trans?ki:-ki,v));                 /* u=A*xi-kr*B*xi-ki*B*xr */
    661:     }
    662:     PetscCall(VecNorm(u,NORM_2,&ni));
    663:     *norm = SlepcAbsEigenvalue(nr,ni);
    664:   }
    665: #endif
    666:   PetscFunctionReturn(PETSC_SUCCESS);
    667: }

    669: /*@
    670:    EPSComputeError - Computes the error (based on the residual norm) associated
    671:    with the i-th computed eigenpair.

    673:    Collective

    675:    Input Parameters:
    676: +  eps  - the eigensolver context
    677: .  i    - the solution index
    678: -  type - the type of error to compute

    680:    Output Parameter:
    681: .  error - the error

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

    687:    Level: beginner

    689: .seealso: EPSErrorType, EPSSolve(), EPSGetErrorEstimate()
    690: @*/
    691: PetscErrorCode EPSComputeError(EPS eps,PetscInt i,EPSErrorType type,PetscReal *error)
    692: {
    693:   Mat            A,B;
    694:   Vec            xr,xi,w[3];
    695:   PetscReal      t,vecnorm=1.0,errorl;
    696:   PetscScalar    kr,ki;
    697:   PetscBool      flg;

    699:   PetscFunctionBegin;
    704:   EPSCheckSolved(eps,1);

    706:   /* allocate work vectors */
    707: #if defined(PETSC_USE_COMPLEX)
    708:   PetscCall(EPSSetWorkVecs(eps,3));
    709:   xi   = NULL;
    710:   w[1] = NULL;
    711: #else
    712:   PetscCall(EPSSetWorkVecs(eps,5));
    713:   xi   = eps->work[3];
    714:   w[1] = eps->work[4];
    715: #endif
    716:   xr   = eps->work[0];
    717:   w[0] = eps->work[1];
    718:   w[2] = eps->work[2];

    720:   /* compute residual norm */
    721:   PetscCall(EPSGetEigenpair(eps,i,&kr,&ki,xr,xi));
    722:   PetscCall(EPSComputeResidualNorm_Private(eps,PETSC_FALSE,kr,ki,xr,xi,w,error));

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

    727:   /* if two-sided, compute left residual norm and take the maximum */
    728:   if (eps->twosided) {
    729:     PetscCall(EPSGetLeftEigenvector(eps,i,xr,xi));
    730:     PetscCall(EPSComputeResidualNorm_Private(eps,PETSC_TRUE,kr,ki,xr,xi,w,&errorl));
    731:     *error = PetscMax(*error,errorl);
    732:   }

    734:   /* compute error */
    735:   switch (type) {
    736:     case EPS_ERROR_ABSOLUTE:
    737:       break;
    738:     case EPS_ERROR_RELATIVE:
    739:       *error /= SlepcAbsEigenvalue(kr,ki)*vecnorm;
    740:       break;
    741:     case EPS_ERROR_BACKWARD:
    742:       /* initialization of matrix norms */
    743:       if (!eps->nrma) {
    744:         PetscCall(STGetMatrix(eps->st,0,&A));
    745:         PetscCall(MatHasOperation(A,MATOP_NORM,&flg));
    746:         PetscCheck(flg,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
    747:         PetscCall(MatNorm(A,NORM_INFINITY,&eps->nrma));
    748:       }
    749:       if (eps->isgeneralized) {
    750:         if (!eps->nrmb) {
    751:           PetscCall(STGetMatrix(eps->st,1,&B));
    752:           PetscCall(MatHasOperation(B,MATOP_NORM,&flg));
    753:           PetscCheck(flg,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_WRONG,"The computation of backward errors requires a matrix norm operation");
    754:           PetscCall(MatNorm(B,NORM_INFINITY,&eps->nrmb));
    755:         }
    756:       } else eps->nrmb = 1.0;
    757:       t = SlepcAbsEigenvalue(kr,ki);
    758:       *error /= (eps->nrma+t*eps->nrmb)*vecnorm;
    759:       break;
    760:     default:
    761:       SETERRQ(PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"Invalid error type");
    762:   }
    763:   PetscFunctionReturn(PETSC_SUCCESS);
    764: }

    766: /*
    767:    EPSGetStartVector - Generate a suitable vector to be used as the starting vector
    768:    for the recurrence that builds the right subspace.

    770:    Collective

    772:    Input Parameters:
    773: +  eps - the eigensolver context
    774: -  i   - iteration number

    776:    Output Parameters:
    777: .  breakdown - flag indicating that a breakdown has occurred

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

    786:    The flag breakdown is set to true if either i=0 and the vector belongs to the
    787:    deflation space, or i>0 and the vector is linearly dependent with respect
    788:    to the V-vectors.
    789: */
    790: PetscErrorCode EPSGetStartVector(EPS eps,PetscInt i,PetscBool *breakdown)
    791: {
    792:   PetscReal      norm;
    793:   PetscBool      lindep;
    794:   Vec            w,z;

    796:   PetscFunctionBegin;

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

    803:   /* Force the vector to be in the range of OP for definite generalized problems */
    804:   if (eps->ispositive || (eps->isgeneralized && eps->ishermitian)) {
    805:     PetscCall(BVCreateVec(eps->V,&w));
    806:     PetscCall(BVCopyVec(eps->V,i,w));
    807:     PetscCall(BVGetColumn(eps->V,i,&z));
    808:     PetscCall(STApply(eps->st,w,z));
    809:     PetscCall(BVRestoreColumn(eps->V,i,&z));
    810:     PetscCall(VecDestroy(&w));
    811:   }

    813:   /* Orthonormalize the vector with respect to previous vectors */
    814:   PetscCall(BVOrthogonalizeColumn(eps->V,i,NULL,&norm,&lindep));
    815:   if (breakdown) *breakdown = lindep;
    816:   else if (lindep || norm == 0.0) {
    817:     PetscCheck(i,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Initial vector is zero or belongs to the deflation space");
    818:     PetscCheck(!i,PetscObjectComm((PetscObject)eps),PETSC_ERR_CONV_FAILED,"Unable to generate more start vectors");
    819:   }
    820:   PetscCall(BVScaleColumn(eps->V,i,1.0/norm));
    821:   PetscFunctionReturn(PETSC_SUCCESS);
    822: }

    824: /*
    825:    EPSGetLeftStartVector - Generate a suitable vector to be used as the left starting
    826:    vector for the recurrence that builds the left subspace. See EPSGetStartVector().
    827: */
    828: PetscErrorCode EPSGetLeftStartVector(EPS eps,PetscInt i,PetscBool *breakdown)
    829: {
    830:   PetscReal      norm;
    831:   PetscBool      lindep;

    833:   PetscFunctionBegin;

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

    840:   /* Orthonormalize the vector with respect to previous vectors */
    841:   PetscCall(BVOrthogonalizeColumn(eps->W,i,NULL,&norm,&lindep));
    842:   if (breakdown) *breakdown = lindep;
    843:   else if (lindep || norm == 0.0) {
    844:     PetscCheck(i,PetscObjectComm((PetscObject)eps),PETSC_ERR_PLIB,"Left initial vector is zero");
    845:     SETERRQ(PetscObjectComm((PetscObject)eps),PETSC_ERR_CONV_FAILED,"Unable to generate more left start vectors");
    846:   }
    847:   PetscCall(BVScaleColumn(eps->W,i,1.0/norm));
    848:   PetscFunctionReturn(PETSC_SUCCESS);
    849: }