xref: /petsc/src/mat/interface/matproduct.c (revision 8d7b260cab71c4b5b13ed763b13fce8991d43895)
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 %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   }
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 
669   PetscFunctionBegin;
670   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
671   MatCheckProduct(mat,1);
672   /* log event */
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 SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ORDER,"Call MatProductSymbolic() first");
700   if (!mat->product) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing product after numeric phase");
701   if (mat->product->clear) {
702     ierr = MatProductClear(mat);CHKERRQ(ierr);
703   }
704   ierr = PetscObjectStateIncrease((PetscObject)mat);CHKERRQ(ierr);
705   PetscFunctionReturn(0);
706 }
707 
708 /* ----------------------------------------------- */
709 /* these are basic implementations relying on the old function pointers
710  * they are dangerous and should be removed in the future */
711 PetscErrorCode MatProductSymbolic_AB(Mat mat)
712 {
713   PetscErrorCode ierr;
714   Mat_Product    *product = mat->product;
715   Mat            A=product->A,B=product->B;
716 
717   PetscFunctionBegin;
718   if (!mat->ops->matmultsymbolic) SETERRQ1(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
719   ierr = (*mat->ops->matmultsymbolic)(A,B,product->fill,mat);CHKERRQ(ierr);
720   mat->ops->productnumeric = MatProductNumeric_AB;
721   PetscFunctionReturn(0);
722 }
723 
724 PetscErrorCode MatProductSymbolic_AtB(Mat mat)
725 {
726   PetscErrorCode ierr;
727   Mat_Product    *product = mat->product;
728   Mat            A=product->A,B=product->B;
729 
730   PetscFunctionBegin;
731   if (!mat->ops->transposematmultsymbolic) SETERRQ1(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
732   ierr = (*mat->ops->transposematmultsymbolic)(A,B,product->fill,mat);CHKERRQ(ierr);
733   mat->ops->productnumeric = MatProductNumeric_AtB;
734   PetscFunctionReturn(0);
735 }
736 
737 PetscErrorCode MatProductSymbolic_ABt(Mat mat)
738 {
739   PetscErrorCode ierr;
740   Mat_Product    *product = mat->product;
741   Mat            A=product->A,B=product->B;
742 
743   PetscFunctionBegin;
744   if (!mat->ops->mattransposemultsymbolic) SETERRQ1(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
745   ierr = (*mat->ops->mattransposemultsymbolic)(A,B,product->fill,mat);CHKERRQ(ierr);
746   mat->ops->productnumeric = MatProductNumeric_ABt;
747   PetscFunctionReturn(0);
748 }
749 
750 PetscErrorCode MatProductSymbolic_ABC(Mat mat)
751 {
752   PetscErrorCode ierr;
753   Mat_Product    *product = mat->product;
754   Mat            A=product->A,B=product->B,C=product->C;
755 
756   PetscFunctionBegin;
757   if (!mat->ops->matmatmultsymbolic) SETERRQ1(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
758   ierr = (*mat->ops->matmatmultsymbolic)(A,B,C,product->fill,mat);CHKERRQ(ierr);
759   mat->ops->productnumeric = MatProductNumeric_ABC;
760   PetscFunctionReturn(0);
761 }
762 
763 /* ----------------------------------------------- */
764 
765 /*@
766    MatProductSymbolic - Perform the symbolic portion of a matrix product, this creates a data structure for use with the numerical produce.
767 
768    Collective on Mat
769 
770    Input/Output Parameter:
771 .  mat - the matrix to hold a product
772 
773    Level: intermediate
774 
775    Notes: MatProductSetFromOptions() must have been called on mat before calling this function
776 
777 .seealso: MatProductCreate(), MatProductCreateWithMat(), MatProductSetFromOptions(), MatProductNumeric(), MatProductSetType(), MatProductSetAlgorithm()
778 @*/
779 PetscErrorCode MatProductSymbolic(Mat mat)
780 {
781   PetscErrorCode ierr;
782   PetscLogEvent  eventtype=-1;
783 
784   PetscFunctionBegin;
785   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
786   MatCheckProduct(mat,1);
787   if (mat->product->data) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ORDER,"Cannot run symbolic phase. Product data not empty");
788   /* log event */
789   switch (mat->product->type) {
790   case MATPRODUCT_AB:
791     eventtype = MAT_MatMultSymbolic;
792     break;
793   case MATPRODUCT_AtB:
794     eventtype = MAT_TransposeMatMultSymbolic;
795     break;
796   case MATPRODUCT_ABt:
797     eventtype = MAT_MatTransposeMultSymbolic;
798     break;
799   case MATPRODUCT_PtAP:
800     eventtype = MAT_PtAPSymbolic;
801     break;
802   case MATPRODUCT_RARt:
803     eventtype = MAT_RARtSymbolic;
804     break;
805   case MATPRODUCT_ABC:
806     eventtype = MAT_MatMatMultSymbolic;
807     break;
808   default: SETERRQ1(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"ProductType %s is not supported",MatProductTypes[mat->product->type]);
809   }
810 
811   mat->ops->productnumeric = NULL;
812   if (mat->ops->productsymbolic) {
813     ierr = PetscLogEventBegin(eventtype,mat,0,0,0);CHKERRQ(ierr);
814     ierr = (*mat->ops->productsymbolic)(mat);CHKERRQ(ierr);
815     ierr = PetscLogEventEnd(eventtype,mat,0,0,0);CHKERRQ(ierr);
816   } else SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ORDER,"Call MatProductSetFromOptions() first");
817   if (!mat->product) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing product after symbolic phase");
818   if (!mat->ops->productnumeric) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Symbolic phase did not specify the numeric phase");
819   PetscFunctionReturn(0);
820 }
821 
822 /*@
823    MatProductSetFill - Set an expected fill of the matrix product.
824 
825    Collective on Mat
826 
827    Input Parameters:
828 +  mat - the matrix product
829 -  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.
830 
831    Level: intermediate
832 
833 .seealso: MatProductSetType(), MatProductSetAlgorithm(), MatProductCreate()
834 @*/
835 PetscErrorCode MatProductSetFill(Mat mat,PetscReal fill)
836 {
837   PetscFunctionBegin;
838   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
839   MatCheckProduct(mat,1);
840   if (fill == PETSC_DEFAULT || fill == PETSC_DECIDE) mat->product->fill = 2.0;
841   else mat->product->fill = fill;
842   PetscFunctionReturn(0);
843 }
844 
845 /*@
846    MatProductSetAlgorithm - Requests a particular algorithm for a matrix product implementation.
847 
848    Collective on Mat
849 
850    Input Parameters:
851 +  mat - the matrix product
852 -  alg - particular implementation algorithm of the matrix product, e.g., MATPRODUCTALGORITHM_DEFAULT.
853 
854    Level: intermediate
855 
856 .seealso: MatProductSetType(), MatProductSetFill(), MatProductCreate(), MatProductAlgorithm
857 @*/
858 PetscErrorCode MatProductSetAlgorithm(Mat mat,MatProductAlgorithm alg)
859 {
860   PetscErrorCode ierr;
861 
862   PetscFunctionBegin;
863   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
864   MatCheckProduct(mat,1);
865   ierr = PetscFree(mat->product->alg);CHKERRQ(ierr);
866   ierr = PetscStrallocpy(alg,&mat->product->alg);CHKERRQ(ierr);
867   PetscFunctionReturn(0);
868 }
869 
870 /*@
871    MatProductSetType - Sets a particular matrix product type
872 
873    Collective on Mat
874 
875    Input Parameters:
876 +  mat - the matrix
877 -  productype   - matrix product type, e.g., MATPRODUCT_AB,MATPRODUCT_AtB,MATPRODUCT_ABt,MATPRODUCT_PtAP,MATPRODUCT_RARt,MATPRODUCT_ABC.
878 
879    Level: intermediate
880 
881 .seealso: MatProductCreate(), MatProductType
882 @*/
883 PetscErrorCode MatProductSetType(Mat mat,MatProductType productype)
884 {
885   PetscErrorCode ierr;
886 
887   PetscFunctionBegin;
888   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
889   MatCheckProduct(mat,1);
890   PetscValidLogicalCollectiveEnum(mat,productype,2);
891   if (productype != mat->product->type) {
892     if (mat->product->destroy) {
893       ierr = (*mat->product->destroy)(mat->product->data);CHKERRQ(ierr);
894     }
895     mat->product->destroy = NULL;
896     mat->product->data = NULL;
897     mat->ops->productsymbolic = NULL;
898     mat->ops->productnumeric  = NULL;
899   }
900   mat->product->type = productype;
901   PetscFunctionReturn(0);
902 }
903 
904 /*@
905    MatProductClear - Clears matrix product internal structure.
906 
907    Collective on Mat
908 
909    Input Parameters:
910 .  mat - the product matrix
911 
912    Level: intermediate
913 
914    Notes: this function should be called to remove any intermediate data used by the product
915           After having called this function, MatProduct operations can no longer be used on mat
916 @*/
917 PetscErrorCode MatProductClear(Mat mat)
918 {
919   PetscErrorCode ierr;
920   Mat_Product    *product = mat->product;
921 
922   PetscFunctionBegin;
923   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
924   if (product) {
925     ierr = MatDestroy(&product->A);CHKERRQ(ierr);
926     ierr = MatDestroy(&product->B);CHKERRQ(ierr);
927     ierr = MatDestroy(&product->C);CHKERRQ(ierr);
928     ierr = PetscFree(product->alg);CHKERRQ(ierr);
929     ierr = MatDestroy(&product->Dwork);CHKERRQ(ierr);
930     if (product->destroy) {
931       ierr = (*product->destroy)(product->data);CHKERRQ(ierr);
932     }
933   }
934   ierr = PetscFree(mat->product);CHKERRQ(ierr);
935   mat->ops->productsymbolic = NULL;
936   mat->ops->productnumeric = NULL;
937   PetscFunctionReturn(0);
938 }
939 
940 /* Create a supporting struct and attach it to the matrix product */
941 PetscErrorCode MatProductCreate_Private(Mat A,Mat B,Mat C,Mat D)
942 {
943   PetscErrorCode ierr;
944   Mat_Product    *product=NULL;
945 
946   PetscFunctionBegin;
947   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
948   if (D->product) SETERRQ(PetscObjectComm((PetscObject)D),PETSC_ERR_PLIB,"Product already present");
949   ierr = PetscNewLog(D,&product);CHKERRQ(ierr);
950   product->A        = A;
951   product->B        = B;
952   product->C        = C;
953   product->type     = MATPRODUCT_UNSPECIFIED;
954   product->Dwork    = NULL;
955   product->api_user = PETSC_FALSE;
956   product->clear    = PETSC_FALSE;
957   D->product        = product;
958 
959   ierr = MatProductSetAlgorithm(D,MATPRODUCTALGORITHM_DEFAULT);CHKERRQ(ierr);
960   ierr = MatProductSetFill(D,PETSC_DEFAULT);CHKERRQ(ierr);
961 
962   ierr = PetscObjectReference((PetscObject)A);CHKERRQ(ierr);
963   ierr = PetscObjectReference((PetscObject)B);CHKERRQ(ierr);
964   ierr = PetscObjectReference((PetscObject)C);CHKERRQ(ierr);
965   PetscFunctionReturn(0);
966 }
967 
968 /*@
969    MatProductCreateWithMat - Setup a given matrix as a matrix product.
970 
971    Collective on Mat
972 
973    Input Parameters:
974 +  A - the first matrix
975 .  B - the second matrix
976 .  C - the third matrix (optional)
977 -  D - the matrix which will be used as a product
978 
979    Output Parameters:
980 .  D - the product matrix
981 
982    Notes:
983      Any product data attached to D will be cleared
984 
985    Level: intermediate
986 
987 .seealso: MatProductCreate(), MatProductClear()
988 @*/
989 PetscErrorCode MatProductCreateWithMat(Mat A,Mat B,Mat C,Mat D)
990 {
991   PetscErrorCode ierr;
992 
993   PetscFunctionBegin;
994   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
995   PetscValidType(A,1);
996   MatCheckPreallocated(A,1);
997   if (!A->assembled) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
998   if (A->factortype) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
999 
1000   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
1001   PetscValidType(B,2);
1002   MatCheckPreallocated(B,2);
1003   if (!B->assembled) SETERRQ(PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1004   if (B->factortype) SETERRQ(PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1005 
1006   if (C) {
1007     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
1008     PetscValidType(C,3);
1009     MatCheckPreallocated(C,3);
1010     if (!C->assembled) SETERRQ(PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1011     if (C->factortype) SETERRQ(PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1012   }
1013 
1014   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
1015   PetscValidType(D,4);
1016   MatCheckPreallocated(D,4);
1017   if (!D->assembled) SETERRQ(PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1018   if (D->factortype) SETERRQ(PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1019 
1020   /* Create a supporting struct and attach it to D */
1021   ierr = MatProductClear(D);CHKERRQ(ierr);
1022   ierr = MatProductCreate_Private(A,B,C,D);CHKERRQ(ierr);
1023   PetscFunctionReturn(0);
1024 }
1025 
1026 /*@
1027    MatProductCreate - create a matrix product object that can be used to compute various matrix times matrix operations.
1028 
1029    Collective on Mat
1030 
1031    Input Parameters:
1032 +  A - the first matrix
1033 .  B - the second matrix
1034 -  C - the third matrix (optional)
1035 
1036    Output Parameters:
1037 .  D - the product matrix
1038 
1039    Level: intermediate
1040 
1041 .seealso: MatProductCreateWithMat(), MatProductSetType(), MatProductSetAlgorithm()
1042 @*/
1043 PetscErrorCode MatProductCreate(Mat A,Mat B,Mat C,Mat *D)
1044 {
1045   PetscErrorCode ierr;
1046 
1047   PetscFunctionBegin;
1048   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
1049   PetscValidType(A,1);
1050   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
1051   PetscValidType(B,2);
1052   if (A->factortype) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix A");
1053   if (B->factortype) SETERRQ(PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix B");
1054 
1055   if (C) {
1056     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
1057     PetscValidType(C,3);
1058     if (C->factortype) SETERRQ(PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix C");
1059   }
1060 
1061   PetscValidPointer(D,4);
1062   ierr = MatCreate(PetscObjectComm((PetscObject)A),D);CHKERRQ(ierr);
1063   ierr = MatProductCreate_Private(A,B,C,*D);CHKERRQ(ierr);
1064   PetscFunctionReturn(0);
1065 }
1066 
1067 /*
1068    These are safe basic implementations of ABC, RARt and PtAP
1069    that do not rely on mat->ops->matmatop function pointers.
1070    They only use the MatProduct API and are currently used by
1071    cuSPARSE and KOKKOS-KERNELS backends
1072 */
1073 typedef struct {
1074   Mat BC;
1075   Mat ABC;
1076 } MatMatMatPrivate;
1077 
1078 static PetscErrorCode MatDestroy_MatMatMatPrivate(void *data)
1079 {
1080   PetscErrorCode   ierr;
1081   MatMatMatPrivate *mmdata = (MatMatMatPrivate *)data;
1082 
1083   PetscFunctionBegin;
1084   ierr = MatDestroy(&mmdata->BC);CHKERRQ(ierr);
1085   ierr = MatDestroy(&mmdata->ABC);CHKERRQ(ierr);
1086   ierr = PetscFree(data);CHKERRQ(ierr);
1087   PetscFunctionReturn(0);
1088 }
1089 
1090 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1091 {
1092   PetscErrorCode   ierr;
1093   Mat_Product      *product = mat->product;
1094   MatMatMatPrivate *mmabc;
1095 
1096   PetscFunctionBegin;
1097   MatCheckProduct(mat,1);
1098   if (!mat->product->data) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data empty");
1099   mmabc = (MatMatMatPrivate *)mat->product->data;
1100   if (!mmabc->BC->ops->productnumeric) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1101   /* use function pointer directly to prevent logging */
1102   ierr = (*mmabc->BC->ops->productnumeric)(mmabc->BC);CHKERRQ(ierr);
1103   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1104   mat->product = mmabc->ABC->product;
1105   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1106   if (!mat->ops->productnumeric) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1107   /* use function pointer directly to prevent logging */
1108   ierr = (*mat->ops->productnumeric)(mat);CHKERRQ(ierr);
1109   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1110   mat->product = product;
1111   PetscFunctionReturn(0);
1112 }
1113 
1114 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1115 {
1116   PetscErrorCode   ierr;
1117   Mat_Product      *product = mat->product;
1118   Mat              A, B ,C;
1119   MatProductType   p1,p2;
1120   MatMatMatPrivate *mmabc;
1121   const char       *prefix;
1122 
1123   PetscFunctionBegin;
1124   MatCheckProduct(mat,1);
1125   if (mat->product->data) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data not empty");
1126   ierr = MatGetOptionsPrefix(mat,&prefix);CHKERRQ(ierr);
1127   ierr = PetscNew(&mmabc);CHKERRQ(ierr);
1128   product->data    = mmabc;
1129   product->destroy = MatDestroy_MatMatMatPrivate;
1130   switch (product->type) {
1131   case MATPRODUCT_PtAP:
1132     p1 = MATPRODUCT_AB;
1133     p2 = MATPRODUCT_AtB;
1134     A = product->B;
1135     B = product->A;
1136     C = product->B;
1137     break;
1138   case MATPRODUCT_RARt:
1139     p1 = MATPRODUCT_ABt;
1140     p2 = MATPRODUCT_AB;
1141     A = product->B;
1142     B = product->A;
1143     C = product->B;
1144     break;
1145   case MATPRODUCT_ABC:
1146     p1 = MATPRODUCT_AB;
1147     p2 = MATPRODUCT_AB;
1148     A = product->A;
1149     B = product->B;
1150     C = product->C;
1151     break;
1152   default:
1153     SETERRQ1(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Not for ProductType %s",MatProductTypes[product->type]);
1154   }
1155   ierr = MatProductCreate(B,C,NULL,&mmabc->BC);CHKERRQ(ierr);
1156   ierr = MatSetOptionsPrefix(mmabc->BC,prefix);CHKERRQ(ierr);
1157   ierr = MatAppendOptionsPrefix(mmabc->BC,"P1_");CHKERRQ(ierr);
1158   ierr = MatProductSetType(mmabc->BC,p1);CHKERRQ(ierr);
1159   ierr = MatProductSetAlgorithm(mmabc->BC,MATPRODUCTALGORITHM_DEFAULT);CHKERRQ(ierr);
1160   ierr = MatProductSetFill(mmabc->BC,product->fill);CHKERRQ(ierr);
1161   mmabc->BC->product->api_user = product->api_user;
1162   ierr = MatProductSetFromOptions(mmabc->BC);CHKERRQ(ierr);
1163   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);
1164   /* use function pointer directly to prevent logging */
1165   ierr = (*mmabc->BC->ops->productsymbolic)(mmabc->BC);CHKERRQ(ierr);
1166 
1167   ierr = MatProductCreate(A,mmabc->BC,NULL,&mmabc->ABC);CHKERRQ(ierr);
1168   ierr = MatSetOptionsPrefix(mmabc->ABC,prefix);CHKERRQ(ierr);
1169   ierr = MatAppendOptionsPrefix(mmabc->ABC,"P2_");CHKERRQ(ierr);
1170   ierr = MatProductSetType(mmabc->ABC,p2);CHKERRQ(ierr);
1171   ierr = MatProductSetAlgorithm(mmabc->ABC,MATPRODUCTALGORITHM_DEFAULT);CHKERRQ(ierr);
1172   ierr = MatProductSetFill(mmabc->ABC,product->fill);CHKERRQ(ierr);
1173   mmabc->ABC->product->api_user = product->api_user;
1174   ierr = MatProductSetFromOptions(mmabc->ABC);CHKERRQ(ierr);
1175   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);
1176   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1177   mat->product = mmabc->ABC->product;
1178   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1179   /* use function pointer directly to prevent logging */
1180   ierr = (*mat->ops->productsymbolic)(mat);CHKERRQ(ierr);
1181   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1182   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1183   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1184   mat->product                    = product;
1185   PetscFunctionReturn(0);
1186 }
1187