Date: 2022-03-04T17:37:53+00:00

Actual source code: lrc.c

      2: #include <petsc/private/matimpl.h>

      4: typedef struct {
      5:   Mat         A;           /* sparse matrix */
      6:   Mat         U,V;         /* dense tall-skinny matrices */
      7:   Vec         c;           /* sequential vector containing the diagonal of C */
      8:   Vec         work1,work2; /* sequential vectors that hold partial products */
      9:   PetscMPIInt nwork;       /* length of work vectors */
     10:   Vec         xl,yl;       /* auxiliary sequential vectors for matmult operation */
     11: } Mat_LRC;

     13: PetscErrorCode MatMult_LRC(Mat N,Vec x,Vec y)
     14: {
     15:   Mat_LRC           *Na = (Mat_LRC*)N->data;
     16:   Mat               Uloc,Vloc;
     17:   PetscErrorCode    ierr;
     18:   PetscScalar       *w1,*w2;
     19:   const PetscScalar *a;

     22:   VecGetArrayRead(x,&a);
     23:   VecPlaceArray(Na->xl,a);
     24:   VecGetLocalVector(y,Na->yl);
     25:   MatDenseGetLocalMatrix(Na->U,&Uloc);
     26:   MatDenseGetLocalMatrix(Na->V,&Vloc);

     28:   /* multiply the local part of V with the local part of x */
     29: #if defined(PETSC_USE_COMPLEX)
     30:   MatMultHermitianTranspose(Vloc,Na->xl,Na->work1);
     31: #else
     32:   MatMultTranspose(Vloc,Na->xl,Na->work1);
     33: #endif

     35:   /* form the sum of all the local multiplies: this is work2 = V'*x =
     36:      sum_{all processors} work1 */
     37:   VecGetArray(Na->work1,&w1);
     38:   VecGetArray(Na->work2,&w2);
     39:   MPIU_Allreduce(w1,w2,Na->nwork,MPIU_SCALAR,MPIU_SUM,PetscObjectComm((PetscObject)N));
     40:   VecRestoreArray(Na->work1,&w1);
     41:   VecRestoreArray(Na->work2,&w2);

     43:   if (Na->c) {  /* work2 = C*work2 */
     44:     VecPointwiseMult(Na->work2,Na->c,Na->work2);
     45:   }

     47:   if (Na->A) {
     48:     /* form y = A*x */
     49:     MatMult(Na->A,x,y);
     50:     /* multiply-add y = y + U*work2 */
     51:     MatMultAdd(Uloc,Na->work2,Na->yl,Na->yl);
     52:   } else {
     53:     /* multiply y = U*work2 */
     54:     MatMult(Uloc,Na->work2,Na->yl);
     55:   }

     57:   VecRestoreArrayRead(x,&a);
     58:   VecResetArray(Na->xl);
     59:   VecRestoreLocalVector(y,Na->yl);
     60:   return(0);
     61: }

     63: PetscErrorCode MatDestroy_LRC(Mat N)
     64: {
     65:   Mat_LRC        *Na = (Mat_LRC*)N->data;

     69:   MatDestroy(&Na->A);
     70:   MatDestroy(&Na->U);
     71:   MatDestroy(&Na->V);
     72:   VecDestroy(&Na->c);
     73:   VecDestroy(&Na->work1);
     74:   VecDestroy(&Na->work2);
     75:   VecDestroy(&Na->xl);
     76:   VecDestroy(&Na->yl);
     77:   PetscFree(N->data);
     78:   PetscObjectComposeFunction((PetscObject)N,"MatLRCGetMats_C",0);
     79:   return(0);
     80: }

     82: PetscErrorCode MatLRCGetMats_LRC(Mat N,Mat *A,Mat *U,Vec *c,Mat *V)
     83: {
     84:   Mat_LRC *Na = (Mat_LRC*)N->data;

     87:   if (A) *A = Na->A;
     88:   if (U) *U = Na->U;
     89:   if (c) *c = Na->c;
     90:   if (V) *V = Na->V;
     91:   return(0);
     92: }

     94: /*@
     95:    MatLRCGetMats - Returns the constituents of an LRC matrix

     97:    Collective on Mat

     99:    Input Parameter:
    100: .  N - matrix of type LRC

    102:    Output Parameters:
    103: +  A - the (sparse) matrix
    104: .  U - first dense rectangular (tall and skinny) matrix
    105: .  c - a sequential vector containing the diagonal of C
    106: -  V - second dense rectangular (tall and skinny) matrix

    108:    Note:
    109:    The returned matrices need not be destroyed by the caller.

    111:    Level: intermediate

    113: .seealso: MatCreateLRC()
    114: @*/
    115: PetscErrorCode  MatLRCGetMats(Mat N,Mat *A,Mat *U,Vec *c,Mat *V)
    116: {

    120:   PetscUseMethod(N,"MatLRCGetMats_C",(Mat,Mat*,Mat*,Vec*,Mat*),(N,A,U,c,V));
    121:   return(0);
    122: }

    124: /*@
    125:    MatCreateLRC - Creates a new matrix object that behaves like A + U*C*V'

    127:    Collective on Mat

    129:    Input Parameters:
    130: +  A    - the (sparse) matrix (can be NULL)
    131: .  U, V - two dense rectangular (tall and skinny) matrices
    132: -  c    - a sequential vector containing the diagonal of C (can be NULL)

    134:    Output Parameter:
    135: .  N    - the matrix that represents A + U*C*V'

    137:    Notes:
    138:    The matrix A + U*C*V' is not formed! Rather the new matrix
    139:    object performs the matrix-vector product by first multiplying by
    140:    A and then adding the other term.

    142:    C is a diagonal matrix (represented as a vector) of order k,
    143:    where k is the number of columns of both U and V.

    145:    If A is NULL then the new object behaves like a low-rank matrix U*C*V'.

    147:    Use V=U (or V=NULL) for a symmetric low-rank correction, A + U*C*U'.

    149:    If c is NULL then the low-rank correction is just U*V'.

    151:    Level: intermediate

    153: .seealso: MatLRCGetMats()
    154: @*/
    155: PetscErrorCode MatCreateLRC(Mat A,Mat U,Vec c,Mat V,Mat *N)
    156: {
    158:   PetscBool      match;
    159:   PetscInt       m,n,k,m1,n1,k1;
    160:   Mat_LRC        *Na;

    167:   else V=U;

    171:   PetscObjectTypeCompareAny((PetscObject)U,&match,MATSEQDENSE,MATMPIDENSE,"");
    172:   if (!match) SETERRQ(PetscObjectComm((PetscObject)U),PETSC_ERR_SUP,"Matrix U must be of type dense");
    173:   if (V) {
    174:     PetscObjectTypeCompareAny((PetscObject)V,&match,MATSEQDENSE,MATMPIDENSE,"");
    175:     if (!match) SETERRQ(PetscObjectComm((PetscObject)V),PETSC_ERR_SUP,"Matrix V must be of type dense");
    176:   }

    178:   MatGetSize(U,NULL,&k);
    179:   MatGetSize(V,NULL,&k1);
    180:   if (k!=k1) SETERRQ2(PetscObjectComm((PetscObject)U),PETSC_ERR_ARG_INCOMP,"U and V have different number of columns (%D vs %D)",k,k1);
    181:   MatGetLocalSize(U,&m,NULL);
    182:   MatGetLocalSize(V,&n,NULL);
    183:   if (A) {
    184:     MatGetLocalSize(A,&m1,&n1);
    185:     if (m!=m1) SETERRQ2(PetscObjectComm((PetscObject)U),PETSC_ERR_ARG_INCOMP,"Local dimensions of U %D and A %D do not match",m,m1);
    186:     if (n!=n1) SETERRQ2(PetscObjectComm((PetscObject)V),PETSC_ERR_ARG_INCOMP,"Local dimensions of V %D and A %D do not match",n,n1);
    187:   }
    188:   if (c) {
    189:     VecGetSize(c,&k1);
    190:     if (k!=k1) SETERRQ2(PetscObjectComm((PetscObject)c),PETSC_ERR_ARG_INCOMP,"The length of c %D does not match the number of columns of U and V (%D)",k1,k);
    191:     VecGetLocalSize(c,&k1);
    192:     if (k!=k1) SETERRQ(PetscObjectComm((PetscObject)c),PETSC_ERR_ARG_INCOMP,"c must be a sequential vector");
    193:   }

    195:   MatCreate(PetscObjectComm((PetscObject)U),N);
    196:   MatSetSizes(*N,m,n,PETSC_DECIDE,PETSC_DECIDE);
    197:   PetscObjectChangeTypeName((PetscObject)*N,MATLRC);

    199:   PetscNewLog(*N,&Na);
    200:   (*N)->data = (void*)Na;
    201:   Na->A      = A;
    202:   Na->U      = U;
    203:   Na->c      = c;
    204:   Na->V      = V;

    206:   if (A) { PetscObjectReference((PetscObject)A); }
    207:   PetscObjectReference((PetscObject)Na->U);
    208:   PetscObjectReference((PetscObject)Na->V);
    209:   if (c) { PetscObjectReference((PetscObject)c); }

    211:   VecCreateSeq(PETSC_COMM_SELF,U->cmap->N,&Na->work1);
    212:   VecDuplicate(Na->work1,&Na->work2);
    213:   PetscMPIIntCast(U->cmap->N,&Na->nwork);

    215:   VecCreateSeqWithArray(PETSC_COMM_SELF,1,V->rmap->n,NULL,&Na->xl);
    216:   VecCreateSeqWithArray(PETSC_COMM_SELF,1,U->rmap->n,NULL,&Na->yl);

    218:   (*N)->ops->destroy = MatDestroy_LRC;
    219:   (*N)->ops->mult    = MatMult_LRC;
    220:   (*N)->assembled    = PETSC_TRUE;
    221:   (*N)->preallocated = PETSC_TRUE;
    222:   (*N)->cmap->N      = V->rmap->N;
    223:   (*N)->rmap->N      = U->rmap->N;
    224:   (*N)->cmap->n      = V->rmap->n;
    225:   (*N)->rmap->n      = U->rmap->n;

    227:   PetscObjectComposeFunction((PetscObject)(*N),"MatLRCGetMats_C",MatLRCGetMats_LRC);
    228:   return(0);
    229: }