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