xref: /petsc/src/mat/interface/matproduct.c (revision db2b9530cea7203cdddcdfcaf19eada576ef3459)
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   PetscCheckFalse(!C->ops->transposematmultnumeric,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 = PetscInfo((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,MATPRODUCTALGORITHMDEFAULT);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,MATPRODUCTALGORITHMDEFAULT);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   PetscCheckFalse(!C->ops->mattransposemultnumeric,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 = PetscInfo((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,MATPRODUCTALGORITHMDEFAULT);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,MATPRODUCTALGORITHMDEFAULT);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   PetscCheckFalse(!mat->ops->transposematmultnumeric,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 = PetscInfo((PetscObject)mat,"for A %s, B %s, C %s is used\n",((PetscObject)product->A)->type_name,((PetscObject)product->B)->type_name,((PetscObject)product->C)->type_name);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,MATPRODUCTALGORITHMDEFAULT);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,MATPRODUCTALGORITHMDEFAULT);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: SETERRQ(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: SETERRQ(PetscObjectComm((PetscObject)C),PETSC_ERR_SUP,"MatProductNumeric type %s not supported for %s and %s matrices",MatProductTypes[product->type],((PetscObject)A)->type_name,((PetscObject)B)->type_name);
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: SETERRQ(PetscObjectComm((PetscObject)C),PETSC_ERR_SUP,"MatProductSymbolic type %s not supported for %s and %s matrices",MatProductTypes[product->type],((PetscObject)A)->type_name,((PetscObject)B)->type_name);
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   PetscCheckFalse(!A,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing A mat");
411   PetscCheckFalse(!B,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing B mat");
412   PetscCheckFalse(product->type == MATPRODUCT_ABC && !C,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   PetscCheckFalse(An != Bm,PetscObjectComm((PetscObject)mat),PETSC_ERR_ARG_SIZ,"Matrix dimensions of A and %s are incompatible for MatProductType %s: A %" PetscInt_FMT "x%" PetscInt_FMT ", %s %" PetscInt_FMT "x%" PetscInt_FMT,bname,MatProductTypes[product->type],A->rmap->N,A->cmap->N,bname,B->rmap->N,B->cmap->N);
428   PetscCheckFalse(Cm && Cm != Bn,PetscObjectComm((PetscObject)mat),PETSC_ERR_ARG_SIZ,"Matrix dimensions of B and C are incompatible for MatProductType %s: B %" PetscInt_FMT "x%" PetscInt_FMT ", C %" PetscInt_FMT "x%" PetscInt_FMT,MatProductTypes[product->type],B->rmap->N,B->cmap->N,Cm,Cn);
429 
430   fA = A->ops->productsetfromoptions;
431   fB = B->ops->productsetfromoptions;
432   fC = C ? C->ops->productsetfromoptions : fA;
433   if (C) {
434     ierr = PetscInfo(mat,"MatProductType %s for A %s, %s %s, C %s\n",MatProductTypes[product->type],((PetscObject)A)->type_name,bname,((PetscObject)B)->type_name,((PetscObject)C)->type_name);CHKERRQ(ierr);
435   } else {
436     ierr = PetscInfo(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 = PetscInfo(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 = PetscInfo(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 = PetscInfo(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 = PetscInfo(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 = PetscInfo(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 = PetscInfo(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   PetscCheckFalse(mat->product->data,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   PetscCheckFalse(!mat->product,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   PetscCheckFalse(!mat->ops->matmultnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric implementation of product %s for mat %s",MatProductTypes[product->type],((PetscObject)mat)->type_name);
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   PetscCheckFalse(!mat->ops->transposematmultnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric implementation of product %s for mat %s",MatProductTypes[product->type],((PetscObject)mat)->type_name);
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   PetscCheckFalse(!mat->ops->mattransposemultnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric implementation of product %s for mat %s",MatProductTypes[product->type],((PetscObject)mat)->type_name);
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   PetscCheckFalse(!mat->ops->ptapnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric implementation of product %s for mat %s",MatProductTypes[product->type],((PetscObject)mat)->type_name);
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   PetscCheckFalse(!mat->ops->rartnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric implementation of product %s for mat %s",MatProductTypes[product->type],((PetscObject)mat)->type_name);
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   PetscCheckFalse(!mat->ops->matmatmultnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric implementation of product %s for mat %s",MatProductTypes[product->type],((PetscObject)mat)->type_name);
644   ierr = (*mat->ops->matmatmultnumeric)(A,B,C,mat);CHKERRQ(ierr);
645   PetscFunctionReturn(0);
646 }
647 
648 /* ----------------------------------------------- */
649 
650 /*@
651    MatProductNumeric - Implement a matrix product with numerical values.
652 
653    Collective on Mat
654 
655    Input/Output Parameter:
656 .  mat - the matrix holding the product
657 
658    Level: intermediate
659 
660    Notes: MatProductSymbolic() must have been called on mat before calling this function
661 
662 .seealso: MatProductCreate(), MatSetType(), MatProductSymbolic()
663 @*/
664 PetscErrorCode MatProductNumeric(Mat mat)
665 {
666   PetscErrorCode ierr;
667   PetscLogEvent  eventtype = -1;
668   PetscBool      missing = PETSC_FALSE;
669 
670   PetscFunctionBegin;
671   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
672   MatCheckProduct(mat,1);
673   switch (mat->product->type) {
674   case MATPRODUCT_AB:
675     eventtype = MAT_MatMultNumeric;
676     break;
677   case MATPRODUCT_AtB:
678     eventtype = MAT_TransposeMatMultNumeric;
679     break;
680   case MATPRODUCT_ABt:
681     eventtype = MAT_MatTransposeMultNumeric;
682     break;
683   case MATPRODUCT_PtAP:
684     eventtype = MAT_PtAPNumeric;
685     break;
686   case MATPRODUCT_RARt:
687     eventtype = MAT_RARtNumeric;
688     break;
689   case MATPRODUCT_ABC:
690     eventtype = MAT_MatMatMultNumeric;
691     break;
692   default: SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"ProductType %s is not supported",MatProductTypes[mat->product->type]);
693   }
694 
695   if (mat->ops->productnumeric) {
696     ierr = PetscLogEventBegin(eventtype,mat,0,0,0);CHKERRQ(ierr);
697     ierr = (*mat->ops->productnumeric)(mat);CHKERRQ(ierr);
698     ierr = PetscLogEventEnd(eventtype,mat,0,0,0);CHKERRQ(ierr);
699   } else missing = PETSC_TRUE;
700 
701   if (missing || !mat->product) {
702     char errstr[256];
703 
704     if (mat->product->type == MATPRODUCT_ABC) {
705       ierr = PetscSNPrintf(errstr,256,"%s with A %s, B %s, C %s",MatProductTypes[mat->product->type],((PetscObject)mat->product->A)->type_name,((PetscObject)mat->product->B)->type_name,((PetscObject)mat->product->C)->type_name);CHKERRQ(ierr);
706     } else {
707       ierr = PetscSNPrintf(errstr,256,"%s with A %s, B %s",MatProductTypes[mat->product->type],((PetscObject)mat->product->A)->type_name,((PetscObject)mat->product->B)->type_name);CHKERRQ(ierr);
708     }
709     PetscCheckFalse(missing,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified numeric phase for product %s",errstr);
710     PetscCheckFalse(!mat->product,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing struct after symbolic phase for product %s",errstr);
711   }
712 
713   if (mat->product->clear) {
714     ierr = MatProductClear(mat);CHKERRQ(ierr);
715   }
716   ierr = PetscObjectStateIncrease((PetscObject)mat);CHKERRQ(ierr);
717   PetscFunctionReturn(0);
718 }
719 
720 /* ----------------------------------------------- */
721 /* these are basic implementations relying on the old function pointers
722  * they are dangerous and should be removed in the future */
723 PetscErrorCode MatProductSymbolic_AB(Mat mat)
724 {
725   PetscErrorCode ierr;
726   Mat_Product    *product = mat->product;
727   Mat            A=product->A,B=product->B;
728 
729   PetscFunctionBegin;
730   PetscCheckFalse(!mat->ops->matmultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
731   ierr = (*mat->ops->matmultsymbolic)(A,B,product->fill,mat);CHKERRQ(ierr);
732   mat->ops->productnumeric = MatProductNumeric_AB;
733   PetscFunctionReturn(0);
734 }
735 
736 PetscErrorCode MatProductSymbolic_AtB(Mat mat)
737 {
738   PetscErrorCode ierr;
739   Mat_Product    *product = mat->product;
740   Mat            A=product->A,B=product->B;
741 
742   PetscFunctionBegin;
743   PetscCheckFalse(!mat->ops->transposematmultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
744   ierr = (*mat->ops->transposematmultsymbolic)(A,B,product->fill,mat);CHKERRQ(ierr);
745   mat->ops->productnumeric = MatProductNumeric_AtB;
746   PetscFunctionReturn(0);
747 }
748 
749 PetscErrorCode MatProductSymbolic_ABt(Mat mat)
750 {
751   PetscErrorCode ierr;
752   Mat_Product    *product = mat->product;
753   Mat            A=product->A,B=product->B;
754 
755   PetscFunctionBegin;
756   PetscCheckFalse(!mat->ops->mattransposemultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
757   ierr = (*mat->ops->mattransposemultsymbolic)(A,B,product->fill,mat);CHKERRQ(ierr);
758   mat->ops->productnumeric = MatProductNumeric_ABt;
759   PetscFunctionReturn(0);
760 }
761 
762 PetscErrorCode MatProductSymbolic_ABC(Mat mat)
763 {
764   PetscErrorCode ierr;
765   Mat_Product    *product = mat->product;
766   Mat            A=product->A,B=product->B,C=product->C;
767 
768   PetscFunctionBegin;
769   PetscCheckFalse(!mat->ops->matmatmultsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing symbolic implementation of product %s",MatProductTypes[product->type]);
770   ierr = (*mat->ops->matmatmultsymbolic)(A,B,C,product->fill,mat);CHKERRQ(ierr);
771   mat->ops->productnumeric = MatProductNumeric_ABC;
772   PetscFunctionReturn(0);
773 }
774 
775 /* ----------------------------------------------- */
776 
777 /*@
778    MatProductSymbolic - Perform the symbolic portion of a matrix product, this creates a data structure for use with the numerical produce.
779 
780    Collective on Mat
781 
782    Input/Output Parameter:
783 .  mat - the matrix to hold a product
784 
785    Level: intermediate
786 
787    Notes: MatProductSetFromOptions() must have been called on mat before calling this function
788 
789 .seealso: MatProductCreate(), MatProductCreateWithMat(), MatProductSetFromOptions(), MatProductNumeric(), MatProductSetType(), MatProductSetAlgorithm()
790 @*/
791 PetscErrorCode MatProductSymbolic(Mat mat)
792 {
793   PetscErrorCode ierr;
794   PetscLogEvent  eventtype = -1;
795   PetscBool      missing = PETSC_FALSE;
796 
797   PetscFunctionBegin;
798   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
799   MatCheckProduct(mat,1);
800   PetscCheckFalse(mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_ORDER,"Cannot run symbolic phase. Product data not empty");
801   switch (mat->product->type) {
802   case MATPRODUCT_AB:
803     eventtype = MAT_MatMultSymbolic;
804     break;
805   case MATPRODUCT_AtB:
806     eventtype = MAT_TransposeMatMultSymbolic;
807     break;
808   case MATPRODUCT_ABt:
809     eventtype = MAT_MatTransposeMultSymbolic;
810     break;
811   case MATPRODUCT_PtAP:
812     eventtype = MAT_PtAPSymbolic;
813     break;
814   case MATPRODUCT_RARt:
815     eventtype = MAT_RARtSymbolic;
816     break;
817   case MATPRODUCT_ABC:
818     eventtype = MAT_MatMatMultSymbolic;
819     break;
820   default: SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"ProductType %s is not supported",MatProductTypes[mat->product->type]);
821   }
822   mat->ops->productnumeric = NULL;
823   if (mat->ops->productsymbolic) {
824     ierr = PetscLogEventBegin(eventtype,mat,0,0,0);CHKERRQ(ierr);
825     ierr = (*mat->ops->productsymbolic)(mat);CHKERRQ(ierr);
826     ierr = PetscLogEventEnd(eventtype,mat,0,0,0);CHKERRQ(ierr);
827   } else missing = PETSC_TRUE;
828 
829   if (missing || !mat->product || !mat->ops->productnumeric) {
830     char errstr[256];
831 
832     if (mat->product->type == MATPRODUCT_ABC) {
833       ierr = PetscSNPrintf(errstr,256,"%s with A %s, B %s, C %s",MatProductTypes[mat->product->type],((PetscObject)mat->product->A)->type_name,((PetscObject)mat->product->B)->type_name,((PetscObject)mat->product->C)->type_name);CHKERRQ(ierr);
834     } else {
835       ierr = PetscSNPrintf(errstr,256,"%s with A %s, B %s",MatProductTypes[mat->product->type],((PetscObject)mat->product->A)->type_name,((PetscObject)mat->product->B)->type_name);CHKERRQ(ierr);
836     }
837     PetscCheckFalse(missing,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified symbolic phase for product %s. Call MatProductSetFromOptions() first",errstr);
838     PetscCheckFalse(!mat->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Unspecified numeric phase for product %s",errstr);
839     PetscCheckFalse(!mat->product,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing struct after symbolic phase for product %s",errstr);
840   }
841   PetscFunctionReturn(0);
842 }
843 
844 /*@
845    MatProductSetFill - Set an expected fill of the matrix product.
846 
847    Collective on Mat
848 
849    Input Parameters:
850 +  mat - the matrix product
851 -  fill - expected fill as ratio of nnz(mat)/(nnz(A) + nnz(B) + nnz(C)); use PETSC_DEFAULT if you do not have a good estimate. If the product is a dense matrix, this is irrelevant.
852 
853    Level: intermediate
854 
855 .seealso: MatProductSetType(), MatProductSetAlgorithm(), MatProductCreate()
856 @*/
857 PetscErrorCode MatProductSetFill(Mat mat,PetscReal fill)
858 {
859   PetscFunctionBegin;
860   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
861   MatCheckProduct(mat,1);
862   if (fill == PETSC_DEFAULT || fill == PETSC_DECIDE) mat->product->fill = 2.0;
863   else mat->product->fill = fill;
864   PetscFunctionReturn(0);
865 }
866 
867 /*@
868    MatProductSetAlgorithm - Requests a particular algorithm for a matrix product implementation.
869 
870    Collective on Mat
871 
872    Input Parameters:
873 +  mat - the matrix product
874 -  alg - particular implementation algorithm of the matrix product, e.g., MATPRODUCTALGORITHMDEFAULT.
875 
876    Options Database Key:
877 .  -mat_product_algorithm <algorithm> - Sets the algorithm; use -help for a list
878     of available algorithms (for instance, scalable, outerproduct, etc.)
879 
880    Level: intermediate
881 
882 .seealso: MatProductSetType(), MatProductSetFill(), MatProductCreate(), MatProductAlgorithm
883 @*/
884 PetscErrorCode MatProductSetAlgorithm(Mat mat,MatProductAlgorithm alg)
885 {
886   PetscErrorCode ierr;
887 
888   PetscFunctionBegin;
889   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
890   MatCheckProduct(mat,1);
891   ierr = PetscFree(mat->product->alg);CHKERRQ(ierr);
892   ierr = PetscStrallocpy(alg,&mat->product->alg);CHKERRQ(ierr);
893   PetscFunctionReturn(0);
894 }
895 
896 /*@
897    MatProductSetType - Sets a particular matrix product type
898 
899    Collective on Mat
900 
901    Input Parameters:
902 +  mat - the matrix
903 -  productype   - matrix product type, e.g., MATPRODUCT_AB,MATPRODUCT_AtB,MATPRODUCT_ABt,MATPRODUCT_PtAP,MATPRODUCT_RARt,MATPRODUCT_ABC.
904 
905    Level: intermediate
906 
907 .seealso: MatProductCreate(), MatProductType
908 @*/
909 PetscErrorCode MatProductSetType(Mat mat,MatProductType productype)
910 {
911   PetscErrorCode ierr;
912 
913   PetscFunctionBegin;
914   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
915   MatCheckProduct(mat,1);
916   PetscValidLogicalCollectiveEnum(mat,productype,2);
917   if (productype != mat->product->type) {
918     if (mat->product->destroy) {
919       ierr = (*mat->product->destroy)(mat->product->data);CHKERRQ(ierr);
920     }
921     mat->product->destroy = NULL;
922     mat->product->data = NULL;
923     mat->ops->productsymbolic = NULL;
924     mat->ops->productnumeric  = NULL;
925   }
926   mat->product->type = productype;
927   PetscFunctionReturn(0);
928 }
929 
930 /*@
931    MatProductClear - Clears matrix product internal structure.
932 
933    Collective on Mat
934 
935    Input Parameters:
936 .  mat - the product matrix
937 
938    Level: intermediate
939 
940    Notes: this function should be called to remove any intermediate data used by the product
941           After having called this function, MatProduct operations can no longer be used on mat
942 @*/
943 PetscErrorCode MatProductClear(Mat mat)
944 {
945   PetscErrorCode ierr;
946   Mat_Product    *product = mat->product;
947 
948   PetscFunctionBegin;
949   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
950   if (product) {
951     ierr = MatDestroy(&product->A);CHKERRQ(ierr);
952     ierr = MatDestroy(&product->B);CHKERRQ(ierr);
953     ierr = MatDestroy(&product->C);CHKERRQ(ierr);
954     ierr = PetscFree(product->alg);CHKERRQ(ierr);
955     ierr = MatDestroy(&product->Dwork);CHKERRQ(ierr);
956     if (product->destroy) {
957       ierr = (*product->destroy)(product->data);CHKERRQ(ierr);
958     }
959   }
960   ierr = PetscFree(mat->product);CHKERRQ(ierr);
961   mat->ops->productsymbolic = NULL;
962   mat->ops->productnumeric = NULL;
963   PetscFunctionReturn(0);
964 }
965 
966 /* Create a supporting struct and attach it to the matrix product */
967 PetscErrorCode MatProductCreate_Private(Mat A,Mat B,Mat C,Mat D)
968 {
969   PetscErrorCode ierr;
970   Mat_Product    *product=NULL;
971 
972   PetscFunctionBegin;
973   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
974   PetscCheckFalse(D->product,PetscObjectComm((PetscObject)D),PETSC_ERR_PLIB,"Product already present");
975   ierr = PetscNewLog(D,&product);CHKERRQ(ierr);
976   product->A        = A;
977   product->B        = B;
978   product->C        = C;
979   product->type     = MATPRODUCT_UNSPECIFIED;
980   product->Dwork    = NULL;
981   product->api_user = PETSC_FALSE;
982   product->clear    = PETSC_FALSE;
983   D->product        = product;
984 
985   ierr = MatProductSetAlgorithm(D,MATPRODUCTALGORITHMDEFAULT);CHKERRQ(ierr);
986   ierr = MatProductSetFill(D,PETSC_DEFAULT);CHKERRQ(ierr);
987 
988   ierr = PetscObjectReference((PetscObject)A);CHKERRQ(ierr);
989   ierr = PetscObjectReference((PetscObject)B);CHKERRQ(ierr);
990   ierr = PetscObjectReference((PetscObject)C);CHKERRQ(ierr);
991   PetscFunctionReturn(0);
992 }
993 
994 /*@
995    MatProductCreateWithMat - Setup a given matrix as a matrix product.
996 
997    Collective on Mat
998 
999    Input Parameters:
1000 +  A - the first matrix
1001 .  B - the second matrix
1002 .  C - the third matrix (optional)
1003 -  D - the matrix which will be used as a product
1004 
1005    Output Parameters:
1006 .  D - the product matrix
1007 
1008    Notes:
1009      Any product data attached to D will be cleared
1010 
1011    Level: intermediate
1012 
1013 .seealso: MatProductCreate(), MatProductClear()
1014 @*/
1015 PetscErrorCode MatProductCreateWithMat(Mat A,Mat B,Mat C,Mat D)
1016 {
1017   PetscErrorCode ierr;
1018 
1019   PetscFunctionBegin;
1020   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
1021   PetscValidType(A,1);
1022   MatCheckPreallocated(A,1);
1023   PetscCheckFalse(!A->assembled,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1024   PetscCheckFalse(A->factortype,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1025 
1026   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
1027   PetscValidType(B,2);
1028   MatCheckPreallocated(B,2);
1029   PetscCheckFalse(!B->assembled,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1030   PetscCheckFalse(B->factortype,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1031 
1032   if (C) {
1033     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
1034     PetscValidType(C,3);
1035     MatCheckPreallocated(C,3);
1036     PetscCheckFalse(!C->assembled,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1037     PetscCheckFalse(C->factortype,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1038   }
1039 
1040   PetscValidHeaderSpecific(D,MAT_CLASSID,4);
1041   PetscValidType(D,4);
1042   MatCheckPreallocated(D,4);
1043   PetscCheckFalse(!D->assembled,PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
1044   PetscCheckFalse(D->factortype,PetscObjectComm((PetscObject)D),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
1045 
1046   /* Create a supporting struct and attach it to D */
1047   ierr = MatProductClear(D);CHKERRQ(ierr);
1048   ierr = MatProductCreate_Private(A,B,C,D);CHKERRQ(ierr);
1049   PetscFunctionReturn(0);
1050 }
1051 
1052 /*@
1053    MatProductCreate - create a matrix product object that can be used to compute various matrix times matrix operations.
1054 
1055    Collective on Mat
1056 
1057    Input Parameters:
1058 +  A - the first matrix
1059 .  B - the second matrix
1060 -  C - the third matrix (optional)
1061 
1062    Output Parameters:
1063 .  D - the product matrix
1064 
1065    Level: intermediate
1066 
1067 .seealso: MatProductCreateWithMat(), MatProductSetType(), MatProductSetAlgorithm()
1068 @*/
1069 PetscErrorCode MatProductCreate(Mat A,Mat B,Mat C,Mat *D)
1070 {
1071   PetscErrorCode ierr;
1072 
1073   PetscFunctionBegin;
1074   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
1075   PetscValidType(A,1);
1076   PetscValidHeaderSpecific(B,MAT_CLASSID,2);
1077   PetscValidType(B,2);
1078   PetscCheckFalse(A->factortype,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix A");
1079   PetscCheckFalse(B->factortype,PetscObjectComm((PetscObject)B),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix B");
1080 
1081   if (C) {
1082     PetscValidHeaderSpecific(C,MAT_CLASSID,3);
1083     PetscValidType(C,3);
1084     PetscCheckFalse(C->factortype,PetscObjectComm((PetscObject)C),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix C");
1085   }
1086 
1087   PetscValidPointer(D,4);
1088   ierr = MatCreate(PetscObjectComm((PetscObject)A),D);CHKERRQ(ierr);
1089   ierr = MatProductCreate_Private(A,B,C,*D);CHKERRQ(ierr);
1090   PetscFunctionReturn(0);
1091 }
1092 
1093 /*
1094    These are safe basic implementations of ABC, RARt and PtAP
1095    that do not rely on mat->ops->matmatop function pointers.
1096    They only use the MatProduct API and are currently used by
1097    cuSPARSE and KOKKOS-KERNELS backends
1098 */
1099 typedef struct {
1100   Mat BC;
1101   Mat ABC;
1102 } MatMatMatPrivate;
1103 
1104 static PetscErrorCode MatDestroy_MatMatMatPrivate(void *data)
1105 {
1106   PetscErrorCode   ierr;
1107   MatMatMatPrivate *mmdata = (MatMatMatPrivate *)data;
1108 
1109   PetscFunctionBegin;
1110   ierr = MatDestroy(&mmdata->BC);CHKERRQ(ierr);
1111   ierr = MatDestroy(&mmdata->ABC);CHKERRQ(ierr);
1112   ierr = PetscFree(data);CHKERRQ(ierr);
1113   PetscFunctionReturn(0);
1114 }
1115 
1116 static PetscErrorCode MatProductNumeric_ABC_Basic(Mat mat)
1117 {
1118   PetscErrorCode   ierr;
1119   Mat_Product      *product = mat->product;
1120   MatMatMatPrivate *mmabc;
1121 
1122   PetscFunctionBegin;
1123   MatCheckProduct(mat,1);
1124   PetscCheckFalse(!mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data empty");
1125   mmabc = (MatMatMatPrivate *)mat->product->data;
1126   PetscCheckFalse(!mmabc->BC->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1127   /* use function pointer directly to prevent logging */
1128   ierr = (*mmabc->BC->ops->productnumeric)(mmabc->BC);CHKERRQ(ierr);
1129   /* swap ABC product stuff with that of ABC for the numeric phase on mat */
1130   mat->product = mmabc->ABC->product;
1131   mat->ops->productnumeric = mmabc->ABC->ops->productnumeric;
1132   PetscCheckFalse(!mat->ops->productnumeric,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Missing numeric stage");
1133   /* use function pointer directly to prevent logging */
1134   ierr = (*mat->ops->productnumeric)(mat);CHKERRQ(ierr);
1135   mat->ops->productnumeric = MatProductNumeric_ABC_Basic;
1136   mat->product = product;
1137   PetscFunctionReturn(0);
1138 }
1139 
1140 PetscErrorCode MatProductSymbolic_ABC_Basic(Mat mat)
1141 {
1142   PetscErrorCode   ierr;
1143   Mat_Product      *product = mat->product;
1144   Mat              A, B ,C;
1145   MatProductType   p1,p2;
1146   MatMatMatPrivate *mmabc;
1147   const char       *prefix;
1148 
1149   PetscFunctionBegin;
1150   MatCheckProduct(mat,1);
1151   PetscCheckFalse(mat->product->data,PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Product data not empty");
1152   ierr = MatGetOptionsPrefix(mat,&prefix);CHKERRQ(ierr);
1153   ierr = PetscNew(&mmabc);CHKERRQ(ierr);
1154   product->data    = mmabc;
1155   product->destroy = MatDestroy_MatMatMatPrivate;
1156   switch (product->type) {
1157   case MATPRODUCT_PtAP:
1158     p1 = MATPRODUCT_AB;
1159     p2 = MATPRODUCT_AtB;
1160     A = product->B;
1161     B = product->A;
1162     C = product->B;
1163     break;
1164   case MATPRODUCT_RARt:
1165     p1 = MATPRODUCT_ABt;
1166     p2 = MATPRODUCT_AB;
1167     A = product->B;
1168     B = product->A;
1169     C = product->B;
1170     break;
1171   case MATPRODUCT_ABC:
1172     p1 = MATPRODUCT_AB;
1173     p2 = MATPRODUCT_AB;
1174     A = product->A;
1175     B = product->B;
1176     C = product->C;
1177     break;
1178   default:
1179     SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_PLIB,"Not for ProductType %s",MatProductTypes[product->type]);
1180   }
1181   ierr = MatProductCreate(B,C,NULL,&mmabc->BC);CHKERRQ(ierr);
1182   ierr = MatSetOptionsPrefix(mmabc->BC,prefix);CHKERRQ(ierr);
1183   ierr = MatAppendOptionsPrefix(mmabc->BC,"P1_");CHKERRQ(ierr);
1184   ierr = MatProductSetType(mmabc->BC,p1);CHKERRQ(ierr);
1185   ierr = MatProductSetAlgorithm(mmabc->BC,MATPRODUCTALGORITHMDEFAULT);CHKERRQ(ierr);
1186   ierr = MatProductSetFill(mmabc->BC,product->fill);CHKERRQ(ierr);
1187   mmabc->BC->product->api_user = product->api_user;
1188   ierr = MatProductSetFromOptions(mmabc->BC);CHKERRQ(ierr);
1189   PetscCheckFalse(!mmabc->BC->ops->productsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Symbolic ProductType %s not supported with %s and %s",MatProductTypes[p1],((PetscObject)B)->type_name,((PetscObject)C)->type_name);
1190   /* use function pointer directly to prevent logging */
1191   ierr = (*mmabc->BC->ops->productsymbolic)(mmabc->BC);CHKERRQ(ierr);
1192 
1193   ierr = MatProductCreate(A,mmabc->BC,NULL,&mmabc->ABC);CHKERRQ(ierr);
1194   ierr = MatSetOptionsPrefix(mmabc->ABC,prefix);CHKERRQ(ierr);
1195   ierr = MatAppendOptionsPrefix(mmabc->ABC,"P2_");CHKERRQ(ierr);
1196   ierr = MatProductSetType(mmabc->ABC,p2);CHKERRQ(ierr);
1197   ierr = MatProductSetAlgorithm(mmabc->ABC,MATPRODUCTALGORITHMDEFAULT);CHKERRQ(ierr);
1198   ierr = MatProductSetFill(mmabc->ABC,product->fill);CHKERRQ(ierr);
1199   mmabc->ABC->product->api_user = product->api_user;
1200   ierr = MatProductSetFromOptions(mmabc->ABC);CHKERRQ(ierr);
1201   PetscCheckFalse(!mmabc->ABC->ops->productsymbolic,PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Symbolic ProductType %s not supported with %s and %s",MatProductTypes[p2],((PetscObject)A)->type_name,((PetscObject)mmabc->BC)->type_name);
1202   /* swap ABC product stuff with that of ABC for the symbolic phase on mat */
1203   mat->product = mmabc->ABC->product;
1204   mat->ops->productsymbolic = mmabc->ABC->ops->productsymbolic;
1205   /* use function pointer directly to prevent logging */
1206   ierr = (*mat->ops->productsymbolic)(mat);CHKERRQ(ierr);
1207   mmabc->ABC->ops->productnumeric = mat->ops->productnumeric;
1208   mat->ops->productsymbolic       = MatProductSymbolic_ABC_Basic;
1209   mat->ops->productnumeric        = MatProductNumeric_ABC_Basic;
1210   mat->product                    = product;
1211   PetscFunctionReturn(0);
1212 }
1213 
1214 /*@
1215    MatProductGetType - Returns the type of the MatProduct.
1216 
1217    Not collective
1218 
1219    Input Parameter:
1220 .  mat - the matrix
1221 
1222    Output Parameter:
1223 .  mtype - the MatProduct type
1224 
1225    Level: intermediate
1226 
1227 .seealso: MatProductCreateWithMat(), MatProductSetType(), MatProductCreate()
1228 @*/
1229 PetscErrorCode MatProductGetType(Mat mat, MatProductType *mtype)
1230 {
1231   PetscFunctionBegin;
1232   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
1233   PetscValidPointer(mtype,2);
1234   *mtype = MATPRODUCT_UNSPECIFIED;
1235   if (mat->product) *mtype = mat->product->type;
1236   PetscFunctionReturn(0);
1237 }
1238 
1239 /*@
1240    MatProductGetMats - Returns the matrices associated with the MatProduct.
1241 
1242    Not collective
1243 
1244    Input Parameter:
1245 .  mat - the product matrix
1246 
1247    Output Parameters:
1248 +  A - the first matrix
1249 .  B - the second matrix
1250 -  C - the third matrix (optional)
1251 
1252    Level: intermediate
1253 
1254 .seealso: MatProductCreateWithMat(), MatProductSetType(), MatProductSetAlgorithm()
1255 @*/
1256 PetscErrorCode MatProductGetMats(Mat mat, Mat *A, Mat *B, Mat *C)
1257 {
1258   PetscFunctionBegin;
1259   PetscValidHeaderSpecific(mat,MAT_CLASSID,1);
1260   if (A) *A = mat->product ? mat->product->A : NULL;
1261   if (B) *B = mat->product ? mat->product->B : NULL;
1262   if (C) *C = mat->product ? mat->product->C : NULL;
1263   PetscFunctionReturn(0);
1264 }
1265