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