xref: /petsc/src/mat/interface/matproduct.c (revision 45601b4b7d8ff587f0d3d0ebcf8818ecb79c2acc)
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   PetscCheck(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   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);
417   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);
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   PetscFunctionBegin;
520   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
521   MatCheckProduct(mat,1);
522   PetscCheck(!mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_ORDER,"Cannot call MatProductSetFromOptions with already present data");
523   PetscObjectOptionsBegin((PetscObject)mat);
524   PetscCall(PetscOptionsBool("-mat_product_clear","Clear intermediate data structures after MatProductNumeric() has been called","MatProductClear",mat->product->clear,&mat->product->clear,NULL));
525   PetscCall(PetscOptionsDeprecated("-mat_freeintermediatedatastructures","-mat_product_clear","3.13","Or call MatProductClear() after MatProductNumeric()"));
526   PetscOptionsEnd();
527   PetscCall(MatProductSetFromOptions_Private(mat));
528   PetscCheck(mat->product,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing product after setup phase");
529   PetscFunctionReturn(0);
530 }
531 
532 /*@C
533    MatProductView - View a MatProduct
534 
535    Logically Collective on Mat
536 
537    Input Parameter:
538 .  mat - the matrix obtained with MatProductCreate() or MatProductCreateWithMat()
539 
540    Level: intermediate
541 
542 .seealso: MatView(), MatProductCreate(), MatProductCreateWithMat()
543 @*/
544 PetscErrorCode MatProductView(Mat mat, PetscViewer viewer)
545 {
546   PetscFunctionBegin;
547   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
548   if (!mat->product) PetscFunctionReturn(0);
549   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)mat),&viewer));
550   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
551   PetscCheckSameComm(mat,1,viewer,2);
552   if (mat->product->view) {
553     PetscCall((*mat->product->view)(mat,viewer));
554   }
555   PetscFunctionReturn(0);
556 }
557 
558 /* ----------------------------------------------- */
559 /* these are basic implementations relying on the old function pointers
560  * they are dangerous and should be removed in the future */
561 PetscErrorCode MatProductNumeric_AB(Mat mat)
562 {
563   Mat_Product    *product = mat->product;
564   Mat            A=product->A,B=product->B;
565 
566   PetscFunctionBegin;
567   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);
568   PetscCall((*mat->ops->matmultnumeric)(A,B,mat));
569   PetscFunctionReturn(0);
570 }
571 
572 PetscErrorCode MatProductNumeric_AtB(Mat mat)
573 {
574   Mat_Product    *product = mat->product;
575   Mat            A=product->A,B=product->B;
576 
577   PetscFunctionBegin;
578   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);
579   PetscCall((*mat->ops->transposematmultnumeric)(A,B,mat));
580   PetscFunctionReturn(0);
581 }
582 
583 PetscErrorCode MatProductNumeric_ABt(Mat mat)
584 {
585   Mat_Product    *product = mat->product;
586   Mat            A=product->A,B=product->B;
587 
588   PetscFunctionBegin;
589   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);
590   PetscCall((*mat->ops->mattransposemultnumeric)(A,B,mat));
591   PetscFunctionReturn(0);
592 }
593 
594 PetscErrorCode MatProductNumeric_PtAP(Mat mat)
595 {
596   Mat_Product    *product = mat->product;
597   Mat            A=product->A,B=product->B;
598 
599   PetscFunctionBegin;
600   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);
601   PetscCall((*mat->ops->ptapnumeric)(A,B,mat));
602   PetscFunctionReturn(0);
603 }
604 
605 PetscErrorCode MatProductNumeric_RARt(Mat mat)
606 {
607   Mat_Product    *product = mat->product;
608   Mat            A=product->A,B=product->B;
609 
610   PetscFunctionBegin;
611   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);
612   PetscCall((*mat->ops->rartnumeric)(A,B,mat));
613   PetscFunctionReturn(0);
614 }
615 
616 PetscErrorCode MatProductNumeric_ABC(Mat mat)
617 {
618   Mat_Product    *product = mat->product;
619   Mat            A=product->A,B=product->B,C=product->C;
620 
621   PetscFunctionBegin;
622   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);
623   PetscCall((*mat->ops->matmatmultnumeric)(A,B,C,mat));
624   PetscFunctionReturn(0);
625 }
626 
627 /* ----------------------------------------------- */
628 
629 /*@
630    MatProductNumeric - Implement a matrix product with numerical values.
631 
632    Collective on Mat
633 
634    Input/Output Parameter:
635 .  mat - the matrix holding the product
636 
637    Level: intermediate
638 
639    Notes: MatProductSymbolic() must have been called on mat before calling this function
640 
641 .seealso: MatProductCreate(), MatSetType(), MatProductSymbolic()
642 @*/
643 PetscErrorCode MatProductNumeric(Mat mat)
644 {
645   PetscLogEvent  eventtype = -1;
646   PetscBool      missing = PETSC_FALSE;
647 
648   PetscFunctionBegin;
649   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
650   MatCheckProduct(mat,1);
651   switch (mat->product->type) {
652   case MATPRODUCT_AB:
653     eventtype = MAT_MatMultNumeric;
654     break;
655   case MATPRODUCT_AtB:
656     eventtype = MAT_TransposeMatMultNumeric;
657     break;
658   case MATPRODUCT_ABt:
659     eventtype = MAT_MatTransposeMultNumeric;
660     break;
661   case MATPRODUCT_PtAP:
662     eventtype = MAT_PtAPNumeric;
663     break;
664   case MATPRODUCT_RARt:
665     eventtype = MAT_RARtNumeric;
666     break;
667   case MATPRODUCT_ABC:
668     eventtype = MAT_MatMatMultNumeric;
669     break;
670   default: SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"ProductType %s is not supported",MatProductTypes[mat->product->type]);
671   }
672 
673   if (mat->ops->productnumeric) {
674     PetscCall(PetscLogEventBegin(eventtype,mat,0,0,0));
675     PetscCall((*mat->ops->productnumeric)(mat));
676     PetscCall(PetscLogEventEnd(eventtype,mat,0,0,0));
677   } else missing = PETSC_TRUE;
678 
679   if (missing || !mat->product) {
680     char errstr[256];
681 
682     if (mat->product->type == MATPRODUCT_ABC) {
683       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));
684     } else {
685       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));
686     }
687     PetscCheck(!missing,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified numeric phase for product %s",errstr);
688     PetscCheck(mat->product,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing struct after symbolic phase for product %s",errstr);
689   }
690 
691   if (mat->product->clear) {
692     PetscCall(MatProductClear(mat));
693   }
694   PetscCall(PetscObjectStateIncrease((PetscObject)mat));
695   PetscFunctionReturn(0);
696 }
697 
698 /* ----------------------------------------------- */
699 /* these are basic implementations relying on the old function pointers
700  * they are dangerous and should be removed in the future */
701 PetscErrorCode MatProductSymbolic_AB(Mat mat)
702 {
703   Mat_Product    *product = mat->product;
704   Mat            A=product->A,B=product->B;
705 
706   PetscFunctionBegin;
707   PetscCheck(mat->ops->matmultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
708   PetscCall((*mat->ops->matmultsymbolic)(A,B,product->fill,mat));
709   mat->ops->productnumeric = MatProductNumeric_AB;
710   PetscFunctionReturn(0);
711 }
712 
713 PetscErrorCode MatProductSymbolic_AtB(Mat mat)
714 {
715   Mat_Product    *product = mat->product;
716   Mat            A=product->A,B=product->B;
717 
718   PetscFunctionBegin;
719   PetscCheck(mat->ops->transposematmultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
720   PetscCall((*mat->ops->transposematmultsymbolic)(A,B,product->fill,mat));
721   mat->ops->productnumeric = MatProductNumeric_AtB;
722   PetscFunctionReturn(0);
723 }
724 
725 PetscErrorCode MatProductSymbolic_ABt(Mat mat)
726 {
727   Mat_Product    *product = mat->product;
728   Mat            A=product->A,B=product->B;
729 
730   PetscFunctionBegin;
731   PetscCheck(mat->ops->mattransposemultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
732   PetscCall((*mat->ops->mattransposemultsymbolic)(A,B,product->fill,mat));
733   mat->ops->productnumeric = MatProductNumeric_ABt;
734   PetscFunctionReturn(0);
735 }
736 
737 PetscErrorCode MatProductSymbolic_ABC(Mat mat)
738 {
739   Mat_Product    *product = mat->product;
740   Mat            A=product->A,B=product->B,C=product->C;
741 
742   PetscFunctionBegin;
743   PetscCheck(mat->ops->matmatmultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
744   PetscCall((*mat->ops->matmatmultsymbolic)(A,B,C,product->fill,mat));
745   mat->ops->productnumeric = MatProductNumeric_ABC;
746   PetscFunctionReturn(0);
747 }
748 
749 /* ----------------------------------------------- */
750 
751 /*@
752    MatProductSymbolic - Perform the symbolic portion of a matrix product, this creates a data structure for use with the numerical produce.
753 
754    Collective on Mat
755 
756    Input/Output Parameter:
757 .  mat - the matrix to hold a product
758 
759    Level: intermediate
760 
761    Notes: MatProductSetFromOptions() must have been called on mat before calling this function
762 
763 .seealso: MatProductCreate(), MatProductCreateWithMat(), MatProductSetFromOptions(), MatProductNumeric(), MatProductSetType(), MatProductSetAlgorithm()
764 @*/
765 PetscErrorCode MatProductSymbolic(Mat mat)
766 {
767   PetscLogEvent  eventtype = -1;
768   PetscBool      missing = PETSC_FALSE;
769 
770   PetscFunctionBegin;
771   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
772   MatCheckProduct(mat,1);
773   PetscCheck(!mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_ORDER,"Cannot run symbolic phase. Product data not empty");
774   switch (mat->product->type) {
775   case MATPRODUCT_AB:
776     eventtype = MAT_MatMultSymbolic;
777     break;
778   case MATPRODUCT_AtB:
779     eventtype = MAT_TransposeMatMultSymbolic;
780     break;
781   case MATPRODUCT_ABt:
782     eventtype = MAT_MatTransposeMultSymbolic;
783     break;
784   case MATPRODUCT_PtAP:
785     eventtype = MAT_PtAPSymbolic;
786     break;
787   case MATPRODUCT_RARt:
788     eventtype = MAT_RARtSymbolic;
789     break;
790   case MATPRODUCT_ABC:
791     eventtype = MAT_MatMatMultSymbolic;
792     break;
793   default: SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"ProductType %s is not supported",MatProductTypes[mat->product->type]);
794   }
795   mat->ops->productnumeric = NULL;
796   if (mat->ops->productsymbolic) {
797     PetscCall(PetscLogEventBegin(eventtype,mat,0,0,0));
798     PetscCall((*mat->ops->productsymbolic)(mat));
799     PetscCall(PetscLogEventEnd(eventtype,mat,0,0,0));
800   } else missing = PETSC_TRUE;
801 
802   if (missing || !mat->product || !mat->ops->productnumeric) {
803     char errstr[256];
804 
805     if (mat->product->type == MATPRODUCT_ABC) {
806       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));
807     } else {
808       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));
809     }
810     PetscCheck(!missing,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified symbolic phase for product %s. Call MatProductSetFromOptions() first",errstr);
811     PetscCheck(mat->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified numeric phase for product %s",errstr);
812     PetscCheck(mat->product,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing struct after symbolic phase for product %s",errstr);
813   }
814   PetscFunctionReturn(0);
815 }
816 
817 /*@
818    MatProductSetFill - Set an expected fill of the matrix product.
819 
820    Collective on Mat
821 
822    Input Parameters:
823 +  mat - the matrix product
824 -  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.
825 
826    Level: intermediate
827 
828 .seealso: MatProductSetType(), MatProductSetAlgorithm(), MatProductCreate()
829 @*/
830 PetscErrorCode MatProductSetFill(Mat mat,PetscReal fill)
831 {
832   PetscFunctionBegin;
833   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
834   MatCheckProduct(mat,1);
835   if (fill == PETSC_DEFAULT || fill == PETSC_DECIDE) mat->product->fill = 2.0;
836   else mat->product->fill = fill;
837   PetscFunctionReturn(0);
838 }
839 
840 /*@
841    MatProductSetAlgorithm - Requests a particular algorithm for a matrix product implementation.
842 
843    Collective on Mat
844 
845    Input Parameters:
846 +  mat - the matrix product
847 -  alg - particular implementation algorithm of the matrix product, e.g., MATPRODUCTALGORITHMDEFAULT.
848 
849    Options Database Key:
850 .  -mat_product_algorithm <algorithm> - Sets the algorithm; use -help for a list
851     of available algorithms (for instance, scalable, outerproduct, etc.)
852 
853    Level: intermediate
854 
855 .seealso: MatProductSetType(), MatProductSetFill(), MatProductCreate(), MatProductAlgorithm
856 @*/
857 PetscErrorCode MatProductSetAlgorithm(Mat mat,MatProductAlgorithm alg)
858 {
859   PetscFunctionBegin;
860   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
861   MatCheckProduct(mat,1);
862   PetscCall(PetscFree(mat->product->alg));
863   PetscCall(PetscStrallocpy(alg,&mat->product->alg));
864   PetscFunctionReturn(0);
865 }
866 
867 /*@
868    MatProductSetType - Sets a particular matrix product type
869 
870    Collective on Mat
871 
872    Input Parameters:
873 +  mat - the matrix
874 -  productype   - matrix product type, e.g., MATPRODUCT_AB,MATPRODUCT_AtB,MATPRODUCT_ABt,MATPRODUCT_PtAP,MATPRODUCT_RARt,MATPRODUCT_ABC.
875 
876    Level: intermediate
877 
878 .seealso: MatProductCreate(), MatProductType
879 @*/
880 PetscErrorCode MatProductSetType(Mat mat,MatProductType productype)
881 {
882   PetscFunctionBegin;
883   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
884   MatCheckProduct(mat,1);
885   PetscValidLogicalCollectiveEnum(mat,productype,2);
886   if (productype != mat->product->type) {
887     if (mat->product->destroy) {
888       PetscCall((*mat->product->destroy)(mat->product->data));
889     }
890     mat->product->destroy = NULL;
891     mat->product->data = NULL;
892     mat->ops->productsymbolic = NULL;
893     mat->ops->productnumeric  = NULL;
894   }
895   mat->product->type = productype;
896   PetscFunctionReturn(0);
897 }
898 
899 /*@
900    MatProductClear - Clears matrix product internal structure.
901 
902    Collective on Mat
903 
904    Input Parameters:
905 .  mat - the product matrix
906 
907    Level: intermediate
908 
909    Notes: this function should be called to remove any intermediate data used by the product
910           After having called this function, MatProduct operations can no longer be used on mat
911 @*/
912 PetscErrorCode MatProductClear(Mat mat)
913 {
914   Mat_Product    *product = mat->product;
915 
916   PetscFunctionBegin;
917   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
918   if (product) {
919     PetscCall(MatDestroy(&product->A));
920     PetscCall(MatDestroy(&product->B));
921     PetscCall(MatDestroy(&product->C));
922     PetscCall(PetscFree(product->alg));
923     PetscCall(MatDestroy(&product->Dwork));
924     if (product->destroy) {
925       PetscCall((*product->destroy)(product->data));
926     }
927   }
928   PetscCall(PetscFree(mat->product));
929   mat->ops->productsymbolic = NULL;
930   mat->ops->productnumeric = NULL;
931   PetscFunctionReturn(0);
932 }
933 
934 /* Create a supporting struct and attach it to the matrix product */
935 PetscErrorCode MatProductCreate_Private(Mat A,Mat B,Mat C,Mat D)
936 {
937   Mat_Product    *product=NULL;
938 
939   PetscFunctionBegin;
940   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
941   PetscCheck(!D->product,PetscObjectComm((PetscObject)D),PETSC_ERR_PLIB,"Product already present");
942   PetscCall(PetscNewLog(D,&product));
943   product->A        = A;
944   product->B        = B;
945   product->C        = C;
946   product->type     = MATPRODUCT_UNSPECIFIED;
947   product->Dwork    = NULL;
948   product->api_user = PETSC_FALSE;
949   product->clear    = PETSC_FALSE;
950   D->product        = product;
951 
952   PetscCall(MatProductSetAlgorithm(D,MATPRODUCTALGORITHMDEFAULT));
953   PetscCall(MatProductSetFill(D,PETSC_DEFAULT));
954 
955   PetscCall(PetscObjectReference((PetscObject)A));
956   PetscCall(PetscObjectReference((PetscObject)B));
957   PetscCall(PetscObjectReference((PetscObject)C));
958   PetscFunctionReturn(0);
959 }
960 
961 /*@
962    MatProductCreateWithMat - Setup a given matrix as a matrix product.
963 
964    Collective on Mat
965 
966    Input Parameters:
967 +  A - the first matrix
968 .  B - the second matrix
969 .  C - the third matrix (optional)
970 -  D - the matrix which will be used as a product
971 
972    Output Parameters:
973 .  D - the product matrix
974 
975    Notes:
976      Any product data attached to D will be cleared
977 
978    Level: intermediate
979 
980 .seealso: MatProductCreate(), MatProductClear()
981 @*/
982 PetscErrorCode MatProductCreateWithMat(Mat A,Mat B,Mat C,Mat D)
983 {
984   PetscFunctionBegin;
985   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
986   PetscValidType(A,1);
987   MatCheckPreallocated(A,1);
988   PetscCheck(A->assembled,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
989   PetscCheck(!A->factortype,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
990 
991   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
992   PetscValidType(B,2);
993   MatCheckPreallocated(B,2);
994   PetscCheck(B->assembled,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
995   PetscCheck(!B->factortype,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
996 
997   if (C) {
998     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
999     PetscValidType(C,3);
1000     MatCheckPreallocated(C,3);
1001     PetscCheck(C->assembled,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1002     PetscCheck(!C->factortype,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1003   }
1004 
1005   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
1006   PetscValidType(D,4);
1007   MatCheckPreallocated(D,4);
1008   PetscCheck(D->assembled,PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1009   PetscCheck(!D->factortype,PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1010 
1011   /* Create a supporting struct and attach it to D */
1012   PetscCall(MatProductClear(D));
1013   PetscCall(MatProductCreate_Private(A,B,C,D));
1014   PetscFunctionReturn(0);
1015 }
1016 
1017 /*@
1018    MatProductCreate - create a matrix product object that can be used to compute various matrix times matrix operations.
1019 
1020    Collective on Mat
1021 
1022    Input Parameters:
1023 +  A - the first matrix
1024 .  B - the second matrix
1025 -  C - the third matrix (optional)
1026 
1027    Output Parameters:
1028 .  D - the product matrix
1029 
1030    Level: intermediate
1031 
1032 .seealso: MatProductCreateWithMat(), MatProductSetType(), MatProductSetAlgorithm()
1033 @*/
1034 PetscErrorCode MatProductCreate(Mat A,Mat B,Mat C,Mat *D)
1035 {
1036   PetscFunctionBegin;
1037   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
1038   PetscValidType(A,1);
1039   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
1040   PetscValidType(B,2);
1041   PetscCheck(!A->factortype,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix A");
1042   PetscCheck(!B->factortype,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix B");
1043 
1044   if (C) {
1045     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
1046     PetscValidType(C,3);
1047     PetscCheck(!C->factortype,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix C");
1048   }
1049 
1050   PetscValidPointer(D,4);
1051   PetscCall(MatCreate(PetscObjectComm((PetscObject)A),D));
1052   PetscCall(MatProductCreate_Private(A,B,C,*D));
1053   PetscFunctionReturn(0);
1054 }
1055 
1056 /*
1057    These are safe basic implementations of ABC, RARt and PtAP
1058    that do not rely on mat->ops->matmatop function pointers.
1059    They only use the MatProduct API and are currently used by
1060    cuSPARSE and KOKKOS-KERNELS backends
1061 */
1062 typedef struct {
1063   Mat BC;
1064   Mat ABC;
1065 } MatMatMatPrivate;
1066 
1067 static PetscErrorCode MatDestroy_MatMatMatPrivate(void *data)
1068 {
1069   MatMatMatPrivate *mmdata = (MatMatMatPrivate *)data;
1070 
1071   PetscFunctionBegin;
1072   PetscCall(MatDestroy(&mmdata->BC));
1073   PetscCall(MatDestroy(&mmdata->ABC));
1074   PetscCall(PetscFree(data));
1075   PetscFunctionReturn(0);
1076 }
1077 
1078 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1079 {
1080   Mat_Product      *product = mat->product;
1081   MatMatMatPrivate *mmabc;
1082 
1083   PetscFunctionBegin;
1084   MatCheckProduct(mat,1);
1085   PetscCheck(mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data empty");
1086   mmabc = (MatMatMatPrivate *)mat->product->data;
1087   PetscCheck(mmabc->BC->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1088   /* use function pointer directly to prevent logging */
1089   PetscCall((*mmabc->BC->ops->productnumeric)(mmabc->BC));
1090   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1091   mat->product = mmabc->ABC->product;
1092   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1093   PetscCheck(mat->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1094   /* use function pointer directly to prevent logging */
1095   PetscCall((*mat->ops->productnumeric)(mat));
1096   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1097   mat->product = product;
1098   PetscFunctionReturn(0);
1099 }
1100 
1101 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1102 {
1103   Mat_Product      *product = mat->product;
1104   Mat              A, B ,C;
1105   MatProductType   p1,p2;
1106   MatMatMatPrivate *mmabc;
1107   const char       *prefix;
1108 
1109   PetscFunctionBegin;
1110   MatCheckProduct(mat,1);
1111   PetscCheck(!mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data not empty");
1112   PetscCall(MatGetOptionsPrefix(mat,&prefix));
1113   PetscCall(PetscNew(&mmabc));
1114   product->data    = mmabc;
1115   product->destroy = MatDestroy_MatMatMatPrivate;
1116   switch (product->type) {
1117   case MATPRODUCT_PtAP:
1118     p1 = MATPRODUCT_AB;
1119     p2 = MATPRODUCT_AtB;
1120     A = product->B;
1121     B = product->A;
1122     C = product->B;
1123     break;
1124   case MATPRODUCT_RARt:
1125     p1 = MATPRODUCT_ABt;
1126     p2 = MATPRODUCT_AB;
1127     A = product->B;
1128     B = product->A;
1129     C = product->B;
1130     break;
1131   case MATPRODUCT_ABC:
1132     p1 = MATPRODUCT_AB;
1133     p2 = MATPRODUCT_AB;
1134     A = product->A;
1135     B = product->B;
1136     C = product->C;
1137     break;
1138   default:
1139     SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Not for ProductType %s",MatProductTypes[product->type]);
1140   }
1141   PetscCall(MatProductCreate(B,C,NULL,&mmabc->BC));
1142   PetscCall(MatSetOptionsPrefix(mmabc->BC,prefix));
1143   PetscCall(MatAppendOptionsPrefix(mmabc->BC,"P1_"));
1144   PetscCall(MatProductSetType(mmabc->BC,p1));
1145   PetscCall(MatProductSetAlgorithm(mmabc->BC,MATPRODUCTALGORITHMDEFAULT));
1146   PetscCall(MatProductSetFill(mmabc->BC,product->fill));
1147   mmabc->BC->product->api_user = product->api_user;
1148   PetscCall(MatProductSetFromOptions(mmabc->BC));
1149   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);
1150   /* use function pointer directly to prevent logging */
1151   PetscCall((*mmabc->BC->ops->productsymbolic)(mmabc->BC));
1152 
1153   PetscCall(MatProductCreate(A,mmabc->BC,NULL,&mmabc->ABC));
1154   PetscCall(MatSetOptionsPrefix(mmabc->ABC,prefix));
1155   PetscCall(MatAppendOptionsPrefix(mmabc->ABC,"P2_"));
1156   PetscCall(MatProductSetType(mmabc->ABC,p2));
1157   PetscCall(MatProductSetAlgorithm(mmabc->ABC,MATPRODUCTALGORITHMDEFAULT));
1158   PetscCall(MatProductSetFill(mmabc->ABC,product->fill));
1159   mmabc->ABC->product->api_user = product->api_user;
1160   PetscCall(MatProductSetFromOptions(mmabc->ABC));
1161   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);
1162   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1163   mat->product = mmabc->ABC->product;
1164   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1165   /* use function pointer directly to prevent logging */
1166   PetscCall((*mat->ops->productsymbolic)(mat));
1167   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1168   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1169   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1170   mat->product                    = product;
1171   PetscFunctionReturn(0);
1172 }
1173 
1174 /*@
1175    MatProductGetType - Returns the type of the MatProduct.
1176 
1177    Not collective
1178 
1179    Input Parameter:
1180 .  mat - the matrix
1181 
1182    Output Parameter:
1183 .  mtype - the MatProduct type
1184 
1185    Level: intermediate
1186 
1187 .seealso: MatProductCreateWithMat(), MatProductSetType(), MatProductCreate()
1188 @*/
1189 PetscErrorCode MatProductGetType(Mat mat, MatProductType *mtype)
1190 {
1191   PetscFunctionBegin;
1192   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
1193   PetscValidPointer(mtype,2);
1194   *mtype = MATPRODUCT_UNSPECIFIED;
1195   if (mat->product) *mtype = mat->product->type;
1196   PetscFunctionReturn(0);
1197 }
1198 
1199 /*@
1200    MatProductGetMats - Returns the matrices associated with the MatProduct.
1201 
1202    Not collective
1203 
1204    Input Parameter:
1205 .  mat - the product matrix
1206 
1207    Output Parameters:
1208 +  A - the first matrix
1209 .  B - the second matrix
1210 -  C - the third matrix (optional)
1211 
1212    Level: intermediate
1213 
1214 .seealso: MatProductCreateWithMat(), MatProductSetType(), MatProductSetAlgorithm()
1215 @*/
1216 PetscErrorCode MatProductGetMats(Mat mat, Mat *A, Mat *B, Mat *C)
1217 {
1218   PetscFunctionBegin;
1219   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
1220   if (A) *A = mat->product ? mat->product->A : NULL;
1221   if (B) *B = mat->product ? mat->product->B : NULL;
1222   if (C) *C = mat->product ? mat->product->C : NULL;
1223   PetscFunctionReturn(0);
1224 }
1225