Actual source code: pepsolve.c

      1: /*
      2:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      3:    SLEPc - Scalable Library for Eigenvalue Problem Computations
      4:    Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain

      6:    This file is part of SLEPc.
      7:    SLEPc is distributed under a 2-clause BSD license (see LICENSE).
      8:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      9: */
     10: /*
     11:    PEP routines related to the solution process

     13:    References:

     15:        [1] C. Campos and J.E. Roman, "Parallel iterative refinement in
     16:            polynomial eigenvalue problems", Numer. Linear Algebra Appl.
     17:            23(4):730-745, 2016.
     18: */

     20: #include <slepc/private/pepimpl.h>
     21: #include <slepc/private/bvimpl.h>
     22: #include <petscdraw.h>

     24: static PetscBool  cited = PETSC_FALSE;
     25: static const char citation[] =
     26:   "@Article{slepc-pep-refine,\n"
     27:   "   author = \"C. Campos and J. E. Roman\",\n"
     28:   "   title = \"Parallel iterative refinement in polynomial eigenvalue problems\",\n"
     29:   "   journal = \"Numer. Linear Algebra Appl.\",\n"
     30:   "   volume = \"23\",\n"
     31:   "   number = \"4\",\n"
     32:   "   pages = \"730--745\",\n"
     33:   "   year = \"2016,\"\n"
     34:   "   doi = \"https://doi.org/10.1002/nla.2052\"\n"
     35:   "}\n";

     37: PetscErrorCode PEPComputeVectors(PEP pep)
     38: {
     39:   PetscFunctionBegin;
     40:   PEPCheckSolved(pep,1);
     41:   if (pep->state==PEP_STATE_SOLVED) PetscTryTypeMethod(pep,computevectors);
     42:   pep->state = PEP_STATE_EIGENVECTORS;
     43:   PetscFunctionReturn(PETSC_SUCCESS);
     44: }

     46: PetscErrorCode PEPExtractVectors(PEP pep)
     47: {
     48:   PetscFunctionBegin;
     49:   PEPCheckSolved(pep,1);
     50:   if (pep->state==PEP_STATE_SOLVED) PetscTryTypeMethod(pep,extractvectors);
     51:   PetscFunctionReturn(PETSC_SUCCESS);
     52: }

     54: /*@
     55:    PEPSolve - Solves the polynomial eigenproblem.

     57:    Collective

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

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

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

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

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

     87:    Level: beginner

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

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

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

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

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

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

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

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

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

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

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

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

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

    180:    Not Collective

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

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

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

    195:    Level: intermediate

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

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

    211:    Not Collective

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

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

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

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

    225:    Level: beginner

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

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

    243:    Not Collective

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

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

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

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

    259:    Level: intermediate

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

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

    277:    Collective

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

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

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

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

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

    304:    The eigenvector is normalized to have unit norm.

    306:    Level: beginner

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

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

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

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

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

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

    344:    Not Collective

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

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

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

    357:    Level: advanced

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

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

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

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

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

    456:    Collective

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

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

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

    470:    Level: beginner

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

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

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

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

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