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