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