xref: /petsc/src/mat/impls/aij/seq/matptap.c (revision f747e1ae54eac4c971caa6b7e82576b5c3be8258)
1 /*
2   Defines projective product routines where A is a AIJ matrix
3           C = P^T * A * P
4 */
5 
6 #include "src/mat/impls/aij/seq/aij.h"   /*I "petscmat.h" I*/
7 #include "src/mat/utils/freespace.h"
8 #include "src/mat/impls/aij/mpi/mpiaij.h"
9 
10 EXTERN int RegisterMatMatMultRoutines_Private(Mat);
11 
12 static int MAT_PtAPSymbolic = 0;
13 static int MAT_PtAPNumeric  = 0;
14 
15 #undef __FUNCT__
16 #define __FUNCT__ "MatPtAP"
17 /*@
18    MatPtAP - Creates the matrix projection C = P^T * A * P
19 
20    Collective on Mat
21 
22    Input Parameters:
23 +  A - the matrix
24 .  P - the projection matrix
25 .  scall - either MAT_INITIAL_MATRIX or MAT_REUSE_MATRIX
26 -  fill - expected fill as ratio of nnz(C)/(nnz(A) + nnz(P))
27 
28    Output Parameters:
29 .  C - the product matrix
30 
31    Notes:
32    C will be created and must be destroyed by the user with MatDestroy().
33 
34    This routine is currently only implemented for pairs of SeqAIJ matrices and classes
35    which inherit from SeqAIJ.  C will be of type MATSEQAIJ.
36 
37    Level: intermediate
38 
39 .seealso: MatPtAPSymbolic(),MatPtAPNumeric(),MatMatMult()
40 @*/
41 int MatPtAP(Mat A,Mat P,MatReuse scall,PetscReal fill,Mat *C) {
42   int ierr;
43 
44   PetscFunctionBegin;
45   PetscValidHeaderSpecific(A,MAT_COOKIE,1);
46   PetscValidType(A,1);
47   MatPreallocated(A);
48   if (!A->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
49   if (A->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
50   PetscValidHeaderSpecific(P,MAT_COOKIE,2);
51   PetscValidType(P,2);
52   MatPreallocated(P);
53   if (!P->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
54   if (P->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
55   PetscValidPointer(C,3);
56   if (P->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",P->M,A->N);
57 
58   if (fill <=0.0) SETERRQ1(PETSC_ERR_ARG_SIZ,"fill=%g must be > 0.0",fill);
59 
60   ierr = PetscLogEventBegin(MAT_PtAP,A,P,0,0);CHKERRQ(ierr);
61   ierr = (*A->ops->matptap)(A,P,scall,fill,C);CHKERRQ(ierr);
62   ierr = PetscLogEventEnd(MAT_PtAP,A,P,0,0);CHKERRQ(ierr);
63   PetscFunctionReturn(0);
64 }
65 
66 #undef __FUNCT__
67 #define __FUNCT__ "MatPtAP_SeqAIJ_SeqAIJ"
68 int MatPtAP_SeqAIJ_SeqAIJ(Mat A,Mat P,MatReuse scall,PetscReal fill,Mat *C)
69 {
70   int ierr;
71   PetscFunctionBegin;
72   if (scall == MAT_INITIAL_MATRIX){
73     ierr = MatPtAPSymbolic_SeqAIJ_SeqAIJ(A,P,fill,C);CHKERRQ(ierr);
74   }
75   ierr = MatPtAPNumeric_SeqAIJ_SeqAIJ(A,P,*C);CHKERRQ(ierr);
76   PetscFunctionReturn(0);
77 }
78 
79 #undef __FUNCT__
80 #define __FUNCT__ "MatPtAPSymbolic"
81 /*@
82    MatPtAPSymbolic - Creates the (i,j) structure of the matrix projection C = P^T * A * P
83 
84    Collective on Mat
85 
86    Input Parameters:
87 +  A - the matrix
88 -  P - the projection matrix
89 
90    Output Parameters:
91 .  C - the (i,j) structure of the product matrix
92 
93    Notes:
94    C will be created and must be destroyed by the user with MatDestroy().
95 
96    This routine is currently only implemented for pairs of SeqAIJ matrices and classes
97    which inherit from SeqAIJ.  C will be of type MATSEQAIJ.  The product is computed using
98    this (i,j) structure by calling MatPtAPNumeric().
99 
100    Level: intermediate
101 
102 .seealso: MatPtAP(),MatPtAPNumeric(),MatMatMultSymbolic()
103 @*/
104 int MatPtAPSymbolic(Mat A,Mat P,PetscReal fill,Mat *C) {
105   int ierr;
106   char funct[80];
107   int (*f)(Mat,Mat,Mat*);
108 
109   PetscFunctionBegin;
110 
111   PetscValidHeaderSpecific(A,MAT_COOKIE,1);
112   PetscValidType(A,1);
113   MatPreallocated(A);
114   if (!A->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
115   if (A->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
116 
117   PetscValidHeaderSpecific(P,MAT_COOKIE,2);
118   PetscValidType(P,2);
119   MatPreallocated(P);
120   if (!P->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
121   if (P->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
122 
123   PetscValidPointer(C,3);
124 
125   if (P->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",P->M,A->N);
126   if (A->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix 'A' must be square, %d != %d",A->M,A->N);
127 
128   /* Currently only _seqaij_seqaij is implemented, so just query for it. */
129   /* When other implementations exist, attack the multiple dispatch problem. */
130   ierr = PetscStrcpy(funct,"MatPtAPSymbolic_seqaij_seqaij");CHKERRQ(ierr);
131   ierr = PetscObjectQueryFunction((PetscObject)P,funct,(PetscVoidFunction)&f);CHKERRQ(ierr);
132   if (!f) SETERRQ1(PETSC_ERR_SUP,"MatPtAPSymbolic is not supported for P of type %s",P->type_name);
133   ierr = PetscObjectQueryFunction((PetscObject)A,funct,(PetscVoidFunction)&f);CHKERRQ(ierr);
134   if (!f) SETERRQ1(PETSC_ERR_SUP,"MatPtAPSymbolic is not supported for A of type %s",A->type_name);
135 
136   ierr = (*f)(A,P,C);CHKERRQ(ierr);
137 
138   PetscFunctionReturn(0);
139 }
140 
141 typedef struct {
142   Mat    symAP;
143 } Mat_PtAPstruct;
144 
145 #undef __FUNCT__
146 #define __FUNCT__ "MatDestroy_SeqAIJ_PtAP"
147 int MatDestroy_SeqAIJ_PtAP(Mat A)
148 {
149   int               ierr;
150   Mat_PtAPstruct    *ptap=(Mat_PtAPstruct*)A->spptr;
151 
152   PetscFunctionBegin;
153   ierr = MatDestroy(ptap->symAP);CHKERRQ(ierr);
154   ierr = PetscFree(ptap);CHKERRQ(ierr);
155 
156   ierr = MatDestroy_SeqAIJ(A);CHKERRQ(ierr);
157   PetscFunctionReturn(0);
158 }
159 
160 #undef __FUNCT__
161 #define __FUNCT__ "MatPtAPSymbolic_SeqAIJ_SeqAIJ"
162 int MatPtAPSymbolic_SeqAIJ_SeqAIJ(Mat A,Mat P,PetscReal fill,Mat *C) {
163   int            ierr,*pti,*ptj;
164   Mat            Pt,AP;
165   Mat_PtAPstruct *ptap;
166 
167   PetscFunctionBegin;
168   /* create symbolic Pt */
169   ierr = MatGetSymbolicTranspose_SeqAIJ(P,&pti,&ptj);CHKERRQ(ierr);
170   ierr = MatCreateSeqAIJWithArrays(P->comm,P->N,P->M,pti,ptj,PETSC_NULL,&Pt);CHKERRQ(ierr);
171 
172   /* get symbolic AP=A*P and C=Pt*AP */
173   ierr = MatMatMultSymbolic_SeqAIJ_SeqAIJ(A,P,fill,&AP);CHKERRQ(ierr);
174   ierr = MatMatMultSymbolic_SeqAIJ_SeqAIJ(Pt,AP,fill,C);CHKERRQ(ierr);
175 
176   /* clean up */
177   ierr = MatRestoreSymbolicTranspose_SeqAIJ(Pt,&pti,&ptj);CHKERRQ(ierr);
178   ierr = MatDestroy(Pt);CHKERRQ(ierr);
179 
180   /* save symbolic AP - to be used by MatPtAPNumeric_SeqAIJ_SeqAIJ() */
181   ierr = PetscNew(Mat_PtAPstruct,&ptap);CHKERRQ(ierr);
182   ptap->symAP = AP;
183   (*C)->spptr = (void*)ptap;
184   (*C)->ops->destroy  = MatDestroy_SeqAIJ_PtAP;
185 
186   PetscFunctionReturn(0);
187 }
188 
189 #include "src/mat/impls/maij/maij.h"
190 EXTERN_C_BEGIN
191 #undef __FUNCT__
192 #define __FUNCT__ "MatPtAPSymbolic_SeqAIJ_SeqMAIJ"
193 int MatPtAPSymbolic_SeqAIJ_SeqMAIJ(Mat A,Mat PP,Mat *C) {
194   /* This routine requires testing -- I don't think it works. */
195   int            ierr;
196   FreeSpaceList  free_space=PETSC_NULL,current_space=PETSC_NULL;
197   Mat_SeqMAIJ    *pp=(Mat_SeqMAIJ*)PP->data;
198   Mat            P=pp->AIJ;
199   Mat_SeqAIJ     *a=(Mat_SeqAIJ*)A->data,*p=(Mat_SeqAIJ*)P->data,*c;
200   int            *pti,*ptj,*ptJ,*ai=a->i,*aj=a->j,*ajj,*pi=p->i,*pj=p->j,*pjj;
201   int            *ci,*cj,*denserow,*sparserow,*ptadenserow,*ptasparserow,*ptaj;
202   int            an=A->N,am=A->M,pn=P->N,pm=P->M,ppdof=pp->dof;
203   int            i,j,k,dof,pdof,ptnzi,arow,anzj,ptanzi,prow,pnzj,cnzi;
204   MatScalar      *ca;
205 
206   PetscFunctionBegin;
207   /* Start timer */
208   ierr = PetscLogEventBegin(MAT_PtAPSymbolic,A,PP,0,0);CHKERRQ(ierr);
209 
210   /* Get ij structure of P^T */
211   ierr = MatGetSymbolicTranspose_SeqAIJ(P,&pti,&ptj);CHKERRQ(ierr);
212 
213   /* Allocate ci array, arrays for fill computation and */
214   /* free space for accumulating nonzero column info */
215   ierr = PetscMalloc((pn+1)*sizeof(int),&ci);CHKERRQ(ierr);
216   ci[0] = 0;
217 
218   ierr = PetscMalloc((2*pn+2*an+1)*sizeof(int),&ptadenserow);CHKERRQ(ierr);
219   ierr = PetscMemzero(ptadenserow,(2*pn+2*an+1)*sizeof(int));CHKERRQ(ierr);
220   ptasparserow = ptadenserow  + an;
221   denserow     = ptasparserow + an;
222   sparserow    = denserow     + pn;
223 
224   /* Set initial free space to be nnz(A) scaled by aspect ratio of P. */
225   /* This should be reasonable if sparsity of PtAP is similar to that of A. */
226   ierr          = GetMoreSpace((ai[am]/pm)*pn,&free_space);
227   current_space = free_space;
228 
229   /* Determine symbolic info for each row of C: */
230   for (i=0;i<pn/ppdof;i++) {
231     ptnzi  = pti[i+1] - pti[i];
232     ptanzi = 0;
233     ptJ    = ptj + pti[i];
234     for (dof=0;dof<ppdof;dof++) {
235     /* Determine symbolic row of PtA: */
236       for (j=0;j<ptnzi;j++) {
237         arow = ptJ[j] + dof;
238         anzj = ai[arow+1] - ai[arow];
239         ajj  = aj + ai[arow];
240         for (k=0;k<anzj;k++) {
241           if (!ptadenserow[ajj[k]]) {
242             ptadenserow[ajj[k]]    = -1;
243             ptasparserow[ptanzi++] = ajj[k];
244           }
245         }
246       }
247       /* Using symbolic info for row of PtA, determine symbolic info for row of C: */
248       ptaj = ptasparserow;
249       cnzi   = 0;
250       for (j=0;j<ptanzi;j++) {
251         pdof = *ptaj%dof;
252         prow = (*ptaj++)/dof;
253         pnzj = pi[prow+1] - pi[prow];
254         pjj  = pj + pi[prow];
255         for (k=0;k<pnzj;k++) {
256           if (!denserow[pjj[k]+pdof]) {
257             denserow[pjj[k]+pdof] = -1;
258             sparserow[cnzi++]     = pjj[k]+pdof;
259           }
260         }
261       }
262 
263       /* sort sparserow */
264       ierr = PetscSortInt(cnzi,sparserow);CHKERRQ(ierr);
265 
266       /* If free space is not available, make more free space */
267       /* Double the amount of total space in the list */
268       if (current_space->local_remaining<cnzi) {
269         ierr = GetMoreSpace(current_space->total_array_size,&current_space);CHKERRQ(ierr);
270       }
271 
272       /* Copy data into free space, and zero out denserows */
273       ierr = PetscMemcpy(current_space->array,sparserow,cnzi*sizeof(int));CHKERRQ(ierr);
274       current_space->array           += cnzi;
275       current_space->local_used      += cnzi;
276       current_space->local_remaining -= cnzi;
277 
278       for (j=0;j<ptanzi;j++) {
279         ptadenserow[ptasparserow[j]] = 0;
280       }
281       for (j=0;j<cnzi;j++) {
282         denserow[sparserow[j]] = 0;
283       }
284       /* Aside: Perhaps we should save the pta info for the numerical factorization. */
285       /*        For now, we will recompute what is needed. */
286       ci[i+1+dof] = ci[i+dof] + cnzi;
287     }
288   }
289   /* nnz is now stored in ci[ptm], column indices are in the list of free space */
290   /* Allocate space for cj, initialize cj, and */
291   /* destroy list of free space and other temporary array(s) */
292   ierr = PetscMalloc((ci[pn]+1)*sizeof(int),&cj);CHKERRQ(ierr);
293   ierr = MakeSpaceContiguous(&free_space,cj);CHKERRQ(ierr);
294   ierr = PetscFree(ptadenserow);CHKERRQ(ierr);
295 
296   /* Allocate space for ca */
297   ierr = PetscMalloc((ci[pn]+1)*sizeof(MatScalar),&ca);CHKERRQ(ierr);
298   ierr = PetscMemzero(ca,(ci[pn]+1)*sizeof(MatScalar));CHKERRQ(ierr);
299 
300   /* put together the new matrix */
301   ierr = MatCreateSeqAIJWithArrays(A->comm,pn,pn,ci,cj,ca,C);CHKERRQ(ierr);
302 
303   /* MatCreateSeqAIJWithArrays flags matrix so PETSc doesn't free the user's arrays. */
304   /* Since these are PETSc arrays, change flags to free them as necessary. */
305   c = (Mat_SeqAIJ *)((*C)->data);
306   c->freedata = PETSC_TRUE;
307   c->nonew    = 0;
308 
309   /* Clean up. */
310   ierr = MatRestoreSymbolicTranspose_SeqAIJ(P,&pti,&ptj);CHKERRQ(ierr);
311 
312   ierr = PetscLogEventEnd(MAT_PtAPSymbolic,A,PP,0,0);CHKERRQ(ierr);
313   PetscFunctionReturn(0);
314 }
315 EXTERN_C_END
316 
317 #undef __FUNCT__
318 #define __FUNCT__ "MatPtAPNumeric"
319 /*@
320    MatPtAPNumeric - Computes the matrix projection C = P^T * A * P
321 
322    Collective on Mat
323 
324    Input Parameters:
325 +  A - the matrix
326 -  P - the projection matrix
327 
328    Output Parameters:
329 .  C - the product matrix
330 
331    Notes:
332    C must have been created by calling MatPtAPSymbolic and must be destroyed by
333    the user using MatDeatroy().
334 
335    This routine is currently only implemented for pairs of SeqAIJ matrices and classes
336    which inherit from SeqAIJ.  C will be of type MATSEQAIJ.
337 
338    Level: intermediate
339 
340 .seealso: MatPtAP(),MatPtAPSymbolic(),MatMatMultNumeric()
341 @*/
342 int MatPtAPNumeric(Mat A,Mat P,Mat C) {
343   int ierr;
344   char funct[80];
345   int (*f)(Mat,Mat,Mat);
346 
347   PetscFunctionBegin;
348 
349   PetscValidHeaderSpecific(A,MAT_COOKIE,1);
350   PetscValidType(A,1);
351   MatPreallocated(A);
352   if (!A->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
353   if (A->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
354 
355   PetscValidHeaderSpecific(P,MAT_COOKIE,2);
356   PetscValidType(P,2);
357   MatPreallocated(P);
358   if (!P->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
359   if (P->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
360 
361   PetscValidHeaderSpecific(C,MAT_COOKIE,3);
362   PetscValidType(C,3);
363   MatPreallocated(C);
364   if (!C->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
365   if (C->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
366 
367   if (P->N!=C->M) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",P->N,C->M);
368   if (P->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",P->M,A->N);
369   if (A->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix 'A' must be square, %d != %d",A->M,A->N);
370   if (P->N!=C->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",P->N,C->N);
371 
372   /* Currently only _seqaij_seqaij is implemented, so just query for it. */
373   /* When other implementations exist, attack the multiple dispatch problem. */
374   ierr = PetscStrcpy(funct,"MatPtAPNumeric_seqaij_seqaij");CHKERRQ(ierr);
375   ierr = PetscObjectQueryFunction((PetscObject)P,funct,(PetscVoidFunction)&f);CHKERRQ(ierr);
376   if (!f) SETERRQ1(PETSC_ERR_SUP,"MatPtAPNumeric is not supported for P of type %s",P->type_name);
377   ierr = PetscObjectQueryFunction((PetscObject)A,funct,(PetscVoidFunction)&f);CHKERRQ(ierr);
378   if (!f) SETERRQ1(PETSC_ERR_SUP,"MatPtAPNumeric is not supported for A of type %s",A->type_name);
379 
380   ierr = (*f)(A,P,C);CHKERRQ(ierr);
381 
382   PetscFunctionReturn(0);
383 }
384 
385 #undef __FUNCT__
386 #define __FUNCT__ "MatPtAPNumeric_SeqAIJ_SeqAIJ"
387 int MatPtAPNumeric_SeqAIJ_SeqAIJ(Mat A,Mat P,Mat C) {
388   int        ierr,flops=0;
389   Mat_SeqAIJ *a  = (Mat_SeqAIJ *) A->data;
390   Mat_SeqAIJ *p  = (Mat_SeqAIJ *) P->data;
391   Mat_SeqAIJ *c  = (Mat_SeqAIJ *) C->data;
392   int        *ai=a->i,*aj=a->j,*apjdense,*pi=p->i,*pj=p->j,*pJ=p->j,*pjj;
393   int        *ci=c->i,*cj=c->j,*cjj;
394   int        am=A->M,cn=C->N,cm=C->M;
395   int        i,j,k,anzi,pnzi,apnzj,nextap,pnzj,prow,crow;
396   MatScalar  *aa=a->a,*apa,*pa=p->a,*pA=p->a,*paj,*ca=c->a,*caj;
397   Mat_PtAPstruct *ptap=(Mat_PtAPstruct*)C->spptr;
398   Mat_SeqAIJ     *ap = (Mat_SeqAIJ *)(ptap->symAP)->data;
399   int            *api=ap->i,*apj=ap->j,apj_nextap;
400 
401   PetscFunctionBegin;
402   /* Allocate temporary array for storage of one row of A*P */
403   ierr = PetscMalloc(cn*sizeof(MatScalar),&apa);CHKERRQ(ierr);
404   ierr = PetscMemzero(apa,cn*sizeof(MatScalar));CHKERRQ(ierr);
405 
406   /* Clear old values in C */
407   ierr = PetscMemzero(ca,ci[cm]*sizeof(MatScalar));CHKERRQ(ierr);
408 
409   for (i=0;i<am;i++) {
410     /* Get sparse values of A*P[i,:] */
411     anzi  = ai[i+1] - ai[i];
412     apnzj = 0;
413     for (j=0;j<anzi;j++) {
414       prow = *aj++;
415       pnzj = pi[prow+1] - pi[prow];
416       pjj  = pj + pi[prow];
417       paj  = pa + pi[prow];
418       for (k=0;k<pnzj;k++) {
419         apa[pjj[k]] += (*aa)*paj[k];
420       }
421       flops += 2*pnzj;
422       aa++;
423     }
424 
425     /* Compute P^T*A*P using outer product (P^T)[:,j]*(A*P)[j,:]. */
426     apj   = ap->j + api[i];
427     apnzj = api[i+1] - api[i];
428     pnzi  = pi[i+1] - pi[i];
429     for (j=0;j<pnzi;j++) {
430       nextap = 0;
431       crow   = *pJ++;
432       cjj    = cj + ci[crow];
433       caj    = ca + ci[crow];
434       /* Perform sparse axpy operation.  Note cjj includes apj. */
435       for (k=0; nextap<apnzj; k++) {
436         apj_nextap = *(apj+nextap);
437         if (cjj[k]==apj_nextap) {
438           caj[k] += (*pA)*apa[apj_nextap];
439           nextap++;
440         }
441       }
442       flops += 2*apnzj;
443       pA++;
444     }
445 
446     /* Zero the current row values for A*P */
447     for (j=0;j<apnzj;j++) apa[apj[j]] = 0.0;
448   }
449 
450   /* Assemble the final matrix and clean up */
451   ierr = MatAssemblyBegin(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
452   ierr = MatAssemblyEnd(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
453   ierr = PetscFree(apa);CHKERRQ(ierr);
454   ierr = PetscLogFlops(flops);CHKERRQ(ierr);
455 
456   PetscFunctionReturn(0);
457 }
458 
459 #undef __FUNCT__
460 #define __FUNCT__ "RegisterPtAPRoutines_Private"
461 int RegisterPtAPRoutines_Private(Mat A) {
462   int ierr;
463 
464   PetscFunctionBegin;
465   if (!MAT_PtAPSymbolic) {
466     ierr = PetscLogEventRegister(&MAT_PtAPSymbolic,"MatPtAPSymbolic",MAT_COOKIE);CHKERRQ(ierr);
467   }
468   if (!MAT_PtAPNumeric) {
469     ierr = PetscLogEventRegister(&MAT_PtAPNumeric,"MatPtAPNumeric",MAT_COOKIE);CHKERRQ(ierr);
470   }
471   ierr = RegisterMatMatMultRoutines_Private(A);CHKERRQ(ierr);
472 
473   PetscFunctionReturn(0);
474 }
475