xref: /petsc/src/mat/graphops/order/sorder.c (revision 1ed6e3ff8437baa411029a28c2b08f047df9ad9a)
18be712e4SBarry Smith /*
28be712e4SBarry Smith      Provides the code that allows PETSc users to register their own
38be712e4SBarry Smith   sequential matrix Ordering routines.
48be712e4SBarry Smith */
58be712e4SBarry Smith #include <petsc/private/matimpl.h>
68be712e4SBarry Smith #include <petscmat.h> /*I "petscmat.h" I*/
78be712e4SBarry Smith 
88be712e4SBarry Smith PetscFunctionList MatOrderingList              = NULL;
98be712e4SBarry Smith PetscBool         MatOrderingRegisterAllCalled = PETSC_FALSE;
108be712e4SBarry Smith 
MatGetOrdering_Natural(Mat mat,MatOrderingType type,IS * irow,IS * icol)118be712e4SBarry Smith PETSC_INTERN PetscErrorCode MatGetOrdering_Natural(Mat mat, MatOrderingType type, IS *irow, IS *icol)
128be712e4SBarry Smith {
138be712e4SBarry Smith   PetscInt  n, i, *ii;
148be712e4SBarry Smith   PetscBool done;
158be712e4SBarry Smith   MPI_Comm  comm;
168be712e4SBarry Smith 
178be712e4SBarry Smith   PetscFunctionBegin;
188be712e4SBarry Smith   PetscCall(PetscObjectGetComm((PetscObject)mat, &comm));
198be712e4SBarry Smith   PetscCall(MatGetRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, &n, NULL, NULL, &done));
208be712e4SBarry Smith   PetscCall(MatRestoreRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, NULL, NULL, NULL, &done));
218be712e4SBarry Smith   if (done) { /* matrix may be "compressed" in symbolic factorization, due to i-nodes or block storage */
228be712e4SBarry Smith     /*
238be712e4SBarry Smith       We actually create general index sets because this avoids mallocs to
248be712e4SBarry Smith       to obtain the indices in the MatSolve() routines.
258be712e4SBarry Smith       PetscCall(ISCreateStride(PETSC_COMM_SELF,n,0,1,irow));
268be712e4SBarry Smith       PetscCall(ISCreateStride(PETSC_COMM_SELF,n,0,1,icol));
278be712e4SBarry Smith     */
288be712e4SBarry Smith     PetscCall(PetscMalloc1(n, &ii));
298be712e4SBarry Smith     for (i = 0; i < n; i++) ii[i] = i;
308be712e4SBarry Smith     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, ii, PETSC_COPY_VALUES, irow));
318be712e4SBarry Smith     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, ii, PETSC_OWN_POINTER, icol));
328be712e4SBarry Smith   } else {
338be712e4SBarry Smith     PetscInt start, end;
348be712e4SBarry Smith 
358be712e4SBarry Smith     PetscCall(MatGetOwnershipRange(mat, &start, &end));
368be712e4SBarry Smith     PetscCall(ISCreateStride(comm, end - start, start, 1, irow));
378be712e4SBarry Smith     PetscCall(ISCreateStride(comm, end - start, start, 1, icol));
388be712e4SBarry Smith   }
398be712e4SBarry Smith   PetscCall(ISSetIdentity(*irow));
408be712e4SBarry Smith   PetscCall(ISSetIdentity(*icol));
418be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
428be712e4SBarry Smith }
438be712e4SBarry Smith 
448be712e4SBarry Smith /*
458be712e4SBarry Smith      Orders the rows (and columns) by the lengths of the rows.
468be712e4SBarry Smith    This produces a symmetric Ordering but does not require a
478be712e4SBarry Smith    matrix with symmetric non-zero structure.
488be712e4SBarry Smith */
MatGetOrdering_RowLength(Mat mat,MatOrderingType type,IS * irow,IS * icol)498be712e4SBarry Smith PETSC_INTERN PetscErrorCode MatGetOrdering_RowLength(Mat mat, MatOrderingType type, IS *irow, IS *icol)
508be712e4SBarry Smith {
518be712e4SBarry Smith   PetscInt        n, *permr, *lens, i;
528be712e4SBarry Smith   const PetscInt *ia, *ja;
538be712e4SBarry Smith   PetscBool       done;
548be712e4SBarry Smith 
558be712e4SBarry Smith   PetscFunctionBegin;
568be712e4SBarry Smith   PetscCall(MatGetRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, &n, &ia, &ja, &done));
578be712e4SBarry Smith   PetscCheck(done, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Cannot get rows for matrix");
588be712e4SBarry Smith 
598be712e4SBarry Smith   PetscCall(PetscMalloc2(n, &lens, n, &permr));
608be712e4SBarry Smith   for (i = 0; i < n; i++) {
618be712e4SBarry Smith     lens[i]  = ia[i + 1] - ia[i];
628be712e4SBarry Smith     permr[i] = i;
638be712e4SBarry Smith   }
648be712e4SBarry Smith   PetscCall(MatRestoreRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, NULL, &ia, &ja, &done));
658be712e4SBarry Smith 
668be712e4SBarry Smith   PetscCall(PetscSortIntWithPermutation(n, lens, permr));
678be712e4SBarry Smith 
688be712e4SBarry Smith   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, permr, PETSC_COPY_VALUES, irow));
698be712e4SBarry Smith   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, permr, PETSC_COPY_VALUES, icol));
708be712e4SBarry Smith   PetscCall(PetscFree2(lens, permr));
718be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
728be712e4SBarry Smith }
738be712e4SBarry Smith 
748be712e4SBarry Smith /*@C
758be712e4SBarry Smith   MatOrderingRegister - Adds a new sparse matrix ordering to the matrix package.
768be712e4SBarry Smith 
77*cc4c1da9SBarry Smith   Not Collective, No Fortran Support
788be712e4SBarry Smith 
798be712e4SBarry Smith   Input Parameters:
808be712e4SBarry Smith + sname    - name of ordering (for example `MATORDERINGND`)
818be712e4SBarry Smith - function - function pointer that creates the ordering
828be712e4SBarry Smith 
838be712e4SBarry Smith   Level: developer
848be712e4SBarry Smith 
858be712e4SBarry Smith   Example Usage:
868be712e4SBarry Smith .vb
878be712e4SBarry Smith    MatOrderingRegister("my_order", MyOrder);
888be712e4SBarry Smith .ve
898be712e4SBarry Smith 
90a3b724e8SBarry Smith   Then, your partitioner can be chosen with the procedural interface via `MatOrderingSetType(part, "my_order)` or at runtime via the option
91a3b724e8SBarry Smith   `-pc_factor_mat_ordering_type my_order`
928be712e4SBarry Smith 
93a3b724e8SBarry Smith .seealso: `Mat`, `MatOrderingType`, `MatOrderingRegisterAll()`, `MatGetOrdering()`
948be712e4SBarry Smith @*/
MatOrderingRegister(const char sname[],PetscErrorCode (* function)(Mat,MatOrderingType,IS *,IS *))958be712e4SBarry Smith PetscErrorCode MatOrderingRegister(const char sname[], PetscErrorCode (*function)(Mat, MatOrderingType, IS *, IS *))
968be712e4SBarry Smith {
978be712e4SBarry Smith   PetscFunctionBegin;
988be712e4SBarry Smith   PetscCall(MatInitializePackage());
998be712e4SBarry Smith   PetscCall(PetscFunctionListAdd(&MatOrderingList, sname, function));
1008be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
1018be712e4SBarry Smith }
1028be712e4SBarry Smith 
1038be712e4SBarry Smith #include <../src/mat/impls/aij/mpi/mpiaij.h>
104*cc4c1da9SBarry Smith /*@
1058be712e4SBarry Smith   MatGetOrdering - Gets a reordering for a matrix to reduce fill or to
1068be712e4SBarry Smith   improve numerical stability of LU factorization.
1078be712e4SBarry Smith 
1088be712e4SBarry Smith   Collective
1098be712e4SBarry Smith 
1108be712e4SBarry Smith   Input Parameters:
1118be712e4SBarry Smith + mat  - the matrix
1128be712e4SBarry Smith - type - type of reordering, one of the following
1138be712e4SBarry Smith .vb
1148be712e4SBarry Smith       MATORDERINGNATURAL_OR_ND - Nested dissection unless matrix is SBAIJ then it is natural
1158be712e4SBarry Smith       MATORDERINGNATURAL - Natural
1168be712e4SBarry Smith       MATORDERINGND - Nested Dissection
1178be712e4SBarry Smith       MATORDERING1WD - One-way Dissection
1188be712e4SBarry Smith       MATORDERINGRCM - Reverse Cuthill-McKee
1198be712e4SBarry Smith       MATORDERINGQMD - Quotient Minimum Degree
1208be712e4SBarry Smith       MATORDERINGEXTERNAL - Use an ordering internal to the factorzation package and do not compute or use PETSc's
1218be712e4SBarry Smith .ve
1228be712e4SBarry Smith 
1238be712e4SBarry Smith   Output Parameters:
1248be712e4SBarry Smith + rperm - row permutation indices
1258be712e4SBarry Smith - cperm - column permutation indices
1268be712e4SBarry Smith 
1278be712e4SBarry Smith   Options Database Key:
1288be712e4SBarry Smith + -mat_view_ordering draw                      - plots matrix nonzero structure in new ordering
129bc50768fSJose E. Roman - -pc_factor_mat_ordering_type <nd,natural,..> - ordering to use with `PC`s based on factorization, `PCLU`, `PCILU`, `PCCHOLESKY`, `PCICC`
1308be712e4SBarry Smith 
1318be712e4SBarry Smith   Level: intermediate
1328be712e4SBarry Smith 
1338be712e4SBarry Smith   Notes:
1348be712e4SBarry Smith   This DOES NOT actually reorder the matrix; it merely returns two index sets
1358be712e4SBarry Smith   that define a reordering. This is usually not used directly, rather use the
1368be712e4SBarry Smith   options `PCFactorSetMatOrderingType()`
1378be712e4SBarry Smith 
1388be712e4SBarry Smith   The user can define additional orderings; see `MatOrderingRegister()`.
1398be712e4SBarry Smith 
1408be712e4SBarry Smith   These are generally only implemented for sequential sparse matrices.
1418be712e4SBarry Smith 
1428be712e4SBarry Smith   Some external packages that PETSc can use for direct factorization such as SuperLU_DIST do not accept orderings provided by
1438be712e4SBarry Smith   this call.
1448be712e4SBarry Smith 
1458be712e4SBarry Smith   If `MATORDERINGEXTERNAL` is used then PETSc does not compute an ordering and utilizes one built into the factorization package
1468be712e4SBarry Smith 
1478be712e4SBarry Smith .seealso: `MatOrderingRegister()`, `PCFactorSetMatOrderingType()`, `MatColoring`, `MatColoringCreate()`, `MatOrderingType`, `Mat`
1488be712e4SBarry Smith @*/
MatGetOrdering(Mat mat,MatOrderingType type,IS * rperm,IS * cperm)1498be712e4SBarry Smith PetscErrorCode MatGetOrdering(Mat mat, MatOrderingType type, IS *rperm, IS *cperm)
1508be712e4SBarry Smith {
1518be712e4SBarry Smith   PetscInt mmat, nmat, mis;
1528be712e4SBarry Smith   PetscErrorCode (*r)(Mat, MatOrderingType, IS *, IS *);
1538be712e4SBarry Smith   PetscBool flg, ismpiaij;
1548be712e4SBarry Smith 
1558be712e4SBarry Smith   PetscFunctionBegin;
1568be712e4SBarry Smith   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1578be712e4SBarry Smith   PetscAssertPointer(rperm, 3);
1588be712e4SBarry Smith   PetscAssertPointer(cperm, 4);
1598be712e4SBarry Smith   PetscCheck(mat->assembled, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1608be712e4SBarry Smith   PetscCheck(!mat->factortype, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1618be712e4SBarry Smith   PetscCheck(type, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Ordering type cannot be null");
1628be712e4SBarry Smith 
1638be712e4SBarry Smith   PetscCall(PetscStrcmp(type, MATORDERINGEXTERNAL, &flg));
1648be712e4SBarry Smith   if (flg) {
1658be712e4SBarry Smith     *rperm = NULL;
1668be712e4SBarry Smith     *cperm = NULL;
1678be712e4SBarry Smith     PetscFunctionReturn(PETSC_SUCCESS);
1688be712e4SBarry Smith   }
1698be712e4SBarry Smith 
1708be712e4SBarry Smith   /* This code is terrible. MatGetOrdering() multiple dispatch should use matrix and this code should move to impls/aij/mpi. */
1718be712e4SBarry Smith   PetscCall(PetscObjectTypeCompare((PetscObject)mat, MATMPIAIJ, &ismpiaij));
1728be712e4SBarry Smith   if (ismpiaij) { /* Reorder using diagonal block */
1738be712e4SBarry Smith     Mat             Ad, Ao;
1748be712e4SBarry Smith     const PetscInt *colmap;
1758be712e4SBarry Smith     IS              lrowperm, lcolperm;
1768be712e4SBarry Smith     PetscInt        i, rstart, rend, *idx;
1778be712e4SBarry Smith     const PetscInt *lidx;
1788be712e4SBarry Smith 
1798be712e4SBarry Smith     PetscCall(MatMPIAIJGetSeqAIJ(mat, &Ad, &Ao, &colmap));
1808be712e4SBarry Smith     PetscCall(MatGetOrdering(Ad, type, &lrowperm, &lcolperm));
1818be712e4SBarry Smith     PetscCall(MatGetOwnershipRange(mat, &rstart, &rend));
1828be712e4SBarry Smith     /* Remap row index set to global space */
1838be712e4SBarry Smith     PetscCall(ISGetIndices(lrowperm, &lidx));
1848be712e4SBarry Smith     PetscCall(PetscMalloc1(rend - rstart, &idx));
1858be712e4SBarry Smith     for (i = 0; i + rstart < rend; i++) idx[i] = rstart + lidx[i];
1868be712e4SBarry Smith     PetscCall(ISRestoreIndices(lrowperm, &lidx));
1878be712e4SBarry Smith     PetscCall(ISDestroy(&lrowperm));
1888be712e4SBarry Smith     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), rend - rstart, idx, PETSC_OWN_POINTER, rperm));
1898be712e4SBarry Smith     PetscCall(ISSetPermutation(*rperm));
1908be712e4SBarry Smith     /* Remap column index set to global space */
1918be712e4SBarry Smith     PetscCall(ISGetIndices(lcolperm, &lidx));
1928be712e4SBarry Smith     PetscCall(PetscMalloc1(rend - rstart, &idx));
1938be712e4SBarry Smith     for (i = 0; i + rstart < rend; i++) idx[i] = rstart + lidx[i];
1948be712e4SBarry Smith     PetscCall(ISRestoreIndices(lcolperm, &lidx));
1958be712e4SBarry Smith     PetscCall(ISDestroy(&lcolperm));
1968be712e4SBarry Smith     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), rend - rstart, idx, PETSC_OWN_POINTER, cperm));
1978be712e4SBarry Smith     PetscCall(ISSetPermutation(*cperm));
1988be712e4SBarry Smith     PetscFunctionReturn(PETSC_SUCCESS);
1998be712e4SBarry Smith   }
2008be712e4SBarry Smith 
2018be712e4SBarry Smith   if (!mat->rmap->N) { /* matrix has zero rows */
2028be712e4SBarry Smith     PetscCall(ISCreateStride(PETSC_COMM_SELF, 0, 0, 1, cperm));
2038be712e4SBarry Smith     PetscCall(ISCreateStride(PETSC_COMM_SELF, 0, 0, 1, rperm));
2048be712e4SBarry Smith     PetscCall(ISSetIdentity(*cperm));
2058be712e4SBarry Smith     PetscCall(ISSetIdentity(*rperm));
2068be712e4SBarry Smith     PetscFunctionReturn(PETSC_SUCCESS);
2078be712e4SBarry Smith   }
2088be712e4SBarry Smith 
2098be712e4SBarry Smith   PetscCall(MatGetLocalSize(mat, &mmat, &nmat));
2108be712e4SBarry Smith   PetscCheck(mmat == nmat, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Must be square matrix, rows %" PetscInt_FMT " columns %" PetscInt_FMT, mmat, nmat);
2118be712e4SBarry Smith 
2128be712e4SBarry Smith   PetscCall(MatOrderingRegisterAll());
2138be712e4SBarry Smith   PetscCall(PetscFunctionListFind(MatOrderingList, type, &r));
2148be712e4SBarry Smith   PetscCheck(r, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown or unregistered type: %s", type);
2158be712e4SBarry Smith 
2168be712e4SBarry Smith   PetscCall(PetscLogEventBegin(MAT_GetOrdering, mat, 0, 0, 0));
2178be712e4SBarry Smith   PetscCall((*r)(mat, type, rperm, cperm));
2188be712e4SBarry Smith   PetscCall(ISSetPermutation(*rperm));
2198be712e4SBarry Smith   PetscCall(ISSetPermutation(*cperm));
2208be712e4SBarry Smith   /* Adjust for inode (reduced matrix ordering) only if row permutation is smaller the matrix size */
2218be712e4SBarry Smith   PetscCall(ISGetLocalSize(*rperm, &mis));
2228be712e4SBarry Smith   if (mmat > mis) PetscCall(MatInodeAdjustForInodes(mat, rperm, cperm));
2238be712e4SBarry Smith   PetscCall(PetscLogEventEnd(MAT_GetOrdering, mat, 0, 0, 0));
2248be712e4SBarry Smith 
2258be712e4SBarry Smith   PetscCall(PetscOptionsHasName(((PetscObject)mat)->options, ((PetscObject)mat)->prefix, "-mat_view_ordering", &flg));
2268be712e4SBarry Smith   if (flg) {
2278be712e4SBarry Smith     Mat tmat;
2288be712e4SBarry Smith     PetscCall(MatPermute(mat, *rperm, *cperm, &tmat));
2298be712e4SBarry Smith     PetscCall(MatViewFromOptions(tmat, (PetscObject)mat, "-mat_view_ordering"));
2308be712e4SBarry Smith     PetscCall(MatDestroy(&tmat));
2318be712e4SBarry Smith   }
2328be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
2338be712e4SBarry Smith }
2348be712e4SBarry Smith 
MatGetOrderingList(PetscFunctionList * list)2358be712e4SBarry Smith PetscErrorCode MatGetOrderingList(PetscFunctionList *list)
2368be712e4SBarry Smith {
2378be712e4SBarry Smith   PetscFunctionBegin;
2388be712e4SBarry Smith   *list = MatOrderingList;
2398be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
2408be712e4SBarry Smith }
241