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