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