xref: /petsc/src/mat/interface/matproduct.c (revision ebead697dbf761eb322f829370bbe90b3bd93fa3)
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()` and `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 - Sets the options for the computation of 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()`, `MatProductNumeric()`, `MatProductSetType()`, `MatProductSetAlgorithm()`
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 the MatProduct algorithm object within a matrix
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 product done with
750   `MatProductNumeric()`
751 
752    Collective on Mat
753 
754    Input/Output Parameter:
755 .  mat - the matrix to hold a product
756 
757    Level: intermediate
758 
759    Notes: `MatProductSetFromOptions()` must have been called on mat before calling this function
760 
761 .seealso: `MatProductCreate()`, `MatProductCreateWithMat()`, `MatProductSetFromOptions()`, `MatProductNumeric()`, `MatProductSetType()`, `MatProductSetAlgorithm()`
762 @*/
763 PetscErrorCode MatProductSymbolic(Mat mat)
764 {
765   PetscLogEvent  eventtype = -1;
766   PetscBool      missing = PETSC_FALSE;
767 
768   PetscFunctionBegin;
769   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
770   MatCheckProduct(mat,1);
771   PetscCheck(!mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_ORDER,"Cannot run symbolic phase. Product data not empty");
772   switch (mat->product->type) {
773   case MATPRODUCT_AB:
774     eventtype = MAT_MatMultSymbolic;
775     break;
776   case MATPRODUCT_AtB:
777     eventtype = MAT_TransposeMatMultSymbolic;
778     break;
779   case MATPRODUCT_ABt:
780     eventtype = MAT_MatTransposeMultSymbolic;
781     break;
782   case MATPRODUCT_PtAP:
783     eventtype = MAT_PtAPSymbolic;
784     break;
785   case MATPRODUCT_RARt:
786     eventtype = MAT_RARtSymbolic;
787     break;
788   case MATPRODUCT_ABC:
789     eventtype = MAT_MatMatMultSymbolic;
790     break;
791   default: SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"ProductType %s is not supported",MatProductTypes[mat->product->type]);
792   }
793   mat->ops->productnumeric = NULL;
794   if (mat->ops->productsymbolic) {
795     PetscCall(PetscLogEventBegin(eventtype,mat,0,0,0));
796     PetscCall((*mat->ops->productsymbolic)(mat));
797     PetscCall(PetscLogEventEnd(eventtype,mat,0,0,0));
798   } else missing = PETSC_TRUE;
799 
800   if (missing || !mat->product || !mat->ops->productnumeric) {
801     char errstr[256];
802 
803     if (mat->product->type == MATPRODUCT_ABC) {
804       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));
805     } else {
806       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));
807     }
808     PetscCheck(!missing,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified symbolic phase for product %s. Call MatProductSetFromOptions() first",errstr);
809     PetscCheck(mat->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified numeric phase for product %s",errstr);
810     PetscCheck(mat->product,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing struct after symbolic phase for product %s",errstr);
811   }
812   PetscFunctionReturn(0);
813 }
814 
815 /*@
816    MatProductSetFill - Set an expected fill of the matrix product.
817 
818    Collective on Mat
819 
820    Input Parameters:
821 +  mat - the matrix product
822 -  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.
823 
824    Level: intermediate
825 
826 .seealso: `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
827 @*/
828 PetscErrorCode MatProductSetFill(Mat mat,PetscReal fill)
829 {
830   PetscFunctionBegin;
831   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
832   MatCheckProduct(mat,1);
833   if (fill == PETSC_DEFAULT || fill == PETSC_DECIDE) mat->product->fill = 2.0;
834   else mat->product->fill = fill;
835   PetscFunctionReturn(0);
836 }
837 
838 /*@
839    MatProductSetAlgorithm - Requests a particular algorithm for a matrix product computation that will perform to compute the given matrix
840 
841    Collective on Mat
842 
843    Input Parameters:
844 +  mat - the matrix product
845 -  alg - particular implementation algorithm of the matrix product, e.g., `MATPRODUCTALGORITHMDEFAULT`.
846 
847    Options Database Key:
848 .  -mat_product_algorithm <algorithm> - Sets the algorithm; use -help for a list
849     of available algorithms (for instance, scalable, outerproduct, etc.)
850 
851    Level: intermediate
852 
853 .seealso: `MatProductSetType()`, `MatProductSetFill()`, `MatProductCreate()`, `MatProductAlgorithm`
854 @*/
855 PetscErrorCode MatProductSetAlgorithm(Mat mat,MatProductAlgorithm alg)
856 {
857   PetscFunctionBegin;
858   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
859   MatCheckProduct(mat,1);
860   PetscCall(PetscFree(mat->product->alg));
861   PetscCall(PetscStrallocpy(alg,&mat->product->alg));
862   PetscFunctionReturn(0);
863 }
864 
865 /*@
866    MatProductSetType - Sets a particular matrix product type to be used to compute the given matrix
867 
868    Collective on Mat
869 
870    Input Parameters:
871 +  mat - the matrix
872 -  productype   - matrix product type, e.g., `MATPRODUCT_AB`,`MATPRODUCT_AtB`,`MATPRODUCT_ABt`,`MATPRODUCT_PtAP`,`MATPRODUCT_RARt`,`MATPRODUCT_ABC`.
873 
874    Level: intermediate
875 
876    Note:
877    The small t represents the traspose operation.
878 
879 .seealso: `MatProductCreate()`, `MatProductType`
880 @*/
881 PetscErrorCode MatProductSetType(Mat mat,MatProductType productype)
882 {
883   PetscFunctionBegin;
884   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
885   MatCheckProduct(mat,1);
886   PetscValidLogicalCollectiveEnum(mat,productype,2);
887   if (productype != mat->product->type) {
888     if (mat->product->destroy) PetscCall((*mat->product->destroy)(mat->product->data));
889     mat->product->destroy = NULL;
890     mat->product->data = NULL;
891     mat->ops->productsymbolic = NULL;
892     mat->ops->productnumeric  = NULL;
893   }
894   mat->product->type = productype;
895   PetscFunctionReturn(0);
896 }
897 
898 /*@
899    MatProductClear - Clears matrix product internal structure.
900 
901    Collective on Mat
902 
903    Input Parameters:
904 .  mat - the product matrix
905 
906    Level: intermediate
907 
908    Notes:
909    This function should be called to remove any intermediate data used to compute the matrix to free up memory.
910 
911    After having called this function, MatProduct operations can no longer be used on mat
912 
913 .seealso: `MatProductCreate()`
914 @*/
915 PetscErrorCode MatProductClear(Mat mat)
916 {
917   Mat_Product    *product = mat->product;
918 
919   PetscFunctionBegin;
920   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
921   if (product) {
922     PetscCall(MatDestroy(&product->A));
923     PetscCall(MatDestroy(&product->B));
924     PetscCall(MatDestroy(&product->C));
925     PetscCall(PetscFree(product->alg));
926     PetscCall(MatDestroy(&product->Dwork));
927     if (product->destroy) PetscCall((*product->destroy)(product->data));
928   }
929   PetscCall(PetscFree(mat->product));
930   mat->ops->productsymbolic = NULL;
931   mat->ops->productnumeric = NULL;
932   PetscFunctionReturn(0);
933 }
934 
935 /* Create a supporting struct and attach it to the matrix product */
936 PetscErrorCode MatProductCreate_Private(Mat A,Mat B,Mat C,Mat D)
937 {
938   Mat_Product    *product=NULL;
939 
940   PetscFunctionBegin;
941   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
942   PetscCheck(!D->product,PetscObjectComm((PetscObject)D),PETSC_ERR_PLIB,"Product already present");
943   PetscCall(PetscNewLog(D,&product));
944   product->A        = A;
945   product->B        = B;
946   product->C        = C;
947   product->type     = MATPRODUCT_UNSPECIFIED;
948   product->Dwork    = NULL;
949   product->api_user = PETSC_FALSE;
950   product->clear    = PETSC_FALSE;
951   D->product        = product;
952 
953   PetscCall(MatProductSetAlgorithm(D,MATPRODUCTALGORITHMDEFAULT));
954   PetscCall(MatProductSetFill(D,PETSC_DEFAULT));
955 
956   PetscCall(PetscObjectReference((PetscObject)A));
957   PetscCall(PetscObjectReference((PetscObject)B));
958   PetscCall(PetscObjectReference((PetscObject)C));
959   PetscFunctionReturn(0);
960 }
961 
962 /*@
963    MatProductCreateWithMat - Setup a given matrix as a matrix product of other matrices
964 
965    Collective on Mat
966 
967    Input Parameters:
968 +  A - the first matrix
969 .  B - the second matrix
970 .  C - the third matrix (optional)
971 -  D - the matrix which will be used as a product
972 
973    Output Parameters:
974 .  D - the product matrix
975 
976    Notes:
977    Use `MatProductCreate()` if the matrix you wish computed (the D matrix) does not already exist
978 
979    See `MatProductCreate()` for details on the usage of the MatProduct routines
980 
981    Any product data currently attached to D will be cleared
982 
983    Level: intermediate
984 
985 .seealso: `MatProductCreate()`, `MatProductClear()`
986 @*/
987 PetscErrorCode MatProductCreateWithMat(Mat A,Mat B,Mat C,Mat D)
988 {
989   PetscFunctionBegin;
990   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
991   PetscValidType(A,1);
992   MatCheckPreallocated(A,1);
993   PetscCheck(A->assembled,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
994   PetscCheck(!A->factortype,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
995 
996   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
997   PetscValidType(B,2);
998   MatCheckPreallocated(B,2);
999   PetscCheck(B->assembled,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1000   PetscCheck(!B->factortype,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1001 
1002   if (C) {
1003     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
1004     PetscValidType(C,3);
1005     MatCheckPreallocated(C,3);
1006     PetscCheck(C->assembled,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1007     PetscCheck(!C->factortype,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1008   }
1009 
1010   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
1011   PetscValidType(D,4);
1012   MatCheckPreallocated(D,4);
1013   PetscCheck(D->assembled,PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1014   PetscCheck(!D->factortype,PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1015 
1016   /* Create a supporting struct and attach it to D */
1017   PetscCall(MatProductClear(D));
1018   PetscCall(MatProductCreate_Private(A,B,C,D));
1019   PetscFunctionReturn(0);
1020 }
1021 
1022 /*@
1023    MatProductCreate - create a matrix product object that can be used to compute various matrix times matrix operations such as A*B, R*A*P
1024 
1025    Collective on Mat
1026 
1027    Input Parameters:
1028 +  A - the first matrix
1029 .  B - the second matrix
1030 -  C - the third matrix (optional)
1031 
1032    Output Parameters:
1033 .  D - the product matrix
1034 
1035    Level: intermediate
1036 
1037    Example of Usage:
1038 .vb
1039     `MatProductCreate`(A,B,C,&D); or `MatProductCreateWithMat`(A,B,C,D)
1040     `MatProductSetType`(D, `MATPRODUCT_AB` or `MATPRODUCT_AtB` or `MATPRODUCT_ABt` or `MATPRODUCT_PtAP` or `MATPRODUCT_RARt` or `MATPRODUCT_ABC`)
1041     `MatProductSetAlgorithm`(D, alg)
1042     `MatProductSetFill`(D,fill)
1043     `MatProductSetFromOptions`(D)
1044     `MatProductSymbolic`(D)
1045     `MatProductNumeric`(D)
1046     Change numerical values in some of the matrices
1047     `MatProductNumeric`(D)
1048 .ve
1049 
1050    Notes:
1051    Use `MatProductCreateWithMat()` if the matrix you wish computed, the D matrix, already exists.
1052 
1053    The information computed during the symbolic stage can be reused for new numerical computations with the same non-zero structure
1054 
1055    Developer Notes:
1056    It is undocumented what happens if the nonzero structure of the input matrices changes. Is the symbolic stage automatically redone? Does it crash?
1057    Is there error checking for it?
1058 
1059 .seealso: `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductClear()`
1060 @*/
1061 PetscErrorCode MatProductCreate(Mat A,Mat B,Mat C,Mat *D)
1062 {
1063   PetscFunctionBegin;
1064   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
1065   PetscValidType(A,1);
1066   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
1067   PetscValidType(B,2);
1068   PetscCheck(!A->factortype,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix A");
1069   PetscCheck(!B->factortype,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix B");
1070 
1071   if (C) {
1072     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
1073     PetscValidType(C,3);
1074     PetscCheck(!C->factortype,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix C");
1075   }
1076 
1077   PetscValidPointer(D,4);
1078   PetscCall(MatCreate(PetscObjectComm((PetscObject)A),D));
1079   PetscCall(MatProductCreate_Private(A,B,C,*D));
1080   PetscFunctionReturn(0);
1081 }
1082 
1083 /*
1084    These are safe basic implementations of ABC, RARt and PtAP
1085    that do not rely on mat->ops->matmatop function pointers.
1086    They only use the MatProduct API and are currently used by
1087    cuSPARSE and KOKKOS-KERNELS backends
1088 */
1089 typedef struct {
1090   Mat BC;
1091   Mat ABC;
1092 } MatMatMatPrivate;
1093 
1094 static PetscErrorCode MatDestroy_MatMatMatPrivate(void *data)
1095 {
1096   MatMatMatPrivate *mmdata = (MatMatMatPrivate *)data;
1097 
1098   PetscFunctionBegin;
1099   PetscCall(MatDestroy(&mmdata->BC));
1100   PetscCall(MatDestroy(&mmdata->ABC));
1101   PetscCall(PetscFree(data));
1102   PetscFunctionReturn(0);
1103 }
1104 
1105 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1106 {
1107   Mat_Product      *product = mat->product;
1108   MatMatMatPrivate *mmabc;
1109 
1110   PetscFunctionBegin;
1111   MatCheckProduct(mat,1);
1112   PetscCheck(mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data empty");
1113   mmabc = (MatMatMatPrivate *)mat->product->data;
1114   PetscCheck(mmabc->BC->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1115   /* use function pointer directly to prevent logging */
1116   PetscCall((*mmabc->BC->ops->productnumeric)(mmabc->BC));
1117   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1118   mat->product = mmabc->ABC->product;
1119   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1120   PetscCheck(mat->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1121   /* use function pointer directly to prevent logging */
1122   PetscCall((*mat->ops->productnumeric)(mat));
1123   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1124   mat->product = product;
1125   PetscFunctionReturn(0);
1126 }
1127 
1128 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1129 {
1130   Mat_Product      *product = mat->product;
1131   Mat              A, B ,C;
1132   MatProductType   p1,p2;
1133   MatMatMatPrivate *mmabc;
1134   const char       *prefix;
1135 
1136   PetscFunctionBegin;
1137   MatCheckProduct(mat,1);
1138   PetscCheck(!mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data not empty");
1139   PetscCall(MatGetOptionsPrefix(mat,&prefix));
1140   PetscCall(PetscNew(&mmabc));
1141   product->data    = mmabc;
1142   product->destroy = MatDestroy_MatMatMatPrivate;
1143   switch (product->type) {
1144   case MATPRODUCT_PtAP:
1145     p1 = MATPRODUCT_AB;
1146     p2 = MATPRODUCT_AtB;
1147     A = product->B;
1148     B = product->A;
1149     C = product->B;
1150     break;
1151   case MATPRODUCT_RARt:
1152     p1 = MATPRODUCT_ABt;
1153     p2 = MATPRODUCT_AB;
1154     A = product->B;
1155     B = product->A;
1156     C = product->B;
1157     break;
1158   case MATPRODUCT_ABC:
1159     p1 = MATPRODUCT_AB;
1160     p2 = MATPRODUCT_AB;
1161     A = product->A;
1162     B = product->B;
1163     C = product->C;
1164     break;
1165   default:
1166     SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Not for ProductType %s",MatProductTypes[product->type]);
1167   }
1168   PetscCall(MatProductCreate(B,C,NULL,&mmabc->BC));
1169   PetscCall(MatSetOptionsPrefix(mmabc->BC,prefix));
1170   PetscCall(MatAppendOptionsPrefix(mmabc->BC,"P1_"));
1171   PetscCall(MatProductSetType(mmabc->BC,p1));
1172   PetscCall(MatProductSetAlgorithm(mmabc->BC,MATPRODUCTALGORITHMDEFAULT));
1173   PetscCall(MatProductSetFill(mmabc->BC,product->fill));
1174   mmabc->BC->product->api_user = product->api_user;
1175   PetscCall(MatProductSetFromOptions(mmabc->BC));
1176   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);
1177   /* use function pointer directly to prevent logging */
1178   PetscCall((*mmabc->BC->ops->productsymbolic)(mmabc->BC));
1179 
1180   PetscCall(MatProductCreate(A,mmabc->BC,NULL,&mmabc->ABC));
1181   PetscCall(MatSetOptionsPrefix(mmabc->ABC,prefix));
1182   PetscCall(MatAppendOptionsPrefix(mmabc->ABC,"P2_"));
1183   PetscCall(MatProductSetType(mmabc->ABC,p2));
1184   PetscCall(MatProductSetAlgorithm(mmabc->ABC,MATPRODUCTALGORITHMDEFAULT));
1185   PetscCall(MatProductSetFill(mmabc->ABC,product->fill));
1186   mmabc->ABC->product->api_user = product->api_user;
1187   PetscCall(MatProductSetFromOptions(mmabc->ABC));
1188   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);
1189   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1190   mat->product = mmabc->ABC->product;
1191   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1192   /* use function pointer directly to prevent logging */
1193   PetscCall((*mat->ops->productsymbolic)(mat));
1194   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1195   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1196   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1197   mat->product                    = product;
1198   PetscFunctionReturn(0);
1199 }
1200 
1201 /*@
1202    MatProductGetType - Returns the type of the MatProduct.
1203 
1204    Not collective
1205 
1206    Input Parameter:
1207 .  mat - the matrix
1208 
1209    Output Parameter:
1210 .  mtype - the MatProduct type
1211 
1212    Level: intermediate
1213 
1214 .seealso: `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductCreate()`, `MatProductType`
1215 @*/
1216 PetscErrorCode MatProductGetType(Mat mat, MatProductType *mtype)
1217 {
1218   PetscFunctionBegin;
1219   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
1220   PetscValidPointer(mtype,2);
1221   *mtype = MATPRODUCT_UNSPECIFIED;
1222   if (mat->product) *mtype = mat->product->type;
1223   PetscFunctionReturn(0);
1224 }
1225 
1226 /*@
1227    MatProductGetMats - Returns the matrices associated with the MatProduct.
1228 
1229    Not collective
1230 
1231    Input Parameter:
1232 .  mat - the product matrix
1233 
1234    Output Parameters:
1235 +  A - the first matrix
1236 .  B - the second matrix
1237 -  C - the third matrix (optional)
1238 
1239    Level: intermediate
1240 
1241 .seealso: `MatProductCreateWithMat()`, `MatProductSetType()`, `MatProductSetAlgorithm()`, `MatProductCreate()`
1242 @*/
1243 PetscErrorCode MatProductGetMats(Mat mat, Mat *A, Mat *B, Mat *C)
1244 {
1245   PetscFunctionBegin;
1246   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
1247   if (A) *A = mat->product ? mat->product->A : NULL;
1248   if (B) *B = mat->product ? mat->product->B : NULL;
1249   if (C) *C = mat->product ? mat->product->C : NULL;
1250   PetscFunctionReturn(0);
1251 }
1252