Date: 2021-02-01T14:42:10+00:00

Actual source code: pepsolve.c
slepc-3.14.2 2021-02-01
Report Typos and Errors

      1: /*
      2:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      3:    SLEPc - Scalable Library for Eigenvalue Problem Computations
      4:    Copyright (c) 2002-2020, 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: {

     42:   PEPCheckSolved(pep,1);
     43:   if (pep->state==PEP_STATE_SOLVED && pep->ops->computevectors) {
     44:     (*pep->ops->computevectors)(pep);
     45:   }
     46:   pep->state = PEP_STATE_EIGENVECTORS;
     47:   return(0);
     48: }

     50: PetscErrorCode PEPExtractVectors(PEP pep)
     51: {

     55:   PEPCheckSolved(pep,1);
     56:   if (pep->state==PEP_STATE_SOLVED && pep->ops->extractvectors) {
     57:     (*pep->ops->extractvectors)(pep);
     58:   }
     59:   return(0);
     60: }

     62: /*@
     63:    PEPSolve - Solves the polynomial eigensystem.

     65:    Collective on pep

     67:    Input Parameter:
     68: .  pep - eigensolver context obtained from PEPCreate()

     70:    Options Database Keys:
     71: +  -pep_view - print information about the solver used
     72: .  -pep_view_matk binary - save any of the coefficient matrices (Ak) to the
     73:                 default binary viewer (replace k by an integer from 0 to nmat-1)
     74: .  -pep_view_vectors binary - save the computed eigenvectors to the default binary viewer
     75: .  -pep_view_values - print computed eigenvalues
     76: .  -pep_converged_reason - print reason for convergence, and number of iterations
     77: .  -pep_error_absolute - print absolute errors of each eigenpair
     78: .  -pep_error_relative - print relative errors of each eigenpair
     79: -  -pep_error_backward - print backward errors of each eigenpair

     81:    Level: beginner

     83: .seealso: PEPCreate(), PEPSetUp(), PEPDestroy(), PEPSetTolerances()
     84: @*/
     85: PetscErrorCode PEPSolve(PEP pep)
     86: {
     88:   PetscInt       i,k;
     89:   PetscBool      flg,islinear;
     90:   char           str[16];

     94:   if (pep->state>=PEP_STATE_SOLVED) return(0);
     95:   PetscLogEventBegin(PEP_Solve,pep,0,0,0);

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

    111:   /* Call solver */
    112:   (*pep->ops->solve)(pep);
    113:   if (!pep->reason) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");
    114:   pep->state = PEP_STATE_SOLVED;

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

    119:   PetscObjectTypeCompare((PetscObject)pep,PEPLINEAR,&islinear);
    120:   if (!islinear) {
    121:     STPostSolve(pep->st);
    122:     /* Map eigenvalues back to the original problem */
    123:     STGetTransform(pep->st,&flg);
    124:     if (flg && pep->ops->backtransform) {
    125:       (*pep->ops->backtransform)(pep);
    126:     }
    127:   }

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

    145:   if (pep->refine!=PEP_REFINE_NONE) {
    146:     PetscCitationsRegister(citation,&cited);
    147:   }

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

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

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

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

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

    179:    Not Collective

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

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

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

    194:    Level: intermediate

    196: .seealso: PEPGetConvergedReason(), PEPSetTolerances()
    197: @*/
    198: PetscErrorCode PEPGetIterationNumber(PEP pep,PetscInt *its)
    199: {
    203:   *its = pep->its;
    204:   return(0);
    205: }

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

    210:    Not Collective

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

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

    218:    Note:
    219:    This function should be called after PEPSolve() has finished.

    221:    Level: beginner

    223: .seealso: PEPSetDimensions(), PEPSolve()
    224: @*/
    225: PetscErrorCode PEPGetConverged(PEP pep,PetscInt *nconv)
    226: {
    230:   PEPCheckSolved(pep,1);
    231:   *nconv = pep->nconv;
    232:   return(0);
    233: }

    235: /*@
    236:    PEPGetConvergedReason - Gets the reason why the PEPSolve() iteration was
    237:    stopped.

    239:    Not Collective

    241:    Input Parameter:
    242: .  pep - the polynomial eigensolver context

    244:    Output Parameter:
    245: .  reason - negative value indicates diverged, positive value converged

    247:    Notes:

    249:    Possible values for reason are
    250: +  PEP_CONVERGED_TOL - converged up to tolerance
    251: .  PEP_CONVERGED_USER - converged due to a user-defined condition
    252: .  PEP_DIVERGED_ITS - required more than max_it iterations to reach convergence
    253: .  PEP_DIVERGED_BREAKDOWN - generic breakdown in method
    254: -  PEP_DIVERGED_SYMMETRY_LOST - pseudo-Lanczos was not able to keep symmetry

    256:    Can only be called after the call to PEPSolve() is complete.

    258:    Level: intermediate

    260: .seealso: PEPSetTolerances(), PEPSolve(), PEPConvergedReason
    261: @*/
    262: PetscErrorCode PEPGetConvergedReason(PEP pep,PEPConvergedReason *reason)
    263: {
    267:   PEPCheckSolved(pep,1);
    268:   *reason = pep->reason;
    269:   return(0);
    270: }

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

    276:    Logically Collective on pep

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

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

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

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

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

    303:    Level: beginner

    305: .seealso: PEPSolve(), PEPGetConverged(), PEPSetWhichEigenpairs()
    306: @*/
    307: PetscErrorCode PEPGetEigenpair(PEP pep,PetscInt i,PetscScalar *eigr,PetscScalar *eigi,Vec Vr,Vec Vi)
    308: {
    309:   PetscInt       k;

    317:   PEPCheckSolved(pep,1);
    318:   if (i<0 || i>=pep->nconv) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");

    320:   PEPComputeVectors(pep);
    321:   k = pep->perm[i];

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

    332:   /* eigenvector */
    333:   BV_GetEigenvector(pep->V,k,pep->eigi[k],Vr,Vi);
    334:   return(0);
    335: }

    337: /*@
    338:    PEPGetErrorEstimate - Returns the error estimate associated to the i-th
    339:    computed eigenpair.

    341:    Not Collective

    343:    Input Parameter:
    344: +  pep - polynomial eigensolver context
    345: -  i   - index of eigenpair

    347:    Output Parameter:
    348: .  errest - the error estimate

    350:    Notes:
    351:    This is the error estimate used internally by the eigensolver. The actual
    352:    error bound can be computed with PEPComputeError(). See also the users
    353:    manual for details.

    355:    Level: advanced

    357: .seealso: PEPComputeError()
    358: @*/
    359: PetscErrorCode PEPGetErrorEstimate(PEP pep,PetscInt i,PetscReal *errest)
    360: {
    364:   PEPCheckSolved(pep,1);
    365:   if (i<0 || i>=pep->nconv) SETERRQ(PetscObjectComm((PetscObject)pep),PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");
    366:   *errest = pep->errest[pep->perm[i]];
    367:   return(0);
    368: }

    370: /*
    371:    PEPComputeResidualNorm_Private - Computes the norm of the residual vector
    372:    associated with an eigenpair.

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

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

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

    458:    Collective on pep

    460:    Input Parameter:
    461: +  pep  - the polynomial eigensolver context
    462: .  i    - the solution index
    463: -  type - the type of error to compute

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

    468:    Notes:
    469:    The error can be computed in various ways, all of them based on the residual
    470:    norm ||P(l)x||_2 where l is the eigenvalue and x is the eigenvector.
    471:    See the users guide for additional details.

    473:    Level: beginner

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

    491:   PEPCheckSolved(pep,1);

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

    509:   /* compute residual norms */
    510:   PEPGetEigenpair(pep,i,&kr,&ki,xr,xi);
    511:   PEPComputeResidualNorm_Private(pep,kr,ki,xr,xi,w,error);

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