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