xref: /petsc/src/mat/utils/multequal.c (revision b63c620b66da884efdf690e70d6f9f5511a4e974)
1 #include <petsc/private/matimpl.h> /*I   "petscmat.h"  I*/
2 
3 /*
4   n; try the MatMult variant n times
5   flg: return the boolean result, equal or not
6   t: 0 => no transpose; 1 => transpose; 2 => Hermitian transpose
7   add:  0 => no add (e.g., y = Ax);  1 => add third vector (e.g., z = Ax + y); 2 => add update (e.g., y = Ax + y)
8 */
9 static PetscErrorCode MatMultEqual_Private(Mat A, Mat B, PetscInt n, PetscBool *flg, PetscInt t, PetscInt add)
10 {
11   Vec         Ax = NULL, Bx = NULL, s1 = NULL, s2 = NULL, Ay = NULL, By = NULL;
12   PetscRandom rctx;
13   PetscReal   r1, r2, tol = PETSC_SQRT_MACHINE_EPSILON;
14   PetscInt    am, an, bm, bn, k;
15   PetscScalar none = -1.0;
16 #if defined(PETSC_USE_INFO)
17   const char *sops[] = {"MatMult", "MatMultAdd", "MatMultAdd (update)", "MatMultTranspose", "MatMultTransposeAdd", "MatMultTransposeAdd (update)", "MatMultHermitianTranspose", "MatMultHermitianTransposeAdd", "MatMultHermitianTransposeAdd (update)"};
18   const char *sop;
19 #endif
20 
21   PetscFunctionBegin;
22   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
23   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
24   PetscCheckSameComm(A, 1, B, 2);
25   PetscValidLogicalCollectiveInt(A, n, 3);
26   PetscAssertPointer(flg, 4);
27   PetscValidLogicalCollectiveInt(A, t, 5);
28   PetscValidLogicalCollectiveInt(A, add, 6);
29   PetscCall(MatGetLocalSize(A, &am, &an));
30   PetscCall(MatGetLocalSize(B, &bm, &bn));
31   PetscCheck(am == bm && an == bn, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Mat A,Mat B: local dim %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT, am, bm, an, bn);
32 #if defined(PETSC_USE_INFO)
33   sop = sops[add + 3 * t];
34 #endif
35   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)A), &rctx));
36   PetscCall(PetscRandomSetFromOptions(rctx));
37   if (t) {
38     PetscCall(MatCreateVecs(A, &s1, &Ax));
39     PetscCall(MatCreateVecs(B, &s2, &Bx));
40   } else {
41     PetscCall(MatCreateVecs(A, &Ax, &s1));
42     PetscCall(MatCreateVecs(B, &Bx, &s2));
43   }
44   if (add) {
45     PetscCall(VecDuplicate(s1, &Ay));
46     PetscCall(VecDuplicate(s2, &By));
47   }
48 
49   *flg = PETSC_TRUE;
50   for (k = 0; k < n; k++) {
51     Vec Aadd = NULL, Badd = NULL;
52 
53     PetscCall(VecSetRandom(Ax, rctx));
54     PetscCall(VecCopy(Ax, Bx));
55     if (add) {
56       PetscCall(VecSetRandom(Ay, rctx));
57       PetscCall(VecCopy(Ay, By));
58       Aadd = Ay;
59       Badd = By;
60       if (add == 2) {
61         PetscCall(VecCopy(Ay, s1));
62         PetscCall(VecCopy(By, s2));
63         Aadd = s1;
64         Badd = s2;
65       }
66     }
67     if (t == 1) {
68       if (add) {
69         PetscCall(MatMultTransposeAdd(A, Ax, Aadd, s1));
70         PetscCall(MatMultTransposeAdd(B, Bx, Badd, s2));
71       } else {
72         PetscCall(MatMultTranspose(A, Ax, s1));
73         PetscCall(MatMultTranspose(B, Bx, s2));
74       }
75     } else if (t == 2) {
76       if (add) {
77         PetscCall(MatMultHermitianTransposeAdd(A, Ax, Aadd, s1));
78         PetscCall(MatMultHermitianTransposeAdd(B, Bx, Badd, s2));
79       } else {
80         PetscCall(MatMultHermitianTranspose(A, Ax, s1));
81         PetscCall(MatMultHermitianTranspose(B, Bx, s2));
82       }
83     } else {
84       if (add) {
85         PetscCall(MatMultAdd(A, Ax, Aadd, s1));
86         PetscCall(MatMultAdd(B, Bx, Badd, s2));
87       } else {
88         PetscCall(MatMult(A, Ax, s1));
89         PetscCall(MatMult(B, Bx, s2));
90       }
91     }
92     PetscCall(VecNorm(s2, NORM_INFINITY, &r2));
93     if (r2 < tol) {
94       PetscCall(VecNorm(s1, NORM_INFINITY, &r1));
95     } else {
96       PetscCall(VecAXPY(s2, none, s1));
97       PetscCall(VecNorm(s2, NORM_INFINITY, &r1));
98       r1 /= r2;
99     }
100     if (r1 > tol) {
101       *flg = PETSC_FALSE;
102       PetscCall(PetscInfo(A, "Error: %" PetscInt_FMT "-th %s() %g\n", k, sop, (double)r1));
103       break;
104     }
105   }
106   PetscCall(PetscRandomDestroy(&rctx));
107   PetscCall(VecDestroy(&Ax));
108   PetscCall(VecDestroy(&Bx));
109   PetscCall(VecDestroy(&Ay));
110   PetscCall(VecDestroy(&By));
111   PetscCall(VecDestroy(&s1));
112   PetscCall(VecDestroy(&s2));
113   PetscFunctionReturn(PETSC_SUCCESS);
114 }
115 
116 static PetscErrorCode MatMatMultEqual_Private(Mat A, Mat B, Mat C, PetscInt n, PetscBool *flg, PetscBool At, PetscBool Bt)
117 {
118   Vec         Ax, Bx, Cx, s1, s2, s3;
119   PetscRandom rctx;
120   PetscReal   r1, r2, tol = PETSC_SQRT_MACHINE_EPSILON;
121   PetscInt    am, an, bm, bn, cm, cn, k;
122   PetscScalar none = -1.0;
123 #if defined(PETSC_USE_INFO)
124   const char *sops[] = {"MatMatMult", "MatTransposeMatMult", "MatMatTransposeMult", "MatTransposeMatTransposeMult"};
125   const char *sop;
126 #endif
127 
128   PetscFunctionBegin;
129   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
130   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
131   PetscCheckSameComm(A, 1, B, 2);
132   PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
133   PetscCheckSameComm(A, 1, C, 3);
134   PetscValidLogicalCollectiveInt(A, n, 4);
135   PetscAssertPointer(flg, 5);
136   PetscValidLogicalCollectiveBool(A, At, 6);
137   PetscValidLogicalCollectiveBool(B, Bt, 7);
138   PetscCall(MatGetLocalSize(A, &am, &an));
139   PetscCall(MatGetLocalSize(B, &bm, &bn));
140   PetscCall(MatGetLocalSize(C, &cm, &cn));
141   if (At) {
142     PetscInt tt = an;
143     an          = am;
144     am          = tt;
145   }
146   if (Bt) {
147     PetscInt tt = bn;
148     bn          = bm;
149     bm          = tt;
150   }
151   PetscCheck(an == bm && am == cm && bn == cn, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Mat A, B, C local dim %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT, am, an, bm, bn, cm, cn);
152 
153 #if defined(PETSC_USE_INFO)
154   sop = sops[(At ? 1 : 0) + 2 * (Bt ? 1 : 0)];
155 #endif
156   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)C), &rctx));
157   PetscCall(PetscRandomSetFromOptions(rctx));
158   if (Bt) {
159     PetscCall(MatCreateVecs(B, &s1, &Bx));
160   } else {
161     PetscCall(MatCreateVecs(B, &Bx, &s1));
162   }
163   if (At) {
164     PetscCall(MatCreateVecs(A, &s2, &Ax));
165   } else {
166     PetscCall(MatCreateVecs(A, &Ax, &s2));
167   }
168   PetscCall(MatCreateVecs(C, &Cx, &s3));
169 
170   *flg = PETSC_TRUE;
171   for (k = 0; k < n; k++) {
172     PetscCall(VecSetRandom(Bx, rctx));
173     if (Bt) {
174       PetscCall(MatMultTranspose(B, Bx, s1));
175     } else {
176       PetscCall(MatMult(B, Bx, s1));
177     }
178     PetscCall(VecCopy(s1, Ax));
179     if (At) {
180       PetscCall(MatMultTranspose(A, Ax, s2));
181     } else {
182       PetscCall(MatMult(A, Ax, s2));
183     }
184     PetscCall(VecCopy(Bx, Cx));
185     PetscCall(MatMult(C, Cx, s3));
186 
187     PetscCall(VecNorm(s2, NORM_INFINITY, &r2));
188     if (r2 < tol) {
189       PetscCall(VecNorm(s3, NORM_INFINITY, &r1));
190     } else {
191       PetscCall(VecAXPY(s2, none, s3));
192       PetscCall(VecNorm(s2, NORM_INFINITY, &r1));
193       r1 /= r2;
194     }
195     if (r1 > tol) {
196       *flg = PETSC_FALSE;
197       PetscCall(PetscInfo(A, "Error: %" PetscInt_FMT "-th %s %g\n", k, sop, (double)r1));
198       break;
199     }
200   }
201   PetscCall(PetscRandomDestroy(&rctx));
202   PetscCall(VecDestroy(&Ax));
203   PetscCall(VecDestroy(&Bx));
204   PetscCall(VecDestroy(&Cx));
205   PetscCall(VecDestroy(&s1));
206   PetscCall(VecDestroy(&s2));
207   PetscCall(VecDestroy(&s3));
208   PetscFunctionReturn(PETSC_SUCCESS);
209 }
210 
211 /*@
212   MatMultEqual - Compares matrix-vector products of two matrices.
213 
214   Collective
215 
216   Input Parameters:
217 + A - the first matrix
218 . B - the second matrix
219 - n - number of random vectors to be tested
220 
221   Output Parameter:
222 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
223 
224   Level: intermediate
225 
226 .seealso: `Mat`, `MatMultAddEqual()`, `MatMultTransposeEqual()`, `MatMultTransposeAddEqual()`, `MatIsLinear()`
227 @*/
228 PetscErrorCode MatMultEqual(Mat A, Mat B, PetscInt n, PetscBool *flg)
229 {
230   PetscFunctionBegin;
231   PetscCall(MatMultEqual_Private(A, B, n, flg, 0, 0));
232   PetscFunctionReturn(PETSC_SUCCESS);
233 }
234 
235 /*@
236   MatMultAddEqual - Compares matrix-vector product plus vector add of two matrices.
237 
238   Collective
239 
240   Input Parameters:
241 + A - the first matrix
242 . B - the second matrix
243 - n - number of random vectors to be tested
244 
245   Output Parameter:
246 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
247 
248   Level: intermediate
249 
250 .seealso: `Mat`, `MatMultEqual()`, `MatMultTransposeEqual()`, `MatMultTransposeAddEqual()`
251 @*/
252 PetscErrorCode MatMultAddEqual(Mat A, Mat B, PetscInt n, PetscBool *flg)
253 {
254   PetscFunctionBegin;
255   PetscCall(MatMultEqual_Private(A, B, n, flg, 0, 1));
256   PetscCall(MatMultEqual_Private(A, B, n, flg, 0, 2));
257   PetscFunctionReturn(PETSC_SUCCESS);
258 }
259 
260 /*@
261   MatMultTransposeEqual - Compares matrix-vector products of two matrices.
262 
263   Collective
264 
265   Input Parameters:
266 + A - the first matrix
267 . B - the second matrix
268 - n - number of random vectors to be tested
269 
270   Output Parameter:
271 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
272 
273   Level: intermediate
274 
275 .seealso: `Mat`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeAddEqual()`
276 @*/
277 PetscErrorCode MatMultTransposeEqual(Mat A, Mat B, PetscInt n, PetscBool *flg)
278 {
279   PetscFunctionBegin;
280   PetscCall(MatMultEqual_Private(A, B, n, flg, 1, 0));
281   PetscFunctionReturn(PETSC_SUCCESS);
282 }
283 
284 /*@
285   MatMultTransposeAddEqual - Compares matrix-vector products of two matrices.
286 
287   Collective
288 
289   Input Parameters:
290 + A - the first matrix
291 . B - the second matrix
292 - n - number of random vectors to be tested
293 
294   Output Parameter:
295 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
296 
297   Level: intermediate
298 
299 .seealso: `Mat`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
300 @*/
301 PetscErrorCode MatMultTransposeAddEqual(Mat A, Mat B, PetscInt n, PetscBool *flg)
302 {
303   PetscFunctionBegin;
304   PetscCall(MatMultEqual_Private(A, B, n, flg, 1, 1));
305   PetscCall(MatMultEqual_Private(A, B, n, flg, 1, 2));
306   PetscFunctionReturn(PETSC_SUCCESS);
307 }
308 
309 /*@
310   MatMultHermitianTransposeEqual - Compares matrix-vector products of two matrices.
311 
312   Collective
313 
314   Input Parameters:
315 + A - the first matrix
316 . B - the second matrix
317 - n - number of random vectors to be tested
318 
319   Output Parameter:
320 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
321 
322   Level: intermediate
323 
324 .seealso: `Mat`, `MatMatMultEqual()`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
325 @*/
326 PetscErrorCode MatMultHermitianTransposeEqual(Mat A, Mat B, PetscInt n, PetscBool *flg)
327 {
328   PetscFunctionBegin;
329   PetscCall(MatMultEqual_Private(A, B, n, flg, 2, 0));
330   PetscFunctionReturn(PETSC_SUCCESS);
331 }
332 
333 /*@
334   MatMultHermitianTransposeAddEqual - Compares matrix-vector products of two matrices.
335 
336   Collective
337 
338   Input Parameters:
339 + A - the first matrix
340 . B - the second matrix
341 - n - number of random vectors to be tested
342 
343   Output Parameter:
344 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
345 
346   Level: intermediate
347 
348 .seealso: `Mat`, `MatMatMultEqual()`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
349 @*/
350 PetscErrorCode MatMultHermitianTransposeAddEqual(Mat A, Mat B, PetscInt n, PetscBool *flg)
351 {
352   PetscFunctionBegin;
353   PetscCall(MatMultEqual_Private(A, B, n, flg, 2, 1));
354   PetscCall(MatMultEqual_Private(A, B, n, flg, 2, 2));
355   PetscFunctionReturn(PETSC_SUCCESS);
356 }
357 
358 /*@
359   MatMatMultEqual - Test A*B*x = C*x for n random vector x
360 
361   Collective
362 
363   Input Parameters:
364 + A - the first matrix
365 . B - the second matrix
366 . C - the third matrix
367 - n - number of random vectors to be tested
368 
369   Output Parameter:
370 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
371 
372   Level: intermediate
373 
374 .seealso: `Mat`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
375 @*/
376 PetscErrorCode MatMatMultEqual(Mat A, Mat B, Mat C, PetscInt n, PetscBool *flg)
377 {
378   PetscFunctionBegin;
379   PetscCall(MatMatMultEqual_Private(A, B, C, n, flg, PETSC_FALSE, PETSC_FALSE));
380   PetscFunctionReturn(PETSC_SUCCESS);
381 }
382 
383 /*@
384   MatTransposeMatMultEqual - Test A^T*B*x = C*x for n random vector x
385 
386   Collective
387 
388   Input Parameters:
389 + A - the first matrix
390 . B - the second matrix
391 . C - the third matrix
392 - n - number of random vectors to be tested
393 
394   Output Parameter:
395 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
396 
397   Level: intermediate
398 
399 .seealso: `Mat`, `MatMatMultEqual()`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
400 @*/
401 PetscErrorCode MatTransposeMatMultEqual(Mat A, Mat B, Mat C, PetscInt n, PetscBool *flg)
402 {
403   PetscFunctionBegin;
404   PetscCall(MatMatMultEqual_Private(A, B, C, n, flg, PETSC_TRUE, PETSC_FALSE));
405   PetscFunctionReturn(PETSC_SUCCESS);
406 }
407 
408 /*@
409   MatMatTransposeMultEqual - Test A*B^T*x = C*x for n random vector x
410 
411   Collective
412 
413   Input Parameters:
414 + A - the first matrix
415 . B - the second matrix
416 . C - the third matrix
417 - n - number of random vectors to be tested
418 
419   Output Parameter:
420 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
421 
422   Level: intermediate
423 
424 .seealso: `Mat`, `MatMatMultEqual()`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
425 @*/
426 PetscErrorCode MatMatTransposeMultEqual(Mat A, Mat B, Mat C, PetscInt n, PetscBool *flg)
427 {
428   PetscFunctionBegin;
429   PetscCall(MatMatMultEqual_Private(A, B, C, n, flg, PETSC_FALSE, PETSC_TRUE));
430   PetscFunctionReturn(PETSC_SUCCESS);
431 }
432 
433 static PetscErrorCode MatProjMultEqual_Private(Mat A, Mat B, Mat C, PetscInt n, PetscBool rart, PetscBool *flg)
434 {
435   Vec         x, v1, v2, v3, v4, Cx, Bx;
436   PetscReal   norm_abs, norm_rel, tol = PETSC_SQRT_MACHINE_EPSILON;
437   PetscInt    i, am, an, bm, bn, cm, cn;
438   PetscRandom rdm;
439   PetscScalar none = -1.0;
440 
441   PetscFunctionBegin;
442   PetscCall(MatGetLocalSize(A, &am, &an));
443   PetscCall(MatGetLocalSize(B, &bm, &bn));
444   if (rart) {
445     PetscInt t = bm;
446     bm         = bn;
447     bn         = t;
448   }
449   PetscCall(MatGetLocalSize(C, &cm, &cn));
450   PetscCheck(an == bm && bn == cm && bn == cn, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Mat A, B, C local dim %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT " %" PetscInt_FMT, am, an, bm, bn, cm, cn);
451 
452   /* Create left vector of A: v2 */
453   PetscCall(MatCreateVecs(A, &Bx, &v2));
454 
455   /* Create right vectors of B: x, v3, v4 */
456   if (rart) {
457     PetscCall(MatCreateVecs(B, &v1, &x));
458   } else {
459     PetscCall(MatCreateVecs(B, &x, &v1));
460   }
461   PetscCall(VecDuplicate(x, &v3));
462 
463   PetscCall(MatCreateVecs(C, &Cx, &v4));
464   PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rdm));
465   PetscCall(PetscRandomSetFromOptions(rdm));
466 
467   *flg = PETSC_TRUE;
468   for (i = 0; i < n; i++) {
469     PetscCall(VecSetRandom(x, rdm));
470     PetscCall(VecCopy(x, Cx));
471     PetscCall(MatMult(C, Cx, v4)); /* v4 = C*x   */
472     if (rart) {
473       PetscCall(MatMultTranspose(B, x, v1));
474     } else {
475       PetscCall(MatMult(B, x, v1));
476     }
477     PetscCall(VecCopy(v1, Bx));
478     PetscCall(MatMult(A, Bx, v2)); /* v2 = A*B*x */
479     PetscCall(VecCopy(v2, v1));
480     if (rart) {
481       PetscCall(MatMult(B, v1, v3)); /* v3 = R*A*R^t*x */
482     } else {
483       PetscCall(MatMultTranspose(B, v1, v3)); /* v3 = Bt*A*B*x */
484     }
485     PetscCall(VecNorm(v4, NORM_2, &norm_abs));
486     PetscCall(VecAXPY(v4, none, v3));
487     PetscCall(VecNorm(v4, NORM_2, &norm_rel));
488 
489     if (norm_abs > tol) norm_rel /= norm_abs;
490     if (norm_rel > tol) {
491       *flg = PETSC_FALSE;
492       PetscCall(PetscInfo(A, "Error: %" PetscInt_FMT "-th Mat%sMult() %g\n", i, rart ? "RARt" : "PtAP", (double)norm_rel));
493       break;
494     }
495   }
496 
497   PetscCall(PetscRandomDestroy(&rdm));
498   PetscCall(VecDestroy(&x));
499   PetscCall(VecDestroy(&Bx));
500   PetscCall(VecDestroy(&Cx));
501   PetscCall(VecDestroy(&v1));
502   PetscCall(VecDestroy(&v2));
503   PetscCall(VecDestroy(&v3));
504   PetscCall(VecDestroy(&v4));
505   PetscFunctionReturn(PETSC_SUCCESS);
506 }
507 
508 /*@
509   MatPtAPMultEqual - Compares matrix-vector products of C = Bt*A*B
510 
511   Collective
512 
513   Input Parameters:
514 + A - the first matrix
515 . B - the second matrix
516 . C - the third matrix
517 - n - number of random vectors to be tested
518 
519   Output Parameter:
520 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
521 
522   Level: intermediate
523 
524 .seealso: `Mat`, `MatMatMultEqual()`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
525 @*/
526 PetscErrorCode MatPtAPMultEqual(Mat A, Mat B, Mat C, PetscInt n, PetscBool *flg)
527 {
528   PetscFunctionBegin;
529   PetscCall(MatProjMultEqual_Private(A, B, C, n, PETSC_FALSE, flg));
530   PetscFunctionReturn(PETSC_SUCCESS);
531 }
532 
533 /*@
534   MatRARtMultEqual - Compares matrix-vector products of C = B*A*B^t
535 
536   Collective
537 
538   Input Parameters:
539 + A - the first matrix
540 . B - the second matrix
541 . C - the third matrix
542 - n - number of random vectors to be tested
543 
544   Output Parameter:
545 . flg - `PETSC_TRUE` if the products are equal; `PETSC_FALSE` otherwise.
546 
547   Level: intermediate
548 
549 .seealso: `Mat`, `MatMatMultEqual()`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
550 @*/
551 PetscErrorCode MatRARtMultEqual(Mat A, Mat B, Mat C, PetscInt n, PetscBool *flg)
552 {
553   PetscFunctionBegin;
554   PetscCall(MatProjMultEqual_Private(A, B, C, n, PETSC_TRUE, flg));
555   PetscFunctionReturn(PETSC_SUCCESS);
556 }
557 
558 /*@
559   MatIsLinear - Check if a shell matrix `A` is a linear operator.
560 
561   Collective
562 
563   Input Parameters:
564 + A - the shell matrix
565 - n - number of random vectors to be tested
566 
567   Output Parameter:
568 . flg - `PETSC_TRUE` if the shell matrix is linear; `PETSC_FALSE` otherwise.
569 
570   Level: intermediate
571 
572 .seealso: `Mat`, `MatMatMultEqual()`, `MatMultEqual()`, `MatMultAddEqual()`, `MatMultTransposeEqual()`
573 @*/
574 PetscErrorCode MatIsLinear(Mat A, PetscInt n, PetscBool *flg)
575 {
576   Vec         x, y, s1, s2;
577   PetscRandom rctx;
578   PetscScalar a;
579   PetscInt    k;
580   PetscReal   norm, normA;
581   MPI_Comm    comm;
582   PetscMPIInt rank;
583 
584   PetscFunctionBegin;
585   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
586   PetscCall(PetscObjectGetComm((PetscObject)A, &comm));
587   PetscCallMPI(MPI_Comm_rank(comm, &rank));
588 
589   PetscCall(PetscRandomCreate(comm, &rctx));
590   PetscCall(PetscRandomSetFromOptions(rctx));
591   PetscCall(MatCreateVecs(A, &x, &s1));
592   PetscCall(VecDuplicate(x, &y));
593   PetscCall(VecDuplicate(s1, &s2));
594 
595   *flg = PETSC_TRUE;
596   for (k = 0; k < n; k++) {
597     PetscCall(VecSetRandom(x, rctx));
598     PetscCall(VecSetRandom(y, rctx));
599     if (rank == 0) PetscCall(PetscRandomGetValue(rctx, &a));
600     PetscCallMPI(MPI_Bcast(&a, 1, MPIU_SCALAR, 0, comm));
601 
602     /* s2 = a*A*x + A*y */
603     PetscCall(MatMult(A, y, s2));  /* s2 = A*y */
604     PetscCall(MatMult(A, x, s1));  /* s1 = A*x */
605     PetscCall(VecAXPY(s2, a, s1)); /* s2 = a s1 + s2 */
606 
607     /* s1 = A * (a x + y) */
608     PetscCall(VecAXPY(y, a, x)); /* y = a x + y */
609     PetscCall(MatMult(A, y, s1));
610     PetscCall(VecNorm(s1, NORM_INFINITY, &normA));
611 
612     PetscCall(VecAXPY(s2, -1.0, s1)); /* s2 = - s1 + s2 */
613     PetscCall(VecNorm(s2, NORM_INFINITY, &norm));
614     if (norm / normA > 100. * PETSC_MACHINE_EPSILON) {
615       *flg = PETSC_FALSE;
616       PetscCall(PetscInfo(A, "Error: %" PetscInt_FMT "-th |A*(ax+y) - (a*A*x+A*y)|/|A(ax+y)| %g > tol %g\n", k, (double)(norm / normA), (double)(100. * PETSC_MACHINE_EPSILON)));
617       break;
618     }
619   }
620   PetscCall(PetscRandomDestroy(&rctx));
621   PetscCall(VecDestroy(&x));
622   PetscCall(VecDestroy(&y));
623   PetscCall(VecDestroy(&s1));
624   PetscCall(VecDestroy(&s2));
625   PetscFunctionReturn(PETSC_SUCCESS);
626 }
627