xref: /petsc/src/mat/impls/aij/seq/matmatmult.c (revision 372db017f0032ab0e5741e4a6f633c752a87ea59)
1 /*
2   Defines matrix-matrix product routines for pairs of AIJ matrices
3           C = A * B
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 typedef struct { /* used by MatMatMult_MPIAIJ_MPIAIJ for reusing symbolic mat product */
11   IS     isrowa,isrowb,iscolb;
12   Mat    *aseq,*bseq,C_seq;
13 } Mat_MatMatMultMPI;
14 
15 #undef __FUNCT__
16 #define __FUNCT__ "MatMatMult"
17 /*@
18    MatMatMult - Performs Matrix-Matrix Multiplication C=A*B.
19 
20    Collective on Mat
21 
22    Input Parameters:
23 +  A - the left matrix
24 .  B - the right matrix
25 .  scall - either MAT_INITIAL_MATRIX or MAT_REUSE_MATRIX
26 -  fill - expected fill as ratio of nnz(C)/(nnz(A) + nnz(B))
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: MatMatMultSymbolic(),MatMatMultNumeric()
40 @*/
41 PetscErrorCode MatMatMult(Mat A,Mat B,MatReuse scall,PetscReal fill,Mat *C)
42 {
43   PetscErrorCode ierr;
44   PetscErrorCode (*fA)(Mat,Mat,MatReuse,PetscReal,Mat*);
45   PetscErrorCode (*fB)(Mat,Mat,MatReuse,PetscReal,Mat*);
46 
47   PetscFunctionBegin;
48   PetscValidHeaderSpecific(A,MAT_COOKIE,1);
49   PetscValidType(A,1);
50   MatPreallocated(A);
51   if (!A->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
52   if (A->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
53   PetscValidHeaderSpecific(B,MAT_COOKIE,2);
54   PetscValidType(B,2);
55   MatPreallocated(B);
56   if (!B->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
57   if (B->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
58   PetscValidPointer(C,3);
59   if (B->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",B->M,A->N);
60 
61   if (fill <=0.0) SETERRQ1(PETSC_ERR_ARG_SIZ,"fill=%g must be > 0.0",fill);
62 
63   /* For now, we do not dispatch based on the type of A and B */
64   /* When implementations like _SeqAIJ_MAIJ exist, attack the multiple dispatch problem. */
65   fA = A->ops->matmult;
66   if (!fA) SETERRQ1(PETSC_ERR_SUP,"MatMatMult not supported for A of type %s",A->type_name);
67   fB = B->ops->matmult;
68   if (!fB) SETERRQ1(PETSC_ERR_SUP,"MatMatMult not supported for B of type %s",B->type_name);
69   if (fB!=fA) SETERRQ2(PETSC_ERR_ARG_INCOMP,"MatMatMult requires A, %s, to be compatible with B, %s",A->type_name,B->type_name);
70 
71   ierr = PetscLogEventBegin(MAT_MatMult,A,B,0,0);CHKERRQ(ierr);
72   ierr = (*A->ops->matmult)(A,B,scall,fill,C);CHKERRQ(ierr);
73   ierr = PetscLogEventEnd(MAT_MatMult,A,B,0,0);CHKERRQ(ierr);
74 
75   PetscFunctionReturn(0);
76 }
77 
78 #undef __FUNCT__
79 #define __FUNCT__ "MatMatMult_MPIAIJ_MPIAIJ"
80 PetscErrorCode MatMatMult_MPIAIJ_MPIAIJ(Mat A,Mat B,MatReuse scall,PetscReal fill, Mat *C)
81 {
82   PetscErrorCode ierr;
83 
84   PetscFunctionBegin;
85   if (scall == MAT_INITIAL_MATRIX){
86     ierr = MatMatMultSymbolic_MPIAIJ_MPIAIJ(A,B,fill,C);CHKERRQ(ierr);/* numeric product is computed as well */
87   } else if (scall == MAT_REUSE_MATRIX){
88     ierr = MatMatMultNumeric_MPIAIJ_MPIAIJ(A,B,*C);CHKERRQ(ierr);
89   } else {
90     SETERRQ1(PETSC_ERR_ARG_WRONG,"Invalid MatReuse %d",scall);
91   }
92   PetscFunctionReturn(0);
93 }
94 
95 #undef __FUNCT__
96 #define __FUNCT__ "MatMatMult_SeqAIJ_SeqAIJ"
97 PetscErrorCode MatMatMult_SeqAIJ_SeqAIJ(Mat A,Mat B,MatReuse scall,PetscReal fill,Mat *C) {
98   PetscErrorCode ierr;
99 
100   PetscFunctionBegin;
101   if (scall == MAT_INITIAL_MATRIX){
102     ierr = MatMatMultSymbolic_SeqAIJ_SeqAIJ(A,B,fill,C);CHKERRQ(ierr);
103   }
104   ierr = MatMatMultNumeric_SeqAIJ_SeqAIJ(A,B,*C);CHKERRQ(ierr);
105   PetscFunctionReturn(0);
106 }
107 
108 #undef __FUNCT__
109 #define __FUNCT__ "MatMatMultSymbolic"
110 /*@
111    MatMatMultSymbolic - Performs construction, preallocation, and computes the ij structure
112    of the matrix-matrix product C=A*B.  Call this routine before calling MatMatMultNumeric().
113 
114    Collective on Mat
115 
116    Input Parameters:
117 +  A - the left matrix
118 .  B - the right matrix
119 -  fill - expected fill as ratio of nnz(C)/(nnz(A) + nnz(B))
120 
121    Output Parameters:
122 .  C - the matrix containing the ij structure of product matrix
123 
124    Notes:
125    C will be created as a MATSEQAIJ matrix and must be destroyed by the user with MatDestroy().
126 
127    This routine is currently only implemented for SeqAIJ matrices and classes which inherit from SeqAIJ.
128 
129    Level: intermediate
130 
131 .seealso: MatMatMult(),MatMatMultNumeric()
132 @*/
133 PetscErrorCode MatMatMultSymbolic(Mat A,Mat B,PetscReal fill,Mat *C) {
134   PetscErrorCode ierr;
135   PetscErrorCode (*Asymbolic)(Mat,Mat,PetscReal,Mat *);
136   PetscErrorCode (*Bsymbolic)(Mat,Mat,PetscReal,Mat *);
137 
138   PetscFunctionBegin;
139   PetscValidHeaderSpecific(A,MAT_COOKIE,1);
140   PetscValidType(A,1);
141   MatPreallocated(A);
142   if (!A->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
143   if (A->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
144 
145   PetscValidHeaderSpecific(B,MAT_COOKIE,2);
146   PetscValidType(B,2);
147   MatPreallocated(B);
148   if (!B->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
149   if (B->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
150   PetscValidPointer(C,3);
151 
152   if (B->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",B->M,A->N);
153   if (fill <=0.0) SETERRQ1(PETSC_ERR_ARG_SIZ,"fill=%g must be > 0.0",fill);
154 
155   /* For now, we do not dispatch based on the type of A and P */
156   /* When implementations like _SeqAIJ_MAIJ exist, attack the multiple dispatch problem. */
157   Asymbolic = A->ops->matmultsymbolic;
158   if (!Asymbolic) SETERRQ1(PETSC_ERR_SUP,"C=A*B not implemented for A of type %s",A->type_name);
159   Bsymbolic = B->ops->matmultsymbolic;
160   if (!Bsymbolic) SETERRQ1(PETSC_ERR_SUP,"C=A*B not implemented for B of type %s",B->type_name);
161   if (Bsymbolic!=Asymbolic) SETERRQ2(PETSC_ERR_ARG_INCOMP,"MatMatMultSymbolic requires A, %s, to be compatible with B, %s",A->type_name,B->type_name);
162 
163   ierr = PetscLogEventBegin(MAT_MatMultSymbolic,A,B,0,0);CHKERRQ(ierr);
164   ierr = (*Asymbolic)(A,B,fill,C);CHKERRQ(ierr);
165   ierr = PetscLogEventEnd(MAT_MatMultSymbolic,A,B,0,0);CHKERRQ(ierr);
166 
167   PetscFunctionReturn(0);
168 }
169 
170 EXTERN PetscErrorCode MatDestroy_MPIAIJ(Mat);
171 #undef __FUNCT__
172 #define __FUNCT__ "MatDestroy_MPIAIJ_MatMatMult"
173 PetscErrorCode MatDestroy_MPIAIJ_MatMatMult(Mat A)
174 {
175   PetscErrorCode ierr;
176   Mat_MatMatMultMPI *mult=(Mat_MatMatMultMPI*)A->spptr;
177 
178   PetscFunctionBegin;
179   ierr = ISDestroy(mult->isrowb);CHKERRQ(ierr);
180   ierr = ISDestroy(mult->iscolb);CHKERRQ(ierr);
181   ierr = ISDestroy(mult->isrowa);CHKERRQ(ierr);
182   ierr = MatDestroyMatrices(1,&mult->aseq);CHKERRQ(ierr);
183   ierr = MatDestroyMatrices(1,&mult->bseq);CHKERRQ(ierr);
184   ierr = MatDestroy(mult->C_seq);CHKERRQ(ierr);
185   ierr = PetscFree(mult);CHKERRQ(ierr);
186 
187   ierr = MatDestroy_MPIAIJ(A);CHKERRQ(ierr);
188 
189   PetscFunctionReturn(0);
190 }
191 
192 #undef __FUNCT__
193 #define __FUNCT__ "MatMatMultSymbolic_MPIAIJ_MPIAIJ"
194 PetscErrorCode MatMatMultSymbolic_MPIAIJ_MPIAIJ(Mat A,Mat B,PetscReal fill,Mat *C)
195 {
196   Mat_MPIAIJ        *a = (Mat_MPIAIJ*)A->data;
197   PetscErrorCode ierr;
198   int               *idx,i,start,end,ncols,imark,nzA,nzB,*cmap;
199   Mat_MatMatMultMPI *mult;
200 
201   PetscFunctionBegin;
202   ierr = PetscNew(Mat_MatMatMultMPI,&mult);CHKERRQ(ierr);
203 
204   /* create a seq matrix B_seq = submatrix of B by taking rows of B that equal to nonzero col of A */
205   start = a->cstart;
206   cmap  = a->garray;
207   nzA   = a->A->n;
208   nzB   = a->B->n;
209   ierr  = PetscMalloc((nzA+nzB)*sizeof(int), &idx);CHKERRQ(ierr);
210   ncols = 0;
211   for (i=0; i<nzB; i++) {
212     if (cmap[i] < start) idx[ncols++] = cmap[i];
213     else break;
214   }
215   imark = i;
216   for (i=0; i<nzA; i++) idx[ncols++] = start + i;
217   for (i=imark; i<nzB; i++) idx[ncols++] = cmap[i];
218   ierr = ISCreateGeneral(PETSC_COMM_SELF,ncols,idx,&mult->isrowb);CHKERRQ(ierr);
219   ierr = PetscFree(idx);CHKERRQ(ierr);
220   ierr = ISCreateStride(PETSC_COMM_SELF,B->N,0,1,&mult->iscolb);CHKERRQ(ierr);
221   ierr = MatGetSubMatrices(B,1,&mult->isrowb,&mult->iscolb,MAT_INITIAL_MATRIX,&mult->bseq);CHKERRQ(ierr)
222 
223   /*  create a seq matrix A_seq = submatrix of A by taking all local rows of A */
224   start = a->rstart; end = a->rend;
225   ierr = ISCreateStride(PETSC_COMM_SELF,end-start,start,1,&mult->isrowa);CHKERRQ(ierr);
226   ierr = MatGetSubMatrices(A,1,&mult->isrowa,&mult->isrowb,MAT_INITIAL_MATRIX,&mult->aseq);CHKERRQ(ierr);
227 
228   /* compute C_seq = A_seq * B_seq */
229   ierr = MatMatMult_SeqAIJ_SeqAIJ(mult->aseq[0],mult->bseq[0],MAT_INITIAL_MATRIX,fill,&mult->C_seq);CHKERRQ(ierr);
230 
231   /* create mpi matrix C by concatinating C_seq */
232   ierr = PetscObjectReference((PetscObject)mult->C_seq);CHKERRQ(ierr); /* prevent C_seq being destroyed by MatMerge() */
233   ierr = MatMerge(A->comm,mult->C_seq,MAT_INITIAL_MATRIX,C);CHKERRQ(ierr);
234 
235   /* attach the supporting struct to C for reuse of symbolic C */
236   (*C)->spptr         = (void*)mult;
237   (*C)->ops->destroy  = MatDestroy_MPIAIJ_MatMatMult;
238 
239   PetscFunctionReturn(0);
240 }
241 
242 #undef __FUNCT__
243 #define __FUNCT__ "MatMatMultSymbolic_SeqAIJ_SeqAIJ"
244 PetscErrorCode MatMatMultSymbolic_SeqAIJ_SeqAIJ(Mat A,Mat B,PetscReal fill,Mat *C)
245 {
246   PetscErrorCode ierr;
247   FreeSpaceList  free_space=PETSC_NULL,current_space=PETSC_NULL;
248   Mat_SeqAIJ     *a=(Mat_SeqAIJ*)A->data,*b=(Mat_SeqAIJ*)B->data,*c;
249   int            *ai=a->i,*aj=a->j,*bi=b->i,*bj=b->j,*bjj;
250   int            *ci,*cj,*lnk;
251   int            am=A->M,bn=B->N,bm=B->M;
252   int            i,j,anzi,brow,bnzj,cnzi,nlnk,lnk_init=-1,nspacedouble=0;
253   MatScalar      *ca;
254 
255   PetscFunctionBegin;
256   /* Set up */
257   /* Allocate ci array, arrays for fill computation and */
258   /* free space for accumulating nonzero column info */
259   ierr = PetscMalloc(((am+1)+1)*sizeof(int),&ci);CHKERRQ(ierr);
260   ci[0] = 0;
261 
262   ierr = PetscMalloc((bn+1)*sizeof(int),&lnk);CHKERRQ(ierr);
263   nlnk = bn+1;
264   ierr = PetscLLInitialize(lnk_init,nlnk,lnk);CHKERRQ(ierr);
265 
266   /* Initial FreeSpace size is fill*(nnz(A)+nnz(B)) */
267   ierr = GetMoreSpace((int)(fill*(ai[am]+bi[bm])),&free_space);CHKERRQ(ierr);
268   current_space = free_space;
269 
270   /* Determine symbolic info for each row of the product: */
271   for (i=0;i<am;i++) {
272     anzi = ai[i+1] - ai[i];
273     cnzi = 0;
274     lnk[bn] = bn;
275     j       = anzi;
276     aj      = a->j + ai[i];
277     while (j){/* assume cols are almost in increasing order, starting from its end saves computation */
278       j--;
279       brow = *(aj + j);
280       bnzj = bi[brow+1] - bi[brow];
281       bjj  = bj + bi[brow];
282       /* add non-zero cols of B into the sorted linked list lnk */
283       ierr = PetscLLAdd(bnzj,bjj,bn,lnk_init,nlnk,lnk);CHKERRQ(ierr);
284       cnzi += nlnk;
285     }
286 
287     /* If free space is not available, make more free space */
288     /* Double the amount of total space in the list */
289     if (current_space->local_remaining<cnzi) {
290       /* printf("MatMatMultSymbolic_SeqAIJ_SeqAIJ()...%d -th row, double space ...\n",i);*/
291       ierr = GetMoreSpace(current_space->total_array_size,&current_space);CHKERRQ(ierr);
292       nspacedouble++;
293     }
294 
295     /* Copy data into free space, then initialize lnk */
296     ierr = PetscLLClear(bn,lnk_init,cnzi,lnk,current_space->array);CHKERRQ(ierr);
297     current_space->array += cnzi;
298 
299     current_space->local_used      += cnzi;
300     current_space->local_remaining -= cnzi;
301 
302     ci[i+1] = ci[i] + cnzi;
303   }
304 
305   /* Column indices are in the list of free space */
306   /* Allocate space for cj, initialize cj, and */
307   /* destroy list of free space and other temporary array(s) */
308   ierr = PetscMalloc((ci[am]+1)*sizeof(int),&cj);CHKERRQ(ierr);
309   ierr = MakeSpaceContiguous(&free_space,cj);CHKERRQ(ierr);
310   ierr = PetscFree(lnk);CHKERRQ(ierr);
311 
312   /* Allocate space for ca */
313   ierr = PetscMalloc((ci[am]+1)*sizeof(MatScalar),&ca);CHKERRQ(ierr);
314   ierr = PetscMemzero(ca,(ci[am]+1)*sizeof(MatScalar));CHKERRQ(ierr);
315 
316   /* put together the new symbolic matrix */
317   ierr = MatCreateSeqAIJWithArrays(A->comm,am,bn,ci,cj,ca,C);CHKERRQ(ierr);
318 
319   /* MatCreateSeqAIJWithArrays flags matrix so PETSc doesn't free the user's arrays. */
320   /* These are PETSc arrays, so change flags so arrays can be deleted by PETSc */
321   c = (Mat_SeqAIJ *)((*C)->data);
322   c->freedata = PETSC_TRUE;
323   c->nonew    = 0;
324 
325   PetscLogInfo((PetscObject)(*C),"Number of calls to GetMoreSpace(): %d\n",nspacedouble);
326   PetscFunctionReturn(0);
327 }
328 
329 #undef __FUNCT__
330 #define __FUNCT__ "MatMatMultNumeric"
331 /*@
332    MatMatMultNumeric - Performs the numeric matrix-matrix product.
333    Call this routine after first calling MatMatMultSymbolic().
334 
335    Collective on Mat
336 
337    Input Parameters:
338 +  A - the left matrix
339 -  B - the right matrix
340 
341    Output Parameters:
342 .  C - the product matrix, whose ij structure was defined from MatMatMultSymbolic().
343 
344    Notes:
345    C must have been created with MatMatMultSymbolic.
346 
347    This routine is currently only implemented for SeqAIJ type matrices.
348 
349    Level: intermediate
350 
351 .seealso: MatMatMult(),MatMatMultSymbolic()
352 @*/
353 PetscErrorCode MatMatMultNumeric(Mat A,Mat B,Mat C){
354   PetscErrorCode ierr;
355   PetscErrorCode (*Anumeric)(Mat,Mat,Mat);
356   PetscErrorCode (*Bnumeric)(Mat,Mat,Mat);
357 
358   PetscFunctionBegin;
359 
360   PetscValidHeaderSpecific(A,MAT_COOKIE,1);
361   PetscValidType(A,1);
362   MatPreallocated(A);
363   if (!A->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
364   if (A->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
365 
366   PetscValidHeaderSpecific(B,MAT_COOKIE,2);
367   PetscValidType(B,2);
368   MatPreallocated(B);
369   if (!B->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
370   if (B->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
371 
372   PetscValidHeaderSpecific(C,MAT_COOKIE,3);
373   PetscValidType(C,3);
374   MatPreallocated(C);
375   if (!C->assembled) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for unassembled matrix");
376   if (C->factor) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
377 
378   if (B->N!=C->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",B->N,C->N);
379   if (B->M!=A->N) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",B->M,A->N);
380   if (A->M!=C->M) SETERRQ2(PETSC_ERR_ARG_SIZ,"Matrix dimensions are incompatible, %d != %d",A->M,C->M);
381 
382   /* For now, we do not dispatch based on the type of A and B */
383   /* When implementations like _SeqAIJ_MAIJ exist, attack the multiple dispatch problem. */
384   Anumeric = A->ops->matmultnumeric;
385   if (!Anumeric) SETERRQ1(PETSC_ERR_SUP,"MatMatMultNumeric not supported for A of type %s",A->type_name);
386   Bnumeric = B->ops->matmultnumeric;
387   if (!Bnumeric) SETERRQ1(PETSC_ERR_SUP,"MatMatMultNumeric not supported for B of type %s",B->type_name);
388   if (Bnumeric!=Anumeric) SETERRQ2(PETSC_ERR_ARG_INCOMP,"MatMatMultNumeric requires A, %s, to be compatible with B, %s",A->type_name,B->type_name);
389 
390   ierr = PetscLogEventBegin(MAT_MatMultNumeric,A,B,0,0);CHKERRQ(ierr);
391   ierr = (*Anumeric)(A,B,C);CHKERRQ(ierr);
392   ierr = PetscLogEventEnd(MAT_MatMultNumeric,A,B,0,0);CHKERRQ(ierr);
393 
394   PetscFunctionReturn(0);
395 }
396 
397 /* This routine is called ONLY in the case of reusing previously computed symbolic C */
398 #undef __FUNCT__
399 #define __FUNCT__ "MatMatMultNumeric_MPIAIJ_MPIAIJ"
400 PetscErrorCode MatMatMultNumeric_MPIAIJ_MPIAIJ(Mat A,Mat B,Mat C)
401 {
402   PetscErrorCode ierr;
403   Mat_MatMatMultMPI *mult=(Mat_MatMatMultMPI*)C->spptr;
404 
405   PetscFunctionBegin;
406   ierr = MatGetSubMatrices(B,1,&mult->isrowb,&mult->iscolb,MAT_REUSE_MATRIX,&mult->bseq);CHKERRQ(ierr)
407   ierr = MatGetSubMatrices(A,1,&mult->isrowa,&mult->isrowb,MAT_REUSE_MATRIX,&mult->aseq);CHKERRQ(ierr);
408   ierr = MatMatMult_SeqAIJ_SeqAIJ(mult->aseq[0],mult->bseq[0],MAT_REUSE_MATRIX,0.0,&mult->C_seq);CHKERRQ(ierr);
409 
410   ierr = PetscObjectReference((PetscObject)mult->C_seq);CHKERRQ(ierr);
411   ierr = MatMerge(A->comm,mult->C_seq,MAT_REUSE_MATRIX,&C);CHKERRQ(ierr);
412 
413   PetscFunctionReturn(0);
414 }
415 
416 #undef __FUNCT__
417 #define __FUNCT__ "MatMatMultNumeric_SeqAIJ_SeqAIJ"
418 PetscErrorCode MatMatMultNumeric_SeqAIJ_SeqAIJ(Mat A,Mat B,Mat C)
419 {
420   PetscErrorCode ierr;
421   int        flops=0;
422   Mat_SeqAIJ *a = (Mat_SeqAIJ *)A->data;
423   Mat_SeqAIJ *b = (Mat_SeqAIJ *)B->data;
424   Mat_SeqAIJ *c = (Mat_SeqAIJ *)C->data;
425   int        *ai=a->i,*aj=a->j,*bi=b->i,*bj=b->j,*bjj,*ci=c->i,*cj=c->j;
426   int        am=A->M,cn=C->N;
427   int        i,j,k,anzi,bnzi,cnzi,brow;
428   MatScalar  *aa=a->a,*ba=b->a,*baj,*ca=c->a,*temp;
429 
430   PetscFunctionBegin;
431 
432   /* Allocate temp accumulation space to avoid searching for nonzero columns in C */
433   ierr = PetscMalloc((cn+1)*sizeof(MatScalar),&temp);CHKERRQ(ierr);
434   ierr = PetscMemzero(temp,cn*sizeof(MatScalar));CHKERRQ(ierr);
435   /* Traverse A row-wise. */
436   /* Build the ith row in C by summing over nonzero columns in A, */
437   /* the rows of B corresponding to nonzeros of A. */
438   for (i=0;i<am;i++) {
439     anzi = ai[i+1] - ai[i];
440     for (j=0;j<anzi;j++) {
441       brow = *aj++;
442       bnzi = bi[brow+1] - bi[brow];
443       bjj  = bj + bi[brow];
444       baj  = ba + bi[brow];
445       for (k=0;k<bnzi;k++) {
446         temp[bjj[k]] += (*aa)*baj[k];
447       }
448       flops += 2*bnzi;
449       aa++;
450     }
451     /* Store row back into C, and re-zero temp */
452     cnzi = ci[i+1] - ci[i];
453     for (j=0;j<cnzi;j++) {
454       ca[j] = temp[cj[j]];
455       temp[cj[j]] = 0.0;
456     }
457     ca += cnzi;
458     cj += cnzi;
459   }
460   ierr = MatAssemblyBegin(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
461   ierr = MatAssemblyEnd(C,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
462 
463   /* Free temp */
464   ierr = PetscFree(temp);CHKERRQ(ierr);
465   ierr = PetscLogFlops(flops);CHKERRQ(ierr);
466   PetscFunctionReturn(0);
467 }
468