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