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