xref: /petsc/src/mat/interface/matproduct.c (revision c69effb2e05caa58810396c101d616d7ad9da0a7)
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 /*@C
201    MatProductReplaceMats - Replace the input matrices for the matrix-matrix product operation inside the computed matrix
202 
203    Collective
204 
205    Input Parameters:
206 +  A - the matrix or `NULL` if not being replaced
207 .  B - the matrix or `NULL` if not being replaced
208 .  C - the matrix or `NULL` if not being replaced
209 -  D - the matrix whose values are computed via a matrix-matrix product operation
210 
211    Level: intermediate
212 
213    Note:
214      To reuse the symbolic phase, the input matrices must have exactly the same data structure as the replaced one.
215      If the type of any of the input matrices is different than what was previously used, or their symmetry flag changed but
216      the symbolic phase took advantage of their symmetry, the product is cleared and `MatProductSetFromOptions()`
217      and `MatProductSymbolic()` are invoked again.
218 
219 .seealso: `Mat`, `MatProductCreate()`, `MatProductSetFromOptions()`, `MatProductSymbolic().` `MatProductClear()`
220 @*/
221 PetscErrorCode MatProductReplaceMats(Mat A, Mat B, Mat C, Mat D)
222 {
223   Mat_Product *product;
224   PetscBool    flgA = PETSC_TRUE, flgB = PETSC_TRUE, flgC = PETSC_TRUE, isset, issym;
225 
226   PetscFunctionBegin;
227   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
228   MatCheckProduct(D, 4);
229   product = D->product;
230   if (A) {
231     PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
232     PetscCall(PetscObjectReference((PetscObject)A));
233     PetscCall(PetscObjectTypeCompare((PetscObject)product->A, ((PetscObject)A)->type_name, &flgA));
234     PetscCall(MatIsSymmetricKnown(A, &isset, &issym));
235     if (product->symbolic_used_the_fact_A_is_symmetric && isset && !issym) { /* symbolic was built around a symmetric A, but the new A is not anymore */
236       flgA                                           = PETSC_FALSE;
237       product->symbolic_used_the_fact_A_is_symmetric = PETSC_FALSE; /* reinit */
238     }
239     PetscCall(MatDestroy(&product->A));
240     product->A = A;
241   }
242   if (B) {
243     PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
244     PetscCall(PetscObjectReference((PetscObject)B));
245     PetscCall(PetscObjectTypeCompare((PetscObject)product->B, ((PetscObject)B)->type_name, &flgB));
246     PetscCall(MatIsSymmetricKnown(B, &isset, &issym));
247     if (product->symbolic_used_the_fact_B_is_symmetric && isset && !issym) {
248       flgB                                           = PETSC_FALSE;
249       product->symbolic_used_the_fact_B_is_symmetric = PETSC_FALSE; /* reinit */
250     }
251     PetscCall(MatDestroy(&product->B));
252     product->B = B;
253   }
254   if (C) {
255     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
256     PetscCall(PetscObjectReference((PetscObject)C));
257     PetscCall(PetscObjectTypeCompare((PetscObject)product->C, ((PetscObject)C)->type_name, &flgC));
258     PetscCall(MatIsSymmetricKnown(C, &isset, &issym));
259     if (product->symbolic_used_the_fact_C_is_symmetric && isset && !issym) {
260       flgC                                           = PETSC_FALSE;
261       product->symbolic_used_the_fact_C_is_symmetric = PETSC_FALSE; /* reinit */
262     }
263     PetscCall(MatDestroy(&product->C));
264     product->C = C;
265   }
266   /* Any of the replaced mats is of a different type, reset */
267   if (!flgA || !flgB || !flgC) {
268     if (D->product->destroy) PetscCall((*D->product->destroy)(D->product->data));
269     D->product->destroy = NULL;
270     D->product->data    = NULL;
271     if (D->ops->productnumeric || D->ops->productsymbolic) {
272       PetscCall(MatProductSetFromOptions(D));
273       PetscCall(MatProductSymbolic(D));
274     }
275   }
276   PetscFunctionReturn(PETSC_SUCCESS);
277 }
278 
279 static PetscErrorCode MatProductNumeric_X_Dense(Mat C)
280 {
281   Mat_Product *product = C->product;
282   Mat          A = product->A, B = product->B;
283   PetscInt     k, K              = B->cmap->N;
284   PetscBool    t = PETSC_TRUE, iscuda = PETSC_FALSE;
285   PetscBool    Bcpu = PETSC_TRUE, Ccpu = PETSC_TRUE;
286   char        *Btype = NULL, *Ctype = NULL;
287 
288   PetscFunctionBegin;
289   switch (product->type) {
290   case MATPRODUCT_AB:
291     t = PETSC_FALSE;
292   case MATPRODUCT_AtB:
293     break;
294   default:
295     SETERRQ(PetscObjectComm((PetscObject)C), PETSC_ERR_SUP, "MatProductNumeric type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name);
296   }
297   if (PetscDefined(HAVE_CUDA)) {
298     VecType vtype;
299 
300     PetscCall(MatGetVecType(A, &vtype));
301     PetscCall(PetscStrcmp(vtype, VECCUDA, &iscuda));
302     if (!iscuda) PetscCall(PetscStrcmp(vtype, VECSEQCUDA, &iscuda));
303     if (!iscuda) PetscCall(PetscStrcmp(vtype, VECMPICUDA, &iscuda));
304     if (iscuda) { /* Make sure we have up-to-date data on the GPU */
305       PetscCall(PetscStrallocpy(((PetscObject)B)->type_name, &Btype));
306       PetscCall(PetscStrallocpy(((PetscObject)C)->type_name, &Ctype));
307       PetscCall(MatConvert(B, MATDENSECUDA, MAT_INPLACE_MATRIX, &B));
308       if (!C->assembled) { /* need to flag the matrix as assembled, otherwise MatConvert will complain */
309         PetscCall(MatAssemblyBegin(C, MAT_FINAL_ASSEMBLY));
310         PetscCall(MatAssemblyEnd(C, MAT_FINAL_ASSEMBLY));
311       }
312       PetscCall(MatConvert(C, MATDENSECUDA, MAT_INPLACE_MATRIX, &C));
313     } else { /* Make sure we have up-to-date data on the CPU */
314 #if defined(PETSC_HAVE_CUDA) || defined(PETSC_HAVE_VIENNACL)
315       Bcpu = B->boundtocpu;
316       Ccpu = C->boundtocpu;
317 #endif
318       PetscCall(MatBindToCPU(B, PETSC_TRUE));
319       PetscCall(MatBindToCPU(C, PETSC_TRUE));
320     }
321   }
322   for (k = 0; k < K; k++) {
323     Vec x, y;
324 
325     PetscCall(MatDenseGetColumnVecRead(B, k, &x));
326     PetscCall(MatDenseGetColumnVecWrite(C, k, &y));
327     if (t) {
328       PetscCall(MatMultTranspose(A, x, y));
329     } else {
330       PetscCall(MatMult(A, x, y));
331     }
332     PetscCall(MatDenseRestoreColumnVecRead(B, k, &x));
333     PetscCall(MatDenseRestoreColumnVecWrite(C, k, &y));
334   }
335   PetscCall(MatAssemblyBegin(C, MAT_FINAL_ASSEMBLY));
336   PetscCall(MatAssemblyEnd(C, MAT_FINAL_ASSEMBLY));
337   if (PetscDefined(HAVE_CUDA)) {
338     if (iscuda) {
339       PetscCall(MatConvert(B, Btype, MAT_INPLACE_MATRIX, &B));
340       PetscCall(MatConvert(C, Ctype, MAT_INPLACE_MATRIX, &C));
341     } else {
342       PetscCall(MatBindToCPU(B, Bcpu));
343       PetscCall(MatBindToCPU(C, Ccpu));
344     }
345   }
346   PetscCall(PetscFree(Btype));
347   PetscCall(PetscFree(Ctype));
348   PetscFunctionReturn(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   #pragma clang diagnostic push
452   #pragma clang diagnostic ignored "-Wformat-pedantic"
453 #elif defined(__GNUC__) || defined(__GNUG__)
454   #pragma GCC diagnostic push
455   #pragma GCC diagnostic ignored "-Wformat"
456 #endif
457     PetscCall(PetscObjectQueryFunction((PetscObject)A, mtypes, &f));
458     PetscCall(PetscInfo(mat, "  querying %s from A? %p\n", mtypes, f));
459     if (!f) {
460       PetscCall(PetscObjectQueryFunction((PetscObject)B, mtypes, &f));
461       PetscCall(PetscInfo(mat, "  querying %s from %s? %p\n", mtypes, bname, f));
462     }
463     if (!f && C) {
464       PetscCall(PetscObjectQueryFunction((PetscObject)C, mtypes, &f));
465       PetscCall(PetscInfo(mat, "  querying %s from C? %p\n", mtypes, f));
466     }
467     if (f) PetscCall((*f)(mat));
468 
469     /* We may have found f but it did not succeed */
470     /* some matrices (i.e. MATTRANSPOSEVIRTUAL, MATSHELL constructed from MatConvert), knows what to do with their inner matrices */
471     if (!mat->ops->productsymbolic) {
472       PetscCall(PetscStrncpy(mtypes, "MatProductSetFromOptions_anytype_C", sizeof(mtypes)));
473       PetscCall(PetscObjectQueryFunction((PetscObject)A, mtypes, &f));
474       PetscCall(PetscInfo(mat, "  querying %s from A? %p\n", mtypes, f));
475       if (!f) {
476         PetscCall(PetscObjectQueryFunction((PetscObject)B, mtypes, &f));
477         PetscCall(PetscInfo(mat, "  querying %s from %s? %p\n", mtypes, bname, f));
478       }
479       if (!f && C) {
480         PetscCall(PetscObjectQueryFunction((PetscObject)C, mtypes, &f));
481         PetscCall(PetscInfo(mat, "  querying %s from C? %p\n", mtypes, f));
482       }
483     }
484     if (f) PetscCall((*f)(mat));
485   }
486 #if defined(__clang__)
487   #pragma clang diagnostic pop
488 #elif defined(__GNUC__) || defined(__GNUG__)
489   #pragma GCC diagnostic pop
490 #endif
491   /* We may have found f but it did not succeed */
492   if (!mat->ops->productsymbolic) {
493     /* we can still compute the product if B is of type dense */
494     if (product->type == MATPRODUCT_AB || product->type == MATPRODUCT_AtB) {
495       PetscBool isdense;
496 
497       PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)B, &isdense, MATSEQDENSE, MATMPIDENSE, ""));
498       if (isdense) {
499         mat->ops->productsymbolic = MatProductSymbolic_X_Dense;
500         PetscCall(PetscInfo(mat, "  using basic looping over columns of a dense matrix\n"));
501       }
502     } else if (product->type != MATPRODUCT_ABt) { /* use MatProductSymbolic/Numeric_Unsafe() for triple products only */
503       /*
504          TODO: this should be changed to a proper setfromoptions, not setting the symbolic pointer here, because we do not know if
505                the combination will succeed. In order to be sure, we need MatProductGetProductType to return the type of the result
506                before computing the symbolic phase
507       */
508       PetscCall(PetscInfo(mat, "  symbolic product not supported, using MatProductSymbolic_Unsafe() implementation\n"));
509       mat->ops->productsymbolic = MatProductSymbolic_Unsafe;
510     }
511   }
512   if (!mat->ops->productsymbolic) PetscCall(PetscInfo(mat, "  symbolic product is not supported\n"));
513   PetscFunctionReturn(PETSC_SUCCESS);
514 }
515 
516 /*@C
517    MatProductSetFromOptions - Sets the options for the computation of a matrix-matrix product operation where the type,
518    the algorithm etc are determined from the options database.
519 
520    Logically Collective
521 
522    Input Parameter:
523 .  mat - the matrix whose values are computed via a matrix-matrix product operation
524 
525    Options Database Key:
526 .    -mat_product_clear - Clear intermediate data structures after `MatProductNumeric()` has been called
527 
528    Level: intermediate
529 
530    Note:
531    This reduces memory usage but means that the matrix cannot be re-used for a matrix-matrix product operation
532 
533 .seealso: `Mat`, `MatSetFromOptions()`, `MatProductCreate()`, `MatProductCreateWithMat()`, `MatProductNumeric()`, `MatProductSetType()`, `MatProductSetAlgorithm()`
534 @*/
535 PetscErrorCode MatProductSetFromOptions(Mat mat)
536 {
537   PetscFunctionBegin;
538   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
539   MatCheckProduct(mat, 1);
540   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_ORDER, "Cannot call MatProductSetFromOptions with already present data");
541   PetscObjectOptionsBegin((PetscObject)mat);
542   PetscCall(PetscOptionsBool("-mat_product_clear", "Clear intermediate data structures after MatProductNumeric() has been called", "MatProductClear", mat->product->clear, &mat->product->clear, NULL));
543   PetscCall(PetscOptionsDeprecated("-mat_freeintermediatedatastructures", "-mat_product_clear", "3.13", "Or call MatProductClear() after MatProductNumeric()"));
544   PetscOptionsEnd();
545   PetscCall(MatProductSetFromOptions_Private(mat));
546   PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing product after setup phase");
547   PetscFunctionReturn(PETSC_SUCCESS);
548 }
549 
550 /*@C
551    MatProductView - View the private matrix-matrix algorithm object within a matrix
552 
553    Logically Collective
554 
555    Input Parameters:
556 +  mat - the matrix obtained with `MatProductCreate()` or `MatProductCreateWithMat()`
557 -  viewer - where `mat` should be reviewed
558 
559    Level: intermediate
560 
561 .seealso: `Mat`, `MatProductSetFromOptions()`, `MatView()`, `MatProductCreate()`, `MatProductCreateWithMat()`
562 @*/
563 PetscErrorCode MatProductView(Mat mat, PetscViewer viewer)
564 {
565   PetscFunctionBegin;
566   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
567   if (!mat->product) PetscFunctionReturn(PETSC_SUCCESS);
568   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)mat), &viewer));
569   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
570   PetscCheckSameComm(mat, 1, viewer, 2);
571   if (mat->product->view) PetscCall((*mat->product->view)(mat, viewer));
572   PetscFunctionReturn(PETSC_SUCCESS);
573 }
574 
575 /* ----------------------------------------------- */
576 /* these are basic implementations relying on the old function pointers
577  * they are dangerous and should be removed in the future */
578 PetscErrorCode MatProductNumeric_AB(Mat mat)
579 {
580   Mat_Product *product = mat->product;
581   Mat          A = product->A, B = product->B;
582 
583   PetscFunctionBegin;
584   PetscCall((*mat->ops->matmultnumeric)(A, B, mat));
585   PetscFunctionReturn(PETSC_SUCCESS);
586 }
587 
588 PetscErrorCode MatProductNumeric_AtB(Mat mat)
589 {
590   Mat_Product *product = mat->product;
591   Mat          A = product->A, B = product->B;
592 
593   PetscFunctionBegin;
594   PetscCall((*mat->ops->transposematmultnumeric)(A, B, mat));
595   PetscFunctionReturn(PETSC_SUCCESS);
596 }
597 
598 PetscErrorCode MatProductNumeric_ABt(Mat mat)
599 {
600   Mat_Product *product = mat->product;
601   Mat          A = product->A, B = product->B;
602 
603   PetscFunctionBegin;
604   PetscCall((*mat->ops->mattransposemultnumeric)(A, B, mat));
605   PetscFunctionReturn(PETSC_SUCCESS);
606 }
607 
608 PetscErrorCode MatProductNumeric_PtAP(Mat mat)
609 {
610   Mat_Product *product = mat->product;
611   Mat          A = product->A, B = product->B;
612 
613   PetscFunctionBegin;
614   PetscCall((*mat->ops->ptapnumeric)(A, B, mat));
615   PetscFunctionReturn(PETSC_SUCCESS);
616 }
617 
618 PetscErrorCode MatProductNumeric_RARt(Mat mat)
619 {
620   Mat_Product *product = mat->product;
621   Mat          A = product->A, B = product->B;
622 
623   PetscFunctionBegin;
624   PetscCall((*mat->ops->rartnumeric)(A, B, mat));
625   PetscFunctionReturn(PETSC_SUCCESS);
626 }
627 
628 PetscErrorCode MatProductNumeric_ABC(Mat mat)
629 {
630   Mat_Product *product = mat->product;
631   Mat          A = product->A, B = product->B, C = product->C;
632 
633   PetscFunctionBegin;
634   PetscCall((*mat->ops->matmatmultnumeric)(A, B, C, mat));
635   PetscFunctionReturn(PETSC_SUCCESS);
636 }
637 
638 /* ----------------------------------------------- */
639 
640 /*@
641    MatProductNumeric - Compute a matrix-matrix product operation with numerical values.
642 
643    Collective
644 
645    Input/Output Parameter:
646 .  mat - the matrix whose values are computed via a matrix-matrix product operation
647 
648    Level: intermediate
649 
650    Note:
651    `MatProductSymbolic()` must have been called on `mat` before calling this function
652 
653 .seealso: `Mat`, `MatProductSetAlgorithm()`, `MatProductSetType()`, `MatProductCreate()`, `MatSetType()`, `MatProductSymbolic()`
654 @*/
655 PetscErrorCode MatProductNumeric(Mat mat)
656 {
657   PetscLogEvent eventtype = -1;
658   PetscBool     missing   = PETSC_FALSE;
659 
660   PetscFunctionBegin;
661   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
662   MatCheckProduct(mat, 1);
663   switch (mat->product->type) {
664   case MATPRODUCT_AB:
665     eventtype = MAT_MatMultNumeric;
666     break;
667   case MATPRODUCT_AtB:
668     eventtype = MAT_TransposeMatMultNumeric;
669     break;
670   case MATPRODUCT_ABt:
671     eventtype = MAT_MatTransposeMultNumeric;
672     break;
673   case MATPRODUCT_PtAP:
674     eventtype = MAT_PtAPNumeric;
675     break;
676   case MATPRODUCT_RARt:
677     eventtype = MAT_RARtNumeric;
678     break;
679   case MATPRODUCT_ABC:
680     eventtype = MAT_MatMatMultNumeric;
681     break;
682   default:
683     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[mat->product->type]);
684   }
685 
686   if (mat->ops->productnumeric) {
687     PetscCall(PetscLogEventBegin(eventtype, mat, 0, 0, 0));
688     PetscUseTypeMethod(mat, productnumeric);
689     PetscCall(PetscLogEventEnd(eventtype, mat, 0, 0, 0));
690   } else missing = PETSC_TRUE;
691 
692   if (missing || !mat->product) {
693     char errstr[256];
694 
695     if (mat->product->type == MATPRODUCT_ABC) {
696       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));
697     } else {
698       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));
699     }
700     PetscCheck(!missing, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Unspecified numeric phase for product %s", errstr);
701     PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing struct after symbolic phase for product %s", errstr);
702   }
703 
704   if (mat->product->clear) PetscCall(MatProductClear(mat));
705   PetscCall(PetscObjectStateIncrease((PetscObject)mat));
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 /* ----------------------------------------------- */
710 /* these are basic implementations relying on the old function pointers
711  * they are dangerous and should be removed in the future */
712 PetscErrorCode MatProductSymbolic_AB(Mat mat)
713 {
714   Mat_Product *product = mat->product;
715   Mat          A = product->A, B = product->B;
716 
717   PetscFunctionBegin;
718   PetscCall((*mat->ops->matmultsymbolic)(A, B, product->fill, mat));
719   mat->ops->productnumeric = MatProductNumeric_AB;
720   PetscFunctionReturn(PETSC_SUCCESS);
721 }
722 
723 PetscErrorCode MatProductSymbolic_AtB(Mat mat)
724 {
725   Mat_Product *product = mat->product;
726   Mat          A = product->A, B = product->B;
727 
728   PetscFunctionBegin;
729   PetscCall((*mat->ops->transposematmultsymbolic)(A, B, product->fill, mat));
730   mat->ops->productnumeric = MatProductNumeric_AtB;
731   PetscFunctionReturn(PETSC_SUCCESS);
732 }
733 
734 PetscErrorCode MatProductSymbolic_ABt(Mat mat)
735 {
736   Mat_Product *product = mat->product;
737   Mat          A = product->A, B = product->B;
738 
739   PetscFunctionBegin;
740   PetscCall((*mat->ops->mattransposemultsymbolic)(A, B, product->fill, mat));
741   mat->ops->productnumeric = MatProductNumeric_ABt;
742   PetscFunctionReturn(PETSC_SUCCESS);
743 }
744 
745 PetscErrorCode MatProductSymbolic_ABC(Mat mat)
746 {
747   Mat_Product *product = mat->product;
748   Mat          A = product->A, B = product->B, C = product->C;
749 
750   PetscFunctionBegin;
751   PetscCall((*mat->ops->matmatmultsymbolic)(A, B, C, product->fill, mat));
752   mat->ops->productnumeric = MatProductNumeric_ABC;
753   PetscFunctionReturn(PETSC_SUCCESS);
754 }
755 
756 /* ----------------------------------------------- */
757 
758 /*@
759    MatProductSymbolic - Perform the symbolic portion of a matrix-matrix product operation, this creates a data structure for use with the numerical product done with
760   `MatProductNumeric()`
761 
762    Collective
763 
764    Input/Output Parameter:
765 .  mat - the matrix whose values are computed via a matrix-matrix product operation
766 
767    Level: intermediate
768 
769    Note:
770    `MatProductSetFromOptions()` must have been called on `mat` before calling this function
771 
772 .seealso: `Mat`, `MatProductCreate()`, `MatProductCreateWithMat()`, `MatProductSetFromOptions()`, `MatProductNumeric()`, `MatProductSetType()`, `MatProductSetAlgorithm()`
773 @*/
774 PetscErrorCode MatProductSymbolic(Mat mat)
775 {
776   PetscLogEvent eventtype = -1;
777   PetscBool     missing   = PETSC_FALSE;
778 
779   PetscFunctionBegin;
780   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
781   MatCheckProduct(mat, 1);
782   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_ORDER, "Cannot run symbolic phase. Product data not empty");
783   switch (mat->product->type) {
784   case MATPRODUCT_AB:
785     eventtype = MAT_MatMultSymbolic;
786     break;
787   case MATPRODUCT_AtB:
788     eventtype = MAT_TransposeMatMultSymbolic;
789     break;
790   case MATPRODUCT_ABt:
791     eventtype = MAT_MatTransposeMultSymbolic;
792     break;
793   case MATPRODUCT_PtAP:
794     eventtype = MAT_PtAPSymbolic;
795     break;
796   case MATPRODUCT_RARt:
797     eventtype = MAT_RARtSymbolic;
798     break;
799   case MATPRODUCT_ABC:
800     eventtype = MAT_MatMatMultSymbolic;
801     break;
802   default:
803     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "ProductType %s is not supported", MatProductTypes[mat->product->type]);
804   }
805   mat->ops->productnumeric = NULL;
806   if (mat->ops->productsymbolic) {
807     PetscCall(PetscLogEventBegin(eventtype, mat, 0, 0, 0));
808     PetscUseTypeMethod(mat, productsymbolic);
809     PetscCall(PetscLogEventEnd(eventtype, mat, 0, 0, 0));
810   } else missing = PETSC_TRUE;
811 
812   if (missing || !mat->product || !mat->ops->productnumeric) {
813     char errstr[256];
814 
815     if (mat->product->type == MATPRODUCT_ABC) {
816       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));
817     } else {
818       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));
819     }
820     PetscCheck(!missing, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Unspecified symbolic phase for product %s. Call MatProductSetFromOptions() first", errstr);
821     PetscCheck(mat->product, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing struct after symbolic phase for product %s", errstr);
822   }
823   PetscFunctionReturn(PETSC_SUCCESS);
824 }
825 
826 /*@
827    MatProductSetFill - Set an expected fill of the matrix whose values are computed via a matrix-matrix product operation
828 
829    Collective
830 
831    Input Parameters:
832 +  mat - the matrix whose values are computed via a matrix-matrix product operation
833 -  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.
834           If the product is a dense matrix, this value is not used.
835 
836    Level: intermediate
837 
838 .seealso: `Mat`, `MatProductSetFromOptions()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
839 @*/
840 PetscErrorCode MatProductSetFill(Mat mat, PetscReal fill)
841 {
842   PetscFunctionBegin;
843   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
844   MatCheckProduct(mat, 1);
845   if (fill == PETSC_DEFAULT || fill == PETSC_DECIDE) mat->product->fill = 2.0;
846   else mat->product->fill = fill;
847   PetscFunctionReturn(PETSC_SUCCESS);
848 }
849 
850 /*@
851    MatProductSetAlgorithm - Requests a particular algorithm for a matrix-matrix product operation that will perform to compute the given matrix
852 
853    Collective
854 
855    Input Parameters:
856 +  mat - the matrix whose values are computed via a matrix-matrix product operation
857 -  alg - particular implementation algorithm of the matrix product, e.g., `MATPRODUCTALGORITHMDEFAULT`.
858 
859    Options Database Key:
860 .  -mat_product_algorithm <algorithm> - Sets the algorithm; use -help for a list
861     of available algorithms (for instance, scalable, outerproduct, etc.)
862 
863    Level: intermediate
864 
865 .seealso: `Mat`, `MatProductClear()`, `MatProductSetType()`, `MatProductSetFill()`, `MatProductCreate()`, `MatProductAlgorithm`, `MatProductType`
866 @*/
867 PetscErrorCode MatProductSetAlgorithm(Mat mat, MatProductAlgorithm alg)
868 {
869   PetscFunctionBegin;
870   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
871   MatCheckProduct(mat, 1);
872   PetscCall(PetscFree(mat->product->alg));
873   PetscCall(PetscStrallocpy(alg, &mat->product->alg));
874   PetscFunctionReturn(PETSC_SUCCESS);
875 }
876 
877 /*@
878    MatProductSetType - Sets a particular matrix-matrix product operation to be used to compute the values of the given matrix
879 
880    Collective
881 
882    Input Parameters:
883 +  mat - the matrix whose values are computed via a matrix-matrix product operation
884 -  productype   - matrix product type, e.g., `MATPRODUCT_AB`,`MATPRODUCT_AtB`,`MATPRODUCT_ABt`,`MATPRODUCT_PtAP`,`MATPRODUCT_RARt`,`MATPRODUCT_ABC`.
885 
886    Level: intermediate
887 
888    Note:
889    The small t represents the transpose operation.
890 
891 .seealso: `Mat`, `MatProductCreate()`, `MatProductType`, `MatProductType`,
892           `MATPRODUCT_AB`, `MATPRODUCT_AtB`, `MATPRODUCT_ABt`, `MATPRODUCT_PtAP`, `MATPRODUCT_RARt`, `MATPRODUCT_ABC`
893 @*/
894 PetscErrorCode MatProductSetType(Mat mat, MatProductType productype)
895 {
896   PetscFunctionBegin;
897   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
898   MatCheckProduct(mat, 1);
899   PetscValidLogicalCollectiveEnum(mat, productype, 2);
900   if (productype != mat->product->type) {
901     if (mat->product->destroy) PetscCall((*mat->product->destroy)(mat->product->data));
902     mat->product->destroy     = NULL;
903     mat->product->data        = NULL;
904     mat->ops->productsymbolic = NULL;
905     mat->ops->productnumeric  = NULL;
906   }
907   mat->product->type = productype;
908   PetscFunctionReturn(PETSC_SUCCESS);
909 }
910 
911 /*@
912    MatProductClear - Clears matrix product internal datastructures.
913 
914    Collective
915 
916    Input Parameters:
917 .  mat - the matrix whose values are computed via a matrix-matrix product operation
918 
919    Options Database Key:
920 .    -mat_product_clear - Clear intermediate data structures after `MatProductNumeric()` has been called
921 
922    Level: intermediate
923 
924    Notes:
925    This function should be called to remove any intermediate data used to compute the matrix to free up memory.
926 
927    After having called this function, matrix-matrix product operations can no longer be used on `mat`
928 
929 .seealso: `Mat`, `MatProductCreate()`
930 @*/
931 PetscErrorCode MatProductClear(Mat mat)
932 {
933   Mat_Product *product = mat->product;
934 
935   PetscFunctionBegin;
936   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
937   if (product) {
938     PetscCall(MatDestroy(&product->A));
939     PetscCall(MatDestroy(&product->B));
940     PetscCall(MatDestroy(&product->C));
941     PetscCall(PetscFree(product->alg));
942     PetscCall(MatDestroy(&product->Dwork));
943     if (product->destroy) PetscCall((*product->destroy)(product->data));
944   }
945   PetscCall(PetscFree(mat->product));
946   mat->ops->productsymbolic = NULL;
947   mat->ops->productnumeric  = NULL;
948   PetscFunctionReturn(PETSC_SUCCESS);
949 }
950 
951 /* Create a supporting struct and attach it to the matrix product */
952 PetscErrorCode MatProductCreate_Private(Mat A, Mat B, Mat C, Mat D)
953 {
954   Mat_Product *product = NULL;
955 
956   PetscFunctionBegin;
957   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
958   PetscCheck(!D->product, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product already present");
959   PetscCall(PetscNew(&product));
960   product->A        = A;
961   product->B        = B;
962   product->C        = C;
963   product->type     = MATPRODUCT_UNSPECIFIED;
964   product->Dwork    = NULL;
965   product->api_user = PETSC_FALSE;
966   product->clear    = PETSC_FALSE;
967   D->product        = product;
968 
969   PetscCall(MatProductSetAlgorithm(D, MATPRODUCTALGORITHMDEFAULT));
970   PetscCall(MatProductSetFill(D, PETSC_DEFAULT));
971 
972   PetscCall(PetscObjectReference((PetscObject)A));
973   PetscCall(PetscObjectReference((PetscObject)B));
974   PetscCall(PetscObjectReference((PetscObject)C));
975   PetscFunctionReturn(PETSC_SUCCESS);
976 }
977 
978 /*@
979    MatProductCreateWithMat - Setup a given matrix as a matrix product of other matrices
980 
981    Collective
982 
983    Input Parameters:
984 +  A - the first matrix
985 .  B - the second matrix
986 .  C - the third matrix (optional, use `NULL` if not needed)
987 -  D - the matrix whose values are computed via a matrix-matrix product operation
988 
989    Level: intermediate
990 
991    Notes:
992    Use `MatProductCreate()` if the matrix you wish computed (the `D` matrix) does not already exist
993 
994    See `MatProductCreate()` for details on the usage of the matrix-matrix product operations
995 
996    Any product data currently attached to `D` will be cleared
997 
998 .seealso: `Mat`, `MatProductCreate()`, `MatProductClear()`
999 @*/
1000 PetscErrorCode MatProductCreateWithMat(Mat A, Mat B, Mat C, Mat D)
1001 {
1002   PetscFunctionBegin;
1003   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1004   PetscValidType(A, 1);
1005   MatCheckPreallocated(A, 1);
1006   PetscCheck(A->assembled, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1007   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1008 
1009   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
1010   PetscValidType(B, 2);
1011   MatCheckPreallocated(B, 2);
1012   PetscCheck(B->assembled, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1013   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1014 
1015   if (C) {
1016     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
1017     PetscValidType(C, 3);
1018     MatCheckPreallocated(C, 3);
1019     PetscCheck(C->assembled, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1020     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1021   }
1022 
1023   PetscValidHeaderSpecific(D, MAT_CLASSID, 4);
1024   PetscValidType(D, 4);
1025   MatCheckPreallocated(D, 4);
1026   PetscCheck(D->assembled, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for unassembled matrix");
1027   PetscCheck(!D->factortype, PetscObjectComm((PetscObject)D), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
1028 
1029   /* Create a supporting struct and attach it to D */
1030   PetscCall(MatProductClear(D));
1031   PetscCall(MatProductCreate_Private(A, B, C, D));
1032   PetscFunctionReturn(PETSC_SUCCESS);
1033 }
1034 
1035 /*@
1036    MatProductCreate - create a matrix to hold the result of a matrix-matrix product operation
1037 
1038    Collective
1039 
1040    Input Parameters:
1041 +  A - the first matrix
1042 .  B - the second matrix
1043 -  C - the third matrix (optional)
1044 
1045    Output Parameters:
1046 .  D - the matrix whose values are computed via a matrix-matrix product operation
1047 
1048    Level: intermediate
1049 
1050    Example:
1051 .vb
1052     MatProductCreate(A,B,C,&D); or MatProductCreateWithMat(A,B,C,D)
1053     MatProductSetType(D, MATPRODUCT_AB or MATPRODUCT_AtB or MATPRODUCT_ABt or MATPRODUCT_PtAP or MATPRODUCT_RARt or MATPRODUCT_ABC)
1054     MatProductSetAlgorithm(D, alg)
1055     MatProductSetFill(D,fill)
1056     MatProductSetFromOptions(D)
1057     MatProductSymbolic(D)
1058     MatProductNumeric(D)
1059     Change numerical values in some of the matrices
1060     MatProductNumeric(D)
1061 .ve
1062 
1063    Notes:
1064    Use `MatProductCreateWithMat()` if the matrix you wish computed, the `D` matrix, already exists.
1065 
1066    The information computed during the symbolic stage can be reused for new numerical computations with the same non-zero structure
1067 
1068    Developer Note:
1069    It is undocumented what happens if the nonzero structure of the input matrices changes. Is the symbolic stage automatically redone? Does it crash?
1070    Is there error checking for it?
1071 
1072 .seealso: `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductClear()`
1073 @*/
1074 PetscErrorCode MatProductCreate(Mat A, Mat B, Mat C, Mat *D)
1075 {
1076   PetscFunctionBegin;
1077   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1078   PetscValidType(A, 1);
1079   PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
1080   PetscValidType(B, 2);
1081   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix A");
1082   PetscCheck(!B->factortype, PetscObjectComm((PetscObject)B), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix B");
1083 
1084   if (C) {
1085     PetscValidHeaderSpecific(C, MAT_CLASSID, 3);
1086     PetscValidType(C, 3);
1087     PetscCheck(!C->factortype, PetscObjectComm((PetscObject)C), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix C");
1088   }
1089 
1090   PetscValidPointer(D, 4);
1091   PetscCall(MatCreate(PetscObjectComm((PetscObject)A), D));
1092   /* Delay setting type of D to the MatProduct symbolic phase, as we allow sparse A and dense B */
1093   PetscCall(MatProductCreate_Private(A, B, C, *D));
1094   PetscFunctionReturn(PETSC_SUCCESS);
1095 }
1096 
1097 /*
1098    These are safe basic implementations of ABC, RARt and PtAP
1099    that do not rely on mat->ops->matmatop function pointers.
1100    They only use the MatProduct API and are currently used by
1101    cuSPARSE and KOKKOS-KERNELS backends
1102 */
1103 typedef struct {
1104   Mat BC;
1105   Mat ABC;
1106 } MatMatMatPrivate;
1107 
1108 static PetscErrorCode MatDestroy_MatMatMatPrivate(void *data)
1109 {
1110   MatMatMatPrivate *mmdata = (MatMatMatPrivate *)data;
1111 
1112   PetscFunctionBegin;
1113   PetscCall(MatDestroy(&mmdata->BC));
1114   PetscCall(MatDestroy(&mmdata->ABC));
1115   PetscCall(PetscFree(data));
1116   PetscFunctionReturn(PETSC_SUCCESS);
1117 }
1118 
1119 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1120 {
1121   Mat_Product      *product = mat->product;
1122   MatMatMatPrivate *mmabc;
1123 
1124   PetscFunctionBegin;
1125   MatCheckProduct(mat, 1);
1126   PetscCheck(mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data empty");
1127   mmabc = (MatMatMatPrivate *)mat->product->data;
1128   PetscCheck(mmabc->BC->ops->productnumeric, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Missing numeric stage");
1129   /* use function pointer directly to prevent logging */
1130   PetscCall((*mmabc->BC->ops->productnumeric)(mmabc->BC));
1131   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1132   mat->product             = mmabc->ABC->product;
1133   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1134   /* use function pointer directly to prevent logging */
1135   PetscUseTypeMethod(mat, productnumeric);
1136   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1137   mat->product             = product;
1138   PetscFunctionReturn(PETSC_SUCCESS);
1139 }
1140 
1141 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1142 {
1143   Mat_Product      *product = mat->product;
1144   Mat               A, B, C;
1145   MatProductType    p1, p2;
1146   MatMatMatPrivate *mmabc;
1147   const char       *prefix;
1148 
1149   PetscFunctionBegin;
1150   MatCheckProduct(mat, 1);
1151   PetscCheck(!mat->product->data, PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Product data not empty");
1152   PetscCall(MatGetOptionsPrefix(mat, &prefix));
1153   PetscCall(PetscNew(&mmabc));
1154   product->data    = mmabc;
1155   product->destroy = MatDestroy_MatMatMatPrivate;
1156   switch (product->type) {
1157   case MATPRODUCT_PtAP:
1158     p1 = MATPRODUCT_AB;
1159     p2 = MATPRODUCT_AtB;
1160     A  = product->B;
1161     B  = product->A;
1162     C  = product->B;
1163     break;
1164   case MATPRODUCT_RARt:
1165     p1 = MATPRODUCT_ABt;
1166     p2 = MATPRODUCT_AB;
1167     A  = product->B;
1168     B  = product->A;
1169     C  = product->B;
1170     break;
1171   case MATPRODUCT_ABC:
1172     p1 = MATPRODUCT_AB;
1173     p2 = MATPRODUCT_AB;
1174     A  = product->A;
1175     B  = product->B;
1176     C  = product->C;
1177     break;
1178   default:
1179     SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_PLIB, "Not for ProductType %s", MatProductTypes[product->type]);
1180   }
1181   PetscCall(MatProductCreate(B, C, NULL, &mmabc->BC));
1182   PetscCall(MatSetOptionsPrefix(mmabc->BC, prefix));
1183   PetscCall(MatAppendOptionsPrefix(mmabc->BC, "P1_"));
1184   PetscCall(MatProductSetType(mmabc->BC, p1));
1185   PetscCall(MatProductSetAlgorithm(mmabc->BC, MATPRODUCTALGORITHMDEFAULT));
1186   PetscCall(MatProductSetFill(mmabc->BC, product->fill));
1187   mmabc->BC->product->api_user = product->api_user;
1188   PetscCall(MatProductSetFromOptions(mmabc->BC));
1189   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);
1190   /* use function pointer directly to prevent logging */
1191   PetscCall((*mmabc->BC->ops->productsymbolic)(mmabc->BC));
1192 
1193   PetscCall(MatProductCreate(A, mmabc->BC, NULL, &mmabc->ABC));
1194   PetscCall(MatSetOptionsPrefix(mmabc->ABC, prefix));
1195   PetscCall(MatAppendOptionsPrefix(mmabc->ABC, "P2_"));
1196   PetscCall(MatProductSetType(mmabc->ABC, p2));
1197   PetscCall(MatProductSetAlgorithm(mmabc->ABC, MATPRODUCTALGORITHMDEFAULT));
1198   PetscCall(MatProductSetFill(mmabc->ABC, product->fill));
1199   mmabc->ABC->product->api_user = product->api_user;
1200   PetscCall(MatProductSetFromOptions(mmabc->ABC));
1201   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);
1202   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1203   mat->product              = mmabc->ABC->product;
1204   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1205   /* use function pointer directly to prevent logging */
1206   PetscUseTypeMethod(mat, productsymbolic);
1207   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1208   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1209   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1210   mat->product                    = product;
1211   PetscFunctionReturn(PETSC_SUCCESS);
1212 }
1213 
1214 /*@
1215    MatProductGetType - Returns the type of matrix-matrix product associated with the given matrix.
1216 
1217    Not Collective
1218 
1219    Input Parameter:
1220 .  mat - the matrix whose values are computed via a matrix-matrix product operation
1221 
1222    Output Parameter:
1223 .  mtype - the `MatProductType`
1224 
1225    Level: intermediate
1226 
1227 .seealso: `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductCreate()`, `MatProductType`, `MatProductAlgorithm`
1228 @*/
1229 PetscErrorCode MatProductGetType(Mat mat, MatProductType *mtype)
1230 {
1231   PetscFunctionBegin;
1232   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1233   PetscValidPointer(mtype, 2);
1234   *mtype = MATPRODUCT_UNSPECIFIED;
1235   if (mat->product) *mtype = mat->product->type;
1236   PetscFunctionReturn(PETSC_SUCCESS);
1237 }
1238 
1239 /*@
1240    MatProductGetMats - Returns the matrices associated with the matrix-matrix product operation this matrix can receive
1241 
1242    Not Collective
1243 
1244    Input Parameter:
1245 .  mat - the matrix whose values are computed via a matrix-matrix product operation
1246 
1247    Output Parameters:
1248 +  A - the first matrix
1249 .  B - the second matrix
1250 -  C - the third matrix (optional)
1251 
1252    Level: intermediate
1253 
1254 .seealso: `Mat`, `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
1255 @*/
1256 PetscErrorCode MatProductGetMats(Mat mat, Mat *A, Mat *B, Mat *C)
1257 {
1258   PetscFunctionBegin;
1259   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
1260   if (A) *A = mat->product ? mat->product->A : NULL;
1261   if (B) *B = mat->product ? mat->product->B : NULL;
1262   if (C) *C = mat->product ? mat->product->C : NULL;
1263   PetscFunctionReturn(PETSC_SUCCESS);
1264 }
1265