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