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