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