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