Date: 2026-05-01T10:02:16+00:00

Actual source code: dsops.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:    DS operations: DSSolve(), DSVectors(), etc
     12: */

     14: #include <slepc/private/dsimpl.h>

     16: /*@
     17:    DSGetLeadingDimension - Returns the leading dimension of the allocated
     18:    matrices.

     20:    Not Collective

     22:    Input Parameter:
     23: .  ds - the direct solver context

     25:    Output Parameter:
     26: .  ld - leading dimension (maximum allowed dimension for the matrices)

     28:    Level: advanced

     30: .seealso: [](sec:ds), `DSAllocate()`, `DSSetDimensions()`
     31: @*/
     32: PetscErrorCode DSGetLeadingDimension(DS ds,PetscInt *ld)
     33: {
     34:   PetscFunctionBegin;
     36:   PetscAssertPointer(ld,2);
     37:   *ld = ds->ld;
     38:   PetscFunctionReturn(PETSC_SUCCESS);
     39: }

     41: /*@
     42:    DSSetState - Change the state of the `DS` object.

     44:    Logically Collective

     46:    Input Parameters:
     47: +  ds    - the direct solver context
     48: -  state - the new state

     50:    Notes:
     51:    The state indicates that the dense system is in an initial state (raw),
     52:    in an intermediate state (such as tridiagonal, Hessenberg or
     53:    Hessenberg-triangular), in a condensed state (such as diagonal, Schur or
     54:    generalized Schur), or in a truncated state.

     56:    The state is automatically changed in functions such as `DSSolve()` or `DSTruncate()`.
     57:    This function is normally used to return to the raw state when the
     58:    condensed structure is destroyed, or to indicate that `DSSolve()` must
     59:    start with a problem that already has an intermediate form.

     61:    Level: advanced

     63: .seealso: [](sec:ds), `DSGetState()`, `DSSolve()`, `DSTruncate()`
     64: @*/
     65: PetscErrorCode DSSetState(DS ds,DSStateType state)
     66: {
     67:   PetscFunctionBegin;
     70:   switch (state) {
     71:     case DS_STATE_RAW:
     72:     case DS_STATE_INTERMEDIATE:
     73:     case DS_STATE_CONDENSED:
     74:     case DS_STATE_TRUNCATED:
     75:       if (ds->state!=state) PetscCall(PetscInfo(ds,"State has changed from %s to %s\n",DSStateTypes[ds->state],DSStateTypes[state]));
     76:       ds->state = state;
     77:       break;
     78:     default:
     79:       SETERRQ(PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_WRONG,"Wrong state");
     80:   }
     81:   PetscFunctionReturn(PETSC_SUCCESS);
     82: }

     84: /*@
     85:    DSGetState - Returns the current state.

     87:    Not Collective

     89:    Input Parameter:
     90: .  ds - the direct solver context

     92:    Output Parameter:
     93: .  state - current state

     95:    Level: advanced

     97: .seealso: [](sec:ds), `DSSetState()`
     98: @*/
     99: PetscErrorCode DSGetState(DS ds,DSStateType *state)
    100: {
    101:   PetscFunctionBegin;
    103:   PetscAssertPointer(state,2);
    104:   *state = ds->state;
    105:   PetscFunctionReturn(PETSC_SUCCESS);
    106: }

    108: /*@
    109:    DSSetDimensions - Resize the matrices in the `DS` object.

    111:    Logically Collective

    113:    Input Parameters:
    114: +  ds - the direct solver context
    115: .  n  - the new size
    116: .  l  - number of locked (inactive) leading columns
    117: -  k  - intermediate dimension (e.g., position of arrow)

    119:    Notes:
    120:    The internal arrays are not reallocated.

    122:    Some `DS` types have additional dimensions, e.g., the number of columns
    123:    in `DSSVD`. For these, you should call a specific interface function.

    125:    Use `PETSC_CURRENT` to leave any of the values unchanged. Use `PETSC_DETERMINE`
    126:    to set `n` to the leading dimension, `l` to the minimum value (0), and `k` to `n/2`.

    128:    Level: intermediate

    130: .seealso: [](sec:ds), `DSGetDimensions()`, `DSAllocate()`, `DSSVDSetDimensions()`
    131: @*/
    132: PetscErrorCode DSSetDimensions(DS ds,PetscInt n,PetscInt l,PetscInt k)
    133: {
    134:   PetscInt       on,ol,ok;
    135:   PetscBool      issvd;

    137:   PetscFunctionBegin;
    139:   DSCheckAlloc(ds,1);
    143:   on = ds->n; ol = ds->l; ok = ds->k;
    144:   if (n == PETSC_DETERMINE) {
    145:     ds->n = ds->extrarow? ds->ld-1: ds->ld;
    146:   } else if (n != PETSC_CURRENT) {
    147:     PetscCheck(n>=0 && n<=ds->ld,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"Illegal value of n. Must be between 0 and ld");
    148:     PetscCall(PetscObjectTypeCompareAny((PetscObject)ds,&issvd,DSSVD,DSGSVD,""));  /* SVD and GSVD have extra column instead of extra row */
    149:     PetscCheck(!ds->extrarow || n<ds->ld || issvd,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"A value of n equal to ld leaves no room for extra row");
    150:     ds->n = n;
    151:   }
    152:   ds->t = ds->n;   /* truncated length equal to the new dimension */
    153:   if (l == PETSC_DETERMINE) {
    154:     ds->l = 0;
    155:   } else if (l != PETSC_CURRENT) {
    156:     PetscCheck(l>=0 && l<=ds->n,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"Illegal value of l. Must be between 0 and n");
    157:     ds->l = l;
    158:   }
    159:   if (k == PETSC_DETERMINE) {
    160:     ds->k = ds->n/2;
    161:   } else if (k != PETSC_CURRENT) {
    162:     PetscCheck(k>=0 || k<=ds->n,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"Illegal value of k. Must be between 0 and n");
    163:     ds->k = k;
    164:   }
    165:   if (on!=ds->n || ol!=ds->l || ok!=ds->k) PetscCall(PetscInfo(ds,"New dimensions are: n=%" PetscInt_FMT ", l=%" PetscInt_FMT ", k=%" PetscInt_FMT "\n",ds->n,ds->l,ds->k));
    166:   PetscFunctionReturn(PETSC_SUCCESS);
    167: }

    169: /*@
    170:    DSGetDimensions - Returns the current dimensions.

    172:    Not Collective

    174:    Input Parameter:
    175: .  ds - the direct solver context

    177:    Output Parameters:
    178: +  n  - the current size
    179: .  l  - number of locked (inactive) leading columns
    180: .  k  - intermediate dimension (e.g., position of arrow)
    181: -  t  - truncated length

    183:    Notes:
    184:    The `t` parameter makes sense only if `DSTruncate()` has been called.
    185:    Otherwise its value equals `n`.

    187:    Some `DS` types have additional dimensions, e.g., the number of columns
    188:    in `DSSVD`. For these, you should call a specific interface function.

    190:    Level: intermediate

    192: .seealso: [](sec:ds), `DSSetDimensions()`, `DSTruncate()`, `DSSVDGetDimensions()`
    193: @*/
    194: PetscErrorCode DSGetDimensions(DS ds,PetscInt *n,PetscInt *l,PetscInt *k,PetscInt *t)
    195: {
    196:   PetscFunctionBegin;
    198:   DSCheckAlloc(ds,1);
    199:   if (n) *n = ds->n;
    200:   if (l) *l = ds->l;
    201:   if (k) *k = ds->k;
    202:   if (t) *t = ds->t;
    203:   PetscFunctionReturn(PETSC_SUCCESS);
    204: }

    206: /*@
    207:    DSTruncate - Truncates the system represented in the `DS` object.

    209:    Logically Collective

    211:    Input Parameters:
    212: +  ds   - the direct solver context
    213: .  n    - the new size
    214: -  trim - a flag to indicate if the factorization must be trimmed

    216:    Note:
    217:    The new size is set to `n`. Note that in some cases the new size could
    218:    be `n+1` or `n-1` to avoid breaking a 2x2 diagonal block (e.g., in real
    219:    Schur form). In cases where the extra row is meaningful, the first
    220:    `n` elements are kept as the extra row for the new system.

    222:    If the flag `trim` is turned on, it resets the locked and intermediate
    223:    dimensions to zero, see `DSSetDimensions()`, and sets the state to
    224:    `DS_STATE_RAW`. It also cleans the extra row if being used.

    226:    The typical usage of `trim=PETSC_TRUE` is to truncate the Schur decomposition
    227:    at the end of a Krylov iteration. In this case, the state must be
    228:    changed to `DS_STATE_RAW` so that `DSVectors()` computes eigenvectors from scratch.

    230:    Level: advanced

    232: .seealso: [](sec:ds), `DSSetDimensions()`, `DSSetExtraRow()`, `DSStateType`
    233: @*/
    234: PetscErrorCode DSTruncate(DS ds,PetscInt n,PetscBool trim)
    235: {
    236:   DSStateType    old;

    238:   PetscFunctionBegin;
    241:   DSCheckAlloc(ds,1);
    244:   PetscCheck(n>=ds->l && n<=ds->n,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"Illegal value of n (%" PetscInt_FMT "). Must be between l (%" PetscInt_FMT ") and n (%" PetscInt_FMT ")",n,ds->l,ds->n);
    245:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
    246:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    247:   PetscUseTypeMethod(ds,truncate,n,trim);
    248:   PetscCall(PetscFPTrapPop());
    249:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
    250:   PetscCall(PetscInfo(ds,"Decomposition %s to size n=%" PetscInt_FMT "\n",trim?"trimmed":"truncated",ds->n));
    251:   old = ds->state;
    252:   ds->state = trim? DS_STATE_RAW: DS_STATE_TRUNCATED;
    253:   if (old!=ds->state) PetscCall(PetscInfo(ds,"State has changed from %s to %s\n",DSStateTypes[old],DSStateTypes[ds->state]));
    254:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    255:   PetscFunctionReturn(PETSC_SUCCESS);
    256: }

    258: /*@
    259:    DSMatGetSize - Returns the numbers of rows and columns of one of the `DS` matrices.

    261:    Not Collective

    263:    Input Parameters:
    264: +  ds - the direct solver context
    265: -  t  - the requested matrix

    267:    Output Parameters:
    268: +  n  - the number of rows
    269: -  m  - the number of columns

    271:    Note:
    272:    This is equivalent to `MatGetSize()` on a matrix obtained with `DSGetMat()`.

    274:    Level: developer

    276: .seealso: [](sec:ds), `DSSetDimensions()`, `DSGetMat()`
    277: @*/
    278: PetscErrorCode DSMatGetSize(DS ds,DSMatType t,PetscInt *m,PetscInt *n)
    279: {
    280:   PetscInt       rows,cols;

    282:   PetscFunctionBegin;
    285:   DSCheckValidMat(ds,t,2);
    286:   if (ds->ops->matgetsize) PetscUseTypeMethod(ds,matgetsize,t,&rows,&cols);
    287:   else {
    288:     if (ds->state==DS_STATE_TRUNCATED && t>=DS_MAT_Q) rows = ds->t;
    289:     else rows = (t==DS_MAT_A && ds->extrarow)? ds->n+1: ds->n;
    290:     if (t==DS_MAT_T) cols = PetscDefined(USE_COMPLEX)? 2: 3;
    291:     else if (t==DS_MAT_D) cols = 1;
    292:     else cols = ds->n;
    293:   }
    294:   if (m) *m = rows;
    295:   if (n) *n = cols;
    296:   PetscFunctionReturn(PETSC_SUCCESS);
    297: }

    299: /*@
    300:    DSMatIsHermitian - Checks if one of the `DS` matrices is known to be Hermitian.

    302:    Not Collective

    304:    Input Parameters:
    305: +  ds - the direct solver context
    306: -  t  - the requested matrix

    308:    Output Parameter:
    309: .  flg - the Hermitian flag

    311:    Note:
    312:    Does not check the matrix values directly. The flag is set according to the
    313:    problem structure. For instance, in `DSHEP` matrix `A` is Hermitian.

    315:    Level: developer

    317: .seealso: [](sec:ds), `DSGetMat()`
    318: @*/
    319: PetscErrorCode DSMatIsHermitian(DS ds,DSMatType t,PetscBool *flg)
    320: {
    321:   PetscFunctionBegin;
    324:   DSCheckValidMat(ds,t,2);
    325:   PetscAssertPointer(flg,3);
    326:   *flg = PETSC_FALSE;
    327:   PetscTryTypeMethod(ds,hermitian,t,flg);
    328:   PetscFunctionReturn(PETSC_SUCCESS);
    329: }

    331: PetscErrorCode DSGetTruncateSize_Default(DS ds,PetscInt l,PetscInt n,PetscInt *k)
    332: {
    333: #if !defined(PETSC_USE_COMPLEX)
    334:   PetscScalar val;
    335: #endif

    337:   PetscFunctionBegin;
    338: #if !defined(PETSC_USE_COMPLEX)
    339:   PetscCall(MatGetValue(ds->omat[DS_MAT_A],l+(*k),l+(*k)-1,&val));
    340:   if (val != 0.0) {
    341:     if (l+(*k)<n-1) (*k)++;
    342:     else (*k)--;
    343:   }
    344: #endif
    345:   PetscFunctionReturn(PETSC_SUCCESS);
    346: }

    348: /*@
    349:    DSGetTruncateSize - Gets the correct size to be used in `DSTruncate()`
    350:    to avoid breaking a 2x2 block.

    352:    Not Collective

    354:    Input Parameters:
    355: +  ds - the direct solver context
    356: .  l  - the size of the locked part (set to 0 to use `ds->l`)
    357: -  n  - the total matrix size (set to 0 to use `ds->n`)

    359:    Output Parameter:
    360: .  k  - the wanted truncation size (possibly modified)

    362:    Notes:
    363:    This should be called before `DSTruncate()` to make sure that the truncation
    364:    does not break a 2x2 block corresponding to a complex conjugate eigenvalue.

    366:    The total size is `n` (either user-provided or `ds->n` if 0 is passed). The
    367:    size where the truncation is intended is equal to `l+k` (where `l` can be
    368:    equal to the locked size `ds->l` if set to 0). Then if there is a 2x2 block
    369:    at the `l+k` limit, the value of `k` is increased (or decreased) by 1.

    371:    Level: advanced

    373: .seealso: [](sec:ds), `DSTruncate()`, `DSSetDimensions()`
    374: @*/
    375: PetscErrorCode DSGetTruncateSize(DS ds,PetscInt l,PetscInt n,PetscInt *k)
    376: {
    377:   PetscFunctionBegin;
    379:   DSCheckAlloc(ds,1);
    382:   PetscAssertPointer(k,4);
    383:   PetscUseTypeMethod(ds,gettruncatesize,l?l:ds->l,n?n:ds->n,k);
    384:   PetscFunctionReturn(PETSC_SUCCESS);
    385: }

    387: /*@
    388:    DSGetMat - Returns a sequential dense `Mat` object containing the requested
    389:    matrix.

    391:    Not Collective

    393:    Input Parameters:
    394: +  ds - the direct solver context
    395: -  m  - the requested matrix

    397:    Output Parameter:
    398: .  A  - Mat object

    400:    Notes:
    401:    The returned `Mat` has sizes equal to the current `DS` dimensions (see `DSSetDimensions()`),
    402:    and contains the values that would be obtained with `DSGetArray()`
    403:    (not `DSGetArrayReal()`). If the `DS` was truncated, then the number of rows
    404:    is equal to the dimension prior to truncation, see `DSTruncate()`.
    405:    The communicator is always `PETSC_COMM_SELF`.

    407:    It is implemented with `MatDenseGetSubMatrix()`, and when no longer needed
    408:    the user must call `DSRestoreMat()` which will invoke `MatDenseRestoreSubMatrix()`.

    410:    For matrices `DS_MAT_T` and `DS_MAT_D`, this function will return a `Mat` object
    411:    that cannot be used directly for computations, since it uses compact storage
    412:    (three and one diagonals for $T$ and $D$, respectively). In complex scalars, the
    413:    internal array stores real values, so it is sufficient with two columns for $T$.

    415:    Level: advanced

    417: .seealso: [](sec:ds), `DSRestoreMat()`, `DSSetDimensions()`, `DSGetArray()`, `DSGetArrayReal()`, `DSTruncate()`, `DSGetMatAndColumn()`
    418: @*/
    419: PetscErrorCode DSGetMat(DS ds,DSMatType m,Mat *A)
    420: {
    421:   PetscInt  rows=0,cols=0;
    422:   PetscBool flg;

    424:   PetscFunctionBegin;
    426:   DSCheckAlloc(ds,1);
    427:   DSCheckValidMat(ds,m,2);
    428:   PetscAssertPointer(A,3);

    430:   PetscCall(DSMatGetSize(ds,m,&rows,&cols));
    431:   PetscCheck(rows && cols,PetscObjectComm((PetscObject)ds),PETSC_ERR_ORDER,"Must call DSSetDimensions() first");
    432:   PetscCall(MatDenseGetSubMatrix(ds->omat[m],0,rows,0,cols,A));

    434:   /* set Hermitian flag */
    435:   PetscCall(DSMatIsHermitian(ds,m,&flg));
    436:   PetscCall(MatSetOption(*A,MAT_SYMMETRIC,flg));
    437:   PetscCall(MatSetOption(*A,MAT_HERMITIAN,flg));
    438:   PetscFunctionReturn(PETSC_SUCCESS);
    439: }

    441: /*@
    442:    DSRestoreMat - Restores the matrix after `DSGetMat()` was called.

    444:    Not Collective

    446:    Input Parameters:
    447: +  ds - the direct solver context
    448: .  m  - the requested matrix
    449: -  A  - the fetched `Mat` object

    451:    Note:
    452:    A call to this function must match a previous call of `DSGetMat()`.

    454:    Level: advanced

    456: .seealso: [](sec:ds), `DSGetMat()`, `DSRestoreArray()`, `DSRestoreArrayReal()`
    457: @*/
    458: PetscErrorCode DSRestoreMat(DS ds,DSMatType m,Mat *A)
    459: {
    460:   PetscFunctionBegin;
    462:   DSCheckAlloc(ds,1);
    463:   DSCheckValidMat(ds,m,2);
    464:   PetscAssertPointer(A,3);

    466:   PetscCall(MatDenseRestoreSubMatrix(ds->omat[m],A));
    467:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    468:   PetscFunctionReturn(PETSC_SUCCESS);
    469: }

    471: /*@
    472:    DSGetMatAndColumn - Returns a sequential dense `Mat` object containing the requested
    473:    matrix and one of its columns as a `Vec`.

    475:    Not Collective

    477:    Input Parameters:
    478: +  ds  - the direct solver context
    479: .  m   - the requested matrix
    480: -  col - the index of the requested column

    482:    Output Parameters:
    483: +  A   - `Mat` object
    484: -  v   - `Vec` object (the column)

    486:    Notes:
    487:    This calls `DSGetMat()` and then it extracts the selected column.
    488:    The user must call `DSRestoreMatAndColumn()` to recover the original state.
    489:    For matrices `DS_MAT_T` and `DS_MAT_D`, in complex scalars this function implies
    490:    copying from real values stored internally to scalar values in the Vec.

    492:    Level: advanced

    494: .seealso: [](sec:ds), `DSRestoreMatAndColumn()`, `DSGetMat()`
    495: @*/
    496: PetscErrorCode DSGetMatAndColumn(DS ds,DSMatType m,PetscInt col,Mat *A,Vec *v)
    497: {
    498:   PetscFunctionBegin;
    500:   DSCheckAlloc(ds,1);
    501:   DSCheckValidMat(ds,m,2);
    502:   PetscAssertPointer(A,4);
    503:   PetscAssertPointer(v,5);

    505:   PetscCall(DSGetMat(ds,m,A));
    506:   if (PetscDefined(USE_COMPLEX) && (m==DS_MAT_T || m==DS_MAT_D)) {
    507:     const PetscScalar *as;
    508:     PetscScalar       *vs;
    509:     PetscReal         *ar;
    510:     PetscInt          i,n,lda;
    511:     PetscCall(MatCreateVecs(*A,NULL,v));
    512:     PetscCall(VecGetSize(*v,&n));
    513:     PetscCall(MatDenseGetLDA(*A,&lda));
    514:     PetscCall(MatDenseGetArrayRead(*A,&as));
    515:     PetscCall(VecGetArrayWrite(*v,&vs));
    516:     ar = (PetscReal*)as;
    517:     for (i=0;i<n;i++) vs[i] = ar[i+col*lda];
    518:     PetscCall(VecRestoreArrayWrite(*v,&vs));
    519:     PetscCall(MatDenseRestoreArrayRead(*A,&as));
    520:   } else PetscCall(MatDenseGetColumnVec(*A,col,v));
    521:   PetscFunctionReturn(PETSC_SUCCESS);
    522: }

    524: /*@
    525:    DSRestoreMatAndColumn - Restores the matrix and vector after `DSGetMatAndColumn()`
    526:    was called.

    528:    Not Collective

    530:    Input Parameters:
    531: +  ds  - the direct solver context
    532: .  m   - the requested matrix
    533: .  col - the requested column
    534: .  A   - the fetched `Mat` object
    535: -  v   - the fetched `Vec` object

    537:    Note:
    538:    A call to this function must match a previous call of `DSGetMatAndColumn()`.

    540:    Level: advanced

    542: .seealso: [](sec:ds), `DSGetMatAndColumn()`, `DSRestoreMat()`
    543: @*/
    544: PetscErrorCode DSRestoreMatAndColumn(DS ds,DSMatType m,PetscInt col,Mat *A,Vec *v)
    545: {
    546:   PetscFunctionBegin;
    548:   DSCheckAlloc(ds,1);
    549:   DSCheckValidMat(ds,m,2);
    550:   PetscAssertPointer(A,4);
    551:   PetscAssertPointer(v,5);

    553:   if (PetscDefined(USE_COMPLEX) && (m==DS_MAT_T || m==DS_MAT_D)) {
    554:     const PetscScalar *vs;
    555:     PetscScalar       *as;
    556:     PetscReal         *ar;
    557:     PetscInt          i,n,lda;
    558:     PetscCall(VecGetSize(*v,&n));
    559:     PetscCall(MatDenseGetLDA(*A,&lda));
    560:     PetscCall(MatDenseGetArray(*A,&as));
    561:     PetscCall(VecGetArrayRead(*v,&vs));
    562:     ar = (PetscReal*)as;
    563:     for (i=0;i<n;i++) ar[i+col*lda] = PetscRealPart(vs[i]);
    564:     PetscCall(VecRestoreArrayRead(*v,&vs));
    565:     PetscCall(MatDenseRestoreArray(*A,&as));
    566:     PetscCall(VecDestroy(v));
    567:   } else PetscCall(MatDenseRestoreColumnVec(*A,col,v));
    568:   PetscCall(DSRestoreMat(ds,m,A));
    569:   PetscFunctionReturn(PETSC_SUCCESS);
    570: }

    572: /*@C
    573:    DSGetArray - Returns a pointer to the internal array of one of the
    574:    matrices. You MUST call `DSRestoreArray()` when you no longer
    575:    need to access the array.

    577:    Not Collective

    579:    Input Parameters:
    580: +  ds - the direct solver context
    581: -  m  - the requested matrix

    583:    Output Parameter:
    584: .  a  - pointer to the values

    586:    Note:
    587:    To get read-only access, use `DSGetMat()` followed by `MatDenseGetArrayRead()`.

    589:    Level: advanced

    591: .seealso: [](sec:ds), `DSRestoreArray()`, `DSGetArrayReal()`
    592: @*/
    593: PetscErrorCode DSGetArray(DS ds,DSMatType m,PetscScalar *a[])
    594: {
    595:   PetscFunctionBegin;
    597:   DSCheckAlloc(ds,1);
    598:   DSCheckValidMat(ds,m,2);
    599:   PetscAssertPointer(a,3);
    600:   PetscCall(MatDenseGetArray(ds->omat[m],a));
    601:   PetscFunctionReturn(PETSC_SUCCESS);
    602: }

    604: /*@C
    605:    DSRestoreArray - Restores the matrix after `DSGetArray()` was called.

    607:    Not Collective

    609:    Input Parameters:
    610: +  ds - the direct solver context
    611: .  m  - the requested matrix
    612: -  a  - pointer to the values

    614:    Level: advanced

    616: .seealso: [](sec:ds), `DSGetArray()`, `DSGetArrayReal()`
    617: @*/
    618: PetscErrorCode DSRestoreArray(DS ds,DSMatType m,PetscScalar *a[])
    619: {
    620:   PetscFunctionBegin;
    622:   DSCheckAlloc(ds,1);
    623:   DSCheckValidMat(ds,m,2);
    624:   PetscAssertPointer(a,3);
    625:   PetscCall(MatDenseRestoreArray(ds->omat[m],a));
    626:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    627:   PetscFunctionReturn(PETSC_SUCCESS);
    628: }

    630: /*@C
    631:    DSGetArrayReal - Returns a real pointer to the internal array of $T$ or $D$.
    632:    You MUST call `DSRestoreArrayReal()` when you no longer need to access the array.

    634:    Not Collective

    636:    Input Parameters:
    637: +  ds - the direct solver context
    638: -  m  - the requested matrix

    640:    Output Parameter:
    641: .  a  - pointer to the values

    643:    Note:
    644:    This function can be used only for `DS_MAT_T` and `DS_MAT_D`. These matrices always
    645:    store real values, even in complex scalars, that is why the returned pointer is
    646:    `PetscReal`.

    648:    Level: advanced

    650: .seealso: [](sec:ds), `DSRestoreArrayReal()`, `DSGetArray()`
    651: @*/
    652: PetscErrorCode DSGetArrayReal(DS ds,DSMatType m,PetscReal *a[])
    653: {
    654: #if defined(PETSC_USE_COMPLEX)
    655:   PetscScalar *as;
    656: #endif

    658:   PetscFunctionBegin;
    660:   DSCheckAlloc(ds,1);
    661:   DSCheckValidMatReal(ds,m,2);
    662:   PetscAssertPointer(a,3);
    663: #if defined(PETSC_USE_COMPLEX)
    664:   PetscCall(MatDenseGetArray(ds->omat[m],&as));
    665:   *a = (PetscReal*)as;
    666: #else
    667:   PetscCall(MatDenseGetArray(ds->omat[m],a));
    668: #endif
    669:   PetscFunctionReturn(PETSC_SUCCESS);
    670: }

    672: /*@C
    673:    DSRestoreArrayReal - Restores the matrix after `DSGetArrayReal()` was called.

    675:    Not Collective

    677:    Input Parameters:
    678: +  ds - the direct solver context
    679: .  m  - the requested matrix
    680: -  a  - pointer to the values

    682:    Level: advanced

    684: .seealso: [](sec:ds), `DSGetArrayReal()`, `DSGetArray()`
    685: @*/
    686: PetscErrorCode DSRestoreArrayReal(DS ds,DSMatType m,PetscReal *a[])
    687: {
    688: #if defined(PETSC_USE_COMPLEX)
    689:   PetscScalar *as;
    690: #endif

    692:   PetscFunctionBegin;
    694:   DSCheckAlloc(ds,1);
    695:   DSCheckValidMatReal(ds,m,2);
    696:   PetscAssertPointer(a,3);
    697: #if defined(PETSC_USE_COMPLEX)
    698:   PetscCall(MatDenseRestoreArray(ds->omat[m],&as));
    699:   *a = NULL;
    700: #else
    701:   PetscCall(MatDenseRestoreArray(ds->omat[m],a));
    702: #endif
    703:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    704:   PetscFunctionReturn(PETSC_SUCCESS);
    705: }

    707: /*@
    708:    DSSolve - Solve the problem.

    710:    Logically Collective

    712:    Input Parameters:
    713: +  ds   - the direct solver context
    714: .  eigr - array to store the computed eigenvalues (real part)
    715: -  eigi - array to store the computed eigenvalues (imaginary part)

    717:    Notes:
    718:    This call brings the dense system to condensed form. No ordering
    719:    of the eigenvalues is enforced (for this, call `DSSort()` afterwards).

    721:    In some `DS` types, the arguments will hold singular values,
    722:    instead of eigenvalues. Singular values are always real.

    724:    Level: intermediate

    726: .seealso: [](sec:ds), `DSSort()`, `DSStateType`
    727: @*/
    728: PetscErrorCode DSSolve(DS ds,PetscScalar eigr[],PetscScalar eigi[])
    729: {
    730:   PetscFunctionBegin;
    733:   DSCheckAlloc(ds,1);
    734:   PetscAssertPointer(eigr,2);
    735:   if (ds->state>=DS_STATE_CONDENSED) PetscFunctionReturn(PETSC_SUCCESS);
    736:   PetscCheck(ds->ops->solve[ds->method],PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_OUTOFRANGE,"The specified method number does not exist for this DS");
    737:   PetscCall(PetscInfo(ds,"Starting solve with problem sizes: n=%" PetscInt_FMT ", l=%" PetscInt_FMT ", k=%" PetscInt_FMT "\n",ds->n,ds->l,ds->k));
    738:   PetscCall(PetscLogEventBegin(DS_Solve,ds,0,0,0));
    739:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    740:   PetscUseTypeMethod(ds,solve[ds->method],eigr,eigi);
    741:   PetscCall(PetscFPTrapPop());
    742:   PetscCall(PetscLogEventEnd(DS_Solve,ds,0,0,0));
    743:   PetscCall(PetscInfo(ds,"State has changed from %s to CONDENSED\n",DSStateTypes[ds->state]));
    744:   ds->state = DS_STATE_CONDENSED;
    745:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    746:   PetscFunctionReturn(PETSC_SUCCESS);
    747: }

    749: /*@
    750:    DSSort - Sort the result of `DSSolve()` according to a given sorting
    751:    criterion.

    753:    Logically Collective

    755:    Input Parameters:
    756: +  ds   - the direct solver context
    757: .  rr   - (optional) array containing auxiliary values (real part)
    758: -  ri   - (optional) array containing auxiliary values (imaginary part)

    760:    Input/Output Parameters:
    761: +  eigr - array containing the computed eigenvalues (real part)
    762: .  eigi - array containing the computed eigenvalues (imaginary part)
    763: -  k    - (optional) number of elements in the leading group

    765:    Notes:
    766:    This routine sorts the arrays provided in `eigr` and `eigi`, and also
    767:    sorts the dense system stored inside `ds` (assumed to be in condensed form).
    768:    The sorting criterion is specified with `DSSetSlepcSC()`.

    770:    If arrays `rr` and `ri` are provided, then a (partial) reordering based on these
    771:    values rather than on the eigenvalues is performed. In symmetric problems
    772:    a total order is obtained (parameter `k` is ignored), but otherwise the result
    773:    is sorted only partially. In this latter case, it is only guaranteed that
    774:    all the first `k` elements satisfy the comparison with any of the last `n-k`
    775:    elements. The output value of parameter `k` is the final number of elements in
    776:    the first set.

    778:    Level: intermediate

    780: .seealso: [](sec:ds), `DSSolve()`, `DSSetSlepcSC()`, `DSSortWithPermutation()`
    781: @*/
    782: PetscErrorCode DSSort(DS ds,PetscScalar eigr[],PetscScalar eigi[],PetscScalar rr[],PetscScalar ri[],PetscInt *k)
    783: {
    784:   PetscInt       i;

    786:   PetscFunctionBegin;
    789:   DSCheckSolved(ds,1);
    790:   PetscAssertPointer(eigr,2);
    791:   if (rr) PetscAssertPointer(rr,4);
    792:   PetscCheck(ds->state<DS_STATE_TRUNCATED,PetscObjectComm((PetscObject)ds),PETSC_ERR_ORDER,"Cannot sort a truncated DS");
    793:   PetscCheck(ds->sc,PetscObjectComm((PetscObject)ds),PETSC_ERR_ORDER,"Must provide a sorting criterion first");
    794:   PetscCheck(!k || rr,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_WRONG,"Argument k can only be used together with rr");

    796:   for (i=0;i<ds->n;i++) ds->perm[i] = i;   /* initialize to trivial permutation */
    797:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
    798:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    799:   PetscUseTypeMethod(ds,sort,eigr,eigi,rr,ri,k);
    800:   PetscCall(PetscFPTrapPop());
    801:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
    802:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    803:   PetscCall(PetscInfo(ds,"Finished sorting\n"));
    804:   PetscFunctionReturn(PETSC_SUCCESS);
    805: }

    807: /*@
    808:    DSSortWithPermutation - Reorders the result of `DSSolve()` according to a given
    809:    permutation.

    811:    Logically Collective

    813:    Input Parameters:
    814: +  ds   - the direct solver context
    815: -  perm - permutation that indicates the new ordering

    817:    Input/Output Parameters:
    818: +  eigr - array with the reordered eigenvalues (real part)
    819: -  eigi - array with the reordered eigenvalues (imaginary part)

    821:    Notes:
    822:    This routine reorders the arrays provided in `eigr` and `eigi`, and also the dense
    823:    system stored inside `ds` (assumed to be in condensed form). There is no sorting
    824:    criterion, as opposed to `DSSort()`. Instead, the new ordering is given in argument
    825:    `perm`.

    827:    Level: advanced

    829: .seealso: [](sec:ds), `DSSolve()`, `DSSort()`
    830: @*/
    831: PetscErrorCode DSSortWithPermutation(DS ds,PetscInt perm[],PetscScalar eigr[],PetscScalar eigi[])
    832: {
    833:   PetscFunctionBegin;
    836:   DSCheckSolved(ds,1);
    837:   PetscAssertPointer(perm,2);
    838:   PetscAssertPointer(eigr,3);
    839:   PetscCheck(ds->state<DS_STATE_TRUNCATED,PetscObjectComm((PetscObject)ds),PETSC_ERR_ORDER,"Cannot sort a truncated DS");

    841:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
    842:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    843:   PetscUseTypeMethod(ds,sortperm,perm,eigr,eigi);
    844:   PetscCall(PetscFPTrapPop());
    845:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
    846:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    847:   PetscCall(PetscInfo(ds,"Finished sorting\n"));
    848:   PetscFunctionReturn(PETSC_SUCCESS);
    849: }

    851: /*@
    852:    DSSynchronize - Make sure that all processes have the same data, performing
    853:    communication if necessary.

    855:    Collective

    857:    Input Parameter:
    858: .  ds   - the direct solver context

    860:    Input/Output Parameters:
    861: +  eigr - (optional) array with the computed eigenvalues (real part)
    862: -  eigi - (optional) array with the computed eigenvalues (imaginary part)

    864:    Notes:
    865:    When the `DS` has been created with a communicator with more than one process,
    866:    the internal data, especially the computed matrices, may diverge in the
    867:    different processes. This happens when using multithreaded BLAS and may
    868:    cause numerical issues in some ill-conditioned problems. This function
    869:    performs the necessary communication among the processes so that the
    870:    internal data is exactly equal in all of them.

    872:    Depending on the parallel mode as set with `DSSetParallel()`, this function
    873:    will either do nothing or synchronize the matrices computed by `DSSolve()`
    874:    and `DSSort()`. The arguments `eigr` and `eigi` are typically those used in the
    875:    calls to `DSSolve()` and `DSSort()`.

    877:    Level: developer

    879: .seealso: [](sec:ds), `DSSetParallel()`, `DSSolve()`, `DSSort()`
    880: @*/
    881: PetscErrorCode DSSynchronize(DS ds,PetscScalar eigr[],PetscScalar eigi[])
    882: {
    883:   PetscMPIInt    size;

    885:   PetscFunctionBegin;
    888:   DSCheckAlloc(ds,1);
    889:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)ds),&size));
    890:   if (size>1 && ds->pmode==DS_PARALLEL_SYNCHRONIZED) {
    891:     PetscCall(PetscLogEventBegin(DS_Synchronize,ds,0,0,0));
    892:     PetscUseTypeMethod(ds,synchronize,eigr,eigi);
    893:     PetscCall(PetscLogEventEnd(DS_Synchronize,ds,0,0,0));
    894:     PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    895:     PetscCall(PetscInfo(ds,"Synchronization completed (%s)\n",DSParallelTypes[ds->pmode]));
    896:   }
    897:   PetscFunctionReturn(PETSC_SUCCESS);
    898: }

    900: /*@
    901:    DSVectors - Compute vectors associated to the dense system such
    902:    as eigenvectors.

    904:    Logically Collective

    906:    Input Parameters:
    907: +  ds  - the direct solver context
    908: -  mat - the matrix, used to indicate which vectors are required

    910:    Input/Output Parameter:
    911: .  j   - (optional) index of vector to be computed

    913:    Output Parameter:
    914: .  rnorm - (optional) computed residual norm

    916:    Notes:
    917:    Allowed values for mat are `DS_MAT_X`, `DS_MAT_Y`, `DS_MAT_U` and `DS_MAT_V`, to
    918:    compute right or left eigenvectors, or left or right singular vectors,
    919:    respectively.

    921:    If `NULL` is passed in argument `j` then all vectors are computed,
    922:    otherwise `j` indicates which vector must be computed. In real non-symmetric
    923:    problems, on exit the index `j` will be incremented when a complex conjugate
    924:    pair is found.

    926:    This function can be invoked after the dense problem has been solved,
    927:    to get the residual norm estimate of the associated Ritz pair. In that
    928:    case, the relevant information is returned in `rnorm`.

    930:    For computing eigenvectors, LAPACK's `_trevc` is used so the matrix must
    931:    be in (quasi-)triangular form, or call `DSSolve()` first.

    933:    Level: intermediate

    935: .seealso: [](sec:ds), `DSSolve()`
    936: @*/
    937: PetscErrorCode DSVectors(DS ds,DSMatType mat,PetscInt *j,PetscReal *rnorm)
    938: {
    939:   PetscFunctionBegin;
    942:   DSCheckAlloc(ds,1);
    944:   PetscCheck(mat<DS_NUM_MAT,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_WRONG,"Invalid matrix");
    945:   PetscCheck(!rnorm || j,PetscObjectComm((PetscObject)ds),PETSC_ERR_ORDER,"Must give a value of j");
    946:   if (!ds->omat[mat]) PetscCall(DSAllocateMat_Private(ds,mat));
    947:   if (!j) PetscCall(PetscInfo(ds,"Computing all vectors on %s\n",DSMatName[mat]));
    948:   PetscCall(PetscLogEventBegin(DS_Vectors,ds,0,0,0));
    949:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    950:   PetscUseTypeMethod(ds,vectors,mat,j,rnorm);
    951:   PetscCall(PetscFPTrapPop());
    952:   PetscCall(PetscLogEventEnd(DS_Vectors,ds,0,0,0));
    953:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    954:   PetscFunctionReturn(PETSC_SUCCESS);
    955: }

    957: /*@
    958:    DSUpdateExtraRow - Performs all necessary operations so that the extra
    959:    row gets up-to-date after a call to `DSSolve()`.

    961:    Logically Collective

    963:    Input Parameter:
    964: .  ds - the direct solver context

    966:    Level: advanced

    968: .seealso: [](sec:ds), `DSSolve()`, `DSSetExtraRow()`
    969: @*/
    970: PetscErrorCode DSUpdateExtraRow(DS ds)
    971: {
    972:   PetscFunctionBegin;
    975:   DSCheckAlloc(ds,1);
    976:   PetscCheck(ds->extrarow,PetscObjectComm((PetscObject)ds),PETSC_ERR_ARG_WRONGSTATE,"Should have called DSSetExtraRow");
    977:   PetscCall(PetscInfo(ds,"Updating extra row\n"));
    978:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
    979:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    980:   PetscUseTypeMethod(ds,update);
    981:   PetscCall(PetscFPTrapPop());
    982:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
    983:   PetscFunctionReturn(PETSC_SUCCESS);
    984: }

    986: /*@
    987:    DSCond - Compute the condition number.

    989:    Logically Collective

    991:    Input Parameters:
    992: +  ds - the direct solver context
    993: -  cond - the computed condition number

    995:    Notes:
    996:    In standard eigenvalue problems, returns the $\infty$-norm condition number of the first
    997:    matrix, computed as $\kappa_\infty(A) = \|A\|_\infty\|A^{-1}\|_\infty$.

    999:    In GSVD problems, returns the maximum of $\kappa_2(A)$ and $\kappa_2(B)$, where $\kappa_2(\cdot)$ is
    1000:    computed as the ratio of the largest and smallest singular values.

    1002:    Does not take into account the extra row.

    1004:    Level: advanced

    1006: .seealso: [](sec:ds), `DSSolve()`, `DSSetExtraRow()`
    1007: @*/
    1008: PetscErrorCode DSCond(DS ds,PetscReal *cond)
    1009: {
    1010:   PetscFunctionBegin;
    1013:   DSCheckAlloc(ds,1);
    1014:   PetscAssertPointer(cond,2);
    1015:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
    1016:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    1017:   PetscUseTypeMethod(ds,cond,cond);
    1018:   PetscCall(PetscFPTrapPop());
    1019:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
    1020:   PetscCall(PetscInfo(ds,"Computed condition number = %g\n",(double)*cond));
    1021:   PetscFunctionReturn(PETSC_SUCCESS);
    1022: }

    1024: /*@
    1025:    DSTranslateHarmonic - Computes a translation of the dense system.

    1027:    Logically Collective

    1029:    Input Parameters:
    1030: +  ds      - the direct solver context
    1031: .  tau     - the translation amount
    1032: .  beta    - last component of vector $b$
    1033: -  recover - boolean flag to indicate whether to recover or not

    1035:    Output Parameters:
    1036: +  g       - the computed vector (optional)
    1037: -  gamma   - scale factor (optional)

    1039:    Notes:
    1040:    This function is intended for use in the context of Krylov methods only.
    1041:    It computes a translation of a Krylov decomposition in order to extract
    1042:    eigenpair approximations by harmonic Rayleigh-Ritz.
    1043:    The matrix is updated as $A + gb^*$ where $g = (A-\tau I)^{-*}b$ and
    1044:    vector $b$ is assumed to be $\beta e_n^*$.

    1046:    The $\gamma$ factor is defined as $\sqrt{1+g^*g}$ and can be interpreted as
    1047:    the factor by which the residual of the Krylov decomposition is scaled.

    1049:    If the `recover` flag is activated, the computed translation undoes the
    1050:    translation done previously. In that case, parameter `tau` is ignored.

    1052:    Level: developer

    1054: .seealso: [](sec:ds), `DSTranslateRKS()`
    1055: @*/
    1056: PetscErrorCode DSTranslateHarmonic(DS ds,PetscScalar tau,PetscReal beta,PetscBool recover,PetscScalar g[],PetscReal *gamma)
    1057: {
    1058:   PetscFunctionBegin;
    1061:   DSCheckAlloc(ds,1);
    1062:   if (recover) PetscCall(PetscInfo(ds,"Undoing the translation\n"));
    1063:   else PetscCall(PetscInfo(ds,"Computing the translation\n"));
    1064:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
    1065:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    1066:   PetscUseTypeMethod(ds,transharm,tau,beta,recover,g,gamma);
    1067:   PetscCall(PetscFPTrapPop());
    1068:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
    1069:   ds->state = DS_STATE_RAW;
    1070:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    1071:   PetscFunctionReturn(PETSC_SUCCESS);
    1072: }

    1074: /*@
    1075:    DSTranslateRKS - Computes a modification of the dense system corresponding
    1076:    to an update of the shift in a rational Krylov method.

    1078:    Logically Collective

    1080:    Input Parameters:
    1081: +  ds    - the direct solver context
    1082: -  alpha - the translation amount

    1084:    Notes:
    1085:    This function is intended for use in the context of Krylov methods only.
    1086:    It takes the leading $(k+1,k)$ submatrix of $A$, containing the truncated
    1087:    Rayleigh quotient of a Krylov-Schur relation computed from a shift
    1088:    $\sigma_1$ and transforms it to obtain a Krylov relation as if computed
    1089:    from a different shift $\sigma_2$. The new matrix is computed as
    1090:    $\alpha^{-1}(I-QR^{-1})$, where $QR=I-\alpha A$ and
    1091:    $\alpha = \sigma_1-\sigma_2$.

    1093:    Matrix $Q$ is placed in `DS_MAT_Q` so that it can be used to update the
    1094:    Krylov basis.

    1096:    Level: developer

    1098: .seealso: [](sec:ds), `DSTranslateHarmonic()`
    1099: @*/
    1100: PetscErrorCode DSTranslateRKS(DS ds,PetscScalar alpha)
    1101: {
    1102:   PetscFunctionBegin;
    1105:   DSCheckAlloc(ds,1);
    1106:   PetscCall(PetscInfo(ds,"Translating with alpha=%g\n",(double)PetscRealPart(alpha)));
    1107:   PetscCall(DSSetCompact(ds,PETSC_FALSE));
    1108:   PetscCall(PetscLogEventBegin(DS_Other,ds,0,0,0));
    1109:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    1110:   PetscUseTypeMethod(ds,transrks,alpha);
    1111:   PetscCall(PetscFPTrapPop());
    1112:   PetscCall(PetscLogEventEnd(DS_Other,ds,0,0,0));
    1113:   ds->state = DS_STATE_RAW;
    1114:   PetscCall(PetscObjectStateIncrease((PetscObject)ds));
    1115:   PetscFunctionReturn(PETSC_SUCCESS);
    1116: }