Date: 2024-10-28T09:13:20+00:00

Actual source code: lyapii.c
slepc-3.22.1 2024-10-28
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:    SLEPc eigensolver: "lyapii"

     13:    Method: Lyapunov inverse iteration

     15:    Algorithm:

     17:        Lyapunov inverse iteration using LME solvers

     19:    References:

     21:        [1] H.C. Elman and M. Wu, "Lyapunov inverse iteration for computing a
     22:            few rightmost eigenvalues of large generalized eigenvalue problems",
     23:            SIAM J. Matrix Anal. Appl. 34(4):1685-1707, 2013.

     25:        [2] K. Meerbergen and A. Spence, "Inverse iteration for purely imaginary
     26:            eigenvalues with application to the detection of Hopf bifurcations in
     27:            large-scale problems", SIAM J. Matrix Anal. Appl. 31:1982-1999, 2010.
     28: */

     30: #include <slepc/private/epsimpl.h>
     31: #include <slepcblaslapack.h>

     33: typedef struct {
     34:   LME      lme;      /* Lyapunov solver */
     35:   DS       ds;       /* used to compute the SVD for compression */
     36:   PetscInt rkl;      /* prescribed rank for the Lyapunov solver */
     37:   PetscInt rkc;      /* the compressed rank, cannot be larger than rkl */
     38: } EPS_LYAPII;

     40: typedef struct {
     41:   Mat      S;        /* the operator matrix, S=A^{-1}*B */
     42:   BV       Q;        /* orthogonal basis of converged eigenvectors */
     43: } EPS_LYAPII_MATSHELL;

     45: typedef struct {
     46:   Mat      S;        /* the matrix from which the implicit operator is built */
     47:   PetscInt n;        /* the size of matrix S, the operator is nxn */
     48:   LME      lme;      /* dummy LME object */
     49: #if defined(PETSC_USE_COMPLEX)
     50:   Mat      A,B,F;
     51:   Vec      w;
     52: #endif
     53: } EPS_EIG_MATSHELL;

     55: static PetscErrorCode EPSSetUp_LyapII(EPS eps)
     56: {
     57:   PetscRandom    rand;
     58:   EPS_LYAPII     *ctx = (EPS_LYAPII*)eps->data;

     60:   PetscFunctionBegin;
     61:   EPSCheckSinvert(eps);
     62:   EPSCheckNotStructured(eps);
     63:   if (eps->ncv!=PETSC_DETERMINE) {
     64:     PetscCheck(eps->ncv>=eps->nev+1,PetscObjectComm((PetscObject)eps),PETSC_ERR_USER_INPUT,"The value of ncv must be at least nev+1");
     65:   } else eps->ncv = eps->nev+1;
     66:   if (eps->mpd!=PETSC_DETERMINE) PetscCall(PetscInfo(eps,"Warning: parameter mpd ignored\n"));
     67:   if (eps->max_it==PETSC_DETERMINE) eps->max_it = PetscMax(1000*eps->nev,100*eps->n);
     68:   if (!eps->which) eps->which=EPS_LARGEST_REAL;
     69:   PetscCheck(eps->which==EPS_LARGEST_REAL,PetscObjectComm((PetscObject)eps),PETSC_ERR_SUP,"This solver supports only largest real eigenvalues");
     70:   EPSCheckUnsupported(eps,EPS_FEATURE_BALANCE | EPS_FEATURE_ARBITRARY | EPS_FEATURE_REGION | EPS_FEATURE_EXTRACTION | EPS_FEATURE_TWOSIDED);

     72:   if (!ctx->rkc) ctx->rkc = 10;
     73:   if (!ctx->rkl) ctx->rkl = 3*ctx->rkc;
     74:   if (!ctx->lme) PetscCall(EPSLyapIIGetLME(eps,&ctx->lme));
     75:   PetscCall(LMESetProblemType(ctx->lme,LME_LYAPUNOV));
     76:   PetscCall(LMESetErrorIfNotConverged(ctx->lme,PETSC_TRUE));

     78:   if (!ctx->ds) {
     79:     PetscCall(DSCreate(PetscObjectComm((PetscObject)eps),&ctx->ds));
     80:     PetscCall(DSSetType(ctx->ds,DSSVD));
     81:   }
     82:   PetscCall(DSAllocate(ctx->ds,ctx->rkl));

     84:   PetscCall(DSSetType(eps->ds,DSNHEP));
     85:   PetscCall(DSAllocate(eps->ds,eps->ncv));

     87:   PetscCall(EPSAllocateSolution(eps,0));
     88:   PetscCall(BVGetRandomContext(eps->V,&rand));  /* make sure the random context is available when duplicating */
     89:   PetscCall(EPSSetWorkVecs(eps,3));
     90:   PetscFunctionReturn(PETSC_SUCCESS);
     91: }

     93: static PetscErrorCode MatMult_EPSLyapIIOperator(Mat M,Vec x,Vec r)
     94: {
     95:   EPS_LYAPII_MATSHELL *matctx;

     97:   PetscFunctionBegin;
     98:   PetscCall(MatShellGetContext(M,&matctx));
     99:   PetscCall(MatMult(matctx->S,x,r));
    100:   PetscCall(BVOrthogonalizeVec(matctx->Q,r,NULL,NULL,NULL));
    101:   PetscFunctionReturn(PETSC_SUCCESS);
    102: }

    104: static PetscErrorCode MatDestroy_EPSLyapIIOperator(Mat M)
    105: {
    106:   EPS_LYAPII_MATSHELL *matctx;

    108:   PetscFunctionBegin;
    109:   PetscCall(MatShellGetContext(M,&matctx));
    110:   PetscCall(MatDestroy(&matctx->S));
    111:   PetscCall(PetscFree(matctx));
    112:   PetscFunctionReturn(PETSC_SUCCESS);
    113: }

    115: static PetscErrorCode MatMult_EigOperator(Mat M,Vec x,Vec y)
    116: {
    117:   EPS_EIG_MATSHELL  *matctx;
    118: #if !defined(PETSC_USE_COMPLEX)
    119:   PetscInt          n,lds;
    120:   PetscScalar       *Y,*C,zero=0.0,done=1.0,dtwo=2.0;
    121:   const PetscScalar *S,*X;
    122:   PetscBLASInt      n_,lds_;
    123: #endif

    125:   PetscFunctionBegin;
    126:   PetscCall(MatShellGetContext(M,&matctx));

    128: #if defined(PETSC_USE_COMPLEX)
    129:   PetscCall(MatMult(matctx->B,x,matctx->w));
    130:   PetscCall(MatSolve(matctx->F,matctx->w,y));
    131: #else
    132:   PetscCall(VecGetArrayRead(x,&X));
    133:   PetscCall(VecGetArray(y,&Y));
    134:   PetscCall(MatDenseGetArrayRead(matctx->S,&S));
    135:   PetscCall(MatDenseGetLDA(matctx->S,&lds));

    137:   n = matctx->n;
    138:   PetscCall(PetscCalloc1(n*n,&C));
    139:   PetscCall(PetscBLASIntCast(n,&n_));
    140:   PetscCall(PetscBLASIntCast(lds,&lds_));

    142:   /* C = 2*S*X*S.' */
    143:   PetscCallBLAS("BLASgemm",BLASgemm_("N","N",&n_,&n_,&n_,&dtwo,S,&lds_,X,&n_,&zero,Y,&n_));
    144:   PetscCallBLAS("BLASgemm",BLASgemm_("N","T",&n_,&n_,&n_,&done,Y,&n_,S,&lds_,&zero,C,&n_));

    146:   /* Solve S*Y + Y*S' = -C */
    147:   PetscCall(LMEDenseLyapunov(matctx->lme,n,(PetscScalar*)S,lds,C,n,Y,n));

    149:   PetscCall(PetscFree(C));
    150:   PetscCall(VecRestoreArrayRead(x,&X));
    151:   PetscCall(VecRestoreArray(y,&Y));
    152:   PetscCall(MatDenseRestoreArrayRead(matctx->S,&S));
    153: #endif
    154:   PetscFunctionReturn(PETSC_SUCCESS);
    155: }

    157: static PetscErrorCode MatDestroy_EigOperator(Mat M)
    158: {
    159:   EPS_EIG_MATSHELL *matctx;

    161:   PetscFunctionBegin;
    162:   PetscCall(MatShellGetContext(M,&matctx));
    163: #if defined(PETSC_USE_COMPLEX)
    164:   PetscCall(MatDestroy(&matctx->A));
    165:   PetscCall(MatDestroy(&matctx->B));
    166:   PetscCall(MatDestroy(&matctx->F));
    167:   PetscCall(VecDestroy(&matctx->w));
    168: #else
    169:   PetscCall(MatDestroy(&matctx->S));
    170: #endif
    171:   PetscCall(PetscFree(matctx));
    172:   PetscFunctionReturn(PETSC_SUCCESS);
    173: }

    175: /*
    176:    EV2x2: solve the eigenproblem for a 2x2 matrix M
    177:  */
    178: static PetscErrorCode EV2x2(PetscScalar *M,PetscInt ld,PetscScalar *wr,PetscScalar *wi,PetscScalar *vec)
    179: {
    180:   PetscBLASInt   lwork=10,ld_;
    181:   PetscScalar    work[10];
    182:   PetscBLASInt   two=2,info;
    183: #if defined(PETSC_USE_COMPLEX)
    184:   PetscReal      rwork[6];
    185: #endif

    187:   PetscFunctionBegin;
    188:   PetscCall(PetscBLASIntCast(ld,&ld_));
    189:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    190: #if !defined(PETSC_USE_COMPLEX)
    191:   PetscCallBLAS("LAPACKgeev",LAPACKgeev_("N","V",&two,M,&ld_,wr,wi,NULL,&ld_,vec,&ld_,work,&lwork,&info));
    192: #else
    193:   PetscCallBLAS("LAPACKgeev",LAPACKgeev_("N","V",&two,M,&ld_,wr,NULL,&ld_,vec,&ld_,work,&lwork,rwork,&info));
    194: #endif
    195:   SlepcCheckLapackInfo("geev",info);
    196:   PetscCall(PetscFPTrapPop());
    197:   PetscFunctionReturn(PETSC_SUCCESS);
    198: }

    200: /*
    201:    LyapIIBuildRHS: prepare the right-hand side of the Lyapunov equation SY + YS' = -2*S*Z*S'
    202:    in factored form:
    203:       if (V)  U=sqrt(2)*S*V    (uses 1 work vector)
    204:       else    U=sqrt(2)*S*U    (uses 2 work vectors)
    205:    where U,V are assumed to have rk columns.
    206:  */
    207: static PetscErrorCode LyapIIBuildRHS(Mat S,PetscInt rk,Mat U,BV V,Vec *work)
    208: {
    209:   PetscScalar    *array,*uu;
    210:   PetscInt       i,nloc;
    211:   Vec            v,u=work[0];

    213:   PetscFunctionBegin;
    214:   PetscCall(MatGetLocalSize(U,&nloc,NULL));
    215:   for (i=0;i<rk;i++) {
    216:     PetscCall(MatDenseGetColumn(U,i,&array));
    217:     if (V) PetscCall(BVGetColumn(V,i,&v));
    218:     else {
    219:       v = work[1];
    220:       PetscCall(VecPlaceArray(v,array));
    221:     }
    222:     PetscCall(MatMult(S,v,u));
    223:     if (V) PetscCall(BVRestoreColumn(V,i,&v));
    224:     else PetscCall(VecResetArray(v));
    225:     PetscCall(VecScale(u,PETSC_SQRT2));
    226:     PetscCall(VecGetArray(u,&uu));
    227:     PetscCall(PetscArraycpy(array,uu,nloc));
    228:     PetscCall(VecRestoreArray(u,&uu));
    229:     PetscCall(MatDenseRestoreColumn(U,&array));
    230:   }
    231:   PetscFunctionReturn(PETSC_SUCCESS);
    232: }

    234: /*
    235:    LyapIIBuildEigenMat: create shell matrix Op=A\B with A = kron(I,S)+kron(S,I), B = -2*kron(S,S)
    236:    where S is a sequential square dense matrix of order n.
    237:    v0 is the initial vector, should have the form v0 = w*w' (for instance 1*1')
    238:  */
    239: static PetscErrorCode LyapIIBuildEigenMat(LME lme,Mat S,Mat *Op,Vec *v0)
    240: {
    241:   PetscInt          n,m;
    242:   PetscBool         create=PETSC_FALSE;
    243:   EPS_EIG_MATSHELL  *matctx;
    244: #if defined(PETSC_USE_COMPLEX)
    245:   PetscScalar       theta,*aa,*bb;
    246:   const PetscScalar *ss;
    247:   PetscInt          i,j,f,c,off,ld,lds;
    248:   IS                perm;
    249: #endif

    251:   PetscFunctionBegin;
    252:   PetscCall(MatGetSize(S,&n,NULL));
    253:   if (!*Op) create=PETSC_TRUE;
    254:   else {
    255:     PetscCall(MatGetSize(*Op,&m,NULL));
    256:     if (m!=n*n) create=PETSC_TRUE;
    257:   }
    258:   if (create) {
    259:     PetscCall(MatDestroy(Op));
    260:     PetscCall(VecDestroy(v0));
    261:     PetscCall(PetscNew(&matctx));
    262: #if defined(PETSC_USE_COMPLEX)
    263:     PetscCall(MatCreateSeqDense(PETSC_COMM_SELF,n*n,n*n,NULL,&matctx->A));
    264:     PetscCall(MatCreateSeqDense(PETSC_COMM_SELF,n*n,n*n,NULL,&matctx->B));
    265:     PetscCall(MatCreateVecs(matctx->A,NULL,&matctx->w));
    266: #else
    267:     PetscCall(MatCreateSeqDense(PETSC_COMM_SELF,n,n,NULL,&matctx->S));
    268: #endif
    269:     PetscCall(MatCreateShell(PETSC_COMM_SELF,n*n,n*n,PETSC_DETERMINE,PETSC_DETERMINE,matctx,Op));
    270:     PetscCall(MatShellSetOperation(*Op,MATOP_MULT,(void(*)(void))MatMult_EigOperator));
    271:     PetscCall(MatShellSetOperation(*Op,MATOP_DESTROY,(void(*)(void))MatDestroy_EigOperator));
    272:     PetscCall(MatCreateVecs(*Op,NULL,v0));
    273:   } else {
    274:     PetscCall(MatShellGetContext(*Op,&matctx));
    275: #if defined(PETSC_USE_COMPLEX)
    276:     PetscCall(MatZeroEntries(matctx->A));
    277: #endif
    278:   }
    279: #if defined(PETSC_USE_COMPLEX)
    280:   PetscCall(MatDenseGetArray(matctx->A,&aa));
    281:   PetscCall(MatDenseGetArray(matctx->B,&bb));
    282:   PetscCall(MatDenseGetArrayRead(S,&ss));
    283:   PetscCall(MatDenseGetLDA(S,&lds));
    284:   ld = n*n;
    285:   for (f=0;f<n;f++) {
    286:     off = f*n+f*n*ld;
    287:     for (i=0;i<n;i++) for (j=0;j<n;j++) aa[off+i+j*ld] = ss[i+j*lds];
    288:     for (c=0;c<n;c++) {
    289:       off = f*n+c*n*ld;
    290:       theta = ss[f+c*lds];
    291:       for (i=0;i<n;i++) aa[off+i+i*ld] += theta;
    292:       for (i=0;i<n;i++) for (j=0;j<n;j++) bb[off+i+j*ld] = -2*theta*ss[i+j*lds];
    293:     }
    294:   }
    295:   PetscCall(MatDenseRestoreArray(matctx->A,&aa));
    296:   PetscCall(MatDenseRestoreArray(matctx->B,&bb));
    297:   PetscCall(MatDenseRestoreArrayRead(S,&ss));
    298:   PetscCall(ISCreateStride(PETSC_COMM_SELF,n*n,0,1,&perm));
    299:   PetscCall(MatDestroy(&matctx->F));
    300:   PetscCall(MatDuplicate(matctx->A,MAT_COPY_VALUES,&matctx->F));
    301:   PetscCall(MatLUFactor(matctx->F,perm,perm,NULL));
    302:   PetscCall(ISDestroy(&perm));
    303: #else
    304:   PetscCall(MatCopy(S,matctx->S,SAME_NONZERO_PATTERN));
    305: #endif
    306:   matctx->lme = lme;
    307:   matctx->n = n;
    308:   PetscCall(VecSet(*v0,1.0));
    309:   PetscFunctionReturn(PETSC_SUCCESS);
    310: }

    312: static PetscErrorCode EPSSolve_LyapII(EPS eps)
    313: {
    314:   EPS_LYAPII          *ctx = (EPS_LYAPII*)eps->data;
    315:   PetscInt            i,ldds,rk,nloc,mloc,nv,idx,k;
    316:   Vec                 v,w,z=eps->work[0],v0=NULL;
    317:   Mat                 S,C,Ux[2],Y,Y1,R,U,W,X,Op=NULL;
    318:   BV                  V;
    319:   BVOrthogType        type;
    320:   BVOrthogRefineType  refine;
    321:   PetscScalar         eigr[2],eigi[2],*array,er,ei,*uu,*s,*xx,*aa,pM[4],vec[4];
    322:   PetscReal           eta;
    323:   EPS                 epsrr;
    324:   PetscReal           norm;
    325:   EPS_LYAPII_MATSHELL *matctx;

    327:   PetscFunctionBegin;
    328:   PetscCall(DSGetLeadingDimension(ctx->ds,&ldds));

    330:   /* Operator for the Lyapunov equation */
    331:   PetscCall(PetscNew(&matctx));
    332:   PetscCall(STGetOperator(eps->st,&matctx->S));
    333:   PetscCall(MatGetLocalSize(matctx->S,&mloc,&nloc));
    334:   PetscCall(MatCreateShell(PetscObjectComm((PetscObject)eps),mloc,nloc,PETSC_DETERMINE,PETSC_DETERMINE,matctx,&S));
    335:   matctx->Q = eps->V;
    336:   PetscCall(MatShellSetOperation(S,MATOP_MULT,(void(*)(void))MatMult_EPSLyapIIOperator));
    337:   PetscCall(MatShellSetOperation(S,MATOP_DESTROY,(void(*)(void))MatDestroy_EPSLyapIIOperator));
    338:   PetscCall(LMESetCoefficients(ctx->lme,S,NULL,NULL,NULL));

    340:   /* Right-hand side */
    341:   PetscCall(BVDuplicateResize(eps->V,ctx->rkl,&V));
    342:   PetscCall(BVGetOrthogonalization(V,&type,&refine,&eta,NULL));
    343:   PetscCall(BVSetOrthogonalization(V,type,refine,eta,BV_ORTHOG_BLOCK_TSQR));
    344:   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)eps),eps->nloc,PETSC_DECIDE,PETSC_DECIDE,1,NULL,&Ux[0]));
    345:   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)eps),eps->nloc,PETSC_DECIDE,PETSC_DECIDE,2,NULL,&Ux[1]));
    346:   nv = ctx->rkl;
    347:   PetscCall(PetscMalloc1(nv,&s));

    349:   /* Initialize first column */
    350:   PetscCall(EPSGetStartVector(eps,0,NULL));
    351:   PetscCall(BVGetColumn(eps->V,0,&v));
    352:   PetscCall(BVInsertVec(V,0,v));
    353:   PetscCall(BVRestoreColumn(eps->V,0,&v));
    354:   PetscCall(BVSetActiveColumns(eps->V,0,0));  /* no deflation at the beginning */
    355:   PetscCall(LyapIIBuildRHS(S,1,Ux[0],V,eps->work));
    356:   idx = 0;

    358:   /* EPS for rank reduction */
    359:   PetscCall(EPSCreate(PETSC_COMM_SELF,&epsrr));
    360:   PetscCall(EPSSetOptionsPrefix(epsrr,((PetscObject)eps)->prefix));
    361:   PetscCall(EPSAppendOptionsPrefix(epsrr,"eps_lyapii_"));
    362:   PetscCall(EPSSetDimensions(epsrr,1,PETSC_CURRENT,PETSC_CURRENT));
    363:   PetscCall(EPSSetTolerances(epsrr,PETSC_MACHINE_EPSILON*100,PETSC_CURRENT));

    365:   while (eps->reason == EPS_CONVERGED_ITERATING) {
    366:     eps->its++;

    368:     /* Matrix for placing the solution of the Lyapunov equation (an alias of V) */
    369:     PetscCall(BVSetActiveColumns(V,0,nv));
    370:     PetscCall(BVGetMat(V,&Y1));
    371:     PetscCall(MatZeroEntries(Y1));
    372:     PetscCall(MatCreateLRC(NULL,Y1,NULL,NULL,&Y));
    373:     PetscCall(LMESetSolution(ctx->lme,Y));

    375:     /* Solve the Lyapunov equation SY + YS' = -2*S*Z*S' */
    376:     PetscCall(MatCreateLRC(NULL,Ux[idx],NULL,NULL,&C));
    377:     PetscCall(LMESetRHS(ctx->lme,C));
    378:     PetscCall(MatDestroy(&C));
    379:     PetscCall(LMESolve(ctx->lme));
    380:     PetscCall(BVRestoreMat(V,&Y1));
    381:     PetscCall(MatDestroy(&Y));

    383:     /* SVD of the solution: [Q,R]=qr(V); [U,Sigma,~]=svd(R) */
    384:     PetscCall(DSSetDimensions(ctx->ds,nv,0,0));
    385:     PetscCall(DSSVDSetDimensions(ctx->ds,nv));
    386:     PetscCall(DSGetMat(ctx->ds,DS_MAT_A,&R));
    387:     PetscCall(BVOrthogonalize(V,R));
    388:     PetscCall(DSRestoreMat(ctx->ds,DS_MAT_A,&R));
    389:     PetscCall(DSSetState(ctx->ds,DS_STATE_RAW));
    390:     PetscCall(DSSolve(ctx->ds,s,NULL));

    392:     /* Determine rank */
    393:     rk = nv;
    394:     for (i=1;i<nv;i++) if (PetscAbsScalar(s[i]/s[0])<PETSC_SQRT_MACHINE_EPSILON) {rk=i; break;}
    395:     PetscCall(PetscInfo(eps,"The computed solution of the Lyapunov equation has rank %" PetscInt_FMT "\n",rk));
    396:     rk = PetscMin(rk,ctx->rkc);
    397:     PetscCall(DSGetMat(ctx->ds,DS_MAT_U,&U));
    398:     PetscCall(BVMultInPlace(V,U,0,rk));
    399:     PetscCall(DSRestoreMat(ctx->ds,DS_MAT_U,&U));
    400:     PetscCall(BVSetActiveColumns(V,0,rk));

    402:     /* Rank reduction */
    403:     PetscCall(DSSetDimensions(ctx->ds,rk,0,0));
    404:     PetscCall(DSSVDSetDimensions(ctx->ds,rk));
    405:     PetscCall(DSGetMat(ctx->ds,DS_MAT_A,&W));
    406:     PetscCall(BVMatProject(V,S,V,W));
    407:     PetscCall(LyapIIBuildEigenMat(ctx->lme,W,&Op,&v0)); /* Op=A\B, A=kron(I,S)+kron(S,I), B=-2*kron(S,S) */
    408:     PetscCall(DSRestoreMat(ctx->ds,DS_MAT_A,&W));
    409:     PetscCall(EPSSetOperators(epsrr,Op,NULL));
    410:     PetscCall(EPSSetInitialSpace(epsrr,1,&v0));
    411:     PetscCall(EPSSolve(epsrr));
    412:     PetscCall(EPSComputeVectors(epsrr));
    413:     /* Copy first eigenvector, vec(A)=x */
    414:     PetscCall(BVGetArray(epsrr->V,&xx));
    415:     PetscCall(DSGetArray(ctx->ds,DS_MAT_A,&aa));
    416:     for (i=0;i<rk;i++) PetscCall(PetscArraycpy(aa+i*ldds,xx+i*rk,rk));
    417:     PetscCall(DSRestoreArray(ctx->ds,DS_MAT_A,&aa));
    418:     PetscCall(BVRestoreArray(epsrr->V,&xx));
    419:     PetscCall(DSSetState(ctx->ds,DS_STATE_RAW));
    420:     /* Compute [U,Sigma,~] = svd(A), its rank should be 1 or 2 */
    421:     PetscCall(DSSolve(ctx->ds,s,NULL));
    422:     if (PetscAbsScalar(s[1]/s[0])<PETSC_SQRT_MACHINE_EPSILON) rk=1;
    423:     else rk = 2;
    424:     PetscCall(PetscInfo(eps,"The eigenvector has rank %" PetscInt_FMT "\n",rk));
    425:     PetscCall(DSGetMat(ctx->ds,DS_MAT_U,&U));
    426:     PetscCall(BVMultInPlace(V,U,0,rk));
    427:     PetscCall(DSRestoreMat(ctx->ds,DS_MAT_U,&U));

    429:     /* Save V in Ux */
    430:     idx = (rk==2)?1:0;
    431:     for (i=0;i<rk;i++) {
    432:       PetscCall(BVGetColumn(V,i,&v));
    433:       PetscCall(VecGetArray(v,&uu));
    434:       PetscCall(MatDenseGetColumn(Ux[idx],i,&array));
    435:       PetscCall(PetscArraycpy(array,uu,eps->nloc));
    436:       PetscCall(MatDenseRestoreColumn(Ux[idx],&array));
    437:       PetscCall(VecRestoreArray(v,&uu));
    438:       PetscCall(BVRestoreColumn(V,i,&v));
    439:     }

    441:     /* Eigenpair approximation */
    442:     PetscCall(BVGetColumn(V,0,&v));
    443:     PetscCall(MatMult(S,v,z));
    444:     PetscCall(VecDot(z,v,pM));
    445:     PetscCall(BVRestoreColumn(V,0,&v));
    446:     if (rk>1) {
    447:       PetscCall(BVGetColumn(V,1,&w));
    448:       PetscCall(VecDot(z,w,pM+1));
    449:       PetscCall(MatMult(S,w,z));
    450:       PetscCall(VecDot(z,w,pM+3));
    451:       PetscCall(BVGetColumn(V,0,&v));
    452:       PetscCall(VecDot(z,v,pM+2));
    453:       PetscCall(BVRestoreColumn(V,0,&v));
    454:       PetscCall(BVRestoreColumn(V,1,&w));
    455:       PetscCall(EV2x2(pM,2,eigr,eigi,vec));
    456:       PetscCall(MatCreateSeqDense(PETSC_COMM_SELF,2,2,vec,&X));
    457:       PetscCall(BVSetActiveColumns(V,0,rk));
    458:       PetscCall(BVMultInPlace(V,X,0,rk));
    459:       PetscCall(MatDestroy(&X));
    460: #if !defined(PETSC_USE_COMPLEX)
    461:       norm = eigr[0]*eigr[0]+eigi[0]*eigi[0];
    462:       er = eigr[0]/norm; ei = -eigi[0]/norm;
    463: #else
    464:       er =1.0/eigr[0]; ei = 0.0;
    465: #endif
    466:     } else {
    467:       eigr[0] = pM[0]; eigi[0] = 0.0;
    468:       er = 1.0/eigr[0]; ei = 0.0;
    469:     }
    470:     PetscCall(BVGetColumn(V,0,&v));
    471:     if (eigi[0]!=0.0) PetscCall(BVGetColumn(V,1,&w));
    472:     else w = NULL;
    473:     eps->eigr[eps->nconv] = eigr[0]; eps->eigi[eps->nconv] = eigi[0];
    474:     PetscCall(EPSComputeResidualNorm_Private(eps,PETSC_FALSE,er,ei,v,w,eps->work,&norm));
    475:     PetscCall(BVRestoreColumn(V,0,&v));
    476:     if (w) PetscCall(BVRestoreColumn(V,1,&w));
    477:     PetscCall((*eps->converged)(eps,er,ei,norm,&eps->errest[eps->nconv],eps->convergedctx));
    478:     k = 0;
    479:     if (eps->errest[eps->nconv]<eps->tol) {
    480:       k++;
    481:       if (rk==2) {
    482: #if !defined (PETSC_USE_COMPLEX)
    483:         eps->eigr[eps->nconv+k] = eigr[0]; eps->eigi[eps->nconv+k] = -eigi[0];
    484: #else
    485:         eps->eigr[eps->nconv+k] = PetscConj(eps->eigr[eps->nconv]);
    486: #endif
    487:         k++;
    488:       }
    489:       /* Store converged eigenpairs and vectors for deflation */
    490:       for (i=0;i<k;i++) {
    491:         PetscCall(BVGetColumn(V,i,&v));
    492:         PetscCall(BVInsertVec(eps->V,eps->nconv+i,v));
    493:         PetscCall(BVRestoreColumn(V,i,&v));
    494:       }
    495:       eps->nconv += k;
    496:       PetscCall(BVSetActiveColumns(eps->V,eps->nconv-rk,eps->nconv));
    497:       PetscCall(BVOrthogonalize(eps->V,NULL));
    498:       PetscCall(DSSetDimensions(eps->ds,eps->nconv,0,0));
    499:       PetscCall(DSGetMat(eps->ds,DS_MAT_A,&W));
    500:       PetscCall(BVMatProject(eps->V,matctx->S,eps->V,W));
    501:       PetscCall(DSRestoreMat(eps->ds,DS_MAT_A,&W));
    502:       if (eps->nconv<eps->nev) {
    503:         idx = 0;
    504:         PetscCall(BVSetRandomColumn(V,0));
    505:         PetscCall(BVNormColumn(V,0,NORM_2,&norm));
    506:         PetscCall(BVScaleColumn(V,0,1.0/norm));
    507:         PetscCall(LyapIIBuildRHS(S,1,Ux[idx],V,eps->work));
    508:       }
    509:     } else {
    510:       /* Prepare right-hand side */
    511:       PetscCall(LyapIIBuildRHS(S,rk,Ux[idx],NULL,eps->work));
    512:     }
    513:     PetscCall((*eps->stopping)(eps,eps->its,eps->max_it,eps->nconv,eps->nev,&eps->reason,eps->stoppingctx));
    514:     PetscCall(EPSMonitor(eps,eps->its,eps->nconv,eps->eigr,eps->eigi,eps->errest,eps->nconv+1));
    515:   }
    516:   PetscCall(STRestoreOperator(eps->st,&matctx->S));
    517:   PetscCall(MatDestroy(&S));
    518:   PetscCall(MatDestroy(&Ux[0]));
    519:   PetscCall(MatDestroy(&Ux[1]));
    520:   PetscCall(MatDestroy(&Op));
    521:   PetscCall(VecDestroy(&v0));
    522:   PetscCall(BVDestroy(&V));
    523:   PetscCall(EPSDestroy(&epsrr));
    524:   PetscCall(PetscFree(s));
    525:   PetscFunctionReturn(PETSC_SUCCESS);
    526: }

    528: static PetscErrorCode EPSSetFromOptions_LyapII(EPS eps,PetscOptionItems *PetscOptionsObject)
    529: {
    530:   EPS_LYAPII     *ctx = (EPS_LYAPII*)eps->data;
    531:   PetscInt       k,array[2]={PETSC_DETERMINE,PETSC_DETERMINE};
    532:   PetscBool      flg;

    534:   PetscFunctionBegin;
    535:   PetscOptionsHeadBegin(PetscOptionsObject,"EPS Lyapunov Inverse Iteration Options");

    537:     k = 2;
    538:     PetscCall(PetscOptionsIntArray("-eps_lyapii_ranks","Ranks for Lyapunov equation (one or two comma-separated integers)","EPSLyapIISetRanks",array,&k,&flg));
    539:     if (flg) PetscCall(EPSLyapIISetRanks(eps,array[0],array[1]));

    541:   PetscOptionsHeadEnd();

    543:   if (!ctx->lme) PetscCall(EPSLyapIIGetLME(eps,&ctx->lme));
    544:   PetscCall(LMESetFromOptions(ctx->lme));
    545:   PetscFunctionReturn(PETSC_SUCCESS);
    546: }

    548: static PetscErrorCode EPSLyapIISetRanks_LyapII(EPS eps,PetscInt rkc,PetscInt rkl)
    549: {
    550:   EPS_LYAPII *ctx = (EPS_LYAPII*)eps->data;

    552:   PetscFunctionBegin;
    553:   if (rkc==PETSC_DETERMINE) {
    554:     if (ctx->rkc != 10) eps->state = EPS_STATE_INITIAL;
    555:     ctx->rkc = 10;
    556:   } else if (rkc!=PETSC_CURRENT) {
    557:     PetscCheck(rkc>1,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The compressed rank %" PetscInt_FMT " must be larger than 1",rkc);
    558:     if (ctx->rkc != rkc) eps->state = EPS_STATE_INITIAL;
    559:     ctx->rkc = rkc;
    560:   }
    561:   if (rkl==PETSC_DETERMINE) {
    562:     if (ctx->rkl != 3*rkc) eps->state = EPS_STATE_INITIAL;
    563:     ctx->rkl = 3*rkc;
    564:   } else if (rkl!=PETSC_CURRENT) {
    565:     PetscCheck(rkl>=rkc,PetscObjectComm((PetscObject)eps),PETSC_ERR_ARG_OUTOFRANGE,"The Lyapunov rank %" PetscInt_FMT " cannot be smaller than the compressed rank %" PetscInt_FMT,rkl,rkc);
    566:     if (ctx->rkl != rkl) eps->state = EPS_STATE_INITIAL;
    567:     ctx->rkl = rkl;
    568:   }
    569:   PetscFunctionReturn(PETSC_SUCCESS);
    570: }

    572: /*@
    573:    EPSLyapIISetRanks - Set the ranks used in the solution of the Lyapunov equation.

    575:    Logically Collective

    577:    Input Parameters:
    578: +  eps - the eigenproblem solver context
    579: .  rkc - the compressed rank
    580: -  rkl - the Lyapunov rank

    582:    Options Database Key:
    583: .  -eps_lyapii_ranks <rkc,rkl> - Sets the rank parameters

    585:    Notes:
    586:    PETSC_CURRENT can be used to preserve the current value of any of the
    587:    arguments, and PETSC_DETERMINE to set them to a default value.

    589:    Lyapunov inverse iteration needs to solve a large-scale Lyapunov equation
    590:    at each iteration of the eigensolver. For this, an iterative solver (LME)
    591:    is used, which requires to prescribe the rank of the solution matrix X. This
    592:    is the meaning of parameter rkl. Later, this matrix is compressed into
    593:    another matrix of rank rkc. If not provided, rkl is a small multiple of rkc.

    595:    Level: intermediate

    597: .seealso: EPSLyapIIGetRanks()
    598: @*/
    599: PetscErrorCode EPSLyapIISetRanks(EPS eps,PetscInt rkc,PetscInt rkl)
    600: {
    601:   PetscFunctionBegin;
    605:   PetscTryMethod(eps,"EPSLyapIISetRanks_C",(EPS,PetscInt,PetscInt),(eps,rkc,rkl));
    606:   PetscFunctionReturn(PETSC_SUCCESS);
    607: }

    609: static PetscErrorCode EPSLyapIIGetRanks_LyapII(EPS eps,PetscInt *rkc,PetscInt *rkl)
    610: {
    611:   EPS_LYAPII *ctx = (EPS_LYAPII*)eps->data;

    613:   PetscFunctionBegin;
    614:   if (rkc) *rkc = ctx->rkc;
    615:   if (rkl) *rkl = ctx->rkl;
    616:   PetscFunctionReturn(PETSC_SUCCESS);
    617: }

    619: /*@
    620:    EPSLyapIIGetRanks - Return the rank values used for the Lyapunov step.

    622:    Not Collective

    624:    Input Parameter:
    625: .  eps - the eigenproblem solver context

    627:    Output Parameters:
    628: +  rkc - the compressed rank
    629: -  rkl - the Lyapunov rank

    631:    Level: intermediate

    633: .seealso: EPSLyapIISetRanks()
    634: @*/
    635: PetscErrorCode EPSLyapIIGetRanks(EPS eps,PetscInt *rkc,PetscInt *rkl)
    636: {
    637:   PetscFunctionBegin;
    639:   PetscUseMethod(eps,"EPSLyapIIGetRanks_C",(EPS,PetscInt*,PetscInt*),(eps,rkc,rkl));
    640:   PetscFunctionReturn(PETSC_SUCCESS);
    641: }

    643: static PetscErrorCode EPSLyapIISetLME_LyapII(EPS eps,LME lme)
    644: {
    645:   EPS_LYAPII     *ctx = (EPS_LYAPII*)eps->data;

    647:   PetscFunctionBegin;
    648:   PetscCall(PetscObjectReference((PetscObject)lme));
    649:   PetscCall(LMEDestroy(&ctx->lme));
    650:   ctx->lme = lme;
    651:   eps->state = EPS_STATE_INITIAL;
    652:   PetscFunctionReturn(PETSC_SUCCESS);
    653: }

    655: /*@
    656:    EPSLyapIISetLME - Associate a linear matrix equation solver object (LME) to the
    657:    eigenvalue solver.

    659:    Collective

    661:    Input Parameters:
    662: +  eps - the eigenproblem solver context
    663: -  lme - the linear matrix equation solver object

    665:    Level: advanced

    667: .seealso: EPSLyapIIGetLME()
    668: @*/
    669: PetscErrorCode EPSLyapIISetLME(EPS eps,LME lme)
    670: {
    671:   PetscFunctionBegin;
    674:   PetscCheckSameComm(eps,1,lme,2);
    675:   PetscTryMethod(eps,"EPSLyapIISetLME_C",(EPS,LME),(eps,lme));
    676:   PetscFunctionReturn(PETSC_SUCCESS);
    677: }

    679: static PetscErrorCode EPSLyapIIGetLME_LyapII(EPS eps,LME *lme)
    680: {
    681:   EPS_LYAPII     *ctx = (EPS_LYAPII*)eps->data;

    683:   PetscFunctionBegin;
    684:   if (!ctx->lme) {
    685:     PetscCall(LMECreate(PetscObjectComm((PetscObject)eps),&ctx->lme));
    686:     PetscCall(LMESetOptionsPrefix(ctx->lme,((PetscObject)eps)->prefix));
    687:     PetscCall(LMEAppendOptionsPrefix(ctx->lme,"eps_lyapii_"));
    688:     PetscCall(PetscObjectIncrementTabLevel((PetscObject)ctx->lme,(PetscObject)eps,1));
    689:   }
    690:   *lme = ctx->lme;
    691:   PetscFunctionReturn(PETSC_SUCCESS);
    692: }

    694: /*@
    695:    EPSLyapIIGetLME - Retrieve the linear matrix equation solver object (LME)
    696:    associated with the eigenvalue solver.

    698:    Not Collective

    700:    Input Parameter:
    701: .  eps - the eigenproblem solver context

    703:    Output Parameter:
    704: .  lme - the linear matrix equation solver object

    706:    Level: advanced

    708: .seealso: EPSLyapIISetLME()
    709: @*/
    710: PetscErrorCode EPSLyapIIGetLME(EPS eps,LME *lme)
    711: {
    712:   PetscFunctionBegin;
    714:   PetscAssertPointer(lme,2);
    715:   PetscUseMethod(eps,"EPSLyapIIGetLME_C",(EPS,LME*),(eps,lme));
    716:   PetscFunctionReturn(PETSC_SUCCESS);
    717: }

    719: static PetscErrorCode EPSView_LyapII(EPS eps,PetscViewer viewer)
    720: {
    721:   EPS_LYAPII     *ctx = (EPS_LYAPII*)eps->data;
    722:   PetscBool      isascii;

    724:   PetscFunctionBegin;
    725:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
    726:   if (isascii) {
    727:     PetscCall(PetscViewerASCIIPrintf(viewer,"  ranks: for Lyapunov solver=%" PetscInt_FMT ", after compression=%" PetscInt_FMT "\n",ctx->rkl,ctx->rkc));
    728:     if (!ctx->lme) PetscCall(EPSLyapIIGetLME(eps,&ctx->lme));
    729:     PetscCall(PetscViewerASCIIPushTab(viewer));
    730:     PetscCall(LMEView(ctx->lme,viewer));
    731:     PetscCall(PetscViewerASCIIPopTab(viewer));
    732:   }
    733:   PetscFunctionReturn(PETSC_SUCCESS);
    734: }

    736: static PetscErrorCode EPSReset_LyapII(EPS eps)
    737: {
    738:   EPS_LYAPII     *ctx = (EPS_LYAPII*)eps->data;

    740:   PetscFunctionBegin;
    741:   if (!ctx->lme) PetscCall(LMEReset(ctx->lme));
    742:   PetscFunctionReturn(PETSC_SUCCESS);
    743: }

    745: static PetscErrorCode EPSDestroy_LyapII(EPS eps)
    746: {
    747:   EPS_LYAPII     *ctx = (EPS_LYAPII*)eps->data;

    749:   PetscFunctionBegin;
    750:   PetscCall(LMEDestroy(&ctx->lme));
    751:   PetscCall(DSDestroy(&ctx->ds));
    752:   PetscCall(PetscFree(eps->data));
    753:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIISetLME_C",NULL));
    754:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIIGetLME_C",NULL));
    755:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIISetRanks_C",NULL));
    756:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIIGetRanks_C",NULL));
    757:   PetscFunctionReturn(PETSC_SUCCESS);
    758: }

    760: static PetscErrorCode EPSSetDefaultST_LyapII(EPS eps)
    761: {
    762:   PetscFunctionBegin;
    763:   if (!((PetscObject)eps->st)->type_name) PetscCall(STSetType(eps->st,STSINVERT));
    764:   PetscFunctionReturn(PETSC_SUCCESS);
    765: }

    767: SLEPC_EXTERN PetscErrorCode EPSCreate_LyapII(EPS eps)
    768: {
    769:   EPS_LYAPII     *ctx;

    771:   PetscFunctionBegin;
    772:   PetscCall(PetscNew(&ctx));
    773:   eps->data = (void*)ctx;

    775:   eps->useds = PETSC_TRUE;

    777:   eps->ops->solve          = EPSSolve_LyapII;
    778:   eps->ops->setup          = EPSSetUp_LyapII;
    779:   eps->ops->setupsort      = EPSSetUpSort_Default;
    780:   eps->ops->setfromoptions = EPSSetFromOptions_LyapII;
    781:   eps->ops->reset          = EPSReset_LyapII;
    782:   eps->ops->destroy        = EPSDestroy_LyapII;
    783:   eps->ops->view           = EPSView_LyapII;
    784:   eps->ops->setdefaultst   = EPSSetDefaultST_LyapII;
    785:   eps->ops->backtransform  = EPSBackTransform_Default;
    786:   eps->ops->computevectors = EPSComputeVectors_Schur;

    788:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIISetLME_C",EPSLyapIISetLME_LyapII));
    789:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIIGetLME_C",EPSLyapIIGetLME_LyapII));
    790:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIISetRanks_C",EPSLyapIISetRanks_LyapII));
    791:   PetscCall(PetscObjectComposeFunction((PetscObject)eps,"EPSLyapIIGetRanks_C",EPSLyapIIGetRanks_LyapII));
    792:   PetscFunctionReturn(PETSC_SUCCESS);
    793: }