xref: /petsc/src/mat/graphops/order/sorder.c (revision 8be712e46db5d855f641c6bd97b4543e0efe65bd)
1*8be712e4SBarry Smith /*
2*8be712e4SBarry Smith      Provides the code that allows PETSc users to register their own
3*8be712e4SBarry Smith   sequential matrix Ordering routines.
4*8be712e4SBarry Smith */
5*8be712e4SBarry Smith #include <petsc/private/matimpl.h>
6*8be712e4SBarry Smith #include <petscmat.h> /*I "petscmat.h" I*/
7*8be712e4SBarry Smith 
8*8be712e4SBarry Smith PetscFunctionList MatOrderingList              = NULL;
9*8be712e4SBarry Smith PetscBool         MatOrderingRegisterAllCalled = PETSC_FALSE;
10*8be712e4SBarry Smith 
11*8be712e4SBarry Smith PETSC_INTERN PetscErrorCode MatGetOrdering_Natural(Mat mat, MatOrderingType type, IS *irow, IS *icol)
12*8be712e4SBarry Smith {
13*8be712e4SBarry Smith   PetscInt  n, i, *ii;
14*8be712e4SBarry Smith   PetscBool done;
15*8be712e4SBarry Smith   MPI_Comm  comm;
16*8be712e4SBarry Smith 
17*8be712e4SBarry Smith   PetscFunctionBegin;
18*8be712e4SBarry Smith   PetscCall(PetscObjectGetComm((PetscObject)mat, &comm));
19*8be712e4SBarry Smith   PetscCall(MatGetRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, &n, NULL, NULL, &done));
20*8be712e4SBarry Smith   PetscCall(MatRestoreRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, NULL, NULL, NULL, &done));
21*8be712e4SBarry Smith   if (done) { /* matrix may be "compressed" in symbolic factorization, due to i-nodes or block storage */
22*8be712e4SBarry Smith     /*
23*8be712e4SBarry Smith       We actually create general index sets because this avoids mallocs to
24*8be712e4SBarry Smith       to obtain the indices in the MatSolve() routines.
25*8be712e4SBarry Smith       PetscCall(ISCreateStride(PETSC_COMM_SELF,n,0,1,irow));
26*8be712e4SBarry Smith       PetscCall(ISCreateStride(PETSC_COMM_SELF,n,0,1,icol));
27*8be712e4SBarry Smith     */
28*8be712e4SBarry Smith     PetscCall(PetscMalloc1(n, &ii));
29*8be712e4SBarry Smith     for (i = 0; i < n; i++) ii[i] = i;
30*8be712e4SBarry Smith     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, ii, PETSC_COPY_VALUES, irow));
31*8be712e4SBarry Smith     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, ii, PETSC_OWN_POINTER, icol));
32*8be712e4SBarry Smith   } else {
33*8be712e4SBarry Smith     PetscInt start, end;
34*8be712e4SBarry Smith 
35*8be712e4SBarry Smith     PetscCall(MatGetOwnershipRange(mat, &start, &end));
36*8be712e4SBarry Smith     PetscCall(ISCreateStride(comm, end - start, start, 1, irow));
37*8be712e4SBarry Smith     PetscCall(ISCreateStride(comm, end - start, start, 1, icol));
38*8be712e4SBarry Smith   }
39*8be712e4SBarry Smith   PetscCall(ISSetIdentity(*irow));
40*8be712e4SBarry Smith   PetscCall(ISSetIdentity(*icol));
41*8be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
42*8be712e4SBarry Smith }
43*8be712e4SBarry Smith 
44*8be712e4SBarry Smith /*
45*8be712e4SBarry Smith      Orders the rows (and columns) by the lengths of the rows.
46*8be712e4SBarry Smith    This produces a symmetric Ordering but does not require a
47*8be712e4SBarry Smith    matrix with symmetric non-zero structure.
48*8be712e4SBarry Smith */
49*8be712e4SBarry Smith PETSC_INTERN PetscErrorCode MatGetOrdering_RowLength(Mat mat, MatOrderingType type, IS *irow, IS *icol)
50*8be712e4SBarry Smith {
51*8be712e4SBarry Smith   PetscInt        n, *permr, *lens, i;
52*8be712e4SBarry Smith   const PetscInt *ia, *ja;
53*8be712e4SBarry Smith   PetscBool       done;
54*8be712e4SBarry Smith 
55*8be712e4SBarry Smith   PetscFunctionBegin;
56*8be712e4SBarry Smith   PetscCall(MatGetRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, &n, &ia, &ja, &done));
57*8be712e4SBarry Smith   PetscCheck(done, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Cannot get rows for matrix");
58*8be712e4SBarry Smith 
59*8be712e4SBarry Smith   PetscCall(PetscMalloc2(n, &lens, n, &permr));
60*8be712e4SBarry Smith   for (i = 0; i < n; i++) {
61*8be712e4SBarry Smith     lens[i]  = ia[i + 1] - ia[i];
62*8be712e4SBarry Smith     permr[i] = i;
63*8be712e4SBarry Smith   }
64*8be712e4SBarry Smith   PetscCall(MatRestoreRowIJ(mat, 0, PETSC_FALSE, PETSC_TRUE, NULL, &ia, &ja, &done));
65*8be712e4SBarry Smith 
66*8be712e4SBarry Smith   PetscCall(PetscSortIntWithPermutation(n, lens, permr));
67*8be712e4SBarry Smith 
68*8be712e4SBarry Smith   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, permr, PETSC_COPY_VALUES, irow));
69*8be712e4SBarry Smith   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, n, permr, PETSC_COPY_VALUES, icol));
70*8be712e4SBarry Smith   PetscCall(PetscFree2(lens, permr));
71*8be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
72*8be712e4SBarry Smith }
73*8be712e4SBarry Smith 
74*8be712e4SBarry Smith /*@C
75*8be712e4SBarry Smith   MatOrderingRegister - Adds a new sparse matrix ordering to the matrix package.
76*8be712e4SBarry Smith 
77*8be712e4SBarry Smith   Not Collective
78*8be712e4SBarry Smith 
79*8be712e4SBarry Smith   Input Parameters:
80*8be712e4SBarry Smith + sname    - name of ordering (for example `MATORDERINGND`)
81*8be712e4SBarry Smith - function - function pointer that creates the ordering
82*8be712e4SBarry Smith 
83*8be712e4SBarry Smith   Level: developer
84*8be712e4SBarry Smith 
85*8be712e4SBarry Smith   Example Usage:
86*8be712e4SBarry Smith .vb
87*8be712e4SBarry Smith    MatOrderingRegister("my_order", MyOrder);
88*8be712e4SBarry Smith .ve
89*8be712e4SBarry Smith 
90*8be712e4SBarry Smith   Then, your partitioner can be chosen with the procedural interface via
91*8be712e4SBarry Smith $     MatOrderingSetType(part, "my_order)
92*8be712e4SBarry Smith   or at runtime via the option
93*8be712e4SBarry Smith $     -pc_factor_mat_ordering_type my_order
94*8be712e4SBarry Smith 
95*8be712e4SBarry Smith .seealso: `MatOrderingRegisterAll()`, `MatGetOrdering()`
96*8be712e4SBarry Smith @*/
97*8be712e4SBarry Smith PetscErrorCode MatOrderingRegister(const char sname[], PetscErrorCode (*function)(Mat, MatOrderingType, IS *, IS *))
98*8be712e4SBarry Smith {
99*8be712e4SBarry Smith   PetscFunctionBegin;
100*8be712e4SBarry Smith   PetscCall(MatInitializePackage());
101*8be712e4SBarry Smith   PetscCall(PetscFunctionListAdd(&MatOrderingList, sname, function));
102*8be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
103*8be712e4SBarry Smith }
104*8be712e4SBarry Smith 
105*8be712e4SBarry Smith #include <../src/mat/impls/aij/mpi/mpiaij.h>
106*8be712e4SBarry Smith /*@C
107*8be712e4SBarry Smith   MatGetOrdering - Gets a reordering for a matrix to reduce fill or to
108*8be712e4SBarry Smith   improve numerical stability of LU factorization.
109*8be712e4SBarry Smith 
110*8be712e4SBarry Smith   Collective
111*8be712e4SBarry Smith 
112*8be712e4SBarry Smith   Input Parameters:
113*8be712e4SBarry Smith + mat  - the matrix
114*8be712e4SBarry Smith - type - type of reordering, one of the following
115*8be712e4SBarry Smith .vb
116*8be712e4SBarry Smith       MATORDERINGNATURAL_OR_ND - Nested dissection unless matrix is SBAIJ then it is natural
117*8be712e4SBarry Smith       MATORDERINGNATURAL - Natural
118*8be712e4SBarry Smith       MATORDERINGND - Nested Dissection
119*8be712e4SBarry Smith       MATORDERING1WD - One-way Dissection
120*8be712e4SBarry Smith       MATORDERINGRCM - Reverse Cuthill-McKee
121*8be712e4SBarry Smith       MATORDERINGQMD - Quotient Minimum Degree
122*8be712e4SBarry Smith       MATORDERINGEXTERNAL - Use an ordering internal to the factorzation package and do not compute or use PETSc's
123*8be712e4SBarry Smith .ve
124*8be712e4SBarry Smith 
125*8be712e4SBarry Smith   Output Parameters:
126*8be712e4SBarry Smith + rperm - row permutation indices
127*8be712e4SBarry Smith - cperm - column permutation indices
128*8be712e4SBarry Smith 
129*8be712e4SBarry Smith   Options Database Key:
130*8be712e4SBarry Smith + -mat_view_ordering draw                      - plots matrix nonzero structure in new ordering
131*8be712e4SBarry Smith - -pc_factor_mat_ordering_type <nd,natural,..> - ordering to use with `PC`s based on factorization, `MATLU`, `MATILU`, MATCHOLESKY`, `MATICC`
132*8be712e4SBarry Smith 
133*8be712e4SBarry Smith   Level: intermediate
134*8be712e4SBarry Smith 
135*8be712e4SBarry Smith   Notes:
136*8be712e4SBarry Smith   This DOES NOT actually reorder the matrix; it merely returns two index sets
137*8be712e4SBarry Smith   that define a reordering. This is usually not used directly, rather use the
138*8be712e4SBarry Smith   options `PCFactorSetMatOrderingType()`
139*8be712e4SBarry Smith 
140*8be712e4SBarry Smith   The user can define additional orderings; see `MatOrderingRegister()`.
141*8be712e4SBarry Smith 
142*8be712e4SBarry Smith   These are generally only implemented for sequential sparse matrices.
143*8be712e4SBarry Smith 
144*8be712e4SBarry Smith   Some external packages that PETSc can use for direct factorization such as SuperLU_DIST do not accept orderings provided by
145*8be712e4SBarry Smith   this call.
146*8be712e4SBarry Smith 
147*8be712e4SBarry Smith   If `MATORDERINGEXTERNAL` is used then PETSc does not compute an ordering and utilizes one built into the factorization package
148*8be712e4SBarry Smith 
149*8be712e4SBarry Smith .seealso: `MatOrderingRegister()`, `PCFactorSetMatOrderingType()`, `MatColoring`, `MatColoringCreate()`, `MatOrderingType`, `Mat`
150*8be712e4SBarry Smith @*/
151*8be712e4SBarry Smith PetscErrorCode MatGetOrdering(Mat mat, MatOrderingType type, IS *rperm, IS *cperm)
152*8be712e4SBarry Smith {
153*8be712e4SBarry Smith   PetscInt mmat, nmat, mis;
154*8be712e4SBarry Smith   PetscErrorCode (*r)(Mat, MatOrderingType, IS *, IS *);
155*8be712e4SBarry Smith   PetscBool flg, ismpiaij;
156*8be712e4SBarry Smith 
157*8be712e4SBarry Smith   PetscFunctionBegin;
158*8be712e4SBarry Smith   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
159*8be712e4SBarry Smith   PetscAssertPointer(rperm, 3);
160*8be712e4SBarry Smith   PetscAssertPointer(cperm, 4);
161*8be712e4SBarry Smith   PetscCheck(mat->assembled, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
162*8be712e4SBarry Smith   PetscCheck(!mat->factortype, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
163*8be712e4SBarry Smith   PetscCheck(type, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Ordering type cannot be null");
164*8be712e4SBarry Smith 
165*8be712e4SBarry Smith   PetscCall(PetscStrcmp(type, MATORDERINGEXTERNAL, &flg));
166*8be712e4SBarry Smith   if (flg) {
167*8be712e4SBarry Smith     *rperm = NULL;
168*8be712e4SBarry Smith     *cperm = NULL;
169*8be712e4SBarry Smith     PetscFunctionReturn(PETSC_SUCCESS);
170*8be712e4SBarry Smith   }
171*8be712e4SBarry Smith 
172*8be712e4SBarry Smith   /* This code is terrible. MatGetOrdering() multiple dispatch should use matrix and this code should move to impls/aij/mpi. */
173*8be712e4SBarry Smith   PetscCall(PetscObjectTypeCompare((PetscObject)mat, MATMPIAIJ, &ismpiaij));
174*8be712e4SBarry Smith   if (ismpiaij) { /* Reorder using diagonal block */
175*8be712e4SBarry Smith     Mat             Ad, Ao;
176*8be712e4SBarry Smith     const PetscInt *colmap;
177*8be712e4SBarry Smith     IS              lrowperm, lcolperm;
178*8be712e4SBarry Smith     PetscInt        i, rstart, rend, *idx;
179*8be712e4SBarry Smith     const PetscInt *lidx;
180*8be712e4SBarry Smith 
181*8be712e4SBarry Smith     PetscCall(MatMPIAIJGetSeqAIJ(mat, &Ad, &Ao, &colmap));
182*8be712e4SBarry Smith     PetscCall(MatGetOrdering(Ad, type, &lrowperm, &lcolperm));
183*8be712e4SBarry Smith     PetscCall(MatGetOwnershipRange(mat, &rstart, &rend));
184*8be712e4SBarry Smith     /* Remap row index set to global space */
185*8be712e4SBarry Smith     PetscCall(ISGetIndices(lrowperm, &lidx));
186*8be712e4SBarry Smith     PetscCall(PetscMalloc1(rend - rstart, &idx));
187*8be712e4SBarry Smith     for (i = 0; i + rstart < rend; i++) idx[i] = rstart + lidx[i];
188*8be712e4SBarry Smith     PetscCall(ISRestoreIndices(lrowperm, &lidx));
189*8be712e4SBarry Smith     PetscCall(ISDestroy(&lrowperm));
190*8be712e4SBarry Smith     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), rend - rstart, idx, PETSC_OWN_POINTER, rperm));
191*8be712e4SBarry Smith     PetscCall(ISSetPermutation(*rperm));
192*8be712e4SBarry Smith     /* Remap column index set to global space */
193*8be712e4SBarry Smith     PetscCall(ISGetIndices(lcolperm, &lidx));
194*8be712e4SBarry Smith     PetscCall(PetscMalloc1(rend - rstart, &idx));
195*8be712e4SBarry Smith     for (i = 0; i + rstart < rend; i++) idx[i] = rstart + lidx[i];
196*8be712e4SBarry Smith     PetscCall(ISRestoreIndices(lcolperm, &lidx));
197*8be712e4SBarry Smith     PetscCall(ISDestroy(&lcolperm));
198*8be712e4SBarry Smith     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), rend - rstart, idx, PETSC_OWN_POINTER, cperm));
199*8be712e4SBarry Smith     PetscCall(ISSetPermutation(*cperm));
200*8be712e4SBarry Smith     PetscFunctionReturn(PETSC_SUCCESS);
201*8be712e4SBarry Smith   }
202*8be712e4SBarry Smith 
203*8be712e4SBarry Smith   if (!mat->rmap->N) { /* matrix has zero rows */
204*8be712e4SBarry Smith     PetscCall(ISCreateStride(PETSC_COMM_SELF, 0, 0, 1, cperm));
205*8be712e4SBarry Smith     PetscCall(ISCreateStride(PETSC_COMM_SELF, 0, 0, 1, rperm));
206*8be712e4SBarry Smith     PetscCall(ISSetIdentity(*cperm));
207*8be712e4SBarry Smith     PetscCall(ISSetIdentity(*rperm));
208*8be712e4SBarry Smith     PetscFunctionReturn(PETSC_SUCCESS);
209*8be712e4SBarry Smith   }
210*8be712e4SBarry Smith 
211*8be712e4SBarry Smith   PetscCall(MatGetLocalSize(mat, &mmat, &nmat));
212*8be712e4SBarry Smith   PetscCheck(mmat == nmat, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Must be square matrix, rows %" PetscInt_FMT " columns %" PetscInt_FMT, mmat, nmat);
213*8be712e4SBarry Smith 
214*8be712e4SBarry Smith   PetscCall(MatOrderingRegisterAll());
215*8be712e4SBarry Smith   PetscCall(PetscFunctionListFind(MatOrderingList, type, &r));
216*8be712e4SBarry Smith   PetscCheck(r, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown or unregistered type: %s", type);
217*8be712e4SBarry Smith 
218*8be712e4SBarry Smith   PetscCall(PetscLogEventBegin(MAT_GetOrdering, mat, 0, 0, 0));
219*8be712e4SBarry Smith   PetscCall((*r)(mat, type, rperm, cperm));
220*8be712e4SBarry Smith   PetscCall(ISSetPermutation(*rperm));
221*8be712e4SBarry Smith   PetscCall(ISSetPermutation(*cperm));
222*8be712e4SBarry Smith   /* Adjust for inode (reduced matrix ordering) only if row permutation is smaller the matrix size */
223*8be712e4SBarry Smith   PetscCall(ISGetLocalSize(*rperm, &mis));
224*8be712e4SBarry Smith   if (mmat > mis) PetscCall(MatInodeAdjustForInodes(mat, rperm, cperm));
225*8be712e4SBarry Smith   PetscCall(PetscLogEventEnd(MAT_GetOrdering, mat, 0, 0, 0));
226*8be712e4SBarry Smith 
227*8be712e4SBarry Smith   PetscCall(PetscOptionsHasName(((PetscObject)mat)->options, ((PetscObject)mat)->prefix, "-mat_view_ordering", &flg));
228*8be712e4SBarry Smith   if (flg) {
229*8be712e4SBarry Smith     Mat tmat;
230*8be712e4SBarry Smith     PetscCall(MatPermute(mat, *rperm, *cperm, &tmat));
231*8be712e4SBarry Smith     PetscCall(MatViewFromOptions(tmat, (PetscObject)mat, "-mat_view_ordering"));
232*8be712e4SBarry Smith     PetscCall(MatDestroy(&tmat));
233*8be712e4SBarry Smith   }
234*8be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
235*8be712e4SBarry Smith }
236*8be712e4SBarry Smith 
237*8be712e4SBarry Smith PetscErrorCode MatGetOrderingList(PetscFunctionList *list)
238*8be712e4SBarry Smith {
239*8be712e4SBarry Smith   PetscFunctionBegin;
240*8be712e4SBarry Smith   *list = MatOrderingList;
241*8be712e4SBarry Smith   PetscFunctionReturn(PETSC_SUCCESS);
242*8be712e4SBarry Smith }
243