Actual source code: sorder.c


  2: /*
  3:      Provides the code that allows PETSc users to register their own
  4:   sequential matrix Ordering routines.
  5: */
  6: #include <petsc/private/matimpl.h>
  7: #include <petscmat.h>

  9: PetscFunctionList MatOrderingList              = NULL;
 10: PetscBool         MatOrderingRegisterAllCalled = PETSC_FALSE;

 12: PetscErrorCode MatGetOrdering_Flow(Mat mat, MatOrderingType type, IS *irow, IS *icol)
 13: {
 14:   PetscFunctionBegin;
 15:   SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Cannot do default flow ordering for matrix type");
 16: }

 18: PETSC_INTERN PetscErrorCode MatGetOrdering_Natural(Mat mat, MatOrderingType type, IS *irow, IS *icol)
 19: {
 20:   PetscInt  n, i, *ii;
 21:   PetscBool done;
 22:   MPI_Comm  comm;

 24:   PetscFunctionBegin;
 25:   PetscCall(PetscObjectGetComm((PetscObject)mat, &comm));
 26:   PetscCall(MatGetRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, &n, NULL, NULL, &done));
 27:   PetscCall(MatRestoreRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, NULL, NULL, NULL, &done));
 28:   if (done) { /* matrix may be "compressed" in symbolic factorization, due to i-nodes or block storage */
 29:     /*
 30:       We actually create general index sets because this avoids mallocs to
 31:       to obtain the indices in the MatSolve() routines.
 32:       PetscCall(ISCreateStride(PETSC_COMM_SELF,n,0,1,irow));
 33:       PetscCall(ISCreateStride(PETSC_COMM_SELF,n,0,1,icol));
 34:     */
 35:     PetscCall(PetscMalloc1(n, &ii));
 36:     for (i = 0; i < n; i++) ii[i] = i;
 37:     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, ii, PETSC_COPY_VALUES, irow));
 38:     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, ii, PETSC_OWN_POINTER, icol));
 39:   } else {
 40:     PetscInt start, end;

 42:     PetscCall(MatGetOwnershipRange(mat, &start, &end));
 43:     PetscCall(ISCreateStride(comm, end - start, start, 1, irow));
 44:     PetscCall(ISCreateStride(comm, end - start, start, 1, icol));
 45:   }
 46:   PetscCall(ISSetIdentity(*irow));
 47:   PetscCall(ISSetIdentity(*icol));
 48:   PetscFunctionReturn(PETSC_SUCCESS);
 49: }

 51: /*
 52:      Orders the rows (and columns) by the lengths of the rows.
 53:    This produces a symmetric Ordering but does not require a
 54:    matrix with symmetric non-zero structure.
 55: */
 56: PETSC_INTERN PetscErrorCode MatGetOrdering_RowLength(Mat mat, MatOrderingType type, IS *irow, IS *icol)
 57: {
 58:   PetscInt        n, *permr, *lens, i;
 59:   const PetscInt *ia, *ja;
 60:   PetscBool       done;

 62:   PetscFunctionBegin;
 63:   PetscCall(MatGetRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, &n, &ia, &ja, &done));
 64:   PetscCheck(done, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Cannot get rows for matrix");

 66:   PetscCall(PetscMalloc2(n, &lens, n, &permr));
 67:   for (i = 0; i < n; i++) {
 68:     lens[i]  = ia[i + 1] - ia[i];
 69:     permr[i] = i;
 70:   }
 71:   PetscCall(MatRestoreRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, NULL, &ia, &ja, &done));

 73:   PetscCall(PetscSortIntWithPermutation(n, lens, permr));

 75:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, permr, PETSC_COPY_VALUES, irow));
 76:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, permr, PETSC_COPY_VALUES, icol));
 77:   PetscCall(PetscFree2(lens, permr));
 78:   PetscFunctionReturn(PETSC_SUCCESS);
 79: }

 81: /*@C
 82:    MatOrderingRegister - Adds a new sparse matrix ordering to the matrix package.

 84:    Not Collective

 86:    Input Parameters:
 87: +  sname - name of ordering (for example `MATORDERINGND`)
 88: -  function - function pointer that creates the ordering

 90:    Level: developer

 92:    Sample usage:
 93: .vb
 94:    MatOrderingRegister("my_order", MyOrder);
 95: .ve

 97:    Then, your partitioner can be chosen with the procedural interface via
 98: $     MatOrderingSetType(part,"my_order)
 99:    or at runtime via the option
100: $     -pc_factor_mat_ordering_type my_order

102: .seealso: `MatOrderingRegisterAll()`, `MatGetOrdering()`
103: @*/
104: PetscErrorCode MatOrderingRegister(const char sname[], PetscErrorCode (*function)(Mat, MatOrderingType, IS *, IS *))
105: {
106:   PetscFunctionBegin;
107:   PetscCall(MatInitializePackage());
108:   PetscCall(PetscFunctionListAdd(&MatOrderingList, sname, function));
109:   PetscFunctionReturn(PETSC_SUCCESS);
110: }

112: #include <../src/mat/impls/aij/mpi/mpiaij.h>
113: /*@C
114:    MatGetOrdering - Gets a reordering for a matrix to reduce fill or to
115:    improve numerical stability of LU factorization.

117:    Collective

119:    Input Parameters:
120: +  mat - the matrix
121: -  type - type of reordering, one of the following

123: .vb
124:       MATORDERINGNATURAL_OR_ND - Nested dissection unless matrix is SBAIJ then it is natural
125:       MATORDERINGNATURAL - Natural
126:       MATORDERINGND - Nested Dissection
127:       MATORDERING1WD - One-way Dissection
128:       MATORDERINGRCM - Reverse Cuthill-McKee
129:       MATORDERINGQMD - Quotient Minimum Degree
130:       MATORDERINGEXTERNAL - Use an ordering internal to the factorzation package and do not compute or use PETSc's
131: .ve

133:    Output Parameters:
134: +  rperm - row permutation indices
135: -  cperm - column permutation indices

137:    Options Database Key:
138: + -mat_view_ordering draw - plots matrix nonzero structure in new ordering
139: - -pc_factor_mat_ordering_type <nd,natural,..> - ordering to use with `PC`s based on factorization, `MATLU`, `MATILU`, MATCHOLESKY`, `MATICC`

141:    Level: intermediate

143:    Notes:
144:       This DOES NOT actually reorder the matrix; it merely returns two index sets
145:    that define a reordering. This is usually not used directly, rather use the
146:    options `PCFactorSetMatOrderingType()`

148:    The user can define additional orderings; see `MatOrderingRegister()`.

150:    These are generally only implemented for sequential sparse matrices.

152:    Some external packages that PETSc can use for direct factorization such as SuperLU_DIST do not accept orderings provided by
153:    this call.

155:    If `MATORDERINGEXTERNAL` is used then PETSc does not compute an ordering and utilizes one built into the factorization package

157: .seealso: `MatOrderingRegister()`, `PCFactorSetMatOrderingType()`, `MatColoring`, `MatColoringCreate()`, `MatOrderingType`, `Mat`
158: @*/
159: PetscErrorCode MatGetOrdering(Mat mat, MatOrderingType type, IS *rperm, IS *cperm)
160: {
161:   PetscInt mmat, nmat, mis;
162:   PetscErrorCode (*r)(Mat, MatOrderingType, IS *, IS *);
163:   PetscBool flg, ismpiaij;

165:   PetscFunctionBegin;
169:   PetscCheck(mat->assembled, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
170:   PetscCheck(!mat->factortype, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
171:   PetscCheck(type, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Ordering type cannot be null");

173:   PetscCall(PetscStrcmp(type, MATORDERINGEXTERNAL, &flg));
174:   if (flg) {
175:     *rperm = NULL;
176:     *cperm = NULL;
177:     PetscFunctionReturn(PETSC_SUCCESS);
178:   }

180:   /* This code is terrible. MatGetOrdering() multiple dispatch should use matrix and this code should move to impls/aij/mpi. */
181:   PetscCall(PetscObjectTypeCompare((PetscObject)mat, MATMPIAIJ, &ismpiaij));
182:   if (ismpiaij) { /* Reorder using diagonal block */
183:     Mat             Ad, Ao;
184:     const PetscInt *colmap;
185:     IS              lrowperm, lcolperm;
186:     PetscInt        i, rstart, rend, *idx;
187:     const PetscInt *lidx;

189:     PetscCall(MatMPIAIJGetSeqAIJ(mat, &Ad, &Ao, &colmap));
190:     PetscCall(MatGetOrdering(Ad, type, &lrowperm, &lcolperm));
191:     PetscCall(MatGetOwnershipRange(mat, &rstart, &rend));
192:     /* Remap row index set to global space */
193:     PetscCall(ISGetIndices(lrowperm, &lidx));
194:     PetscCall(PetscMalloc1(rend - rstart, &idx));
195:     for (i = 0; i + rstart < rend; i++) idx[i] = rstart + lidx[i];
196:     PetscCall(ISRestoreIndices(lrowperm, &lidx));
197:     PetscCall(ISDestroy(&lrowperm));
198:     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), rend - rstart, idx, PETSC_OWN_POINTER, rperm));
199:     PetscCall(ISSetPermutation(*rperm));
200:     /* Remap column index set to global space */
201:     PetscCall(ISGetIndices(lcolperm, &lidx));
202:     PetscCall(PetscMalloc1(rend - rstart, &idx));
203:     for (i = 0; i + rstart < rend; i++) idx[i] = rstart + lidx[i];
204:     PetscCall(ISRestoreIndices(lcolperm, &lidx));
205:     PetscCall(ISDestroy(&lcolperm));
206:     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), rend - rstart, idx, PETSC_OWN_POINTER, cperm));
207:     PetscCall(ISSetPermutation(*cperm));
208:     PetscFunctionReturn(PETSC_SUCCESS);
209:   }

211:   if (!mat->rmap->N) { /* matrix has zero rows */
212:     PetscCall(ISCreateStride(PETSC_COMM_SELF, 0, 0, 1, cperm));
213:     PetscCall(ISCreateStride(PETSC_COMM_SELF, 0, 0, 1, rperm));
214:     PetscCall(ISSetIdentity(*cperm));
215:     PetscCall(ISSetIdentity(*rperm));
216:     PetscFunctionReturn(PETSC_SUCCESS);
217:   }

219:   PetscCall(MatGetLocalSize(mat, &mmat, &nmat));
220:   PetscCheck(mmat == nmat, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Must be square matrix, rows %" PetscInt_FMT " columns %" PetscInt_FMT, mmat, nmat);

222:   PetscCall(MatOrderingRegisterAll());
223:   PetscCall(PetscFunctionListFind(MatOrderingList, type, &r));
224:   PetscCheck(r, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown or unregistered type: %s", type);

226:   PetscCall(PetscLogEventBegin(MAT_GetOrdering, mat, 0, 0, 0));
227:   PetscCall((*r)(mat, type, rperm, cperm));
228:   PetscCall(ISSetPermutation(*rperm));
229:   PetscCall(ISSetPermutation(*cperm));
230:   /* Adjust for inode (reduced matrix ordering) only if row permutation is smaller the matrix size */
231:   PetscCall(ISGetLocalSize(*rperm, &mis));
232:   if (mmat > mis) PetscCall(MatInodeAdjustForInodes(mat, rperm, cperm));
233:   PetscCall(PetscLogEventEnd(MAT_GetOrdering, mat, 0, 0, 0));

235:   PetscCall(PetscOptionsHasName(((PetscObject)mat)->options, ((PetscObject)mat)->prefix, "-mat_view_ordering", &flg));
236:   if (flg) {
237:     Mat tmat;
238:     PetscCall(MatPermute(mat, *rperm, *cperm, &tmat));
239:     PetscCall(MatViewFromOptions(tmat, (PetscObject)mat, "-mat_view_ordering"));
240:     PetscCall(MatDestroy(&tmat));
241:   }
242:   PetscFunctionReturn(PETSC_SUCCESS);
243: }

245: PetscErrorCode MatGetOrderingList(PetscFunctionList *list)
246: {
247:   PetscFunctionBegin;
248:   *list = MatOrderingList;
249:   PetscFunctionReturn(PETSC_SUCCESS);
250: }