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