xref: /petsc/src/mat/utils/axpy.c (revision 607e733f3db3ee7f6f605a13295c517df8dbb9c9)
1 #include <petsc/private/matimpl.h> /*I   "petscmat.h"  I*/
2 
3 static PetscErrorCode MatTransposeAXPY_Private(Mat Y, PetscScalar a, Mat X, MatStructure str, Mat T)
4 {
5   Mat         A, F;
6   PetscScalar vshift, vscale;
7   PetscErrorCode (*f)(Mat, Mat *);
8 
9   PetscFunctionBegin;
10   if (T == X) PetscCall(MatShellGetScalingShifts(T, &vshift, &vscale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
11   else {
12     vshift = 0.0;
13     vscale = 1.0;
14   }
15   PetscCall(PetscObjectQueryFunction((PetscObject)T, "MatTransposeGetMat_C", &f));
16   if (f) {
17     PetscCall(MatTransposeGetMat(T, &A));
18     if (T == X) {
19       PetscCall(PetscInfo(NULL, "Explicitly transposing X of type MATTRANSPOSEVIRTUAL to perform MatAXPY()\n"));
20       PetscCall(MatTranspose(A, MAT_INITIAL_MATRIX, &F));
21       A = Y;
22     } else {
23       PetscCall(PetscInfo(NULL, "Transposing X because Y of type MATTRANSPOSEVIRTUAL to perform MatAXPY()\n"));
24       PetscCall(MatTranspose(X, MAT_INITIAL_MATRIX, &F));
25     }
26   } else {
27     PetscCall(MatHermitianTransposeGetMat(T, &A));
28     if (T == X) {
29       PetscCall(PetscInfo(NULL, "Explicitly Hermitian transposing X of type MATHERMITIANTRANSPOSEVIRTUAL to perform MatAXPY()\n"));
30       PetscCall(MatHermitianTranspose(A, MAT_INITIAL_MATRIX, &F));
31       A = Y;
32     } else {
33       PetscCall(PetscInfo(NULL, "Hermitian transposing X because Y of type MATHERMITIANTRANSPOSEVIRTUAL to perform MatAXPY()\n"));
34       PetscCall(MatHermitianTranspose(X, MAT_INITIAL_MATRIX, &F));
35     }
36   }
37   PetscCall(MatAXPY(A, a * vscale, F, str));
38   PetscCall(MatShift(A, a * vshift));
39   PetscCall(MatDestroy(&F));
40   PetscFunctionReturn(PETSC_SUCCESS);
41 }
42 
43 static PetscErrorCode MatAXPY_BasicWithTypeCompare(Mat Y, PetscScalar a, Mat X, MatStructure str)
44 {
45   PetscBool flg;
46 
47   PetscFunctionBegin;
48   PetscCall(MatIsShell(Y, &flg));
49   if (flg) { /* MatShell has special support for AXPY */
50     PetscErrorCode (*f)(Mat, PetscScalar, Mat, MatStructure);
51 
52     PetscCall(MatGetOperation(Y, MATOP_AXPY, (PetscErrorCodeFn **)&f));
53     if (f) {
54       PetscCall((*f)(Y, a, X, str));
55       PetscFunctionReturn(PETSC_SUCCESS);
56     }
57   } else {
58     /* no need to preallocate if Y is dense */
59     PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)Y, &flg, MATSEQDENSE, MATMPIDENSE, ""));
60     if (flg) {
61       PetscCall(PetscObjectTypeCompare((PetscObject)X, MATNEST, &flg));
62       if (flg) {
63         PetscCall(MatAXPY_Dense_Nest(Y, a, X));
64         PetscFunctionReturn(PETSC_SUCCESS);
65       }
66     }
67     PetscCall(PetscObjectTypeCompareAny((PetscObject)X, &flg, MATSCALAPACK, MATELEMENTAL, ""));
68     if (flg) { /* Avoid MatAXPY_Basic() due to missing MatGetRow() */
69       Mat C;
70 
71       PetscCall(MatConvert(X, ((PetscObject)Y)->type_name, MAT_INITIAL_MATRIX, &C));
72       PetscCall(MatAXPY(Y, a, C, str));
73       PetscCall(MatDestroy(&C));
74       PetscFunctionReturn(PETSC_SUCCESS);
75     }
76   }
77   PetscCall(MatAXPY_Basic(Y, a, X, str));
78   PetscFunctionReturn(PETSC_SUCCESS);
79 }
80 
81 /*@
82   MatAXPY - Computes Y = a*X + Y.
83 
84   Logically Collective
85 
86   Input Parameters:
87 + a   - the scalar multiplier
88 . X   - the first matrix
89 . Y   - the second matrix
90 - str - either `SAME_NONZERO_PATTERN`, `DIFFERENT_NONZERO_PATTERN`, `UNKNOWN_NONZERO_PATTERN`, or `SUBSET_NONZERO_PATTERN` (nonzeros of `X` is a subset of `Y`'s)
91 
92   Level: intermediate
93 
94 .seealso: [](ch_matrices), `Mat`, `MatAYPX()`
95  @*/
96 PetscErrorCode MatAXPY(Mat Y, PetscScalar a, Mat X, MatStructure str)
97 {
98   PetscInt  M1, M2, N1, N2;
99   PetscInt  m1, m2, n1, n2;
100   PetscBool sametype, transpose;
101 
102   PetscFunctionBegin;
103   PetscValidHeaderSpecific(Y, MAT_CLASSID, 1);
104   PetscValidLogicalCollectiveScalar(Y, a, 2);
105   PetscValidHeaderSpecific(X, MAT_CLASSID, 3);
106   PetscCheckSameComm(Y, 1, X, 3);
107   PetscCall(MatGetSize(X, &M1, &N1));
108   PetscCall(MatGetSize(Y, &M2, &N2));
109   PetscCall(MatGetLocalSize(X, &m1, &n1));
110   PetscCall(MatGetLocalSize(Y, &m2, &n2));
111   PetscCheck(M1 == M2 && N1 == N2, PetscObjectComm((PetscObject)Y), PETSC_ERR_ARG_SIZ, "Non conforming matrix add: global sizes X %" PetscInt_FMT " x %" PetscInt_FMT ", Y %" PetscInt_FMT " x %" PetscInt_FMT, M1, N1, M2, N2);
112   PetscCheck(m1 == m2 && n1 == n2, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Non conforming matrix add: local sizes X %" PetscInt_FMT " x %" PetscInt_FMT ", Y %" PetscInt_FMT " x %" PetscInt_FMT, m1, n1, m2, n2);
113   PetscCheck(Y->assembled, PetscObjectComm((PetscObject)Y), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix (Y)");
114   PetscCheck(X->assembled, PetscObjectComm((PetscObject)X), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix (X)");
115   if (a == (PetscScalar)0.0) PetscFunctionReturn(PETSC_SUCCESS);
116   if (Y == X) {
117     PetscCall(MatScale(Y, 1.0 + a));
118     PetscFunctionReturn(PETSC_SUCCESS);
119   }
120   PetscCall(PetscObjectObjectTypeCompare((PetscObject)X, (PetscObject)Y, &sametype));
121   PetscCall(PetscLogEventBegin(MAT_AXPY, Y, 0, 0, 0));
122   if (Y->ops->axpy && (sametype || X->ops->axpy == Y->ops->axpy)) {
123     PetscUseTypeMethod(Y, axpy, a, X, str);
124   } else {
125     PetscCall(PetscObjectTypeCompareAny((PetscObject)X, &transpose, MATTRANSPOSEVIRTUAL, MATHERMITIANTRANSPOSEVIRTUAL, ""));
126     if (transpose) {
127       PetscCall(MatTransposeAXPY_Private(Y, a, X, str, X));
128     } else {
129       PetscCall(PetscObjectTypeCompareAny((PetscObject)Y, &transpose, MATTRANSPOSEVIRTUAL, MATHERMITIANTRANSPOSEVIRTUAL, ""));
130       if (transpose) {
131         PetscCall(MatTransposeAXPY_Private(Y, a, X, str, Y));
132       } else {
133         PetscCall(MatAXPY_BasicWithTypeCompare(Y, a, X, str));
134       }
135     }
136   }
137   PetscCall(PetscLogEventEnd(MAT_AXPY, Y, 0, 0, 0));
138   PetscFunctionReturn(PETSC_SUCCESS);
139 }
140 
141 PetscErrorCode MatAXPY_Basic_Preallocate(Mat Y, Mat X, Mat *B)
142 {
143   PetscErrorCode (*preall)(Mat, Mat, Mat *) = NULL;
144 
145   PetscFunctionBegin;
146   /* look for any available faster alternative to the general preallocator */
147   PetscCall(PetscObjectQueryFunction((PetscObject)Y, "MatAXPYGetPreallocation_C", &preall));
148   if (preall) {
149     PetscCall((*preall)(Y, X, B));
150   } else { /* Use MatPrellocator, assumes same row-col distribution */
151     Mat      preallocator;
152     PetscInt r, rstart, rend;
153     PetscInt m, n, M, N;
154 
155     PetscCall(MatGetRowUpperTriangular(Y));
156     PetscCall(MatGetRowUpperTriangular(X));
157     PetscCall(MatGetSize(Y, &M, &N));
158     PetscCall(MatGetLocalSize(Y, &m, &n));
159     PetscCall(MatCreate(PetscObjectComm((PetscObject)Y), &preallocator));
160     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
161     PetscCall(MatSetLayouts(preallocator, Y->rmap, Y->cmap));
162     PetscCall(MatSetUp(preallocator));
163     PetscCall(MatGetOwnershipRange(preallocator, &rstart, &rend));
164     for (r = rstart; r < rend; ++r) {
165       PetscInt           ncols;
166       const PetscInt    *row;
167       const PetscScalar *vals;
168 
169       PetscCall(MatGetRow(Y, r, &ncols, &row, &vals));
170       PetscCall(MatSetValues(preallocator, 1, &r, ncols, row, vals, INSERT_VALUES));
171       PetscCall(MatRestoreRow(Y, r, &ncols, &row, &vals));
172       PetscCall(MatGetRow(X, r, &ncols, &row, &vals));
173       PetscCall(MatSetValues(preallocator, 1, &r, ncols, row, vals, INSERT_VALUES));
174       PetscCall(MatRestoreRow(X, r, &ncols, &row, &vals));
175     }
176     PetscCall(MatSetOption(preallocator, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE));
177     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
178     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
179     PetscCall(MatRestoreRowUpperTriangular(Y));
180     PetscCall(MatRestoreRowUpperTriangular(X));
181 
182     PetscCall(MatCreate(PetscObjectComm((PetscObject)Y), B));
183     PetscCall(MatSetType(*B, ((PetscObject)Y)->type_name));
184     PetscCall(MatSetLayouts(*B, Y->rmap, Y->cmap));
185     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_FALSE, *B));
186     PetscCall(MatDestroy(&preallocator));
187   }
188   PetscFunctionReturn(PETSC_SUCCESS);
189 }
190 
191 PetscErrorCode MatAXPY_Basic(Mat Y, PetscScalar a, Mat X, MatStructure str)
192 {
193   PetscFunctionBegin;
194   if (str == DIFFERENT_NONZERO_PATTERN || str == UNKNOWN_NONZERO_PATTERN) {
195     PetscBool isdense;
196 
197     /* no need to preallocate if Y is dense */
198     PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)Y, &isdense, MATSEQDENSE, MATMPIDENSE, ""));
199     if (isdense) str = SUBSET_NONZERO_PATTERN;
200   }
201   if (str != DIFFERENT_NONZERO_PATTERN && str != UNKNOWN_NONZERO_PATTERN) {
202     PetscInt           i, start, end, j, ncols, m, n;
203     const PetscInt    *row;
204     PetscScalar       *val;
205     const PetscScalar *vals;
206     PetscBool          option;
207 
208     PetscCall(MatGetSize(X, &m, &n));
209     PetscCall(MatGetOwnershipRange(X, &start, &end));
210     PetscCall(MatGetRowUpperTriangular(X));
211     if (a == 1.0) {
212       for (i = start; i < end; i++) {
213         PetscCall(MatGetRow(X, i, &ncols, &row, &vals));
214         PetscCall(MatSetValues(Y, 1, &i, ncols, row, vals, ADD_VALUES));
215         PetscCall(MatRestoreRow(X, i, &ncols, &row, &vals));
216       }
217     } else {
218       PetscInt vs = 100;
219       /* realloc if needed, as this function may be used in parallel */
220       PetscCall(PetscMalloc1(vs, &val));
221       for (i = start; i < end; i++) {
222         PetscCall(MatGetRow(X, i, &ncols, &row, &vals));
223         if (vs < ncols) {
224           vs = PetscMin(2 * ncols, n);
225           PetscCall(PetscRealloc(vs * sizeof(*val), &val));
226         }
227         for (j = 0; j < ncols; j++) val[j] = a * vals[j];
228         PetscCall(MatSetValues(Y, 1, &i, ncols, row, val, ADD_VALUES));
229         PetscCall(MatRestoreRow(X, i, &ncols, &row, &vals));
230       }
231       PetscCall(PetscFree(val));
232     }
233     PetscCall(MatRestoreRowUpperTriangular(X));
234     PetscCall(MatGetOption(Y, MAT_NO_OFF_PROC_ENTRIES, &option));
235     PetscCall(MatSetOption(Y, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE));
236     PetscCall(MatAssemblyBegin(Y, MAT_FINAL_ASSEMBLY));
237     PetscCall(MatAssemblyEnd(Y, MAT_FINAL_ASSEMBLY));
238     PetscCall(MatSetOption(Y, MAT_NO_OFF_PROC_ENTRIES, option));
239   } else {
240     Mat B;
241 
242     PetscCall(MatAXPY_Basic_Preallocate(Y, X, &B));
243     PetscCall(MatAXPY_BasicWithPreallocation(B, Y, a, X, str));
244     PetscCall(MatHeaderMerge(Y, &B));
245   }
246   PetscFunctionReturn(PETSC_SUCCESS);
247 }
248 
249 PetscErrorCode MatAXPY_BasicWithPreallocation(Mat B, Mat Y, PetscScalar a, Mat X, MatStructure str)
250 {
251   PetscInt           i, start, end, j, ncols, m, n;
252   const PetscInt    *row;
253   PetscScalar       *val;
254   const PetscScalar *vals;
255   PetscBool          option;
256 
257   PetscFunctionBegin;
258   PetscCall(MatGetSize(X, &m, &n));
259   PetscCall(MatGetOwnershipRange(X, &start, &end));
260   PetscCall(MatGetRowUpperTriangular(Y));
261   PetscCall(MatGetRowUpperTriangular(X));
262   if (a == 1.0) {
263     for (i = start; i < end; i++) {
264       PetscCall(MatGetRow(Y, i, &ncols, &row, &vals));
265       PetscCall(MatSetValues(B, 1, &i, ncols, row, vals, ADD_VALUES));
266       PetscCall(MatRestoreRow(Y, i, &ncols, &row, &vals));
267 
268       PetscCall(MatGetRow(X, i, &ncols, &row, &vals));
269       PetscCall(MatSetValues(B, 1, &i, ncols, row, vals, ADD_VALUES));
270       PetscCall(MatRestoreRow(X, i, &ncols, &row, &vals));
271     }
272   } else {
273     PetscInt vs = 100;
274     /* realloc if needed, as this function may be used in parallel */
275     PetscCall(PetscMalloc1(vs, &val));
276     for (i = start; i < end; i++) {
277       PetscCall(MatGetRow(Y, i, &ncols, &row, &vals));
278       PetscCall(MatSetValues(B, 1, &i, ncols, row, vals, ADD_VALUES));
279       PetscCall(MatRestoreRow(Y, i, &ncols, &row, &vals));
280 
281       PetscCall(MatGetRow(X, i, &ncols, &row, &vals));
282       if (vs < ncols) {
283         vs = PetscMin(2 * ncols, n);
284         PetscCall(PetscRealloc(vs * sizeof(*val), &val));
285       }
286       for (j = 0; j < ncols; j++) val[j] = a * vals[j];
287       PetscCall(MatSetValues(B, 1, &i, ncols, row, val, ADD_VALUES));
288       PetscCall(MatRestoreRow(X, i, &ncols, &row, &vals));
289     }
290     PetscCall(PetscFree(val));
291   }
292   PetscCall(MatRestoreRowUpperTriangular(Y));
293   PetscCall(MatRestoreRowUpperTriangular(X));
294   PetscCall(MatGetOption(B, MAT_NO_OFF_PROC_ENTRIES, &option));
295   PetscCall(MatSetOption(B, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE));
296   PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
297   PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
298   PetscCall(MatSetOption(B, MAT_NO_OFF_PROC_ENTRIES, option));
299   PetscFunctionReturn(PETSC_SUCCESS);
300 }
301 
302 /*@
303   MatShift - Computes `Y =  Y + a I`, where `a` is a `PetscScalar`
304 
305   Neighbor-wise Collective
306 
307   Input Parameters:
308 + Y - the matrix
309 - a - the `PetscScalar`
310 
311   Level: intermediate
312 
313   Notes:
314   If `Y` is a rectangular matrix, the shift is done on the main diagonal of the matrix (https://en.wikipedia.org/wiki/Main_diagonal)
315 
316   If the matrix `Y` is missing some diagonal entries this routine can be very slow. To make it fast one should initially
317   fill the matrix so that all diagonal entries have a value (with a value of zero for those locations that would not have an
318   entry). No operation is performed when a is zero.
319 
320   To form Y = Y + diag(V) use `MatDiagonalSet()`
321 
322 .seealso: [](ch_matrices), `Mat`, `MatDiagonalSet()`, `MatScale()`, `MatDiagonalScale()`
323  @*/
324 PetscErrorCode MatShift(Mat Y, PetscScalar a)
325 {
326   PetscFunctionBegin;
327   PetscValidHeaderSpecific(Y, MAT_CLASSID, 1);
328   PetscCheck(Y->assembled, PetscObjectComm((PetscObject)Y), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
329   PetscCheck(!Y->factortype, PetscObjectComm((PetscObject)Y), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
330   MatCheckPreallocated(Y, 1);
331   if (a == 0.0) PetscFunctionReturn(PETSC_SUCCESS);
332 
333   if (Y->ops->shift) PetscUseTypeMethod(Y, shift, a);
334   else PetscCall(MatShift_Basic(Y, a));
335 
336   PetscCall(PetscObjectStateIncrease((PetscObject)Y));
337   PetscFunctionReturn(PETSC_SUCCESS);
338 }
339 
340 PetscErrorCode MatDiagonalSet_Default(Mat Y, Vec D, InsertMode is)
341 {
342   PetscInt           i, start, end;
343   const PetscScalar *v;
344 
345   PetscFunctionBegin;
346   PetscCall(MatGetOwnershipRange(Y, &start, &end));
347   PetscCall(VecGetArrayRead(D, &v));
348   for (i = start; i < end; i++) PetscCall(MatSetValues(Y, 1, &i, 1, &i, v + i - start, is));
349   PetscCall(VecRestoreArrayRead(D, &v));
350   PetscCall(MatAssemblyBegin(Y, MAT_FINAL_ASSEMBLY));
351   PetscCall(MatAssemblyEnd(Y, MAT_FINAL_ASSEMBLY));
352   PetscFunctionReturn(PETSC_SUCCESS);
353 }
354 
355 /*@
356   MatDiagonalSet - Computes `Y` = `Y` + `D`, where `D` is a diagonal matrix
357   that is represented as a vector. Or Y[i,i] = D[i] if `InsertMode` is
358   `INSERT_VALUES`.
359 
360   Neighbor-wise Collective
361 
362   Input Parameters:
363 + Y  - the input matrix
364 . D  - the diagonal matrix, represented as a vector
365 - is - `INSERT_VALUES` or `ADD_VALUES`
366 
367   Level: intermediate
368 
369   Note:
370   If the matrix `Y` is missing some diagonal entries this routine can be very slow. To make it fast one should initially
371   fill the matrix so that all diagonal entries have a value (with a value of zero for those locations that would not have an
372   entry).
373 
374 .seealso: [](ch_matrices), `Mat`, `MatShift()`, `MatScale()`, `MatDiagonalScale()`
375 @*/
376 PetscErrorCode MatDiagonalSet(Mat Y, Vec D, InsertMode is)
377 {
378   PetscInt matlocal, veclocal;
379 
380   PetscFunctionBegin;
381   PetscValidHeaderSpecific(Y, MAT_CLASSID, 1);
382   PetscValidHeaderSpecific(D, VEC_CLASSID, 2);
383   MatCheckPreallocated(Y, 1);
384   PetscCall(MatGetLocalSize(Y, &matlocal, NULL));
385   PetscCall(VecGetLocalSize(D, &veclocal));
386   PetscCheck(matlocal == veclocal, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Number local rows of matrix %" PetscInt_FMT " does not match that of vector for diagonal %" PetscInt_FMT, matlocal, veclocal);
387   if (Y->ops->diagonalset) PetscUseTypeMethod(Y, diagonalset, D, is);
388   else PetscCall(MatDiagonalSet_Default(Y, D, is));
389   PetscCall(PetscObjectStateIncrease((PetscObject)Y));
390   PetscFunctionReturn(PETSC_SUCCESS);
391 }
392 
393 /*@
394   MatAYPX - Computes Y = a*Y + X.
395 
396   Logically Collective
397 
398   Input Parameters:
399 + a   - the `PetscScalar` multiplier
400 . Y   - the first matrix
401 . X   - the second matrix
402 - str - either `SAME_NONZERO_PATTERN`, `DIFFERENT_NONZERO_PATTERN`, `UNKNOWN_NONZERO_PATTERN`, or `SUBSET_NONZERO_PATTERN` (nonzeros of `X` is a subset of `Y`'s)
403 
404   Level: intermediate
405 
406 .seealso: [](ch_matrices), `Mat`, `MatAXPY()`
407  @*/
408 PetscErrorCode MatAYPX(Mat Y, PetscScalar a, Mat X, MatStructure str)
409 {
410   PetscFunctionBegin;
411   PetscCall(MatScale(Y, a));
412   PetscCall(MatAXPY(Y, 1.0, X, str));
413   PetscFunctionReturn(PETSC_SUCCESS);
414 }
415 
416 /*@
417   MatComputeOperator - Computes the explicit matrix
418 
419   Collective
420 
421   Input Parameters:
422 + inmat   - the matrix
423 - mattype - the matrix type for the explicit operator
424 
425   Output Parameter:
426 . mat - the explicit  operator
427 
428   Level: advanced
429 
430   Note:
431   This computation is done by applying the operator to columns of the identity matrix.
432   This routine is costly in general, and is recommended for use only with relatively small systems.
433   Currently, this routine uses a dense matrix format if `mattype` == `NULL`.
434 
435 .seealso: [](ch_matrices), `Mat`, `MatConvert()`, `MatMult()`, `MatComputeOperatorTranspose()`
436 @*/
437 PetscErrorCode MatComputeOperator(Mat inmat, MatType mattype, Mat *mat)
438 {
439   PetscFunctionBegin;
440   PetscValidHeaderSpecific(inmat, MAT_CLASSID, 1);
441   PetscAssertPointer(mat, 3);
442   PetscCall(MatConvert_Shell(inmat, mattype ? mattype : MATDENSE, MAT_INITIAL_MATRIX, mat));
443   PetscFunctionReturn(PETSC_SUCCESS);
444 }
445 
446 /*@
447   MatComputeOperatorTranspose - Computes the explicit matrix representation of
448   a give matrix that can apply `MatMultTranspose()`
449 
450   Collective
451 
452   Input Parameters:
453 + inmat   - the matrix
454 - mattype - the matrix type for the explicit operator
455 
456   Output Parameter:
457 . mat - the explicit  operator transposed
458 
459   Level: advanced
460 
461   Note:
462   This computation is done by applying the transpose of the operator to columns of the identity matrix.
463   This routine is costly in general, and is recommended for use only with relatively small systems.
464   Currently, this routine uses a dense matrix format if `mattype` == `NULL`.
465 
466 .seealso: [](ch_matrices), `Mat`, `MatConvert()`, `MatMult()`, `MatComputeOperator()`
467 @*/
468 PetscErrorCode MatComputeOperatorTranspose(Mat inmat, MatType mattype, Mat *mat)
469 {
470   Mat A;
471 
472   PetscFunctionBegin;
473   PetscValidHeaderSpecific(inmat, MAT_CLASSID, 1);
474   PetscAssertPointer(mat, 3);
475   PetscCall(MatCreateTranspose(inmat, &A));
476   PetscCall(MatConvert_Shell(A, mattype ? mattype : MATDENSE, MAT_INITIAL_MATRIX, mat));
477   PetscCall(MatDestroy(&A));
478   PetscFunctionReturn(PETSC_SUCCESS);
479 }
480 
481 /*@
482   MatFilter - Set all values in the matrix with an absolute value less than or equal to the tolerance to zero, and optionally compress the underlying storage
483 
484   Input Parameters:
485 + A        - The matrix
486 . tol      - The zero tolerance
487 . compress - Whether the storage from the input matrix `A` should be compressed once values less than or equal to `tol` are set to zero
488 - keep     - If `compress` is true and for a given row of `A`, the diagonal coefficient is less than or equal to `tol`, indicates whether it should be left in the structure or eliminated as well
489 
490   Level: intermediate
491 
492 .seealso: [](ch_matrices), `Mat`, `MatCreate()`, `MatZeroEntries()`, `MatEliminateZeros()`, `VecFilter()`
493  @*/
494 PetscErrorCode MatFilter(Mat A, PetscReal tol, PetscBool compress, PetscBool keep)
495 {
496   Mat          a;
497   PetscScalar *newVals;
498   PetscInt    *newCols, rStart, rEnd, maxRows, r, colMax = 0, nnz0 = 0, nnz1 = 0;
499   PetscBool    flg;
500 
501   PetscFunctionBegin;
502   PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)A, &flg, MATSEQDENSE, MATMPIDENSE, ""));
503   if (flg) {
504     PetscCall(MatDenseGetLocalMatrix(A, &a));
505     PetscCall(MatDenseGetLDA(a, &r));
506     PetscCall(MatGetSize(a, &rStart, &rEnd));
507     PetscCall(MatDenseGetArray(a, &newVals));
508     for (; colMax < rEnd; ++colMax) {
509       for (maxRows = 0; maxRows < rStart; ++maxRows) newVals[maxRows + colMax * r] = PetscAbsScalar(newVals[maxRows + colMax * r]) <= tol ? 0.0 : newVals[maxRows + colMax * r];
510     }
511     PetscCall(MatDenseRestoreArray(a, &newVals));
512   } else {
513     const PetscInt *ranges;
514     PetscMPIInt     rank, size;
515 
516     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
517     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
518     PetscCall(MatGetOwnershipRanges(A, &ranges));
519     rStart = ranges[rank];
520     rEnd   = ranges[rank + 1];
521     PetscCall(MatGetRowUpperTriangular(A));
522     for (r = rStart; r < rEnd; ++r) {
523       PetscInt ncols;
524 
525       PetscCall(MatGetRow(A, r, &ncols, NULL, NULL));
526       colMax = PetscMax(colMax, ncols);
527       PetscCall(MatRestoreRow(A, r, &ncols, NULL, NULL));
528     }
529     maxRows = 0;
530     for (r = 0; r < size; ++r) maxRows = PetscMax(maxRows, ranges[r + 1] - ranges[r]);
531     PetscCall(PetscCalloc2(colMax, &newCols, colMax, &newVals));
532     PetscCall(MatGetOption(A, MAT_NO_OFF_PROC_ENTRIES, &flg)); /* cache user-defined value */
533     PetscCall(MatSetOption(A, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE));
534     /* short-circuit code in MatAssemblyBegin() and MatAssemblyEnd()             */
535     /* that are potentially called many times depending on the distribution of A */
536     for (r = rStart; r < rStart + maxRows; ++r) {
537       if (r < rEnd) {
538         const PetscScalar *vals;
539         const PetscInt    *cols;
540         PetscInt           ncols, newcols = 0, c;
541 
542         PetscCall(MatGetRow(A, r, &ncols, &cols, &vals));
543         nnz0 += ncols - 1;
544         for (c = 0; c < ncols; ++c) {
545           if (PetscUnlikely(PetscAbsScalar(vals[c]) <= tol)) newCols[newcols++] = cols[c];
546         }
547         nnz1 += ncols - newcols - 1;
548         PetscCall(MatRestoreRow(A, r, &ncols, &cols, &vals));
549         PetscCall(MatSetValues(A, 1, &r, newcols, newCols, newVals, INSERT_VALUES));
550       }
551       PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
552       PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
553     }
554     PetscCall(MatRestoreRowUpperTriangular(A));
555     PetscCall(PetscFree2(newCols, newVals));
556     PetscCall(MatSetOption(A, MAT_NO_OFF_PROC_ENTRIES, flg)); /* reset option to its user-defined value */
557     if (nnz0 > 0) PetscCall(PetscInfo(NULL, "Filtering left %g%% edges in graph\n", 100 * (double)nnz1 / (double)nnz0));
558     else PetscCall(PetscInfo(NULL, "Warning: %" PetscInt_FMT " edges to filter with %" PetscInt_FMT " rows\n", nnz0, maxRows));
559   }
560   if (compress && A->ops->eliminatezeros) {
561     Mat       B;
562     PetscBool flg;
563 
564     PetscCall(PetscObjectTypeCompareAny((PetscObject)A, &flg, MATSEQAIJHIPSPARSE, MATMPIAIJHIPSPARSE, ""));
565     if (!flg) {
566       PetscCall(MatEliminateZeros(A, keep));
567       PetscCall(MatDuplicate(A, MAT_COPY_VALUES, &B));
568       PetscCall(MatHeaderReplace(A, &B));
569     }
570   }
571   PetscFunctionReturn(PETSC_SUCCESS);
572 }
573