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