Date: 2025-03-28T21:10:55+00:00

Actual source code: cp.c

      1: #include <petsc/private/pcimpl.h>
      2: #include <../src/mat/impls/aij/seq/aij.h>

      4: /*
      5:    Private context (data structure) for the CP preconditioner.
      6: */
      7: typedef struct {
      8:   PetscInt     n, m;
      9:   Vec          work;
     10:   PetscScalar *d;     /* sum of squares of each column */
     11:   PetscScalar *a;     /* non-zeros by column */
     12:   PetscInt    *i, *j; /* offsets of nonzeros by column, non-zero indices by column */
     13: } PC_CP;

     15: static PetscErrorCode PCSetUp_CP(PC pc)
     16: {
     17:   PC_CP      *cp = (PC_CP *)pc->data;
     18:   PetscInt    i, j, *colcnt;
     19:   PetscBool   flg;
     20:   Mat_SeqAIJ *aij = (Mat_SeqAIJ *)pc->pmat->data;

     22:   PetscFunctionBegin;
     23:   PetscCall(PetscObjectTypeCompare((PetscObject)pc->pmat, MATSEQAIJ, &flg));
     24:   PetscCheck(flg, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Currently only handles SeqAIJ matrices");

     26:   PetscCall(MatGetLocalSize(pc->pmat, &cp->m, &cp->n));
     27:   PetscCheck(cp->m == cp->n, PETSC_COMM_SELF, PETSC_ERR_SUP, "Currently only for square matrices");

     29:   if (!cp->work) PetscCall(MatCreateVecs(pc->pmat, &cp->work, NULL));
     30:   if (!cp->d) PetscCall(PetscMalloc1(cp->n, &cp->d));
     31:   if (cp->a && pc->flag != SAME_NONZERO_PATTERN) {
     32:     PetscCall(PetscFree3(cp->a, cp->i, cp->j));
     33:     cp->a = NULL;
     34:   }

     36:   /* convert to column format */
     37:   if (!cp->a) PetscCall(PetscMalloc3(aij->nz, &cp->a, cp->n + 1, &cp->i, aij->nz, &cp->j));
     38:   PetscCall(PetscCalloc1(cp->n, &colcnt));

     40:   for (i = 0; i < aij->nz; i++) colcnt[aij->j[i]]++;
     41:   cp->i[0] = 0;
     42:   for (i = 0; i < cp->n; i++) cp->i[i + 1] = cp->i[i] + colcnt[i];
     43:   PetscCall(PetscArrayzero(colcnt, cp->n));
     44:   for (i = 0; i < cp->m; i++) {                   /* over rows */
     45:     for (j = aij->i[i]; j < aij->i[i + 1]; j++) { /* over columns in row */
     46:       cp->j[cp->i[aij->j[j]] + colcnt[aij->j[j]]]   = i;
     47:       cp->a[cp->i[aij->j[j]] + colcnt[aij->j[j]]++] = aij->a[j];
     48:     }
     49:   }
     50:   PetscCall(PetscFree(colcnt));

     52:   /* compute sum of squares of each column d[] */
     53:   for (i = 0; i < cp->n; i++) { /* over columns */
     54:     cp->d[i] = 0.;
     55:     for (j = cp->i[i]; j < cp->i[i + 1]; j++) cp->d[i] += cp->a[j] * cp->a[j]; /* over rows in column */
     56:     cp->d[i] = 1.0 / cp->d[i];
     57:   }
     58:   PetscFunctionReturn(PETSC_SUCCESS);
     59: }

     61: static PetscErrorCode PCApply_CP(PC pc, Vec bb, Vec xx)
     62: {
     63:   PC_CP       *cp = (PC_CP *)pc->data;
     64:   PetscScalar *b, *x, xt;
     65:   PetscInt     i, j;

     67:   PetscFunctionBegin;
     68:   PetscCall(VecCopy(bb, cp->work));
     69:   PetscCall(VecGetArray(cp->work, &b));
     70:   PetscCall(VecGetArray(xx, &x));

     72:   for (i = 0; i < cp->n; i++) { /* over columns */
     73:     xt = 0.;
     74:     for (j = cp->i[i]; j < cp->i[i + 1]; j++) xt += cp->a[j] * b[cp->j[j]]; /* over rows in column */
     75:     xt *= cp->d[i];
     76:     x[i] = xt;
     77:     for (j = cp->i[i]; j < cp->i[i + 1]; j++) b[cp->j[j]] -= xt * cp->a[j]; /* over rows in column updating b*/
     78:   }
     79:   for (i = cp->n - 1; i > -1; i--) { /* over columns */
     80:     xt = 0.;
     81:     for (j = cp->i[i]; j < cp->i[i + 1]; j++) xt += cp->a[j] * b[cp->j[j]]; /* over rows in column */
     82:     xt *= cp->d[i];
     83:     x[i] = xt;
     84:     for (j = cp->i[i]; j < cp->i[i + 1]; j++) b[cp->j[j]] -= xt * cp->a[j]; /* over rows in column updating b*/
     85:   }

     87:   PetscCall(VecRestoreArray(cp->work, &b));
     88:   PetscCall(VecRestoreArray(xx, &x));
     89:   PetscFunctionReturn(PETSC_SUCCESS);
     90: }

     92: static PetscErrorCode PCReset_CP(PC pc)
     93: {
     94:   PC_CP *cp = (PC_CP *)pc->data;

     96:   PetscFunctionBegin;
     97:   PetscCall(PetscFree(cp->d));
     98:   PetscCall(VecDestroy(&cp->work));
     99:   PetscCall(PetscFree3(cp->a, cp->i, cp->j));
    100:   PetscFunctionReturn(PETSC_SUCCESS);
    101: }

    103: static PetscErrorCode PCDestroy_CP(PC pc)
    104: {
    105:   PC_CP *cp = (PC_CP *)pc->data;

    107:   PetscFunctionBegin;
    108:   PetscCall(PCReset_CP(pc));
    109:   PetscCall(PetscFree(cp->d));
    110:   PetscCall(PetscFree3(cp->a, cp->i, cp->j));
    111:   PetscCall(PetscFree(pc->data));
    112:   PetscFunctionReturn(PETSC_SUCCESS);
    113: }

    115: static PetscErrorCode PCSetFromOptions_CP(PC pc, PetscOptionItems *PetscOptionsObject)
    116: {
    117:   PetscFunctionBegin;
    118:   PetscFunctionReturn(PETSC_SUCCESS);
    119: }

    121: /*MC
    122:      PCCP - a "column-projection" preconditioner. Iteratively projects the current residual onto the one dimensional spaces
    123:             spanned by each of the columns of the matrix.

    125:      This is a terrible preconditioner and is not recommended, ever!

    127:      Loops over the entries of x computing dx_i (e_i is the unit vector in the ith direction) to
    128: .vb

    130:         min || b - A(x + dx_i e_i ||_2
    131:         dx_i

    133:     That is, it changes a single entry of x to minimize the new residual norm.
    134:    Let A_i represent the ith column of A, then the minimization can be written as

    136:        min || r - (dx_i) A e_i ||_2
    137:        dx_i
    138:    or   min || r - (dx_i) A_i ||_2
    139:         dx_i

    141:     take the derivative with respect to dx_i to obtain
    142:         dx_i = (A_i^T A_i)^(-1) A_i^T r

    144:     This is equivalent to using Gauss-Seidel on the normal equations
    145: .ve

    147:     Notes:
    148:     This procedure can also be done with block columns or any groups of columns
    149:     but this is not coded.

    151:     These "projections" can be done simultaneously for all columns (similar to the Jacobi method)
    152:     or sequentially (similar to Gauss-Seidel/SOR). This is only coded for SOR type.

    154:     This is related to, but not the same as "row projection" methods.

    156:     This is currently coded only for `MATSEQAIJ` matrices

    158:   Level: intermediate

    160: .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PCJACOBI`, `PCSOR`
    161: M*/

    163: PETSC_EXTERN PetscErrorCode PCCreate_CP(PC pc)
    164: {
    165:   PC_CP *cp;

    167:   PetscFunctionBegin;
    168:   PetscCall(PetscNew(&cp));
    169:   pc->data = (void *)cp;

    171:   pc->ops->apply           = PCApply_CP;
    172:   pc->ops->applytranspose  = PCApply_CP;
    173:   pc->ops->setup           = PCSetUp_CP;
    174:   pc->ops->reset           = PCReset_CP;
    175:   pc->ops->destroy         = PCDestroy_CP;
    176:   pc->ops->setfromoptions  = PCSetFromOptions_CP;
    177:   pc->ops->view            = NULL;
    178:   pc->ops->applyrichardson = NULL;
    179:   PetscFunctionReturn(PETSC_SUCCESS);
    180: }