xref: /petsc/src/mat/interface/matproduct.c (revision d8e4f26e7eadd05aa3d9f3d0f2220b3e914971d9)
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: `Mat`, `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: `Mat`, `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 Parameters:
542 +  mat - the matrix obtained with `MatProductCreate()` or `MatProductCreateWithMat()`
543 -  viewer - where `mat` should be reviewed
544 
545    Level: intermediate
546 
547 .seealso: `Mat`, `MatProductSetFromOptions()`, `MatView()`, `MatProductCreate()`, `MatProductCreateWithMat()`
548 @*/
549 PetscErrorCode MatProductView(Mat mat, PetscViewer viewer)
550 {
551   PetscFunctionBegin;
552   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
553   if (!mat->product) PetscFunctionReturn(0);
554   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)mat), &viewer));
555   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
556   PetscCheckSameComm(mat, 1, viewer, 2);
557   if (mat->product->view) PetscCall((*mat->product->view)(mat, viewer));
558   PetscFunctionReturn(0);
559 }
560 
561 /* ----------------------------------------------- */
562 /* these are basic implementations relying on the old function pointers
563  * they are dangerous and should be removed in the future */
564 PetscErrorCode MatProductNumeric_AB(Mat mat)
565 {
566   Mat_Product *product = mat->product;
567   Mat          A = product->A, B = product->B;
568 
569   PetscFunctionBegin;
570   PetscCall((*mat->ops->matmultnumeric)(A, B, mat));
571   PetscFunctionReturn(0);
572 }
573 
574 PetscErrorCode MatProductNumeric_AtB(Mat mat)
575 {
576   Mat_Product *product = mat->product;
577   Mat          A = product->A, B = product->B;
578 
579   PetscFunctionBegin;
580   PetscCall((*mat->ops->transposematmultnumeric)(A, B, mat));
581   PetscFunctionReturn(0);
582 }
583 
584 PetscErrorCode MatProductNumeric_ABt(Mat mat)
585 {
586   Mat_Product *product = mat->product;
587   Mat          A = product->A, B = product->B;
588 
589   PetscFunctionBegin;
590   PetscCall((*mat->ops->mattransposemultnumeric)(A, B, mat));
591   PetscFunctionReturn(0);
592 }
593 
594 PetscErrorCode MatProductNumeric_PtAP(Mat mat)
595 {
596   Mat_Product *product = mat->product;
597   Mat          A = product->A, B = product->B;
598 
599   PetscFunctionBegin;
600   PetscCall((*mat->ops->ptapnumeric)(A, B, mat));
601   PetscFunctionReturn(0);
602 }
603 
604 PetscErrorCode MatProductNumeric_RARt(Mat mat)
605 {
606   Mat_Product *product = mat->product;
607   Mat          A = product->A, B = product->B;
608 
609   PetscFunctionBegin;
610   PetscCall((*mat->ops->rartnumeric)(A, B, mat));
611   PetscFunctionReturn(0);
612 }
613 
614 PetscErrorCode MatProductNumeric_ABC(Mat mat)
615 {
616   Mat_Product *product = mat->product;
617   Mat          A = product->A, B = product->B, C = product->C;
618 
619   PetscFunctionBegin;
620   PetscCall((*mat->ops->matmatmultnumeric)(A, B, C, mat));
621   PetscFunctionReturn(0);
622 }
623 
624 /* ----------------------------------------------- */
625 
626 /*@
627    MatProductNumeric - Compute a matrix product with numerical values.
628 
629    Collective
630 
631    Input/Output Parameter:
632 .  mat - the matrix holding the product
633 
634    Level: intermediate
635 
636    Note:
637    `MatProductSymbolic()` must have been called on mat before calling this function
638 
639 .seealso: `Mat`, `MatProductSetAlgorithm()`, `MatProductSetType()`, `MatProductCreate()`, `MatSetType()`, `MatProductSymbolic()`
640 @*/
641 PetscErrorCode MatProductNumeric(Mat mat)
642 {
643   PetscLogEvent eventtype = -1;
644   PetscBool     missing   = PETSC_FALSE;
645 
646   PetscFunctionBegin;
647   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
648   MatCheckProduct(mat, 1);
649   switch (mat->product->type) {
650   case MATPRODUCT_AB:
651     eventtype = MAT_MatMultNumeric;
652     break;
653   case MATPRODUCT_AtB:
654     eventtype = MAT_TransposeMatMultNumeric;
655     break;
656   case MATPRODUCT_ABt:
657     eventtype = MAT_MatTransposeMultNumeric;
658     break;
659   case MATPRODUCT_PtAP:
660     eventtype = MAT_PtAPNumeric;
661     break;
662   case MATPRODUCT_RARt:
663     eventtype = MAT_RARtNumeric;
664     break;
665   case MATPRODUCT_ABC:
666     eventtype = MAT_MatMatMultNumeric;
667     break;
668   default:
669     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[mat->product->type]);
670   }
671 
672   if (mat->ops->productnumeric) {
673     PetscCall(PetscLogEventBegin(eventtype, mat, 0, 0, 0));
674     PetscUseTypeMethod(mat, productnumeric);
675     PetscCall(PetscLogEventEnd(eventtype, mat, 0, 0, 0));
676   } else missing = PETSC_TRUE;
677 
678   if (missing || !mat->product) {
679     char errstr[256];
680 
681     if (mat->product->type == MATPRODUCT_ABC) {
682       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));
683     } else {
684       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));
685     }
686     PetscCheck(!missing, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Unspecified numeric phase for product %s", errstr);
687     PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing struct after symbolic phase for product %s", errstr);
688   }
689 
690   if (mat->product->clear) PetscCall(MatProductClear(mat));
691   PetscCall(PetscObjectStateIncrease((PetscObject)mat));
692   PetscFunctionReturn(0);
693 }
694 
695 /* ----------------------------------------------- */
696 /* these are basic implementations relying on the old function pointers
697  * they are dangerous and should be removed in the future */
698 PetscErrorCode MatProductSymbolic_AB(Mat mat)
699 {
700   Mat_Product *product = mat->product;
701   Mat          A = product->A, B = product->B;
702 
703   PetscFunctionBegin;
704   PetscCall((*mat->ops->matmultsymbolic)(A, B, product->fill, mat));
705   mat->ops->productnumeric = MatProductNumeric_AB;
706   PetscFunctionReturn(0);
707 }
708 
709 PetscErrorCode MatProductSymbolic_AtB(Mat mat)
710 {
711   Mat_Product *product = mat->product;
712   Mat          A = product->A, B = product->B;
713 
714   PetscFunctionBegin;
715   PetscCall((*mat->ops->transposematmultsymbolic)(A, B, product->fill, mat));
716   mat->ops->productnumeric = MatProductNumeric_AtB;
717   PetscFunctionReturn(0);
718 }
719 
720 PetscErrorCode MatProductSymbolic_ABt(Mat mat)
721 {
722   Mat_Product *product = mat->product;
723   Mat          A = product->A, B = product->B;
724 
725   PetscFunctionBegin;
726   PetscCall((*mat->ops->mattransposemultsymbolic)(A, B, product->fill, mat));
727   mat->ops->productnumeric = MatProductNumeric_ABt;
728   PetscFunctionReturn(0);
729 }
730 
731 PetscErrorCode MatProductSymbolic_ABC(Mat mat)
732 {
733   Mat_Product *product = mat->product;
734   Mat          A = product->A, B = product->B, C = product->C;
735 
736   PetscFunctionBegin;
737   PetscCall((*mat->ops->matmatmultsymbolic)(A, B, C, product->fill, mat));
738   mat->ops->productnumeric = MatProductNumeric_ABC;
739   PetscFunctionReturn(0);
740 }
741 
742 /* ----------------------------------------------- */
743 
744 /*@
745    MatProductSymbolic - Perform the symbolic portion of a matrix product, this creates a data structure for use with the numerical product done with
746   `MatProductNumeric()`
747 
748    Collective
749 
750    Input/Output Parameter:
751 .  mat - the matrix to hold a product
752 
753    Level: intermediate
754 
755    Note:
756    `MatProductSetFromOptions()` must have been called on mat before calling this function
757 
758 .seealso: `Mat`, `MatProductCreate()`, `MatProductCreateWithMat()`, `MatProductSetFromOptions()`, `MatProductNumeric()`, `MatProductSetType()`, `MatProductSetAlgorithm()`
759 @*/
760 PetscErrorCode MatProductSymbolic(Mat mat)
761 {
762   PetscLogEvent eventtype = -1;
763   PetscBool     missing   = PETSC_FALSE;
764 
765   PetscFunctionBegin;
766   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
767   MatCheckProduct(mat, 1);
768   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_ORDER, "Cannot run symbolic phase. Product data not empty");
769   switch (mat->product->type) {
770   case MATPRODUCT_AB:
771     eventtype = MAT_MatMultSymbolic;
772     break;
773   case MATPRODUCT_AtB:
774     eventtype = MAT_TransposeMatMultSymbolic;
775     break;
776   case MATPRODUCT_ABt:
777     eventtype = MAT_MatTransposeMultSymbolic;
778     break;
779   case MATPRODUCT_PtAP:
780     eventtype = MAT_PtAPSymbolic;
781     break;
782   case MATPRODUCT_RARt:
783     eventtype = MAT_RARtSymbolic;
784     break;
785   case MATPRODUCT_ABC:
786     eventtype = MAT_MatMatMultSymbolic;
787     break;
788   default:
789     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[mat->product->type]);
790   }
791   mat->ops->productnumeric = NULL;
792   if (mat->ops->productsymbolic) {
793     PetscCall(PetscLogEventBegin(eventtype, mat, 0, 0, 0));
794     PetscUseTypeMethod(mat, productsymbolic);
795     PetscCall(PetscLogEventEnd(eventtype, mat, 0, 0, 0));
796   } else missing = PETSC_TRUE;
797 
798   if (missing || !mat->product || !mat->ops->productnumeric) {
799     char errstr[256];
800 
801     if (mat->product->type == MATPRODUCT_ABC) {
802       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));
803     } else {
804       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));
805     }
806     PetscCheck(!missing, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Unspecified symbolic phase for product %s. Call MatProductSetFromOptions() first", errstr);
807     PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing struct after symbolic phase for product %s", errstr);
808   }
809   PetscFunctionReturn(0);
810 }
811 
812 /*@
813    MatProductSetFill - Set an expected fill of the matrix product.
814 
815    Collective on Mat
816 
817    Input Parameters:
818 +  mat - the matrix product result matrix
819 -  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.
820 
821    Level: intermediate
822 
823 .seealso: `Mat`, `MatProductSetFromOptions()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
824 @*/
825 PetscErrorCode MatProductSetFill(Mat mat, PetscReal fill)
826 {
827   PetscFunctionBegin;
828   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
829   MatCheckProduct(mat, 1);
830   if (fill == PETSC_DEFAULT || fill == PETSC_DECIDE) mat->product->fill = 2.0;
831   else mat->product->fill = fill;
832   PetscFunctionReturn(0);
833 }
834 
835 /*@
836    MatProductSetAlgorithm - Requests a particular algorithm for a matrix product computation that will perform to compute the given matrix
837 
838    Collective
839 
840    Input Parameters:
841 +  mat - the matrix product
842 -  alg - particular implementation algorithm of the matrix product, e.g., `MATPRODUCTALGORITHMDEFAULT`.
843 
844    Options Database Key:
845 .  -mat_product_algorithm <algorithm> - Sets the algorithm; use -help for a list
846     of available algorithms (for instance, scalable, outerproduct, etc.)
847 
848    Level: intermediate
849 
850 .seealso: `Mat`, `MatProductSetType()`, `MatProductSetFill()`, `MatProductCreate()`, `MatProductAlgorithm`, `MatProductType`
851 @*/
852 PetscErrorCode MatProductSetAlgorithm(Mat mat, MatProductAlgorithm alg)
853 {
854   PetscFunctionBegin;
855   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
856   MatCheckProduct(mat, 1);
857   PetscCall(PetscFree(mat->product->alg));
858   PetscCall(PetscStrallocpy(alg, &mat->product->alg));
859   PetscFunctionReturn(0);
860 }
861 
862 /*@
863    MatProductSetType - Sets a particular matrix product type to be used to compute the given matrix
864 
865    Collective
866 
867    Input Parameters:
868 +  mat - the matrix
869 -  productype   - matrix product type, e.g., `MATPRODUCT_AB`,`MATPRODUCT_AtB`,`MATPRODUCT_ABt`,`MATPRODUCT_PtAP`,`MATPRODUCT_RARt`,`MATPRODUCT_ABC`.
870 
871    Level: intermediate
872 
873    Note:
874    The small t represents the transpose operation.
875 
876 .seealso: `Mat`, `MatProductCreate()`, `MatProductType`, `MatProductType`,
877           `MATPRODUCT_AB`, `MATPRODUCT_AtB`, `MATPRODUCT_ABt`, `MATPRODUCT_PtAP`, `MATPRODUCT_RARt`, `MATPRODUCT_ABC`
878 @*/
879 PetscErrorCode MatProductSetType(Mat mat, MatProductType productype)
880 {
881   PetscFunctionBegin;
882   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
883   MatCheckProduct(mat, 1);
884   PetscValidLogicalCollectiveEnum(mat, productype, 2);
885   if (productype != mat->product->type) {
886     if (mat->product->destroy) PetscCall((*mat->product->destroy)(mat->product->data));
887     mat->product->destroy     = NULL;
888     mat->product->data        = NULL;
889     mat->ops->productsymbolic = NULL;
890     mat->ops->productnumeric  = NULL;
891   }
892   mat->product->type = productype;
893   PetscFunctionReturn(0);
894 }
895 
896 /*@
897    MatProductClear - Clears matrix product internal datastructures.
898 
899    Collective
900 
901    Input Parameters:
902 .  mat - the product matrix
903 
904    Level: intermediate
905 
906    Notes:
907    This function should be called to remove any intermediate data used to compute the matrix to free up memory.
908 
909    After having called this function, matrix-matrix operations can no longer be used on mat
910 
911 .seealso: `Mat`, `MatProductCreate()`
912 @*/
913 PetscErrorCode MatProductClear(Mat mat)
914 {
915   Mat_Product *product = mat->product;
916 
917   PetscFunctionBegin;
918   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
919   if (product) {
920     PetscCall(MatDestroy(&product->A));
921     PetscCall(MatDestroy(&product->B));
922     PetscCall(MatDestroy(&product->C));
923     PetscCall(PetscFree(product->alg));
924     PetscCall(MatDestroy(&product->Dwork));
925     if (product->destroy) PetscCall((*product->destroy)(product->data));
926   }
927   PetscCall(PetscFree(mat->product));
928   mat->ops->productsymbolic = NULL;
929   mat->ops->productnumeric  = NULL;
930   PetscFunctionReturn(0);
931 }
932 
933 /* Create a supporting struct and attach it to the matrix product */
934 PetscErrorCode MatProductCreate_Private(Mat A, Mat B, Mat C, Mat D)
935 {
936   Mat_Product *product = NULL;
937 
938   PetscFunctionBegin;
939   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
940   PetscCheck(!D->product, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product already present");
941   PetscCall(PetscNew(&product));
942   product->A        = A;
943   product->B        = B;
944   product->C        = C;
945   product->type     = MATPRODUCT_UNSPECIFIED;
946   product->Dwork    = NULL;
947   product->api_user = PETSC_FALSE;
948   product->clear    = PETSC_FALSE;
949   D->product        = product;
950 
951   PetscCall(MatProductSetAlgorithm(D, MATPRODUCTALGORITHMDEFAULT));
952   PetscCall(MatProductSetFill(D, PETSC_DEFAULT));
953 
954   PetscCall(PetscObjectReference((PetscObject)A));
955   PetscCall(PetscObjectReference((PetscObject)B));
956   PetscCall(PetscObjectReference((PetscObject)C));
957   PetscFunctionReturn(0);
958 }
959 
960 /*@
961    MatProductCreateWithMat - Setup a given matrix as a matrix product of other matrices
962 
963    Collective on Mat
964 
965    Input Parameters:
966 +  A - the first matrix
967 .  B - the second matrix
968 .  C - the third matrix (optional, use `NULL` if not needed)
969 -  D - the matrix which will be used to hold the product
970 
971    Level: intermediate
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 .seealso: `Mat`, `MatProductCreate()`, `MatProductClear()`
981 @*/
982 PetscErrorCode MatProductCreateWithMat(Mat A, Mat B, Mat C, Mat D)
983 {
984   PetscFunctionBegin;
985   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
986   PetscValidType(A, 1);
987   MatCheckPreallocated(A, 1);
988   PetscCheck(A->assembled, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
989   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
990 
991   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
992   PetscValidType(B, 2);
993   MatCheckPreallocated(B, 2);
994   PetscCheck(B->assembled, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
995   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
996 
997   if (C) {
998     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
999     PetscValidType(C, 3);
1000     MatCheckPreallocated(C, 3);
1001     PetscCheck(C->assembled, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1002     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1003   }
1004 
1005   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
1006   PetscValidType(D, 4);
1007   MatCheckPreallocated(D, 4);
1008   PetscCheck(D->assembled, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1009   PetscCheck(!D->factortype, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1010 
1011   /* Create a supporting struct and attach it to D */
1012   PetscCall(MatProductClear(D));
1013   PetscCall(MatProductCreate_Private(A, B, C, D));
1014   PetscFunctionReturn(0);
1015 }
1016 
1017 /*@
1018    MatProductCreate - create a matrix to hold the result of a matrix-matrix product operation
1019 
1020    Collective on A
1021 
1022    Input Parameters:
1023 +  A - the first matrix
1024 .  B - the second matrix
1025 -  C - the third matrix (optional)
1026 
1027    Output Parameters:
1028 .  D - the product matrix
1029 
1030    Level: intermediate
1031 
1032    Example of Usage:
1033 .vb
1034     MatProductCreate(A,B,C,&D); or MatProductCreateWithMat(A,B,C,D)
1035     MatProductSetType(D, MATPRODUCT_AB or MATPRODUCT_AtB or MATPRODUCT_ABt or MATPRODUCT_PtAP or MATPRODUCT_RARt or MATPRODUCT_ABC)
1036     MatProductSetAlgorithm(D, alg)
1037     MatProductSetFill(D,fill)
1038     MatProductSetFromOptions(D)
1039     MatProductSymbolic(D)
1040     MatProductNumeric(D)
1041     Change numerical values in some of the matrices
1042     MatProductNumeric(D)
1043 .ve
1044 
1045    Notes:
1046    Use `MatProductCreateWithMat()` if the matrix you wish computed, the D matrix, already exists.
1047 
1048    The information computed during the symbolic stage can be reused for new numerical computations with the same non-zero structure
1049 
1050    Developer Note:
1051    It is undocumented what happens if the nonzero structure of the input matrices changes. Is the symbolic stage automatically redone? Does it crash?
1052    Is there error checking for it?
1053 
1054 .seealso: `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductClear()`
1055 @*/
1056 PetscErrorCode MatProductCreate(Mat A, Mat B, Mat C, Mat *D)
1057 {
1058   PetscFunctionBegin;
1059   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1060   PetscValidType(A, 1);
1061   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
1062   PetscValidType(B, 2);
1063   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix A");
1064   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix B");
1065 
1066   if (C) {
1067     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
1068     PetscValidType(C, 3);
1069     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix C");
1070   }
1071 
1072   PetscValidPointer(D, 4);
1073   PetscCall(MatCreate(PetscObjectComm((PetscObject)A), D));
1074   /* Delay setting type of D to the MatProduct symbolic phase, as we allow sparse A and dense B */
1075   PetscCall(MatProductCreate_Private(A, B, C, *D));
1076   PetscFunctionReturn(0);
1077 }
1078 
1079 /*
1080    These are safe basic implementations of ABC, RARt and PtAP
1081    that do not rely on mat->ops->matmatop function pointers.
1082    They only use the MatProduct API and are currently used by
1083    cuSPARSE and KOKKOS-KERNELS backends
1084 */
1085 typedef struct {
1086   Mat BC;
1087   Mat ABC;
1088 } MatMatMatPrivate;
1089 
1090 static PetscErrorCode MatDestroy_MatMatMatPrivate(void *data)
1091 {
1092   MatMatMatPrivate *mmdata = (MatMatMatPrivate *)data;
1093 
1094   PetscFunctionBegin;
1095   PetscCall(MatDestroy(&mmdata->BC));
1096   PetscCall(MatDestroy(&mmdata->ABC));
1097   PetscCall(PetscFree(data));
1098   PetscFunctionReturn(0);
1099 }
1100 
1101 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1102 {
1103   Mat_Product      *product = mat->product;
1104   MatMatMatPrivate *mmabc;
1105 
1106   PetscFunctionBegin;
1107   MatCheckProduct(mat, 1);
1108   PetscCheck(mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data empty");
1109   mmabc = (MatMatMatPrivate *)mat->product->data;
1110   PetscCheck(mmabc->BC->ops->productnumeric, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing numeric stage");
1111   /* use function pointer directly to prevent logging */
1112   PetscCall((*mmabc->BC->ops->productnumeric)(mmabc->BC));
1113   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1114   mat->product             = mmabc->ABC->product;
1115   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1116   /* use function pointer directly to prevent logging */
1117   PetscUseTypeMethod(mat, productnumeric);
1118   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1119   mat->product             = product;
1120   PetscFunctionReturn(0);
1121 }
1122 
1123 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1124 {
1125   Mat_Product      *product = mat->product;
1126   Mat               A, B, C;
1127   MatProductType    p1, p2;
1128   MatMatMatPrivate *mmabc;
1129   const char       *prefix;
1130 
1131   PetscFunctionBegin;
1132   MatCheckProduct(mat, 1);
1133   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data not empty");
1134   PetscCall(MatGetOptionsPrefix(mat, &prefix));
1135   PetscCall(PetscNew(&mmabc));
1136   product->data    = mmabc;
1137   product->destroy = MatDestroy_MatMatMatPrivate;
1138   switch (product->type) {
1139   case MATPRODUCT_PtAP:
1140     p1 = MATPRODUCT_AB;
1141     p2 = MATPRODUCT_AtB;
1142     A  = product->B;
1143     B  = product->A;
1144     C  = product->B;
1145     break;
1146   case MATPRODUCT_RARt:
1147     p1 = MATPRODUCT_ABt;
1148     p2 = MATPRODUCT_AB;
1149     A  = product->B;
1150     B  = product->A;
1151     C  = product->B;
1152     break;
1153   case MATPRODUCT_ABC:
1154     p1 = MATPRODUCT_AB;
1155     p2 = MATPRODUCT_AB;
1156     A  = product->A;
1157     B  = product->B;
1158     C  = product->C;
1159     break;
1160   default:
1161     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Not for ProductType %s", MatProductTypes[product->type]);
1162   }
1163   PetscCall(MatProductCreate(B, C, NULL, &mmabc->BC));
1164   PetscCall(MatSetOptionsPrefix(mmabc->BC, prefix));
1165   PetscCall(MatAppendOptionsPrefix(mmabc->BC, "P1_"));
1166   PetscCall(MatProductSetType(mmabc->BC, p1));
1167   PetscCall(MatProductSetAlgorithm(mmabc->BC, MATPRODUCTALGORITHMDEFAULT));
1168   PetscCall(MatProductSetFill(mmabc->BC, product->fill));
1169   mmabc->BC->product->api_user = product->api_user;
1170   PetscCall(MatProductSetFromOptions(mmabc->BC));
1171   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);
1172   /* use function pointer directly to prevent logging */
1173   PetscCall((*mmabc->BC->ops->productsymbolic)(mmabc->BC));
1174 
1175   PetscCall(MatProductCreate(A, mmabc->BC, NULL, &mmabc->ABC));
1176   PetscCall(MatSetOptionsPrefix(mmabc->ABC, prefix));
1177   PetscCall(MatAppendOptionsPrefix(mmabc->ABC, "P2_"));
1178   PetscCall(MatProductSetType(mmabc->ABC, p2));
1179   PetscCall(MatProductSetAlgorithm(mmabc->ABC, MATPRODUCTALGORITHMDEFAULT));
1180   PetscCall(MatProductSetFill(mmabc->ABC, product->fill));
1181   mmabc->ABC->product->api_user = product->api_user;
1182   PetscCall(MatProductSetFromOptions(mmabc->ABC));
1183   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);
1184   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1185   mat->product              = mmabc->ABC->product;
1186   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1187   /* use function pointer directly to prevent logging */
1188   PetscUseTypeMethod(mat, productsymbolic);
1189   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1190   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1191   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1192   mat->product                    = product;
1193   PetscFunctionReturn(0);
1194 }
1195 
1196 /*@
1197    MatProductGetType - Returns the type of matrix-matrix product associated with the given matrix.
1198 
1199    Not collective
1200 
1201    Input Parameter:
1202 .  mat - the matrix
1203 
1204    Output Parameter:
1205 .  mtype - the `MatProductType`
1206 
1207    Level: intermediate
1208 
1209 .seealso: `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductCreate()`, `MatProductType`, `MatProductAlgorithm`
1210 @*/
1211 PetscErrorCode MatProductGetType(Mat mat, MatProductType *mtype)
1212 {
1213   PetscFunctionBegin;
1214   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1215   PetscValidPointer(mtype, 2);
1216   *mtype = MATPRODUCT_UNSPECIFIED;
1217   if (mat->product) *mtype = mat->product->type;
1218   PetscFunctionReturn(0);
1219 }
1220 
1221 /*@
1222    MatProductGetMats - Returns the matrices associated with the matrix-matrix product this matrix can receive
1223 
1224    Not collective
1225 
1226    Input Parameter:
1227 .  mat - the product matrix
1228 
1229    Output Parameters:
1230 +  A - the first matrix
1231 .  B - the second matrix
1232 -  C - the third matrix (optional)
1233 
1234    Level: intermediate
1235 
1236 .seealso: `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
1237 @*/
1238 PetscErrorCode MatProductGetMats(Mat mat, Mat *A, Mat *B, Mat *C)
1239 {
1240   PetscFunctionBegin;
1241   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1242   if (A) *A = mat->product ? mat->product->A : NULL;
1243   if (B) *B = mat->product ? mat->product->B : NULL;
1244   if (C) *C = mat->product ? mat->product->C : NULL;
1245   PetscFunctionReturn(0);
1246 }
1247