xref: /petsc/src/mat/interface/matproduct.c (revision d52a580b706c59ca78066c1e38754e45b6b56e2b)
1 /*
2     Routines for matrix products. Calling procedure:
3 
4     MatProductCreate(A,B,C,&D); or MatProductCreateWithMat(A,B,C,D)
5     MatProductSetType(D, MATPRODUCT_AB/AtB/ABt/PtAP/RARt/ABC)
6     MatProductSetAlgorithm(D, alg)
7     MatProductSetFill(D,fill)
8     MatProductSetFromOptions(D)
9       -> MatProductSetFromOptions_Private(D)
10            # Check matrix global sizes
11            if the matrices have the same setfromoptions routine, use it
12            if not, try:
13              -> Query MatProductSetFromOptions_Atype_Btype_Ctype_C(D) from A, B and C (in order)
14              if found -> run the specific setup that must set the symbolic operation (these callbacks should never fail)
15            if callback not found or no symbolic operation set
16              -> Query MatProductSetFromOptions_anytype_C(D) from A, B and C (in order) (e.g, matrices may have inner matrices like MATTRANSPOSEVIRTUAL)
17            if dispatch found but combination still not present do
18              -> check if B is dense and product type AtB or AB -> if true, basic looping of dense columns
19              -> check if triple product (PtAP, RARt or ABC) -> if true, set the Basic routines
20 
21     #  The setfromoptions calls MatProductSetFromOptions_Atype_Btype_Ctype should
22     #    Check matrix local sizes for mpi matrices
23     #    Set default algorithm
24     #    Get runtime option
25     #    Set D->ops->productsymbolic = MatProductSymbolic_productype_Atype_Btype_Ctype if found
26 
27     MatProductSymbolic(D)
28       # Call MatProductSymbolic_productype_Atype_Btype_Ctype()
29         the callback must set the numeric phase D->ops->productnumeric = MatProductNumeric_productype_Atype_Btype_Ctype
30 
31     MatProductNumeric(D)
32       # Call the numeric phase
33 
34     # The symbolic phases are allowed to set extra data structures and attach those to the product
35     # this additional data can be reused between multiple numeric phases with the same matrices
36     # if not needed, call
37     MatProductClear(D)
38 */
39 
40 #include <petsc/private/matimpl.h> /*I "petscmat.h" I*/
41 
42 const char *const MatProductTypes[] = {"UNSPECIFIED", "AB", "AtB", "ABt", "PtAP", "RARt", "ABC"};
43 
44 /* these are basic implementations relying on the old function pointers
45  * they are dangerous and should be removed in the future */
46 static PetscErrorCode MatProductNumeric_PtAP_Unsafe(Mat C)
47 {
48   Mat_Product *product = C->product;
49   Mat          P = product->B, AP = product->Dwork;
50 
51   PetscFunctionBegin;
52   /* AP = A*P */
53   PetscCall(MatProductNumeric(AP));
54   /* C = P^T*AP */
55   product->type = MATPRODUCT_AtB;
56   PetscCall((*C->ops->transposematmultnumeric)(P, AP, C));
57   product->type = MATPRODUCT_PtAP;
58   PetscFunctionReturn(PETSC_SUCCESS);
59 }
60 
61 static PetscErrorCode MatProductSymbolic_PtAP_Unsafe(Mat C)
62 {
63   Mat_Product *product = C->product;
64   Mat          A = product->A, P = product->B, AP;
65   PetscReal    fill = product->fill;
66 
67   PetscFunctionBegin;
68   PetscCall(PetscInfo(C, "for A %s, P %s is used\n", ((PetscObject)product->A)->type_name, ((PetscObject)product->B)->type_name));
69   /* AP = A*P */
70   PetscCall(MatProductCreate(A, P, NULL, &AP));
71   PetscCall(MatProductSetType(AP, MATPRODUCT_AB));
72   PetscCall(MatProductSetAlgorithm(AP, MATPRODUCTALGORITHMDEFAULT));
73   PetscCall(MatProductSetFill(AP, fill));
74   PetscCall(MatProductSetFromOptions(AP));
75   PetscCall(MatProductSymbolic(AP));
76 
77   /* C = P^T*AP */
78   PetscCall(MatProductSetType(C, MATPRODUCT_AtB));
79   PetscCall(MatProductSetAlgorithm(C, MATPRODUCTALGORITHMDEFAULT));
80   product->A = P;
81   product->B = AP;
82   PetscCall(MatProductSetFromOptions(C));
83   PetscCall(MatProductSymbolic(C));
84 
85   /* resume user's original input matrix setting for A and B */
86   product->type  = MATPRODUCT_PtAP;
87   product->A     = A;
88   product->B     = P;
89   product->Dwork = AP;
90 
91   C->ops->productnumeric = MatProductNumeric_PtAP_Unsafe;
92   PetscFunctionReturn(PETSC_SUCCESS);
93 }
94 
95 static PetscErrorCode MatProductNumeric_RARt_Unsafe(Mat C)
96 {
97   Mat_Product *product = C->product;
98   Mat          R = product->B, RA = product->Dwork;
99 
100   PetscFunctionBegin;
101   /* RA = R*A */
102   PetscCall(MatProductNumeric(RA));
103   /* C = RA*R^T */
104   product->type = MATPRODUCT_ABt;
105   PetscCall((*C->ops->mattransposemultnumeric)(RA, R, C));
106   product->type = MATPRODUCT_RARt;
107   PetscFunctionReturn(PETSC_SUCCESS);
108 }
109 
110 static PetscErrorCode MatProductSymbolic_RARt_Unsafe(Mat C)
111 {
112   Mat_Product *product = C->product;
113   Mat          A = product->A, R = product->B, RA;
114   PetscReal    fill = product->fill;
115 
116   PetscFunctionBegin;
117   PetscCall(PetscInfo(C, "for A %s, R %s is used\n", ((PetscObject)product->A)->type_name, ((PetscObject)product->B)->type_name));
118   /* RA = R*A */
119   PetscCall(MatProductCreate(R, A, NULL, &RA));
120   PetscCall(MatProductSetType(RA, MATPRODUCT_AB));
121   PetscCall(MatProductSetAlgorithm(RA, MATPRODUCTALGORITHMDEFAULT));
122   PetscCall(MatProductSetFill(RA, fill));
123   PetscCall(MatProductSetFromOptions(RA));
124   PetscCall(MatProductSymbolic(RA));
125 
126   /* C = RA*R^T */
127   PetscCall(MatProductSetType(C, MATPRODUCT_ABt));
128   PetscCall(MatProductSetAlgorithm(C, MATPRODUCTALGORITHMDEFAULT));
129   product->A = RA;
130   PetscCall(MatProductSetFromOptions(C));
131   PetscCall(MatProductSymbolic(C));
132 
133   /* resume user's original input matrix setting for A */
134   product->type          = MATPRODUCT_RARt;
135   product->A             = A;
136   product->Dwork         = RA; /* save here so it will be destroyed with product C */
137   C->ops->productnumeric = MatProductNumeric_RARt_Unsafe;
138   PetscFunctionReturn(PETSC_SUCCESS);
139 }
140 
141 static PetscErrorCode MatProductNumeric_ABC_Unsafe(Mat mat)
142 {
143   Mat_Product *product = mat->product;
144   Mat          A = product->A, BC = product->Dwork;
145 
146   PetscFunctionBegin;
147   /* Numeric BC = B*C */
148   PetscCall(MatProductNumeric(BC));
149   /* Numeric mat = A*BC */
150   product->type = MATPRODUCT_AB;
151   PetscCall((*mat->ops->matmultnumeric)(A, BC, mat));
152   product->type = MATPRODUCT_ABC;
153   PetscFunctionReturn(PETSC_SUCCESS);
154 }
155 
156 static PetscErrorCode MatProductSymbolic_ABC_Unsafe(Mat mat)
157 {
158   Mat_Product *product = mat->product;
159   Mat          B = product->B, C = product->C, BC;
160   PetscReal    fill = product->fill;
161 
162   PetscFunctionBegin;
163   PetscCall(PetscInfo(mat, "for A %s, B %s, C %s is used\n", ((PetscObject)product->A)->type_name, ((PetscObject)product->B)->type_name, ((PetscObject)product->C)->type_name));
164   /* Symbolic BC = B*C */
165   PetscCall(MatProductCreate(B, C, NULL, &BC));
166   PetscCall(MatProductSetType(BC, MATPRODUCT_AB));
167   PetscCall(MatProductSetAlgorithm(BC, MATPRODUCTALGORITHMDEFAULT));
168   PetscCall(MatProductSetFill(BC, fill));
169   PetscCall(MatProductSetFromOptions(BC));
170   PetscCall(MatProductSymbolic(BC));
171 
172   /* Symbolic mat = A*BC */
173   PetscCall(MatProductSetType(mat, MATPRODUCT_AB));
174   PetscCall(MatProductSetAlgorithm(mat, MATPRODUCTALGORITHMDEFAULT));
175   product->B     = BC;
176   product->Dwork = BC;
177   PetscCall(MatProductSetFromOptions(mat));
178   PetscCall(MatProductSymbolic(mat));
179 
180   /* resume user's original input matrix setting for B */
181   product->type            = MATPRODUCT_ABC;
182   product->B               = B;
183   mat->ops->productnumeric = MatProductNumeric_ABC_Unsafe;
184   PetscFunctionReturn(PETSC_SUCCESS);
185 }
186 
187 static PetscErrorCode MatProductSymbolic_Unsafe(Mat mat)
188 {
189   Mat_Product *product = mat->product;
190 
191   PetscFunctionBegin;
192   switch (product->type) {
193   case MATPRODUCT_PtAP:
194     PetscCall(MatProductSymbolic_PtAP_Unsafe(mat));
195     break;
196   case MATPRODUCT_RARt:
197     PetscCall(MatProductSymbolic_RARt_Unsafe(mat));
198     break;
199   case MATPRODUCT_ABC:
200     PetscCall(MatProductSymbolic_ABC_Unsafe(mat));
201     break;
202   default:
203     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[product->type]);
204   }
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 /*@
209   MatProductReplaceMats - Replace the input matrices for the matrix-matrix product operation inside the computed matrix
210 
211   Collective
212 
213   Input Parameters:
214 + A - the matrix or `NULL` if not being replaced
215 . B - the matrix or `NULL` if not being replaced
216 . C - the matrix or `NULL` if not being replaced
217 - D - the matrix whose values are computed via a matrix-matrix product operation
218 
219   Level: intermediate
220 
221   Note:
222   To reuse the symbolic phase, the input matrices must have exactly the same data structure as the replaced one.
223   If the type of any of the input matrices is different than what was previously used, or their symmetry flag changed but
224   the symbolic phase took advantage of their symmetry, the product is cleared and `MatProductSetFromOptions()`
225   and `MatProductSymbolic()` are invoked again.
226 
227 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreate()`, `MatProductSetFromOptions()`, `MatProductSymbolic()`, `MatProductClear()`
228 @*/
229 PetscErrorCode MatProductReplaceMats(Mat A, Mat B, Mat C, Mat D)
230 {
231   Mat_Product *product;
232   PetscBool    flgA = PETSC_TRUE, flgB = PETSC_TRUE, flgC = PETSC_TRUE, isset, issym;
233 
234   PetscFunctionBegin;
235   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
236   MatCheckProduct(D, 4);
237   product = D->product;
238   if (A) {
239     PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
240     PetscCall(PetscObjectReference((PetscObject)A));
241     PetscCall(PetscObjectTypeCompare((PetscObject)product->A, ((PetscObject)A)->type_name, &flgA));
242     PetscCall(MatIsSymmetricKnown(A, &isset, &issym));
243     if (product->symbolic_used_the_fact_A_is_symmetric && isset && !issym) { /* symbolic was built around a symmetric A, but the new A is not anymore */
244       flgA                                           = PETSC_FALSE;
245       product->symbolic_used_the_fact_A_is_symmetric = PETSC_FALSE; /* reinit */
246     }
247     PetscCall(MatDestroy(&product->A));
248     product->A = A;
249   }
250   if (B) {
251     PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
252     PetscCall(PetscObjectReference((PetscObject)B));
253     PetscCall(PetscObjectTypeCompare((PetscObject)product->B, ((PetscObject)B)->type_name, &flgB));
254     PetscCall(MatIsSymmetricKnown(B, &isset, &issym));
255     if (product->symbolic_used_the_fact_B_is_symmetric && isset && !issym) {
256       flgB                                           = PETSC_FALSE;
257       product->symbolic_used_the_fact_B_is_symmetric = PETSC_FALSE; /* reinit */
258     }
259     PetscCall(MatDestroy(&product->B));
260     product->B = B;
261   }
262   if (C) {
263     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
264     PetscCall(PetscObjectReference((PetscObject)C));
265     PetscCall(PetscObjectTypeCompare((PetscObject)product->C, ((PetscObject)C)->type_name, &flgC));
266     PetscCall(MatIsSymmetricKnown(C, &isset, &issym));
267     if (product->symbolic_used_the_fact_C_is_symmetric && isset && !issym) {
268       flgC                                           = PETSC_FALSE;
269       product->symbolic_used_the_fact_C_is_symmetric = PETSC_FALSE; /* reinit */
270     }
271     PetscCall(MatDestroy(&product->C));
272     product->C = C;
273   }
274   /* Any of the replaced mats is of a different type, reset */
275   if (!flgA || !flgB || !flgC) {
276     if (D->product->destroy) PetscCall((*D->product->destroy)(&D->product->data));
277     D->product->destroy = NULL;
278     D->product->data    = NULL;
279     if (D->ops->productnumeric || D->ops->productsymbolic) {
280       PetscCall(MatProductSetFromOptions(D));
281       PetscCall(MatProductSymbolic(D));
282     }
283   }
284   PetscFunctionReturn(PETSC_SUCCESS);
285 }
286 
287 static PetscErrorCode MatProductNumeric_X_Dense(Mat C)
288 {
289   Mat_Product *product = C->product;
290   Mat          A = product->A, B = product->B;
291   PetscInt     k, K              = B->cmap->N;
292   PetscBool    t = PETSC_TRUE, iscuda = PETSC_FALSE;
293   PetscBool    Bcpu = PETSC_TRUE, Ccpu = PETSC_TRUE;
294   char        *Btype = NULL, *Ctype = NULL;
295 
296   PetscFunctionBegin;
297   switch (product->type) {
298   case MATPRODUCT_AB:
299     t = PETSC_FALSE;
300   case MATPRODUCT_AtB:
301     break;
302   default:
303     SETERRQ(PetscObjectComm((PetscObject)C), PETSC_ERR_SUP, "MatProductNumeric type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name);
304   }
305   if (PetscDefined(HAVE_CUDA)) {
306     VecType vtype;
307 
308     PetscCall(MatGetVecType(A, &vtype));
309     PetscCall(PetscStrcmp(vtype, VECCUDA, &iscuda));
310     if (!iscuda) PetscCall(PetscStrcmp(vtype, VECSEQCUDA, &iscuda));
311     if (!iscuda) PetscCall(PetscStrcmp(vtype, VECMPICUDA, &iscuda));
312     if (iscuda) { /* Make sure we have up-to-date data on the GPU */
313       PetscCall(PetscStrallocpy(((PetscObject)B)->type_name, &Btype));
314       PetscCall(PetscStrallocpy(((PetscObject)C)->type_name, &Ctype));
315       PetscCall(MatConvert(B, MATDENSECUDA, MAT_INPLACE_MATRIX, &B));
316       if (!C->assembled) { /* need to flag the matrix as assembled, otherwise MatConvert will complain */
317         PetscCall(MatAssemblyBegin(C, MAT_FINAL_ASSEMBLY));
318         PetscCall(MatAssemblyEnd(C, MAT_FINAL_ASSEMBLY));
319       }
320       PetscCall(MatConvert(C, MATDENSECUDA, MAT_INPLACE_MATRIX, &C));
321     } else { /* Make sure we have up-to-date data on the CPU */
322 #if defined(PETSC_HAVE_CUDA) || defined(PETSC_HAVE_VIENNACL)
323       Bcpu = B->boundtocpu;
324       Ccpu = C->boundtocpu;
325 #endif
326       PetscCall(MatBindToCPU(B, PETSC_TRUE));
327       PetscCall(MatBindToCPU(C, PETSC_TRUE));
328     }
329   }
330   for (k = 0; k < K; k++) {
331     Vec x, y;
332 
333     PetscCall(MatDenseGetColumnVecRead(B, k, &x));
334     PetscCall(MatDenseGetColumnVecWrite(C, k, &y));
335     if (t) {
336       PetscCall(MatMultTranspose(A, x, y));
337     } else {
338       PetscCall(MatMult(A, x, y));
339     }
340     PetscCall(MatDenseRestoreColumnVecRead(B, k, &x));
341     PetscCall(MatDenseRestoreColumnVecWrite(C, k, &y));
342   }
343   PetscCall(MatSetOption(C, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE));
344   PetscCall(MatAssemblyBegin(C, MAT_FINAL_ASSEMBLY));
345   PetscCall(MatAssemblyEnd(C, MAT_FINAL_ASSEMBLY));
346   if (PetscDefined(HAVE_CUDA)) {
347     if (iscuda) {
348       PetscCall(MatConvert(B, Btype, MAT_INPLACE_MATRIX, &B));
349       PetscCall(MatConvert(C, Ctype, MAT_INPLACE_MATRIX, &C));
350     } else {
351       PetscCall(MatBindToCPU(B, Bcpu));
352       PetscCall(MatBindToCPU(C, Ccpu));
353     }
354   }
355   PetscCall(PetscFree(Btype));
356   PetscCall(PetscFree(Ctype));
357   PetscFunctionReturn(PETSC_SUCCESS);
358 }
359 
360 static PetscErrorCode MatProductSymbolic_X_Dense(Mat C)
361 {
362   Mat_Product *product = C->product;
363   Mat          A = product->A, B = product->B;
364   PetscBool    isdense;
365 
366   PetscFunctionBegin;
367   switch (product->type) {
368   case MATPRODUCT_AB:
369     PetscCall(MatSetSizes(C, A->rmap->n, B->cmap->n, A->rmap->N, B->cmap->N));
370     break;
371   case MATPRODUCT_AtB:
372     PetscCall(MatSetSizes(C, A->cmap->n, B->cmap->n, A->cmap->N, B->cmap->N));
373     break;
374   default:
375     SETERRQ(PetscObjectComm((PetscObject)C), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name);
376   }
377   PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)C, &isdense, MATSEQDENSE, MATMPIDENSE, ""));
378   if (!isdense) {
379     PetscCall(MatSetType(C, ((PetscObject)B)->type_name));
380     /* If matrix type of C was not set or not dense, we need to reset the pointer */
381     C->ops->productsymbolic = MatProductSymbolic_X_Dense;
382   }
383   C->ops->productnumeric = MatProductNumeric_X_Dense;
384   PetscCall(MatSetUp(C));
385   PetscFunctionReturn(PETSC_SUCCESS);
386 }
387 
388 /* a single driver to query the dispatching */
389 static PetscErrorCode MatProductSetFromOptions_Private(Mat mat)
390 {
391   Mat_Product      *product = mat->product;
392   PetscInt          Am, An, Bm, Bn, Cm, Cn;
393   Mat               A = product->A, B = product->B, C = product->C;
394   const char *const Bnames[] = {"B", "R", "P"};
395   const char       *bname;
396   PetscErrorCode (*fA)(Mat);
397   PetscErrorCode (*fB)(Mat);
398   PetscErrorCode (*fC)(Mat);
399   PetscErrorCode (*f)(Mat) = NULL;
400 
401   PetscFunctionBegin;
402   mat->ops->productsymbolic = NULL;
403   mat->ops->productnumeric  = NULL;
404   if (product->type == MATPRODUCT_UNSPECIFIED) PetscFunctionReturn(PETSC_SUCCESS);
405   PetscCheck(A, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing A mat");
406   PetscCheck(B, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing B mat");
407   PetscCheck(product->type != MATPRODUCT_ABC || C, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing C mat");
408   if (product->type != MATPRODUCT_ABC) C = NULL; /* do not use C if not needed */
409   if (product->type == MATPRODUCT_RARt) bname = Bnames[1];
410   else if (product->type == MATPRODUCT_PtAP) bname = Bnames[2];
411   else bname = Bnames[0];
412 
413   /* Check matrices sizes */
414   Am = A->rmap->N;
415   An = A->cmap->N;
416   Bm = B->rmap->N;
417   Bn = B->cmap->N;
418   Cm = C ? C->rmap->N : 0;
419   Cn = C ? C->cmap->N : 0;
420   if (product->type == MATPRODUCT_RARt || product->type == MATPRODUCT_ABt) {
421     PetscInt t = Bn;
422     Bn         = Bm;
423     Bm         = t;
424   }
425   if (product->type == MATPRODUCT_AtB) An = Am;
426 
427   PetscCheck(An == Bm, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_SIZ, "Matrix dimensions of A and %s are incompatible for MatProductType %s: A %" PetscInt_FMT "x%" PetscInt_FMT ", %s %" PetscInt_FMT "x%" PetscInt_FMT, bname,
428              MatProductTypes[product->type], A->rmap->N, A->cmap->N, bname, B->rmap->N, B->cmap->N);
429   PetscCheck(!Cm || Cm == Bn, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_SIZ, "Matrix dimensions of B and C are incompatible for MatProductType %s: B %" PetscInt_FMT "x%" PetscInt_FMT ", C %" PetscInt_FMT "x%" PetscInt_FMT,
430              MatProductTypes[product->type], B->rmap->N, B->cmap->N, Cm, Cn);
431 
432   fA = A->ops->productsetfromoptions;
433   fB = B->ops->productsetfromoptions;
434   fC = C ? C->ops->productsetfromoptions : fA;
435   if (C) {
436     PetscCall(PetscInfo(mat, "MatProductType %s for A %s, %s %s, C %s\n", MatProductTypes[product->type], ((PetscObject)A)->type_name, bname, ((PetscObject)B)->type_name, ((PetscObject)C)->type_name));
437   } else {
438     PetscCall(PetscInfo(mat, "MatProductType %s for A %s, %s %s\n", MatProductTypes[product->type], ((PetscObject)A)->type_name, bname, ((PetscObject)B)->type_name));
439   }
440   if (fA == fB && fA == fC && fA) {
441     PetscCall(PetscInfo(mat, "  matching op\n"));
442     PetscCall((*fA)(mat));
443   }
444   /* We may have found f but it did not succeed */
445   if (!mat->ops->productsymbolic) { /* query MatProductSetFromOptions_Atype_Btype_Ctype */
446     char mtypes[256];
447     PetscCall(PetscStrncpy(mtypes, "MatProductSetFromOptions_", sizeof(mtypes)));
448     PetscCall(PetscStrlcat(mtypes, ((PetscObject)A)->type_name, sizeof(mtypes)));
449     PetscCall(PetscStrlcat(mtypes, "_", sizeof(mtypes)));
450     PetscCall(PetscStrlcat(mtypes, ((PetscObject)B)->type_name, sizeof(mtypes)));
451     if (C) {
452       PetscCall(PetscStrlcat(mtypes, "_", sizeof(mtypes)));
453       PetscCall(PetscStrlcat(mtypes, ((PetscObject)C)->type_name, sizeof(mtypes)));
454     }
455     PetscCall(PetscStrlcat(mtypes, "_C", sizeof(mtypes)));
456 #if defined(__clang__)
457     PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wformat-pedantic")
458 #elif defined(__GNUC__) || defined(__GNUG__)
459     PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wformat")
460 #endif
461     PetscCall(PetscObjectQueryFunction((PetscObject)A, mtypes, &f));
462     PetscCall(PetscInfo(mat, "  querying %s from A? %p\n", mtypes, f));
463     if (!f) {
464       PetscCall(PetscObjectQueryFunction((PetscObject)B, mtypes, &f));
465       PetscCall(PetscInfo(mat, "  querying %s from %s? %p\n", mtypes, bname, f));
466     }
467     if (!f && C) {
468       PetscCall(PetscObjectQueryFunction((PetscObject)C, mtypes, &f));
469       PetscCall(PetscInfo(mat, "  querying %s from C? %p\n", mtypes, f));
470     }
471     if (f) PetscCall((*f)(mat));
472 
473     /* We may have found f but it did not succeed */
474     /* some matrices (i.e. MATTRANSPOSEVIRTUAL, MATSHELL constructed from MatConvert), knows what to do with their inner matrices */
475     if (!mat->ops->productsymbolic) {
476       PetscCall(PetscStrncpy(mtypes, "MatProductSetFromOptions_anytype_C", sizeof(mtypes)));
477       PetscCall(PetscObjectQueryFunction((PetscObject)A, mtypes, &f));
478       PetscCall(PetscInfo(mat, "  querying %s from A? %p\n", mtypes, f));
479       if (!f) {
480         PetscCall(PetscObjectQueryFunction((PetscObject)B, mtypes, &f));
481         PetscCall(PetscInfo(mat, "  querying %s from %s? %p\n", mtypes, bname, f));
482       }
483       if (!f && C) {
484         PetscCall(PetscObjectQueryFunction((PetscObject)C, mtypes, &f));
485         PetscCall(PetscInfo(mat, "  querying %s from C? %p\n", mtypes, f));
486       }
487     }
488     if (f) PetscCall((*f)(mat));
489   }
490   PETSC_PRAGMA_DIAGNOSTIC_IGNORED_END()
491   /* We may have found f but it did not succeed */
492   if (!mat->ops->productsymbolic) {
493     /* we can still compute the product if B is of type dense */
494     if (product->type == MATPRODUCT_AB || product->type == MATPRODUCT_AtB) {
495       PetscBool isdense;
496 
497       PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)B, &isdense, MATSEQDENSE, MATMPIDENSE, ""));
498       if (isdense) {
499         mat->ops->productsymbolic = MatProductSymbolic_X_Dense;
500         PetscCall(PetscInfo(mat, "  using basic looping over columns of a dense matrix\n"));
501       }
502     } else if (product->type != MATPRODUCT_ABt) { /* use MatProductSymbolic/Numeric_Unsafe() for triple products only */
503       /*
504          TODO: this should be changed to a proper setfromoptions, not setting the symbolic pointer here, because we do not know if
505                the combination will succeed. In order to be sure, we need MatProductGetProductType to return the type of the result
506                before computing the symbolic phase
507       */
508       PetscCall(PetscInfo(mat, "  symbolic product not supported, using MatProductSymbolic_Unsafe() implementation\n"));
509       mat->ops->productsymbolic = MatProductSymbolic_Unsafe;
510     }
511   }
512   if (!mat->ops->productsymbolic) PetscCall(PetscInfo(mat, "  symbolic product is not supported\n"));
513   PetscFunctionReturn(PETSC_SUCCESS);
514 }
515 
516 /*@
517   MatProductSetFromOptions - Sets the options for the computation of a matrix-matrix product operation where the type,
518   the algorithm etc are determined from the options database.
519 
520   Logically Collective
521 
522   Input Parameter:
523 . mat - the matrix whose values are computed via a matrix-matrix product operation
524 
525   Options Database Keys:
526 + -mat_product_clear                 - Clear intermediate data structures after `MatProductNumeric()` has been called
527 . -mat_product_algorithm <algorithm> - Sets the algorithm, see `MatProductAlgorithm` for possible values
528 - -mat_product_algorithm_backend_cpu - Use the CPU to perform the computation even if the matrix is a GPU matrix
529 
530   Level: intermediate
531 
532   Note:
533   The `-mat_product_clear` option reduces memory usage but means that the matrix cannot be re-used for a matrix-matrix product operation
534 
535 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatSetFromOptions()`, `MatProductCreate()`, `MatProductCreateWithMat()`, `MatProductNumeric()`,
536           `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductAlgorithm`
537 @*/
538 PetscErrorCode MatProductSetFromOptions(Mat mat)
539 {
540   PetscFunctionBegin;
541   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
542   MatCheckProduct(mat, 1);
543   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_ORDER, "Cannot call MatProductSetFromOptions() with already present data");
544   mat->product->setfromoptionscalled = PETSC_TRUE;
545   PetscObjectOptionsBegin((PetscObject)mat);
546   PetscCall(PetscOptionsBool("-mat_product_clear", "Clear intermediate data structures after MatProductNumeric() has been called", "MatProductClear", mat->product->clear, &mat->product->clear, NULL));
547   PetscCall(PetscOptionsDeprecated("-mat_freeintermediatedatastructures", "-mat_product_clear", "3.13", "Or call MatProductClear() after MatProductNumeric()"));
548   PetscOptionsEnd();
549   PetscCall(MatProductSetFromOptions_Private(mat));
550   PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing product after setup phase");
551   PetscFunctionReturn(PETSC_SUCCESS);
552 }
553 
554 /*@
555   MatProductView - View the private matrix-matrix algorithm object within a matrix
556 
557   Logically Collective
558 
559   Input Parameters:
560 + mat    - the matrix obtained with `MatProductCreate()` or `MatProductCreateWithMat()`
561 - viewer - where the information on the matrix-matrix algorithm of `mat` should be reviewed
562 
563   Level: intermediate
564 
565   Developer Note:
566   Shouldn't this information be printed from an appropriate `MatView()` with perhaps certain formats set?
567 
568 .seealso: [](ch_matrices), `MatProductType`, `Mat`, `MatProductSetFromOptions()`, `MatView()`, `MatProductCreate()`, `MatProductCreateWithMat()`
569 @*/
570 PetscErrorCode MatProductView(Mat mat, PetscViewer viewer)
571 {
572   PetscFunctionBegin;
573   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
574   if (!mat->product) PetscFunctionReturn(PETSC_SUCCESS);
575   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)mat), &viewer));
576   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
577   PetscCheckSameComm(mat, 1, viewer, 2);
578   if (mat->product->view) PetscCall((*mat->product->view)(mat, viewer));
579   PetscFunctionReturn(PETSC_SUCCESS);
580 }
581 
582 /* these are basic implementations relying on the old function pointers
583  * they are dangerous and should be removed in the future */
584 PetscErrorCode MatProductNumeric_AB(Mat mat)
585 {
586   Mat_Product *product = mat->product;
587   Mat          A = product->A, B = product->B;
588 
589   PetscFunctionBegin;
590   PetscCall((*mat->ops->matmultnumeric)(A, B, mat));
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 PetscErrorCode MatProductNumeric_AtB(Mat mat)
595 {
596   Mat_Product *product = mat->product;
597   Mat          A = product->A, B = product->B;
598 
599   PetscFunctionBegin;
600   PetscCall((*mat->ops->transposematmultnumeric)(A, B, mat));
601   PetscFunctionReturn(PETSC_SUCCESS);
602 }
603 
604 PetscErrorCode MatProductNumeric_ABt(Mat mat)
605 {
606   Mat_Product *product = mat->product;
607   Mat          A = product->A, B = product->B;
608 
609   PetscFunctionBegin;
610   PetscCall((*mat->ops->mattransposemultnumeric)(A, B, mat));
611   PetscFunctionReturn(PETSC_SUCCESS);
612 }
613 
614 PetscErrorCode MatProductNumeric_PtAP(Mat mat)
615 {
616   Mat_Product *product = mat->product;
617   Mat          A = product->A, B = product->B;
618 
619   PetscFunctionBegin;
620   PetscCall((*mat->ops->ptapnumeric)(A, B, mat));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode MatProductNumeric_RARt(Mat mat)
625 {
626   Mat_Product *product = mat->product;
627   Mat          A = product->A, B = product->B;
628 
629   PetscFunctionBegin;
630   PetscCall((*mat->ops->rartnumeric)(A, B, mat));
631   PetscFunctionReturn(PETSC_SUCCESS);
632 }
633 
634 PetscErrorCode MatProductNumeric_ABC(Mat mat)
635 {
636   Mat_Product *product = mat->product;
637   Mat          A = product->A, B = product->B, C = product->C;
638 
639   PetscFunctionBegin;
640   PetscCall((*mat->ops->matmatmultnumeric)(A, B, C, mat));
641   PetscFunctionReturn(PETSC_SUCCESS);
642 }
643 
644 /*@
645   MatProductNumeric - Compute a matrix-matrix product operation with the numerical values
646 
647   Collective
648 
649   Input/Output Parameter:
650 . mat - the matrix whose values are computed via a matrix-matrix product operation
651 
652   Level: intermediate
653 
654   Note:
655   `MatProductSymbolic()` must have been called on `mat` before calling this function
656 
657 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductSetAlgorithm()`, `MatProductSetType()`, `MatProductCreate()`, `MatSetType()`, `MatProductSymbolic()`
658 @*/
659 PetscErrorCode MatProductNumeric(Mat mat)
660 {
661   PetscLogEvent eventtype = -1;
662 
663   PetscFunctionBegin;
664   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
665   MatCheckProduct(mat, 1);
666   switch (mat->product->type) {
667   case MATPRODUCT_AB:
668     eventtype = MAT_MatMultNumeric;
669     break;
670   case MATPRODUCT_AtB:
671     eventtype = MAT_TransposeMatMultNumeric;
672     break;
673   case MATPRODUCT_ABt:
674     eventtype = MAT_MatTransposeMultNumeric;
675     break;
676   case MATPRODUCT_PtAP:
677     eventtype = MAT_PtAPNumeric;
678     break;
679   case MATPRODUCT_RARt:
680     eventtype = MAT_RARtNumeric;
681     break;
682   case MATPRODUCT_ABC:
683     eventtype = MAT_MatMatMultNumeric;
684     break;
685   default:
686     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[mat->product->type]);
687   }
688 
689   if (mat->ops->productnumeric) {
690     PetscCall(PetscLogEventBegin(eventtype, mat, 0, 0, 0));
691     PetscUseTypeMethod(mat, productnumeric);
692     PetscCall(PetscLogEventEnd(eventtype, mat, 0, 0, 0));
693   } else if (mat->product) {
694     char errstr[256];
695 
696     if (mat->product->type == MATPRODUCT_ABC) {
697       PetscCall(PetscSNPrintf(errstr, 256, "%s with A %s, B %s, C %s", MatProductTypes[mat->product->type], ((PetscObject)mat->product->A)->type_name, ((PetscObject)mat->product->B)->type_name, ((PetscObject)mat->product->C)->type_name));
698     } else {
699       PetscCall(PetscSNPrintf(errstr, 256, "%s with A %s, B %s", MatProductTypes[mat->product->type], ((PetscObject)mat->product->A)->type_name, ((PetscObject)mat->product->B)->type_name));
700     }
701     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Unspecified numeric phase for product %s", errstr);
702   }
703   PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing struct after numeric phase for product");
704 
705   if (mat->product->clear) PetscCall(MatProductClear(mat));
706   PetscCall(PetscObjectStateIncrease((PetscObject)mat));
707   PetscFunctionReturn(PETSC_SUCCESS);
708 }
709 
710 /* these are basic implementations relying on the old function pointers
711  * they are dangerous and should be removed in the future */
712 PetscErrorCode MatProductSymbolic_AB(Mat mat)
713 {
714   Mat_Product *product = mat->product;
715   Mat          A = product->A, B = product->B;
716 
717   PetscFunctionBegin;
718   PetscCall((*mat->ops->matmultsymbolic)(A, B, product->fill, mat));
719   mat->ops->productnumeric = MatProductNumeric_AB;
720   PetscFunctionReturn(PETSC_SUCCESS);
721 }
722 
723 PetscErrorCode MatProductSymbolic_AtB(Mat mat)
724 {
725   Mat_Product *product = mat->product;
726   Mat          A = product->A, B = product->B;
727 
728   PetscFunctionBegin;
729   PetscCall((*mat->ops->transposematmultsymbolic)(A, B, product->fill, mat));
730   mat->ops->productnumeric = MatProductNumeric_AtB;
731   PetscFunctionReturn(PETSC_SUCCESS);
732 }
733 
734 PetscErrorCode MatProductSymbolic_ABt(Mat mat)
735 {
736   Mat_Product *product = mat->product;
737   Mat          A = product->A, B = product->B;
738 
739   PetscFunctionBegin;
740   PetscCall((*mat->ops->mattransposemultsymbolic)(A, B, product->fill, mat));
741   mat->ops->productnumeric = MatProductNumeric_ABt;
742   PetscFunctionReturn(PETSC_SUCCESS);
743 }
744 
745 PetscErrorCode MatProductSymbolic_ABC(Mat mat)
746 {
747   Mat_Product *product = mat->product;
748   Mat          A = product->A, B = product->B, C = product->C;
749 
750   PetscFunctionBegin;
751   PetscCall((*mat->ops->matmatmultsymbolic)(A, B, C, product->fill, mat));
752   mat->ops->productnumeric = MatProductNumeric_ABC;
753   PetscFunctionReturn(PETSC_SUCCESS);
754 }
755 
756 /*@
757   MatProductSymbolic - Perform the symbolic portion of a matrix-matrix product operation, this creates a data structure for use with the numerical
758   product to be done with `MatProductNumeric()`
759 
760   Collective
761 
762   Input/Output Parameter:
763 . mat - the matrix whose values are to be computed via a matrix-matrix product operation
764 
765   Level: intermediate
766 
767   Note:
768   `MatProductSetFromOptions()` must have been called on `mat` before calling this function
769 
770 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreate()`, `MatProductCreateWithMat()`, `MatProductSetFromOptions()`, `MatProductNumeric()`, `MatProductSetType()`, `MatProductSetAlgorithm()`
771 @*/
772 PetscErrorCode MatProductSymbolic(Mat mat)
773 {
774   PetscLogEvent eventtype = -1;
775   PetscBool     missing   = PETSC_FALSE;
776   Mat_Product  *product   = mat->product;
777   Mat           A         = product->A;
778   Mat           B         = product->B;
779   Mat           C         = product->C;
780 
781   PetscFunctionBegin;
782   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
783   MatCheckProduct(mat, 1);
784   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_ORDER, "Cannot run symbolic phase. Product data not empty");
785   switch (mat->product->type) {
786   case MATPRODUCT_AB:
787     eventtype = MAT_MatMultSymbolic;
788     break;
789   case MATPRODUCT_AtB:
790     eventtype = MAT_TransposeMatMultSymbolic;
791     break;
792   case MATPRODUCT_ABt:
793     eventtype = MAT_MatTransposeMultSymbolic;
794     break;
795   case MATPRODUCT_PtAP:
796     eventtype = MAT_PtAPSymbolic;
797     break;
798   case MATPRODUCT_RARt:
799     eventtype = MAT_RARtSymbolic;
800     break;
801   case MATPRODUCT_ABC:
802     eventtype = MAT_MatMatMultSymbolic;
803     break;
804   default:
805     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[mat->product->type]);
806   }
807   mat->ops->productnumeric = NULL;
808   if (mat->ops->productsymbolic) {
809     PetscCall(PetscLogEventBegin(eventtype, mat, 0, 0, 0));
810     PetscUseTypeMethod(mat, productsymbolic);
811     PetscCall(PetscLogEventEnd(eventtype, mat, 0, 0, 0));
812   } else missing = PETSC_TRUE;
813   if (missing || !mat->product || !mat->ops->productnumeric) {
814     char errstr[256];
815 
816     if (mat->product->type == MATPRODUCT_ABC) {
817       PetscCall(PetscSNPrintf(errstr, 256, "%s with A %s, B %s, C %s", MatProductTypes[mat->product->type], ((PetscObject)mat->product->A)->type_name, ((PetscObject)mat->product->B)->type_name, ((PetscObject)mat->product->C)->type_name));
818     } else {
819       PetscCall(PetscSNPrintf(errstr, 256, "%s with A %s, B %s", MatProductTypes[mat->product->type], ((PetscObject)mat->product->A)->type_name, ((PetscObject)mat->product->B)->type_name));
820     }
821     PetscCheck(mat->product->setfromoptionscalled, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Unspecified symbolic phase for product %s. Call MatProductSetFromOptions() first", errstr);
822     PetscCheck(!missing, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Unspecified symbolic phase for product %s. The product is not supported", errstr);
823     PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing struct after symbolic phase for product %s", errstr);
824   }
825 #if defined(PETSC_HAVE_DEVICE)
826   PetscBool bindingpropagates;
827   bindingpropagates = (PetscBool)((A->boundtocpu && A->bindingpropagates) || (B->boundtocpu && B->bindingpropagates));
828   if (C) bindingpropagates = (PetscBool)(bindingpropagates || (C->boundtocpu && C->bindingpropagates));
829   if (bindingpropagates) {
830     PetscCall(MatBindToCPU(mat, PETSC_TRUE));
831     PetscCall(MatSetBindingPropagates(mat, PETSC_TRUE));
832   }
833 #endif
834   /* set block sizes */
835   switch (product->type) {
836   case MATPRODUCT_PtAP:
837     if (B->cmap->bs > 1) PetscCall(MatSetBlockSizes(mat, B->cmap->bs, B->cmap->bs));
838     break;
839   case MATPRODUCT_RARt:
840     if (B->rmap->bs > 1) PetscCall(MatSetBlockSizes(mat, B->rmap->bs, B->rmap->bs));
841     break;
842   case MATPRODUCT_ABC:
843     PetscCall(MatSetBlockSizesFromMats(mat, A, C));
844     break;
845   case MATPRODUCT_AB:
846     PetscCall(MatSetBlockSizesFromMats(mat, A, B));
847     break;
848   case MATPRODUCT_AtB:
849     if (A->cmap->bs > 1 || B->cmap->bs > 1) PetscCall(MatSetBlockSizes(mat, A->cmap->bs, B->cmap->bs));
850     break;
851   case MATPRODUCT_ABt:
852     if (A->rmap->bs > 1 || B->rmap->bs > 1) PetscCall(MatSetBlockSizes(mat, A->rmap->bs, B->rmap->bs));
853     break;
854   default:
855     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Not for ProductType %s", MatProductTypes[product->type]);
856   }
857   PetscFunctionReturn(PETSC_SUCCESS);
858 }
859 
860 /*@
861   MatProductSetFill - Set an expected fill of the matrix whose values are computed via a matrix-matrix product operation
862 
863   Collective
864 
865   Input Parameters:
866 + mat  - the matrix whose values are to be computed via a matrix-matrix product operation
867 - fill - expected fill as ratio of nnz(mat)/(nnz(A) + nnz(B) + nnz(C)); use `PETSC_DETERMINE` or `PETSC_CURRENT` if you do not have a good estimate.
868          If the product is a dense matrix, this value is not used.
869 
870   Level: intermediate
871 
872   Notes:
873   Use `fill` of `PETSC_DETERMINE` to use the default value.
874 
875   The deprecated `PETSC_DEFAULT` is also supported to mean use the current value.
876 
877 .seealso: [](ch_matrices), `MatProduct`, `PETSC_DETERMINE`, `Mat`, `MatProductSetFromOptions()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
878 @*/
879 PetscErrorCode MatProductSetFill(Mat mat, PetscReal fill)
880 {
881   PetscFunctionBegin;
882   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
883   MatCheckProduct(mat, 1);
884   if (fill == (PetscReal)PETSC_DETERMINE) mat->product->fill = mat->product->default_fill;
885   else if (fill != (PetscReal)PETSC_CURRENT) mat->product->fill = fill;
886   PetscFunctionReturn(PETSC_SUCCESS);
887 }
888 
889 /*@
890   MatProductSetAlgorithm - Requests a particular algorithm for a matrix-matrix product operation that will perform to compute the given matrix
891 
892   Collective
893 
894   Input Parameters:
895 + mat - the matrix whose values are computed via a matrix-matrix product operation
896 - alg - particular implementation algorithm of the matrix product, e.g., `MATPRODUCTALGORITHMDEFAULT`.
897 
898   Options Database Key:
899 . -mat_product_algorithm <algorithm> - Sets the algorithm, see `MatProductAlgorithm`
900 
901   Level: intermediate
902 
903 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductClear()`, `MatProductSetType()`, `MatProductSetFill()`, `MatProductCreate()`, `MatProductAlgorithm`, `MatProductType`, `MatProductGetAlgorithm()`
904 @*/
905 PetscErrorCode MatProductSetAlgorithm(Mat mat, MatProductAlgorithm alg)
906 {
907   PetscFunctionBegin;
908   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
909   MatCheckProduct(mat, 1);
910   PetscCall(PetscFree(mat->product->alg));
911   PetscCall(PetscStrallocpy(alg, &mat->product->alg));
912   PetscFunctionReturn(PETSC_SUCCESS);
913 }
914 
915 /*@
916   MatProductGetAlgorithm - Returns the selected algorithm for a matrix-matrix product operation
917 
918   Not Collective
919 
920   Input Parameter:
921 . mat - the matrix whose values are computed via a matrix-matrix product operation
922 
923   Output Parameter:
924 . alg - the selected algorithm of the matrix product, e.g., `MATPRODUCTALGORITHMDEFAULT`.
925 
926   Level: intermediate
927 
928 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductSetAlgorithm()`
929 @*/
930 PetscErrorCode MatProductGetAlgorithm(Mat mat, MatProductAlgorithm *alg)
931 {
932   PetscFunctionBegin;
933   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
934   PetscAssertPointer(alg, 2);
935   if (mat->product) *alg = mat->product->alg;
936   else *alg = NULL;
937   PetscFunctionReturn(PETSC_SUCCESS);
938 }
939 
940 /*@
941   MatProductSetType - Sets a particular matrix-matrix product operation to be used to compute the values of the given matrix
942 
943   Collective
944 
945   Input Parameters:
946 + mat        - the matrix whose values are computed via a matrix-matrix product operation
947 - productype - matrix product type, e.g., `MATPRODUCT_AB`,`MATPRODUCT_AtB`,`MATPRODUCT_ABt`,`MATPRODUCT_PtAP`,`MATPRODUCT_RARt`,`MATPRODUCT_ABC`,
948                see `MatProductType`
949 
950   Level: intermediate
951 
952   Note:
953   The small t represents the transpose operation.
954 
955 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreate()`, `MatProductType`,
956           `MATPRODUCT_AB`, `MATPRODUCT_AtB`, `MATPRODUCT_ABt`, `MATPRODUCT_PtAP`, `MATPRODUCT_RARt`, `MATPRODUCT_ABC`
957 @*/
958 PetscErrorCode MatProductSetType(Mat mat, MatProductType productype)
959 {
960   PetscFunctionBegin;
961   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
962   MatCheckProduct(mat, 1);
963   PetscValidLogicalCollectiveEnum(mat, productype, 2);
964   if (productype != mat->product->type) {
965     if (mat->product->destroy) PetscCall((*mat->product->destroy)(&mat->product->data));
966     mat->product->destroy     = NULL;
967     mat->product->data        = NULL;
968     mat->ops->productsymbolic = NULL;
969     mat->ops->productnumeric  = NULL;
970   }
971   mat->product->type = productype;
972   PetscFunctionReturn(PETSC_SUCCESS);
973 }
974 
975 /*@
976   MatProductClear - Clears from the matrix any internal data structures related to the computation of the values of the matrix from matrix-matrix product operations
977 
978   Collective
979 
980   Input Parameter:
981 . mat - the matrix whose values are to be computed via a matrix-matrix product operation
982 
983   Options Database Key:
984 . -mat_product_clear - Clear intermediate data structures after `MatProductNumeric()` has been called
985 
986   Level: intermediate
987 
988   Notes:
989   This function should be called to remove any intermediate data used to compute the matrix to free up memory.
990 
991   After having called this function, matrix-matrix product operations can no longer be used on `mat`
992 
993   Developer Note:
994   This frees the `Mat_Product` context that was attached to the matrix during `MatProductCreate()` or `MatProductCreateWithMat()`
995 
996 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreate()`
997 @*/
998 PetscErrorCode MatProductClear(Mat mat)
999 {
1000   Mat_Product *product = mat->product;
1001 
1002   PetscFunctionBegin;
1003   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1004   if (product) {
1005     PetscCall(MatDestroy(&product->A));
1006     PetscCall(MatDestroy(&product->B));
1007     PetscCall(MatDestroy(&product->C));
1008     PetscCall(PetscFree(product->alg));
1009     PetscCall(MatDestroy(&product->Dwork));
1010     if (product->destroy) PetscCall((*product->destroy)(&product->data));
1011   }
1012   PetscCall(PetscFree(mat->product));
1013   mat->ops->productsymbolic = NULL;
1014   mat->ops->productnumeric  = NULL;
1015   PetscFunctionReturn(PETSC_SUCCESS);
1016 }
1017 
1018 /* Create a supporting struct and attach it to the matrix product */
1019 PetscErrorCode MatProductCreate_Private(Mat A, Mat B, Mat C, Mat D)
1020 {
1021   Mat_Product *product = NULL;
1022 
1023   PetscFunctionBegin;
1024   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
1025   PetscCheck(!D->product, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product already present");
1026   PetscCall(PetscNew(&product));
1027   product->A                    = A;
1028   product->B                    = B;
1029   product->C                    = C;
1030   product->type                 = MATPRODUCT_UNSPECIFIED;
1031   product->Dwork                = NULL;
1032   product->api_user             = PETSC_FALSE;
1033   product->clear                = PETSC_FALSE;
1034   product->setfromoptionscalled = PETSC_FALSE;
1035   PetscObjectParameterSetDefault(product, fill, 2);
1036   D->product = product;
1037 
1038   PetscCall(MatProductSetAlgorithm(D, MATPRODUCTALGORITHMDEFAULT));
1039   PetscCall(MatProductSetFill(D, PETSC_DEFAULT));
1040 
1041   PetscCall(PetscObjectReference((PetscObject)A));
1042   PetscCall(PetscObjectReference((PetscObject)B));
1043   PetscCall(PetscObjectReference((PetscObject)C));
1044   PetscFunctionReturn(PETSC_SUCCESS);
1045 }
1046 
1047 /*@
1048   MatProductCreateWithMat - Set a given matrix to have its values computed via matrix-matrix operations on other matrices.
1049 
1050   Collective
1051 
1052   Input Parameters:
1053 + A - the first matrix
1054 . B - the second matrix
1055 . C - the third matrix (optional, use `NULL` if not needed)
1056 - D - the matrix whose values are to be computed via a matrix-matrix product operation
1057 
1058   Level: intermediate
1059 
1060   Notes:
1061   Use `MatProductCreate()` if the matrix you wish computed `D` does not exist
1062 
1063   See `MatProductCreate()` for details on the usage of the matrix-matrix product operations
1064 
1065   Any product data currently attached to `D` will be freed
1066 
1067 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductType`, `MatProductSetType()`, `MatProductAlgorithm`,
1068           `MatProductSetAlgorithm`, `MatProductCreate()`, `MatProductClear()`
1069 @*/
1070 PetscErrorCode MatProductCreateWithMat(Mat A, Mat B, Mat C, Mat D)
1071 {
1072   PetscFunctionBegin;
1073   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1074   PetscValidType(A, 1);
1075   MatCheckPreallocated(A, 1);
1076   PetscCheck(A->assembled, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1077   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1078 
1079   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
1080   PetscValidType(B, 2);
1081   MatCheckPreallocated(B, 2);
1082   PetscCheck(B->assembled, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1083   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1084 
1085   if (C) {
1086     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
1087     PetscValidType(C, 3);
1088     MatCheckPreallocated(C, 3);
1089     PetscCheck(C->assembled, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1090     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1091   }
1092 
1093   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
1094   PetscValidType(D, 4);
1095   MatCheckPreallocated(D, 4);
1096   PetscCheck(D->assembled, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1097   PetscCheck(!D->factortype, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1098 
1099   /* Create a supporting struct and attach it to D */
1100   PetscCall(MatProductClear(D));
1101   PetscCall(MatProductCreate_Private(A, B, C, D));
1102   PetscFunctionReturn(PETSC_SUCCESS);
1103 }
1104 
1105 /*@
1106   MatProductCreate - create a matrix to hold the result of a matrix-matrix (or matrix-matrix-matrix) product operation
1107 
1108   Collective
1109 
1110   Input Parameters:
1111 + A - the first matrix
1112 . B - the second matrix
1113 - C - the third matrix (or `NULL`)
1114 
1115   Output Parameter:
1116 . D - the matrix whose values are to be computed via a matrix-matrix product operation
1117 
1118   Level: intermediate
1119 
1120   Example:
1121 .vb
1122     MatProductCreate(A,B,C,&D); or MatProductCreateWithMat(A,B,C,D)
1123     MatProductSetType(D, MATPRODUCT_AB or MATPRODUCT_AtB or MATPRODUCT_ABt or MATPRODUCT_PtAP or MATPRODUCT_RARt or MATPRODUCT_ABC)
1124     MatProductSetAlgorithm(D, alg)
1125     MatProductSetFill(D,fill)
1126     MatProductSetFromOptions(D)
1127     MatProductSymbolic(D)
1128     MatProductNumeric(D)
1129     Change numerical values in some of the matrices
1130     MatProductNumeric(D)
1131 .ve
1132 
1133   Notes:
1134   Use `MatProductCreateWithMat()` if `D` the matrix you wish computed already exists.
1135 
1136   The information computed during the symbolic stage can be reused for new numerical computations with the same non-zero structure of the input matrices.
1137 
1138   Developer Notes:
1139   It is undocumented what happens if the nonzero structure of the input matrices changes. Is the symbolic stage automatically redone? Does it crash?
1140   Is there error checking for it?
1141 
1142   On this call, auxiliary data needed to compute the product is stored in `D` in a `Mat_Product` context. A call to `MatProductClear()` frees this
1143   information.
1144 
1145   Each `MatProductAlgorithm` associated with a particular `MatType` stores additional data needed for the product computation
1146   (generally this data is computed in `MatProductSymbolic()`) inside the `Mat_Product` context in a `MatProductCtx_XXX` data structure
1147   and provides a `MatProductCtxDestroy_XXX()` routine to free that data. The `MatProductAlgorithm` and `MatType` specific destroy routine is called by
1148   `MatProductClear()`.
1149 
1150 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductClear()`,
1151           `MatProductSymbolic()`, `MatProductNumeric()`, `MatProductAlgorithm`, `MatProductType`
1152 @*/
1153 PetscErrorCode MatProductCreate(Mat A, Mat B, Mat C, Mat *D)
1154 {
1155   PetscFunctionBegin;
1156   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1157   PetscValidType(A, 1);
1158   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
1159   PetscValidType(B, 2);
1160   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix A");
1161   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix B");
1162 
1163   if (C) {
1164     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
1165     PetscValidType(C, 3);
1166     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix C");
1167   }
1168 
1169   PetscAssertPointer(D, 4);
1170   PetscCall(MatCreate(PetscObjectComm((PetscObject)A), D));
1171   /* Delay setting type of D to the MatProduct symbolic phase, as we allow sparse A and dense B */
1172   PetscCall(MatProductCreate_Private(A, B, C, *D));
1173   PetscFunctionReturn(PETSC_SUCCESS);
1174 }
1175 
1176 /*
1177    These are safe basic implementations of ABC, RARt and PtAP
1178    that do not rely on mat->ops->matmatop function pointers.
1179    They only use the MatProduct API and are currently used by
1180    cuSPARSE and KOKKOS-KERNELS backends
1181 */
1182 typedef struct {
1183   Mat BC;
1184   Mat ABC;
1185 } MatProductCtx_MatMatMatPrivate;
1186 
1187 static PetscErrorCode MatProductCtxDestroy_MatMatMatPrivate(PetscCtxRt data)
1188 {
1189   MatProductCtx_MatMatMatPrivate *mmdata = *(MatProductCtx_MatMatMatPrivate **)data;
1190 
1191   PetscFunctionBegin;
1192   PetscCall(MatDestroy(&mmdata->BC));
1193   PetscCall(MatDestroy(&mmdata->ABC));
1194   PetscCall(PetscFree(mmdata));
1195   PetscFunctionReturn(PETSC_SUCCESS);
1196 }
1197 
1198 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1199 {
1200   Mat_Product                    *product = mat->product;
1201   MatProductCtx_MatMatMatPrivate *mmabc;
1202 
1203   PetscFunctionBegin;
1204   MatCheckProduct(mat, 1);
1205   PetscCheck(mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data empty");
1206   mmabc = (MatProductCtx_MatMatMatPrivate *)mat->product->data;
1207   PetscCheck(mmabc->BC->ops->productnumeric, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing numeric stage");
1208   /* use function pointer directly to prevent logging */
1209   PetscCall((*mmabc->BC->ops->productnumeric)(mmabc->BC));
1210   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1211   mat->product             = mmabc->ABC->product;
1212   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1213   /* use function pointer directly to prevent logging */
1214   PetscUseTypeMethod(mat, productnumeric);
1215   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1216   mat->product             = product;
1217   PetscFunctionReturn(PETSC_SUCCESS);
1218 }
1219 
1220 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1221 {
1222   Mat_Product                    *product = mat->product;
1223   Mat                             A, B, C;
1224   MatProductType                  p1, p2;
1225   MatProductCtx_MatMatMatPrivate *mmabc;
1226   const char                     *prefix;
1227 
1228   PetscFunctionBegin;
1229   MatCheckProduct(mat, 1);
1230   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data not empty");
1231   PetscCall(MatGetOptionsPrefix(mat, &prefix));
1232   PetscCall(PetscNew(&mmabc));
1233   product->data    = mmabc;
1234   product->destroy = MatProductCtxDestroy_MatMatMatPrivate;
1235   switch (product->type) {
1236   case MATPRODUCT_PtAP:
1237     p1 = MATPRODUCT_AB;
1238     p2 = MATPRODUCT_AtB;
1239     A  = product->B;
1240     B  = product->A;
1241     C  = product->B;
1242     if (A->cmap->bs > 0 && C->cmap->bs > 0) PetscCall(MatSetBlockSizes(mat, A->cmap->bs, C->cmap->bs));
1243     break;
1244   case MATPRODUCT_RARt:
1245     p1 = MATPRODUCT_ABt;
1246     p2 = MATPRODUCT_AB;
1247     A  = product->B;
1248     B  = product->A;
1249     C  = product->B;
1250     if (A->rmap->bs > 0 && C->rmap->bs > 0) PetscCall(MatSetBlockSizes(mat, A->rmap->bs, C->rmap->bs));
1251     break;
1252   case MATPRODUCT_ABC:
1253     p1 = MATPRODUCT_AB;
1254     p2 = MATPRODUCT_AB;
1255     A  = product->A;
1256     B  = product->B;
1257     C  = product->C;
1258     PetscCall(MatSetBlockSizesFromMats(mat, A, C));
1259     break;
1260   default:
1261     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Not for ProductType %s", MatProductTypes[product->type]);
1262   }
1263   PetscCall(MatProductCreate(B, C, NULL, &mmabc->BC));
1264   PetscCall(MatSetOptionsPrefix(mmabc->BC, prefix));
1265   PetscCall(MatAppendOptionsPrefix(mmabc->BC, "P1_"));
1266   PetscCall(MatProductSetType(mmabc->BC, p1));
1267   PetscCall(MatProductSetAlgorithm(mmabc->BC, MATPRODUCTALGORITHMDEFAULT));
1268   PetscCall(MatProductSetFill(mmabc->BC, product->fill));
1269   mmabc->BC->product->api_user = product->api_user;
1270   PetscCall(MatProductSetFromOptions(mmabc->BC));
1271   PetscCheck(mmabc->BC->ops->productsymbolic, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Symbolic ProductType %s not supported with %s and %s", MatProductTypes[p1], ((PetscObject)B)->type_name, ((PetscObject)C)->type_name);
1272   /* use function pointer directly to prevent logging */
1273   PetscCall((*mmabc->BC->ops->productsymbolic)(mmabc->BC));
1274 
1275   PetscCall(MatProductCreate(A, mmabc->BC, NULL, &mmabc->ABC));
1276   PetscCall(MatSetOptionsPrefix(mmabc->ABC, prefix));
1277   PetscCall(MatAppendOptionsPrefix(mmabc->ABC, "P2_"));
1278   PetscCall(MatProductSetType(mmabc->ABC, p2));
1279   PetscCall(MatProductSetAlgorithm(mmabc->ABC, MATPRODUCTALGORITHMDEFAULT));
1280   PetscCall(MatProductSetFill(mmabc->ABC, product->fill));
1281   mmabc->ABC->product->api_user = product->api_user;
1282   PetscCall(MatProductSetFromOptions(mmabc->ABC));
1283   PetscCheck(mmabc->ABC->ops->productsymbolic, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Symbolic ProductType %s not supported with %s and %s", MatProductTypes[p2], ((PetscObject)A)->type_name, ((PetscObject)mmabc->BC)->type_name);
1284   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1285   mat->product              = mmabc->ABC->product;
1286   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1287   /* use function pointer directly to prevent logging */
1288   PetscUseTypeMethod(mat, productsymbolic);
1289   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1290   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1291   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1292   mat->product                    = product;
1293   PetscFunctionReturn(PETSC_SUCCESS);
1294 }
1295 
1296 /*@
1297   MatProductGetType - Returns the type of matrix-matrix product associated with computing values for the given matrix
1298 
1299   Not Collective
1300 
1301   Input Parameter:
1302 . mat - the matrix whose values are to be computed via a matrix-matrix product operation
1303 
1304   Output Parameter:
1305 . mtype - the `MatProductType`
1306 
1307   Level: intermediate
1308 
1309 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductCreate()`, `MatProductType`, `MatProductAlgorithm`
1310 @*/
1311 PetscErrorCode MatProductGetType(Mat mat, MatProductType *mtype)
1312 {
1313   PetscFunctionBegin;
1314   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1315   PetscAssertPointer(mtype, 2);
1316   *mtype = MATPRODUCT_UNSPECIFIED;
1317   if (mat->product) *mtype = mat->product->type;
1318   PetscFunctionReturn(PETSC_SUCCESS);
1319 }
1320 
1321 /*@
1322   MatProductGetMats - Returns the matrices associated with the matrix-matrix product associated with computing values for the given matrix
1323 
1324   Not Collective
1325 
1326   Input Parameter:
1327 . mat - the matrix whose values are to be computed via a matrix-matrix product operation
1328 
1329   Output Parameters:
1330 + A - the first matrix
1331 . B - the second matrix
1332 - C - the third matrix (may be `NULL` for some `MatProductType`)
1333 
1334   Level: intermediate
1335 
1336 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
1337 @*/
1338 PetscErrorCode MatProductGetMats(Mat mat, Mat *A, Mat *B, Mat *C)
1339 {
1340   PetscFunctionBegin;
1341   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1342   if (A) *A = mat->product ? mat->product->A : NULL;
1343   if (B) *B = mat->product ? mat->product->B : NULL;
1344   if (C) *C = mat->product ? mat->product->C : NULL;
1345   PetscFunctionReturn(PETSC_SUCCESS);
1346 }
1347