xref: /petsc/src/mat/impls/aij/seq/matmatmult.c (revision c8a8475e04bcaa43590892a5c3e60c6f87bc31f7)
1 /*$Id: matmatmult.c,v 1.15 2001/09/07 20:04:44 buschelm Exp $*/
2 /*
3   Defines matrix-matrix product routines for pairs of SeqAIJ matrices
4           C = A * B
5           C = P * A * P^T
6 */
7 
8 #include "src/mat/impls/aij/seq/aij.h"
9 #include "src/mat/utils/freespace.h"
10 
11 static int logkey_matmatmult            = 0;
12 static int logkey_matmatmult_symbolic   = 0;
13 static int logkey_matmatmult_numeric    = 0;
14 
15 static int logkey_matapplypapt          = 0;
16 static int logkey_matapplypapt_symbolic = 0;
17 static int logkey_matapplypapt_numeric  = 0;
18 
19 /*
20      MatMatMult_Symbolic_SeqAIJ_SeqAIJ - Forms the symbolic product of two SeqAIJ matrices
21            C = A * B;
22 
23      Note: C is assumed to be uncreated.
24            If this is not the case, Destroy C before calling this routine.
25 */
26 #undef __FUNCT__
27 #define __FUNCT__ "MatMatMult_Symbolic_SeqAIJ_SeqAIJ"
28 int MatMatMult_Symbolic_SeqAIJ_SeqAIJ(Mat A,Mat B,Mat *C)
29 {
30   int            ierr;
31   FreeSpaceList  free_space=PETSC_NULL,current_space=PETSC_NULL;
32   Mat_SeqAIJ     *a=(Mat_SeqAIJ*)A->data,*b=(Mat_SeqAIJ*)B->data,*c;
33   int            aishift=a->indexshift,bishift=b->indexshift;
34   int            *ai=a->i,*aj=a->j,*bi=b->i,*bj=b->j,*bjj;
35   int            *ci,*cj,*denserow,*sparserow;
36   int            an=A->N,am=A->M,bn=B->N,bm=B->M;
37   int            i,j,k,anzi,brow,bnzj,cnzi;
38   MatScalar      *ca;
39 
40   PetscFunctionBegin;
41   /* some error checking which could be moved into interface layer */
42   if (aishift || bishift) SETERRQ(PETSC_ERR_SUP,"Shifted matrix indices are not supported.");
43   if (an!=bm) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",an,bm);
44 
45   /* Set up timers */
46   if (!logkey_matmatmult_symbolic) {
47     ierr = PetscLogEventRegister(&logkey_matmatmult_symbolic,"MatMatMult_Symbolic",MAT_COOKIE);CHKERRQ(ierr);
48   }
49   ierr = PetscLogEventBegin(logkey_matmatmult_symbolic,A,B,0,0);CHKERRQ(ierr);
50 
51   /* Set up */
52   /* Allocate ci array, arrays for fill computation and */
53   /* free space for accumulating nonzero column info */
54   ierr = PetscMalloc(((am+1)+1)*sizeof(int),&ci);CHKERRQ(ierr);
55   ci[0] = 0;
56 
57   ierr = PetscMalloc((2*bn+1)*sizeof(int),&denserow);CHKERRQ(ierr);
58   ierr = PetscMemzero(denserow,(2*bn+1)*sizeof(int));CHKERRQ(ierr);
59   sparserow = denserow + bn;
60 
61   /* Initial FreeSpace size is nnz(B)=bi[bm] */
62   ierr          = GetMoreSpace(bi[bm],&free_space);CHKERRQ(ierr);
63   current_space = free_space;
64 
65   /* Determine symbolic info for each row of the product: */
66   for (i=0;i<am;i++) {
67     anzi = ai[i+1] - ai[i];
68     cnzi = 0;
69     for (j=0;j<anzi;j++) {
70       brow = *aj++;
71       bnzj = bi[brow+1] - bi[brow];
72       bjj  = bj + bi[brow];
73       for (k=0;k<bnzj;k++) {
74         /* If column is not marked, mark it in compressed and uncompressed locations. */
75         /* For simplicity, leave uncompressed row unsorted until finished with row, */
76         /* and increment nonzero count for this row. */
77         if (!denserow[bjj[k]]) {
78           denserow[bjj[k]]  = -1;
79           sparserow[cnzi++] = bjj[k];
80         }
81       }
82     }
83 
84     /* sort sparserow */
85     ierr = PetscSortInt(cnzi,sparserow);CHKERRQ(ierr);
86 
87     /* If free space is not available, make more free space */
88     /* Double the amount of total space in the list */
89     if (current_space->local_remaining<cnzi) {
90       ierr = GetMoreSpace(current_space->total_array_size,&current_space);CHKERRQ(ierr);
91     }
92 
93     /* Copy data into free space, and zero out denserow */
94     ierr = PetscMemcpy(current_space->array,sparserow,cnzi*sizeof(int));CHKERRQ(ierr);
95     current_space->array           += cnzi;
96     current_space->local_used      += cnzi;
97     current_space->local_remaining -= cnzi;
98     for (j=0;j<cnzi;j++) {
99       denserow[sparserow[j]] = 0;
100     }
101     ci[i+1] = ci[i] + cnzi;
102   }
103 
104   /* Column indices are in the list of free space */
105   /* Allocate space for cj, initialize cj, and */
106   /* destroy list of free space and other temporary array(s) */
107   ierr = PetscMalloc((ci[am]+1)*sizeof(int),&cj);CHKERRQ(ierr);
108   ierr = MakeSpaceContiguous(&free_space,cj);CHKERRQ(ierr);
109   ierr = PetscFree(denserow);CHKERRQ(ierr);
110 
111   /* Allocate space for ca */
112   ierr = PetscMalloc((ci[am]+1)*sizeof(MatScalar),&ca);CHKERRQ(ierr);
113   ierr = PetscMemzero(ca,(ci[am]+1)*sizeof(MatScalar));CHKERRQ(ierr);
114 
115   /* put together the new matrix */
116   ierr = MatCreateSeqAIJWithArrays(A->comm,am,bn,ci,cj,ca,C);CHKERRQ(ierr);
117 
118   /* MatCreateSeqAIJWithArrays flags matrix so PETSc doesn't free the user's arrays. */
119   /* These are PETSc arrays, so change flags so arrays can be deleted by PETSc */
120   c = (Mat_SeqAIJ *)((*C)->data);
121   c->freedata = PETSC_TRUE;
122   c->nonew    = 0;
123 
124   ierr = PetscLogEventEnd(logkey_matmatmult_symbolic,A,B,0,0);CHKERRQ(ierr);
125   PetscFunctionReturn(0);
126 }
127 
128 /*
129      MatMatMult_Numeric_SeqAIJ_SeqAIJ - Forms the numeric product of two SeqAIJ matrices
130            C=A*B;
131      Note: C must have been created by calling MatMatMult_Symbolic_SeqAIJ_SeqAIJ.
132 */
133 #undef __FUNCT__
134 #define __FUNCT__ "MatMatMult_Numeric_SeqAIJ_SeqAIJ"
135 int MatMatMult_Numeric_SeqAIJ_SeqAIJ(Mat A,Mat B,Mat C)
136 {
137   int        ierr,flops=0;
138   Mat_SeqAIJ *a = (Mat_SeqAIJ *)A->data;
139   Mat_SeqAIJ *b = (Mat_SeqAIJ *)B->data;
140   Mat_SeqAIJ *c = (Mat_SeqAIJ *)C->data;
141   int        aishift=a->indexshift,bishift=b->indexshift,cishift=c->indexshift;
142   int        *ai=a->i,*aj=a->j,*bi=b->i,*bj=b->j,*bjj,*ci=c->i,*cj=c->j;
143   int        an=A->N,am=A->M,bn=B->N,bm=B->M,cn=C->N,cm=C->M;
144   int        i,j,k,anzi,bnzi,cnzi,brow;
145   MatScalar  *aa=a->a,*ba=b->a,*baj,*ca=c->a,*temp;
146 
147   PetscFunctionBegin;
148 
149   /* This error checking should be unnecessary if the symbolic was performed */
150   if (aishift || bishift || cishift) SETERRQ(PETSC_ERR_SUP,"Shifted matrix indices are not supported.");
151   if (am!=cm) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",am,cm);
152   if (an!=bm) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",an,bm);
153   if (bn!=cn) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",bn,cn);
154 
155   /* Set up timers */
156   if (!logkey_matmatmult_numeric) {
157     ierr = PetscLogEventRegister(&logkey_matmatmult_numeric,"MatMatMult_Numeric",MAT_COOKIE);CHKERRQ(ierr);
158   }
159   ierr = PetscLogEventBegin(logkey_matmatmult_numeric,A,B,C,0);CHKERRQ(ierr);
160 
161   /* Allocate temp accumulation space to avoid searching for nonzero columns in C */
162   ierr = PetscMalloc((cn+1)*sizeof(MatScalar),&temp);CHKERRQ(ierr);
163   ierr = PetscMemzero(temp,cn*sizeof(MatScalar));CHKERRQ(ierr);
164   /* Traverse A row-wise. */
165   /* Build the ith row in C by summing over nonzero columns in A, */
166   /* the rows of B corresponding to nonzeros of A. */
167   for (i=0;i<am;i++) {
168     anzi = ai[i+1] - ai[i];
169     for (j=0;j<anzi;j++) {
170       brow = *aj++;
171       bnzi = bi[brow+1] - bi[brow];
172       bjj  = bj + bi[brow];
173       baj  = ba + bi[brow];
174       for (k=0;k<bnzi;k++) {
175         temp[bjj[k]] += (*aa)*baj[k];
176       }
177       flops += 2*bnzi;
178       aa++;
179     }
180     /* Store row back into C, and re-zero temp */
181     cnzi = ci[i+1] - ci[i];
182     for (j=0;j<cnzi;j++) {
183       ca[j] = temp[cj[j]];
184       temp[cj[j]] = 0.0;
185     }
186     ca += cnzi;
187     cj += cnzi;
188   }
189   ierr = MatAssemblyBegin(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
190   ierr = MatAssemblyEnd(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
191 
192   /* Free temp */
193   ierr = PetscFree(temp);CHKERRQ(ierr);
194   ierr = PetscLogFlops(flops);CHKERRQ(ierr);
195   ierr = PetscLogEventEnd(logkey_matmatmult_numeric,A,B,C,0);CHKERRQ(ierr);
196   PetscFunctionReturn(0);
197 }
198 
199 #undef __FUNCT__
200 #define __FUNCT__ "MatMatMult_SeqAIJ_SeqAIJ"
201 int MatMatMult_SeqAIJ_SeqAIJ(Mat A,Mat B,Mat *C) {
202   int ierr;
203 
204   PetscFunctionBegin;
205   if (!logkey_matmatmult) {
206     ierr = PetscLogEventRegister(&logkey_matmatmult,"MatMatMult",MAT_COOKIE);CHKERRQ(ierr);
207   }
208   ierr = PetscLogEventBegin(logkey_matmatmult,A,B,0,0);CHKERRQ(ierr);
209   ierr = MatMatMult_Symbolic_SeqAIJ_SeqAIJ(A,B,C);CHKERRQ(ierr);
210   ierr = MatMatMult_Numeric_SeqAIJ_SeqAIJ(A,B,*C);CHKERRQ(ierr);
211   ierr = PetscLogEventEnd(logkey_matmatmult,A,B,0,0);CHKERRQ(ierr);
212   PetscFunctionReturn(0);
213 }
214 
215 
216 /*
217      MatApplyPAPt_Symbolic_SeqAIJ_SeqAIJ - Forms the symbolic product of two SeqAIJ matrices
218            C = P * A * P^T;
219 
220      Note: C is assumed to be uncreated.
221            If this is not the case, Destroy C before calling this routine.
222 */
223 #undef __FUNCT__
224 #define __FUNCT__ "MatApplyPAPt_Symbolic_SeqAIJ_SeqAIJ"
225 int MatApplyPAPt_Symbolic_SeqAIJ_SeqAIJ(Mat A,Mat P,Mat *C) {
226   /* Note: This code is virtually identical to that of MatApplyPtAP_SeqAIJ_Symbolic */
227   /*        and MatMatMult_SeqAIJ_SeqAIJ_Symbolic.  Perhaps they could be merged nicely. */
228   int            ierr;
229   FreeSpaceList  free_space=PETSC_NULL,current_space=PETSC_NULL;
230   Mat_SeqAIJ     *a=(Mat_SeqAIJ*)A->data,*p=(Mat_SeqAIJ*)P->data,*c;
231   int            aishift=a->indexshift,pishift=p->indexshift;
232   int            *ai=a->i,*aj=a->j,*ajj,*pi=p->i,*pj=p->j,*pti,*ptj,*ptjj;
233   int            *ci,*cj,*paj,*padenserow,*pasparserow,*denserow,*sparserow;
234   int            an=A->N,am=A->M,pn=P->N,pm=P->M;
235   int            i,j,k,pnzi,arow,anzj,panzi,ptrow,ptnzj,cnzi;
236   MatScalar      *ca;
237 
238   PetscFunctionBegin;
239 
240   /* some error checking which could be moved into interface layer */
241   if (aishift || pishift) SETERRQ(PETSC_ERR_SUP,"Shifted matrix indices are not supported.");
242   if (pn!=am) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",pn,am);
243   if (am!=an) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix 'A' must be square, %d != %d",am, an);
244 
245   /* Set up timers */
246   if (!logkey_matapplypapt_symbolic) {
247     ierr = PetscLogEventRegister(&logkey_matapplypapt_symbolic,"MatApplyPAPt_Symbolic",MAT_COOKIE);CHKERRQ(ierr);
248   }
249   ierr = PetscLogEventBegin(logkey_matapplypapt_symbolic,A,P,0,0);CHKERRQ(ierr);
250 
251   /* Create ij structure of P^T */
252   ierr = MatGetSymbolicTranspose_SeqAIJ(P,&pti,&ptj);CHKERRQ(ierr);
253 
254   /* Allocate ci array, arrays for fill computation and */
255   /* free space for accumulating nonzero column info */
256   ierr = PetscMalloc(((pm+1)*1)*sizeof(int),&ci);CHKERRQ(ierr);
257   ci[0] = 0;
258 
259   ierr = PetscMalloc((2*an+2*pm+1)*sizeof(int),&padenserow);CHKERRQ(ierr);
260   ierr = PetscMemzero(padenserow,(2*an+2*pm+1)*sizeof(int));CHKERRQ(ierr);
261   pasparserow  = padenserow  + an;
262   denserow     = pasparserow + an;
263   sparserow    = denserow    + pm;
264 
265   /* Set initial free space to be nnz(A) scaled by aspect ratio of Pt. */
266   /* This should be reasonable if sparsity of PAPt is similar to that of A. */
267   ierr          = GetMoreSpace((ai[am]/pn)*pm,&free_space);
268   current_space = free_space;
269 
270   /* Determine fill for each row of C: */
271   for (i=0;i<pm;i++) {
272     pnzi  = pi[i+1] - pi[i];
273     panzi = 0;
274     /* Get symbolic sparse row of PA: */
275     for (j=0;j<pnzi;j++) {
276       arow = *pj++;
277       anzj = ai[arow+1] - ai[arow];
278       ajj  = aj + ai[arow];
279       for (k=0;k<anzj;k++) {
280         if (!padenserow[ajj[k]]) {
281           padenserow[ajj[k]]   = -1;
282           pasparserow[panzi++] = ajj[k];
283         }
284       }
285     }
286     /* Using symbolic row of PA, determine symbolic row of C: */
287     paj    = pasparserow;
288     cnzi   = 0;
289     for (j=0;j<panzi;j++) {
290       ptrow = *paj++;
291       ptnzj = pti[ptrow+1] - pti[ptrow];
292       ptjj  = ptj + pti[ptrow];
293       for (k=0;k<ptnzj;k++) {
294         if (!denserow[ptjj[k]]) {
295           denserow[ptjj[k]] = -1;
296           sparserow[cnzi++] = ptjj[k];
297         }
298       }
299     }
300 
301     /* sort sparse representation */
302     ierr = PetscSortInt(cnzi,sparserow);CHKERRQ(ierr);
303 
304     /* If free space is not available, make more free space */
305     /* Double the amount of total space in the list */
306     if (current_space->local_remaining<cnzi) {
307       ierr = GetMoreSpace(current_space->total_array_size,&current_space);CHKERRQ(ierr);
308     }
309 
310     /* Copy data into free space, and zero out dense row */
311     ierr = PetscMemcpy(current_space->array,sparserow,cnzi*sizeof(int));CHKERRQ(ierr);
312     current_space->array           += cnzi;
313     current_space->local_used      += cnzi;
314     current_space->local_remaining -= cnzi;
315 
316     for (j=0;j<panzi;j++) {
317       padenserow[pasparserow[j]] = 0;
318     }
319     for (j=0;j<cnzi;j++) {
320       denserow[sparserow[j]] = 0;
321     }
322     ci[i+1] = ci[i] + cnzi;
323   }
324   /* column indices are in the list of free space */
325   /* Allocate space for cj, initialize cj, and */
326   /* destroy list of free space and other temporary array(s) */
327   ierr = PetscMalloc((ci[pm]+1)*sizeof(int),&cj);CHKERRQ(ierr);
328   ierr = MakeSpaceContiguous(&free_space,cj);CHKERRQ(ierr);
329   ierr = PetscFree(padenserow);CHKERRQ(ierr);
330 
331   /* Allocate space for ca */
332   ierr = PetscMalloc((ci[pm]+1)*sizeof(MatScalar),&ca);CHKERRQ(ierr);
333   ierr = PetscMemzero(ca,(ci[pm]+1)*sizeof(MatScalar));CHKERRQ(ierr);
334 
335   /* put together the new matrix */
336   ierr = MatCreateSeqAIJWithArrays(A->comm,pm,pm,ci,cj,ca,C);CHKERRQ(ierr);
337 
338   /* MatCreateSeqAIJWithArrays flags matrix so PETSc doesn't free the user's arrays. */
339   /* Since these are PETSc arrays, change flags to free them as necessary. */
340   c = (Mat_SeqAIJ *)((*C)->data);
341   c->freedata = PETSC_TRUE;
342   c->nonew    = 0;
343 
344   /* Clean up. */
345   ierr = MatRestoreSymbolicTranspose_SeqAIJ(P,&pti,&ptj);CHKERRQ(ierr);
346 
347   ierr = PetscLogEventEnd(logkey_matapplypapt_symbolic,A,P,0,0);CHKERRQ(ierr);
348   PetscFunctionReturn(0);
349 }
350 
351 /*
352      MatApplyPAPt_Numeric_SeqAIJ - Forms the numeric product of two SeqAIJ matrices
353            C = P * A * P^T;
354      Note: C must have been created by calling MatApplyPAPt_Symbolic_SeqAIJ.
355 */
356 #undef __FUNCT__
357 #define __FUNCT__ "MatApplyPAPt_Numeric_SeqAIJ_SeqAIJ"
358 int MatApplyPAPt_Numeric_SeqAIJ_SeqAIJ(Mat A,Mat P,Mat C) {
359   int        ierr,flops=0;
360   Mat_SeqAIJ *a  = (Mat_SeqAIJ *) A->data;
361   Mat_SeqAIJ *p  = (Mat_SeqAIJ *) P->data;
362   Mat_SeqAIJ *c  = (Mat_SeqAIJ *) C->data;
363   int        aishift=a->indexshift,pishift=p->indexshift,cishift=c->indexshift;
364   int        *ai=a->i,*aj=a->j,*ajj,*pi=p->i,*pj=p->j,*pjj=p->j,*paj,*pajdense,*ptj;
365   int        *ci=c->i,*cj=c->j;
366   int        an=A->N,am=A->M,pn=P->N,pm=P->M,cn=C->N,cm=C->M;
367   int        i,j,k,k1,k2,pnzi,anzj,panzj,arow,ptcol,ptnzj,cnzi;
368   MatScalar  *aa=a->a,*pa=p->a,*pta=p->a,*ptaj,*paa,*aaj,*ca=c->a,sum;
369 
370   PetscFunctionBegin;
371 
372   /* This error checking should be unnecessary if the symbolic was performed */
373   if (aishift || pishift || cishift) SETERRQ(PETSC_ERR_SUP,"Shifted matrix indices are not supported.");
374   if (pm!=cm) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",pm,cm);
375   if (pn!=am) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",pn,am);
376   if (am!=an) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix 'A' must be square, %d != %d",am, an);
377   if (pm!=cn) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",pm, cn);
378 
379   /* Set up timers */
380   if (!logkey_matapplypapt_numeric) {
381     ierr = PetscLogEventRegister(&logkey_matapplypapt_numeric,"MatApplyPAPt_Numeric",MAT_COOKIE);CHKERRQ(ierr);
382   }
383   ierr = PetscLogEventBegin(logkey_matapplypapt_numeric,A,P,C,0);CHKERRQ(ierr);
384 
385   ierr = PetscMalloc(an*(sizeof(MatScalar)+2*sizeof(int)),&paa);CHKERRQ(ierr);
386   ierr = PetscMemzero(paa,an*(sizeof(MatScalar)+2*sizeof(int)));CHKERRQ(ierr);
387   ierr = PetscMemzero(ca,ci[cm]*sizeof(MatScalar));CHKERRQ(ierr);
388 
389   paj      = (int *)(paa + an);
390   pajdense = paj + an;
391 
392   for (i=0;i<pm;i++) {
393     /* Form sparse row of P*A */
394     pnzi  = pi[i+1] - pi[i];
395     panzj = 0;
396     for (j=0;j<pnzi;j++) {
397       arow = *pj++;
398       anzj = ai[arow+1] - ai[arow];
399       ajj  = aj + ai[arow];
400       aaj  = aa + ai[arow];
401       for (k=0;k<anzj;k++) {
402         if (!pajdense[ajj[k]]) {
403           pajdense[ajj[k]] = -1;
404           paj[panzj++]     = ajj[k];
405         }
406         paa[ajj[k]] += (*pa)*aaj[k];
407       }
408       flops += 2*anzj;
409       pa++;
410     }
411 
412     /* Sort the j index array for quick sparse axpy. */
413     ierr = PetscSortInt(panzj,paj);CHKERRQ(ierr);
414 
415     /* Compute P*A*P^T using sparse inner products. */
416     /* Take advantage of pre-computed (i,j) of C for locations of non-zeros. */
417     cnzi = ci[i+1] - ci[i];
418     for (j=0;j<cnzi;j++) {
419       /* Form sparse inner product of current row of P*A with (*cj++) col of P^T. */
420       ptcol = *cj++;
421       ptnzj = pi[ptcol+1] - pi[ptcol];
422       ptj   = pjj + pi[ptcol];
423       ptaj  = pta + pi[ptcol];
424       sum   = 0.;
425       k1    = 0;
426       k2    = 0;
427       while ((k1<panzj) && (k2<ptnzj)) {
428         if (paj[k1]==ptj[k2]) {
429           sum += paa[paj[k1++]]*ptaj[k2++];
430         } else if (paj[k1] < ptj[k2]) {
431           k1++;
432         } else /* if (paj[k1] > ptj[k2]) */ {
433           k2++;
434         }
435       }
436       *ca++ = sum;
437     }
438 
439     /* Zero the current row info for P*A */
440     for (j=0;j<panzj;j++) {
441       paa[paj[j]]      = 0.;
442       pajdense[paj[j]] = 0;
443     }
444   }
445 
446   ierr = MatAssemblyBegin(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
447   ierr = MatAssemblyEnd(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
448   ierr = PetscLogFlops(flops);CHKERRQ(ierr);
449   ierr = PetscLogEventEnd(logkey_matapplypapt_numeric,A,P,C,0);CHKERRQ(ierr);
450   PetscFunctionReturn(0);
451 }
452 
453 #undef __FUNCT__
454 #define __FUNCT__ "MatApplyPAPt_SeqAIJ_SeqAIJ"
455 int MatApplyPAPt_SeqAIJ_SeqAIJ(Mat A,Mat P,Mat *C) {
456   int ierr;
457 
458   PetscFunctionBegin;
459   if (!logkey_matapplypapt) {
460     ierr = PetscLogEventRegister(&logkey_matapplypapt,"MatApplyPAPt",MAT_COOKIE);CHKERRQ(ierr);
461   }
462   ierr = PetscLogEventBegin(logkey_matapplypapt,A,P,0,0);CHKERRQ(ierr);
463   ierr = MatApplyPAPt_Symbolic_SeqAIJ_SeqAIJ(A,P,C);CHKERRQ(ierr);
464   ierr = MatApplyPAPt_Numeric_SeqAIJ_SeqAIJ(A,P,*C);CHKERRQ(ierr);
465   ierr = PetscLogEventEnd(logkey_matapplypapt,A,P,0,0);CHKERRQ(ierr);
466   PetscFunctionReturn(0);
467 }
468