xref: /petsc/src/mat/interface/matproduct.c (revision 6dd63270497ad23dcf16ae500a87ff2b2a0b7474)
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(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(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(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) An = Am;
417 
418   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,
419              MatProductTypes[product->type], A->rmap->N, A->cmap->N, bname, B->rmap->N, B->cmap->N);
420   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,
421              MatProductTypes[product->type], B->rmap->N, B->cmap->N, Cm, Cn);
422 
423   fA = A->ops->productsetfromoptions;
424   fB = B->ops->productsetfromoptions;
425   fC = C ? C->ops->productsetfromoptions : fA;
426   if (C) {
427     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));
428   } else {
429     PetscCall(PetscInfo(mat, "MatProductType %s for A %s, %s %s\n", MatProductTypes[product->type], ((PetscObject)A)->type_name, bname, ((PetscObject)B)->type_name));
430   }
431   if (fA == fB && fA == fC && fA) {
432     PetscCall(PetscInfo(mat, "  matching op\n"));
433     PetscCall((*fA)(mat));
434   }
435   /* We may have found f but it did not succeed */
436   if (!mat->ops->productsymbolic) { /* query MatProductSetFromOptions_Atype_Btype_Ctype */
437     char mtypes[256];
438     PetscCall(PetscStrncpy(mtypes, "MatProductSetFromOptions_", sizeof(mtypes)));
439     PetscCall(PetscStrlcat(mtypes, ((PetscObject)A)->type_name, sizeof(mtypes)));
440     PetscCall(PetscStrlcat(mtypes, "_", sizeof(mtypes)));
441     PetscCall(PetscStrlcat(mtypes, ((PetscObject)B)->type_name, sizeof(mtypes)));
442     if (C) {
443       PetscCall(PetscStrlcat(mtypes, "_", sizeof(mtypes)));
444       PetscCall(PetscStrlcat(mtypes, ((PetscObject)C)->type_name, sizeof(mtypes)));
445     }
446     PetscCall(PetscStrlcat(mtypes, "_C", sizeof(mtypes)));
447 #if defined(__clang__)
448     PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wformat-pedantic")
449 #elif defined(__GNUC__) || defined(__GNUG__)
450     PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wformat")
451 #endif
452     PetscCall(PetscObjectQueryFunction((PetscObject)A, mtypes, &f));
453     PetscCall(PetscInfo(mat, "  querying %s from A? %p\n", mtypes, f));
454     if (!f) {
455       PetscCall(PetscObjectQueryFunction((PetscObject)B, mtypes, &f));
456       PetscCall(PetscInfo(mat, "  querying %s from %s? %p\n", mtypes, bname, f));
457     }
458     if (!f && C) {
459       PetscCall(PetscObjectQueryFunction((PetscObject)C, mtypes, &f));
460       PetscCall(PetscInfo(mat, "  querying %s from C? %p\n", mtypes, f));
461     }
462     if (f) PetscCall((*f)(mat));
463 
464     /* We may have found f but it did not succeed */
465     /* some matrices (i.e. MATTRANSPOSEVIRTUAL, MATSHELL constructed from MatConvert), knows what to do with their inner matrices */
466     if (!mat->ops->productsymbolic) {
467       PetscCall(PetscStrncpy(mtypes, "MatProductSetFromOptions_anytype_C", sizeof(mtypes)));
468       PetscCall(PetscObjectQueryFunction((PetscObject)A, mtypes, &f));
469       PetscCall(PetscInfo(mat, "  querying %s from A? %p\n", mtypes, f));
470       if (!f) {
471         PetscCall(PetscObjectQueryFunction((PetscObject)B, mtypes, &f));
472         PetscCall(PetscInfo(mat, "  querying %s from %s? %p\n", mtypes, bname, f));
473       }
474       if (!f && C) {
475         PetscCall(PetscObjectQueryFunction((PetscObject)C, mtypes, &f));
476         PetscCall(PetscInfo(mat, "  querying %s from C? %p\n", mtypes, f));
477       }
478     }
479     if (f) PetscCall((*f)(mat));
480   }
481   PETSC_PRAGMA_DIAGNOSTIC_IGNORED_END()
482   /* We may have found f but it did not succeed */
483   if (!mat->ops->productsymbolic) {
484     /* we can still compute the product if B is of type dense */
485     if (product->type == MATPRODUCT_AB || product->type == MATPRODUCT_AtB) {
486       PetscBool isdense;
487 
488       PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)B, &isdense, MATSEQDENSE, MATMPIDENSE, ""));
489       if (isdense) {
490         mat->ops->productsymbolic = MatProductSymbolic_X_Dense;
491         PetscCall(PetscInfo(mat, "  using basic looping over columns of a dense matrix\n"));
492       }
493     } else if (product->type != MATPRODUCT_ABt) { /* use MatProductSymbolic/Numeric_Unsafe() for triple products only */
494       /*
495          TODO: this should be changed to a proper setfromoptions, not setting the symbolic pointer here, because we do not know if
496                the combination will succeed. In order to be sure, we need MatProductGetProductType to return the type of the result
497                before computing the symbolic phase
498       */
499       PetscCall(PetscInfo(mat, "  symbolic product not supported, using MatProductSymbolic_Unsafe() implementation\n"));
500       mat->ops->productsymbolic = MatProductSymbolic_Unsafe;
501     }
502   }
503   if (!mat->ops->productsymbolic) PetscCall(PetscInfo(mat, "  symbolic product is not supported\n"));
504   PetscFunctionReturn(PETSC_SUCCESS);
505 }
506 
507 /*@
508   MatProductSetFromOptions - Sets the options for the computation of a matrix-matrix product operation where the type,
509   the algorithm etc are determined from the options database.
510 
511   Logically Collective
512 
513   Input Parameter:
514 . mat - the matrix whose values are computed via a matrix-matrix product operation
515 
516   Options Database Keys:
517 + -mat_product_clear                 - Clear intermediate data structures after `MatProductNumeric()` has been called
518 . -mat_product_algorithm <algorithm> - Sets the algorithm, see `MatProductAlgorithm` for possible values
519 - -mat_product_algorithm_backend_cpu - Use the CPU to perform the computation even if the matrix is a GPU matrix
520 
521   Level: intermediate
522 
523   Note:
524   The `-mat_product_clear` option reduces memory usage but means that the matrix cannot be re-used for a matrix-matrix product operation
525 
526 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatSetFromOptions()`, `MatProductCreate()`, `MatProductCreateWithMat()`, `MatProductNumeric()`,
527           `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductAlgorithm`
528 @*/
529 PetscErrorCode MatProductSetFromOptions(Mat mat)
530 {
531   PetscFunctionBegin;
532   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
533   MatCheckProduct(mat, 1);
534   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_ORDER, "Cannot call MatProductSetFromOptions() with already present data");
535   mat->product->setfromoptionscalled = PETSC_TRUE;
536   PetscObjectOptionsBegin((PetscObject)mat);
537   PetscCall(PetscOptionsBool("-mat_product_clear", "Clear intermediate data structures after MatProductNumeric() has been called", "MatProductClear", mat->product->clear, &mat->product->clear, NULL));
538   PetscCall(PetscOptionsDeprecated("-mat_freeintermediatedatastructures", "-mat_product_clear", "3.13", "Or call MatProductClear() after MatProductNumeric()"));
539   PetscOptionsEnd();
540   PetscCall(MatProductSetFromOptions_Private(mat));
541   PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing product after setup phase");
542   PetscFunctionReturn(PETSC_SUCCESS);
543 }
544 
545 /*@
546   MatProductView - View the private matrix-matrix algorithm object within a matrix
547 
548   Logically Collective
549 
550   Input Parameters:
551 + mat    - the matrix obtained with `MatProductCreate()` or `MatProductCreateWithMat()`
552 - viewer - where the information on the matrix-matrix algorithm of `mat` should be reviewed
553 
554   Level: intermediate
555 
556   Developer Note:
557   Shouldn't this information be printed from an appropriate `MatView()` with perhaps certain formats set?
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   Mat_Product  *product   = mat->product;
768   Mat           A         = product->A;
769   Mat           B         = product->B;
770   Mat           C         = product->C;
771 
772   PetscFunctionBegin;
773   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
774   MatCheckProduct(mat, 1);
775   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_ORDER, "Cannot run symbolic phase. Product data not empty");
776   switch (mat->product->type) {
777   case MATPRODUCT_AB:
778     eventtype = MAT_MatMultSymbolic;
779     break;
780   case MATPRODUCT_AtB:
781     eventtype = MAT_TransposeMatMultSymbolic;
782     break;
783   case MATPRODUCT_ABt:
784     eventtype = MAT_MatTransposeMultSymbolic;
785     break;
786   case MATPRODUCT_PtAP:
787     eventtype = MAT_PtAPSymbolic;
788     break;
789   case MATPRODUCT_RARt:
790     eventtype = MAT_RARtSymbolic;
791     break;
792   case MATPRODUCT_ABC:
793     eventtype = MAT_MatMatMultSymbolic;
794     break;
795   default:
796     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[mat->product->type]);
797   }
798   mat->ops->productnumeric = NULL;
799   if (mat->ops->productsymbolic) {
800     PetscCall(PetscLogEventBegin(eventtype, mat, 0, 0, 0));
801     PetscUseTypeMethod(mat, productsymbolic);
802     PetscCall(PetscLogEventEnd(eventtype, mat, 0, 0, 0));
803   } else missing = PETSC_TRUE;
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 #if defined(PETSC_HAVE_DEVICE)
817   PetscBool bindingpropagates;
818   bindingpropagates = (PetscBool)((A->boundtocpu && A->bindingpropagates) || (B->boundtocpu && B->bindingpropagates));
819   if (C) bindingpropagates = (PetscBool)(bindingpropagates || (C->boundtocpu && C->bindingpropagates));
820   if (bindingpropagates) {
821     PetscCall(MatBindToCPU(mat, PETSC_TRUE));
822     PetscCall(MatSetBindingPropagates(mat, PETSC_TRUE));
823   }
824 #endif
825   /* set block sizes */
826   switch (product->type) {
827   case MATPRODUCT_PtAP:
828     if (B->cmap->bs > 1) PetscCall(MatSetBlockSizes(mat, B->cmap->bs, B->cmap->bs));
829     break;
830   case MATPRODUCT_RARt:
831     if (B->rmap->bs > 1) PetscCall(MatSetBlockSizes(mat, B->rmap->bs, B->rmap->bs));
832     break;
833   case MATPRODUCT_ABC:
834     PetscCall(MatSetBlockSizesFromMats(mat, A, C));
835     break;
836   case MATPRODUCT_AB:
837     PetscCall(MatSetBlockSizesFromMats(mat, A, B));
838     break;
839   case MATPRODUCT_AtB:
840     if (A->cmap->bs > 1 || B->cmap->bs > 1) PetscCall(MatSetBlockSizes(mat, A->cmap->bs, B->cmap->bs));
841     break;
842   case MATPRODUCT_ABt:
843     if (A->rmap->bs > 1 || B->rmap->bs > 1) PetscCall(MatSetBlockSizes(mat, A->rmap->bs, B->rmap->bs));
844     break;
845   default:
846     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Not for ProductType %s", MatProductTypes[product->type]);
847   }
848   PetscFunctionReturn(PETSC_SUCCESS);
849 }
850 
851 /*@
852   MatProductSetFill - Set an expected fill of the matrix whose values are computed via a matrix-matrix product operation
853 
854   Collective
855 
856   Input Parameters:
857 + mat  - the matrix whose values are to be computed via a matrix-matrix product operation
858 - 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.
859          If the product is a dense matrix, this value is not used.
860 
861   Level: intermediate
862 
863   Notes:
864   Use `fill` of `PETSC_DETERMINE` to use the default value.
865 
866   The deprecated `PETSC_DEFAULT` is also supported to mean use the current value.
867 
868 .seealso: [](ch_matrices), `MatProduct`, `PETSC_DETERMINE`, `Mat`, `MatProductSetFromOptions()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
869 @*/
870 PetscErrorCode MatProductSetFill(Mat mat, PetscReal fill)
871 {
872   PetscFunctionBegin;
873   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
874   MatCheckProduct(mat, 1);
875   if (fill == (PetscReal)PETSC_DETERMINE) mat->product->fill = mat->product->default_fill;
876   else if (fill != (PetscReal)PETSC_CURRENT) mat->product->fill = fill;
877   PetscFunctionReturn(PETSC_SUCCESS);
878 }
879 
880 /*@
881   MatProductSetAlgorithm - Requests a particular algorithm for a matrix-matrix product operation that will perform to compute the given matrix
882 
883   Collective
884 
885   Input Parameters:
886 + mat - the matrix whose values are computed via a matrix-matrix product operation
887 - alg - particular implementation algorithm of the matrix product, e.g., `MATPRODUCTALGORITHMDEFAULT`.
888 
889   Options Database Key:
890 . -mat_product_algorithm <algorithm> - Sets the algorithm, see `MatProductAlgorithm`
891 
892   Level: intermediate
893 
894 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductClear()`, `MatProductSetType()`, `MatProductSetFill()`, `MatProductCreate()`, `MatProductAlgorithm`, `MatProductType`, `MatProductGetAlgorithm()`
895 @*/
896 PetscErrorCode MatProductSetAlgorithm(Mat mat, MatProductAlgorithm alg)
897 {
898   PetscFunctionBegin;
899   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
900   MatCheckProduct(mat, 1);
901   PetscCall(PetscFree(mat->product->alg));
902   PetscCall(PetscStrallocpy(alg, &mat->product->alg));
903   PetscFunctionReturn(PETSC_SUCCESS);
904 }
905 
906 /*@
907   MatProductGetAlgorithm - Returns the selected algorithm for a matrix-matrix product operation
908 
909   Not Collective
910 
911   Input Parameter:
912 . mat - the matrix whose values are computed via a matrix-matrix product operation
913 
914   Output Parameter:
915 . alg - the selected algorithm of the matrix product, e.g., `MATPRODUCTALGORITHMDEFAULT`.
916 
917   Level: intermediate
918 
919 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductSetAlgorithm()`
920 @*/
921 PetscErrorCode MatProductGetAlgorithm(Mat mat, MatProductAlgorithm *alg)
922 {
923   PetscFunctionBegin;
924   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
925   PetscAssertPointer(alg, 2);
926   if (mat->product) *alg = mat->product->alg;
927   else *alg = NULL;
928   PetscFunctionReturn(PETSC_SUCCESS);
929 }
930 
931 /*@
932   MatProductSetType - Sets a particular matrix-matrix product operation to be used to compute the values of the given matrix
933 
934   Collective
935 
936   Input Parameters:
937 + mat        - the matrix whose values are computed via a matrix-matrix product operation
938 - productype - matrix product type, e.g., `MATPRODUCT_AB`,`MATPRODUCT_AtB`,`MATPRODUCT_ABt`,`MATPRODUCT_PtAP`,`MATPRODUCT_RARt`,`MATPRODUCT_ABC`,
939                   see `MatProductType`
940 
941   Level: intermediate
942 
943   Note:
944   The small t represents the transpose operation.
945 
946 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreate()`, `MatProductType`,
947           `MATPRODUCT_AB`, `MATPRODUCT_AtB`, `MATPRODUCT_ABt`, `MATPRODUCT_PtAP`, `MATPRODUCT_RARt`, `MATPRODUCT_ABC`
948 @*/
949 PetscErrorCode MatProductSetType(Mat mat, MatProductType productype)
950 {
951   PetscFunctionBegin;
952   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
953   MatCheckProduct(mat, 1);
954   PetscValidLogicalCollectiveEnum(mat, productype, 2);
955   if (productype != mat->product->type) {
956     if (mat->product->destroy) PetscCall((*mat->product->destroy)(mat->product->data));
957     mat->product->destroy     = NULL;
958     mat->product->data        = NULL;
959     mat->ops->productsymbolic = NULL;
960     mat->ops->productnumeric  = NULL;
961   }
962   mat->product->type = productype;
963   PetscFunctionReturn(PETSC_SUCCESS);
964 }
965 
966 /*@
967   MatProductClear - Clears from the matrix any internal data structures related to the computation of the values of the matrix from matrix-matrix product operations
968 
969   Collective
970 
971   Input Parameter:
972 . mat - the matrix whose values are to be computed via a matrix-matrix product operation
973 
974   Options Database Key:
975 . -mat_product_clear - Clear intermediate data structures after `MatProductNumeric()` has been called
976 
977   Level: intermediate
978 
979   Notes:
980   This function should be called to remove any intermediate data used to compute the matrix to free up memory.
981 
982   After having called this function, matrix-matrix product operations can no longer be used on `mat`
983 
984 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreate()`
985 @*/
986 PetscErrorCode MatProductClear(Mat mat)
987 {
988   Mat_Product *product = mat->product;
989 
990   PetscFunctionBegin;
991   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
992   if (product) {
993     PetscCall(MatDestroy(&product->A));
994     PetscCall(MatDestroy(&product->B));
995     PetscCall(MatDestroy(&product->C));
996     PetscCall(PetscFree(product->alg));
997     PetscCall(MatDestroy(&product->Dwork));
998     if (product->destroy) PetscCall((*product->destroy)(product->data));
999   }
1000   PetscCall(PetscFree(mat->product));
1001   mat->ops->productsymbolic = NULL;
1002   mat->ops->productnumeric  = NULL;
1003   PetscFunctionReturn(PETSC_SUCCESS);
1004 }
1005 
1006 /* Create a supporting struct and attach it to the matrix product */
1007 PetscErrorCode MatProductCreate_Private(Mat A, Mat B, Mat C, Mat D)
1008 {
1009   Mat_Product *product = NULL;
1010 
1011   PetscFunctionBegin;
1012   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
1013   PetscCheck(!D->product, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product already present");
1014   PetscCall(PetscNew(&product));
1015   product->A                    = A;
1016   product->B                    = B;
1017   product->C                    = C;
1018   product->type                 = MATPRODUCT_UNSPECIFIED;
1019   product->Dwork                = NULL;
1020   product->api_user             = PETSC_FALSE;
1021   product->clear                = PETSC_FALSE;
1022   product->setfromoptionscalled = PETSC_FALSE;
1023   PetscObjectParameterSetDefault(product, fill, 2);
1024   D->product = product;
1025 
1026   PetscCall(MatProductSetAlgorithm(D, MATPRODUCTALGORITHMDEFAULT));
1027   PetscCall(MatProductSetFill(D, PETSC_DEFAULT));
1028 
1029   PetscCall(PetscObjectReference((PetscObject)A));
1030   PetscCall(PetscObjectReference((PetscObject)B));
1031   PetscCall(PetscObjectReference((PetscObject)C));
1032   PetscFunctionReturn(PETSC_SUCCESS);
1033 }
1034 
1035 /*@
1036   MatProductCreateWithMat - Set a given matrix to have its values computed via matrix-matrix operations on other matrices.
1037 
1038   Collective
1039 
1040   Input Parameters:
1041 + A - the first matrix
1042 . B - the second matrix
1043 . C - the third matrix (optional, use `NULL` if not needed)
1044 - D - the matrix whose values are to be computed via a matrix-matrix product operation
1045 
1046   Level: intermediate
1047 
1048   Notes:
1049   Use `MatProductCreate()` if the matrix you wish computed (the `D` matrix) does not already exist
1050 
1051   See `MatProductCreate()` for details on the usage of the matrix-matrix product operations
1052 
1053   Any product data currently attached to `D` will be cleared
1054 
1055 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductType`, `MatProductSetType()`, `MatProductAlgorithm`,
1056           `MatProductSetAlgorithm`, `MatProductCreate()`, `MatProductClear()`
1057 @*/
1058 PetscErrorCode MatProductCreateWithMat(Mat A, Mat B, Mat C, Mat D)
1059 {
1060   PetscFunctionBegin;
1061   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1062   PetscValidType(A, 1);
1063   MatCheckPreallocated(A, 1);
1064   PetscCheck(A->assembled, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1065   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1066 
1067   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
1068   PetscValidType(B, 2);
1069   MatCheckPreallocated(B, 2);
1070   PetscCheck(B->assembled, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1071   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1072 
1073   if (C) {
1074     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
1075     PetscValidType(C, 3);
1076     MatCheckPreallocated(C, 3);
1077     PetscCheck(C->assembled, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1078     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1079   }
1080 
1081   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
1082   PetscValidType(D, 4);
1083   MatCheckPreallocated(D, 4);
1084   PetscCheck(D->assembled, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1085   PetscCheck(!D->factortype, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1086 
1087   /* Create a supporting struct and attach it to D */
1088   PetscCall(MatProductClear(D));
1089   PetscCall(MatProductCreate_Private(A, B, C, D));
1090   PetscFunctionReturn(PETSC_SUCCESS);
1091 }
1092 
1093 /*@
1094   MatProductCreate - create a matrix to hold the result of a matrix-matrix product operation
1095 
1096   Collective
1097 
1098   Input Parameters:
1099 + A - the first matrix
1100 . B - the second matrix
1101 - C - the third matrix (or `NULL`)
1102 
1103   Output Parameter:
1104 . D - the matrix whose values are to be computed via a matrix-matrix product operation
1105 
1106   Level: intermediate
1107 
1108   Example:
1109 .vb
1110     MatProductCreate(A,B,C,&D); or MatProductCreateWithMat(A,B,C,D)
1111     MatProductSetType(D, MATPRODUCT_AB or MATPRODUCT_AtB or MATPRODUCT_ABt or MATPRODUCT_PtAP or MATPRODUCT_RARt or MATPRODUCT_ABC)
1112     MatProductSetAlgorithm(D, alg)
1113     MatProductSetFill(D,fill)
1114     MatProductSetFromOptions(D)
1115     MatProductSymbolic(D)
1116     MatProductNumeric(D)
1117     Change numerical values in some of the matrices
1118     MatProductNumeric(D)
1119 .ve
1120 
1121   Notes:
1122   Use `MatProductCreateWithMat()` if the matrix you wish computed, the `D` matrix, already exists.
1123 
1124   The information computed during the symbolic stage can be reused for new numerical computations with the same non-zero structure
1125 
1126   Developer Notes:
1127   It is undocumented what happens if the nonzero structure of the input matrices changes. Is the symbolic stage automatically redone? Does it crash?
1128   Is there error checking for it?
1129 
1130 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductClear()`
1131 @*/
1132 PetscErrorCode MatProductCreate(Mat A, Mat B, Mat C, Mat *D)
1133 {
1134   PetscFunctionBegin;
1135   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1136   PetscValidType(A, 1);
1137   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
1138   PetscValidType(B, 2);
1139   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix A");
1140   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix B");
1141 
1142   if (C) {
1143     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
1144     PetscValidType(C, 3);
1145     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix C");
1146   }
1147 
1148   PetscAssertPointer(D, 4);
1149   PetscCall(MatCreate(PetscObjectComm((PetscObject)A), D));
1150   /* Delay setting type of D to the MatProduct symbolic phase, as we allow sparse A and dense B */
1151   PetscCall(MatProductCreate_Private(A, B, C, *D));
1152   PetscFunctionReturn(PETSC_SUCCESS);
1153 }
1154 
1155 /*
1156    These are safe basic implementations of ABC, RARt and PtAP
1157    that do not rely on mat->ops->matmatop function pointers.
1158    They only use the MatProduct API and are currently used by
1159    cuSPARSE and KOKKOS-KERNELS backends
1160 */
1161 typedef struct {
1162   Mat BC;
1163   Mat ABC;
1164 } MatMatMatPrivate;
1165 
1166 static PetscErrorCode MatDestroy_MatMatMatPrivate(void *data)
1167 {
1168   MatMatMatPrivate *mmdata = (MatMatMatPrivate *)data;
1169 
1170   PetscFunctionBegin;
1171   PetscCall(MatDestroy(&mmdata->BC));
1172   PetscCall(MatDestroy(&mmdata->ABC));
1173   PetscCall(PetscFree(data));
1174   PetscFunctionReturn(PETSC_SUCCESS);
1175 }
1176 
1177 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1178 {
1179   Mat_Product      *product = mat->product;
1180   MatMatMatPrivate *mmabc;
1181 
1182   PetscFunctionBegin;
1183   MatCheckProduct(mat, 1);
1184   PetscCheck(mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data empty");
1185   mmabc = (MatMatMatPrivate *)mat->product->data;
1186   PetscCheck(mmabc->BC->ops->productnumeric, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing numeric stage");
1187   /* use function pointer directly to prevent logging */
1188   PetscCall((*mmabc->BC->ops->productnumeric)(mmabc->BC));
1189   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1190   mat->product             = mmabc->ABC->product;
1191   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1192   /* use function pointer directly to prevent logging */
1193   PetscUseTypeMethod(mat, productnumeric);
1194   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1195   mat->product             = product;
1196   PetscFunctionReturn(PETSC_SUCCESS);
1197 }
1198 
1199 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1200 {
1201   Mat_Product      *product = mat->product;
1202   Mat               A, B, C;
1203   MatProductType    p1, p2;
1204   MatMatMatPrivate *mmabc;
1205   const char       *prefix;
1206 
1207   PetscFunctionBegin;
1208   MatCheckProduct(mat, 1);
1209   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data not empty");
1210   PetscCall(MatGetOptionsPrefix(mat, &prefix));
1211   PetscCall(PetscNew(&mmabc));
1212   product->data    = mmabc;
1213   product->destroy = MatDestroy_MatMatMatPrivate;
1214   switch (product->type) {
1215   case MATPRODUCT_PtAP:
1216     p1 = MATPRODUCT_AB;
1217     p2 = MATPRODUCT_AtB;
1218     A  = product->B;
1219     B  = product->A;
1220     C  = product->B;
1221     if (A->cmap->bs > 0 && C->cmap->bs > 0) PetscCall(MatSetBlockSizes(mat, A->cmap->bs, C->cmap->bs));
1222     break;
1223   case MATPRODUCT_RARt:
1224     p1 = MATPRODUCT_ABt;
1225     p2 = MATPRODUCT_AB;
1226     A  = product->B;
1227     B  = product->A;
1228     C  = product->B;
1229     if (A->rmap->bs > 0 && C->rmap->bs > 0) PetscCall(MatSetBlockSizes(mat, A->rmap->bs, C->rmap->bs));
1230     break;
1231   case MATPRODUCT_ABC:
1232     p1 = MATPRODUCT_AB;
1233     p2 = MATPRODUCT_AB;
1234     A  = product->A;
1235     B  = product->B;
1236     C  = product->C;
1237     PetscCall(MatSetBlockSizesFromMats(mat, A, C));
1238     break;
1239   default:
1240     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Not for ProductType %s", MatProductTypes[product->type]);
1241   }
1242   PetscCall(MatProductCreate(B, C, NULL, &mmabc->BC));
1243   PetscCall(MatSetOptionsPrefix(mmabc->BC, prefix));
1244   PetscCall(MatAppendOptionsPrefix(mmabc->BC, "P1_"));
1245   PetscCall(MatProductSetType(mmabc->BC, p1));
1246   PetscCall(MatProductSetAlgorithm(mmabc->BC, MATPRODUCTALGORITHMDEFAULT));
1247   PetscCall(MatProductSetFill(mmabc->BC, product->fill));
1248   mmabc->BC->product->api_user = product->api_user;
1249   PetscCall(MatProductSetFromOptions(mmabc->BC));
1250   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);
1251   /* use function pointer directly to prevent logging */
1252   PetscCall((*mmabc->BC->ops->productsymbolic)(mmabc->BC));
1253 
1254   PetscCall(MatProductCreate(A, mmabc->BC, NULL, &mmabc->ABC));
1255   PetscCall(MatSetOptionsPrefix(mmabc->ABC, prefix));
1256   PetscCall(MatAppendOptionsPrefix(mmabc->ABC, "P2_"));
1257   PetscCall(MatProductSetType(mmabc->ABC, p2));
1258   PetscCall(MatProductSetAlgorithm(mmabc->ABC, MATPRODUCTALGORITHMDEFAULT));
1259   PetscCall(MatProductSetFill(mmabc->ABC, product->fill));
1260   mmabc->ABC->product->api_user = product->api_user;
1261   PetscCall(MatProductSetFromOptions(mmabc->ABC));
1262   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);
1263   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1264   mat->product              = mmabc->ABC->product;
1265   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1266   /* use function pointer directly to prevent logging */
1267   PetscUseTypeMethod(mat, productsymbolic);
1268   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1269   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1270   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1271   mat->product                    = product;
1272   PetscFunctionReturn(PETSC_SUCCESS);
1273 }
1274 
1275 /*@
1276   MatProductGetType - Returns the type of matrix-matrix product associated with computing values for the given matrix
1277 
1278   Not Collective
1279 
1280   Input Parameter:
1281 . mat - the matrix whose values are to be computed via a matrix-matrix product operation
1282 
1283   Output Parameter:
1284 . mtype - the `MatProductType`
1285 
1286   Level: intermediate
1287 
1288 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductCreate()`, `MatProductType`, `MatProductAlgorithm`
1289 @*/
1290 PetscErrorCode MatProductGetType(Mat mat, MatProductType *mtype)
1291 {
1292   PetscFunctionBegin;
1293   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1294   PetscAssertPointer(mtype, 2);
1295   *mtype = MATPRODUCT_UNSPECIFIED;
1296   if (mat->product) *mtype = mat->product->type;
1297   PetscFunctionReturn(PETSC_SUCCESS);
1298 }
1299 
1300 /*@
1301   MatProductGetMats - Returns the matrices associated with the matrix-matrix product associated with computing values for the given matrix
1302 
1303   Not Collective
1304 
1305   Input Parameter:
1306 . mat - the matrix whose values are to be computed via a matrix-matrix product operation
1307 
1308   Output Parameters:
1309 + A - the first matrix
1310 . B - the second matrix
1311 - C - the third matrix (may be `NULL` for some `MatProductType`)
1312 
1313   Level: intermediate
1314 
1315 .seealso: [](ch_matrices), `MatProduct`, `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
1316 @*/
1317 PetscErrorCode MatProductGetMats(Mat mat, Mat *A, Mat *B, Mat *C)
1318 {
1319   PetscFunctionBegin;
1320   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1321   if (A) *A = mat->product ? mat->product->A : NULL;
1322   if (B) *B = mat->product ? mat->product->B : NULL;
1323   if (C) *C = mat->product ? mat->product->C : NULL;
1324   PetscFunctionReturn(PETSC_SUCCESS);
1325 }
1326