xref: /petsc/src/mat/impls/aij/seq/bas/spbas_cholesky.h (revision b2f1ab58077089250fe93bab20362d49e466d970)
1 
2 /*
3    spbas_cholesky_row_alloc:
4       in the data arrays, find a place where another row may be stored.
5       Return PETSC_ERR_MEM if there is insufficient space to store the
6       row, so garbage collection and/or re-allocation may be done.
7 */
8 #undef __FUNCT__
9 #define __FUNCT__ "spbas_cholesky_row_alloc"
10 PetscErrorCode spbas_cholesky_row_alloc(spbas_matrix retval, PetscInt k, PetscInt r_nnz,PetscInt * n_alloc_used)
11 {
12    PetscFunctionBegin;
13    retval.icols[k]  = &retval.alloc_icol[*n_alloc_used];
14    retval.values[k] = &retval.alloc_val[*n_alloc_used];
15    *n_alloc_used   += r_nnz;
16    if (*n_alloc_used > retval.n_alloc_icol)
17    {
18       PetscFunctionReturn(PETSC_ERR_MEM);
19    }
20    else
21    {
22       PetscFunctionReturn(0);
23    }
24 }
25 
26 
27 /*
28   spbas_cholesky_garbage_collect:
29      move the rows which have been calculated so far, as well as
30      those currently under construction, to the front of the
31      array, while putting them in the proper order.
32      When it seems necessary, enlarge the current arrays.
33 
34      NB: re-allocation is being simulated using
35          PetscMalloc, memcpy, PetscFree, because
36          PetscRealloc does not seem to exist.
37 
38 */
39 #undef __FUNCT__
40 #define __FUNCT__ "spbas_cholesky_garbage_collect"
41 PetscErrorCode spbas_cholesky_garbage_collect(
42       spbas_matrix * result, // I/O: the Cholesky factor matrix being constructed.
43                              //      Only the storage, not the contents of this matrix
44                              //      is changed in this function
45       PetscInt i_row,             // I  : Number of rows for which the final contents are
46                              //      known
47       PetscInt *n_row_alloc_ok,   // I/O: Number of rows which are already in their final
48                              //      places in the arrays: they need not be moved any more
49       PetscInt *n_alloc_used,     // I/O:
50       PetscInt *max_row_nnz       // I  : Over-estimate of the number of nonzeros needed to
51                              //      store each row
52      )
53 {
54 
55 #undef garbage_debug
56 
57    // PSEUDO-CODE:
58    //  1. Choose the appropriate size for the arrays
59    //  2. Rescue the arrays which would be lost during garbage collection
60    //  3. Reallocate and correct administration
61    //  4. Move all arrays so that they are in proper order
62 
63    PetscInt i,j;
64    PetscInt nrows = result->nrows;
65    PetscInt n_alloc_ok=0;
66    PetscInt n_alloc_ok_max=0;
67 
68    PetscErrorCode ierr;
69    PetscInt need_already= 0;
70    PetscInt n_rows_ahead=0;
71    PetscInt max_need_extra= 0;
72    PetscInt n_alloc_max, n_alloc_est, n_alloc;
73    PetscInt n_alloc_now = result->n_alloc_icol;
74    PetscInt *alloc_icol_old = result->alloc_icol;
75    PetscScalar *alloc_val_old  = result->alloc_val;
76    PetscInt *icol_rescue;
77    PetscScalar *val_rescue;
78    PetscInt n_rescue;
79    PetscInt n_row_rescue;
80    PetscInt i_here, i_last, n_copy;
81    const PetscScalar xtra_perc = 20;
82 
83    PetscFunctionBegin;
84 
85    //*********************************************************
86    // 1. Choose appropriate array size
87    // Count number of rows and memory usage which is already final
88    for (i=0;i<i_row; i++)
89    {
90       n_alloc_ok += result->row_nnz[i];
91       n_alloc_ok_max += max_row_nnz[i];
92    }
93 
94    // Count currently needed memory usage and future memory requirements
95    // (max, predicted)
96    for (i=i_row; i<nrows; i++)
97    {
98       if (result->row_nnz[i]==0)
99       {
100          max_need_extra  += max_row_nnz[i];
101       }
102       else
103       {
104          need_already    += max_row_nnz[i];
105          n_rows_ahead++;
106       }
107    }
108 
109    // Make maximal and realistic memory requirement estimates
110    n_alloc_max = n_alloc_ok + need_already + max_need_extra;
111    n_alloc_est = n_alloc_ok + need_already + (int)
112             ((PetscScalar) max_need_extra *
113                           (PetscScalar) n_alloc_ok /
114                           (PetscScalar) n_alloc_ok_max);
115 
116    // Choose array sizes
117    if (n_alloc_max == n_alloc_est)
118       { n_alloc = n_alloc_max; }
119    else if (n_alloc_now >= n_alloc_est)
120       { n_alloc = n_alloc_now; }
121    else if (n_alloc_max < n_alloc_est * (1+xtra_perc/100.0))
122       { n_alloc  = n_alloc_max;  }
123    else
124       { n_alloc = (int) (n_alloc_est * (1+xtra_perc/100.0)); }
125 
126    // If new estimate is less than what we already have,
127    //   don't reallocate, just garbage-collect
128    if (n_alloc_max != n_alloc_est && n_alloc < result->n_alloc_icol)
129    {
130       n_alloc = result->n_alloc_icol;
131    }
132 
133 #ifdef garbage_debug
134    // Print allocation considerations
135    printf(" + Final version of rows 1..%d is available\n",i_row);
136    printf("   They have %d nonzeros: %6.2f per row\n",n_alloc_ok,
137                   (PetscScalar) n_alloc_ok / (PetscScalar)i_row);
138    printf("   Without droptol, I would have had %d nonzeros\n",
139               n_alloc_ok_max);
140    printf("   So I use %6.2f%% of the allowed sparseness\n",
141                   100.0 * (PetscScalar) n_alloc_ok /
142                           (PetscScalar) n_alloc_ok_max);
143    printf(" + I have %d rows for which I need full sparseness pattern\n",
144                   n_rows_ahead);
145    printf("   They require %d nonzeros\n",
146                need_already);
147    printf(" + After that, I shall need %d more rows\n",
148               nrows-n_rows_ahead-i_row);
149    printf("   Maximally, I will need %d nonzeros there\n",
150                        max_need_extra);
151    printf("   Realistically, I expect to need %6.2f nonzeros there\n",
152                (PetscScalar) max_need_extra *
153                           (PetscScalar) n_alloc_ok /
154                           (PetscScalar) n_alloc_ok_max);
155    printf(" + New allocated arrays should be %d (max) or %d (realistic) long\n",
156               n_alloc_max, n_alloc_est);
157 
158 #endif
159    // Motivate dimension choice
160    printf("   Allocating %d nonzeros: ",n_alloc);
161    if (n_alloc_max == n_alloc_est)
162       { printf("this is the correct size\n"); }
163    else if (n_alloc_now >= n_alloc_est)
164       { printf("the current size, which seems enough\n"); }
165    else if (n_alloc_max < n_alloc_est * (1+xtra_perc/100.0))
166       { printf("the maximum estimate\n"); }
167    else
168       { printf("%6.2f %% more than the estimate\n",xtra_perc); }
169 
170 
171    //*********************************************************
172    // 2. Rescue arrays which would be lost
173    // Count how many rows and nonzeros will have to be rescued
174    // when moving all arrays in place
175    n_row_rescue = 0; n_rescue = 0;
176    if (*n_row_alloc_ok==0) { *n_alloc_used = 0; }
177    else
178    {
179       i = *n_row_alloc_ok - 1;
180       *n_alloc_used = (result->icols[i]-result->alloc_icol) +
181                        result->row_nnz[i];
182    }
183 
184 #ifdef garbage_debug
185    printf("\n\n");
186    printf("Rows 0..%d are already OK\n", *n_row_alloc_ok);
187    printf("Rows 0..%d have the right dimensions\n", i_row-1);
188 #endif
189 
190    for (i=*n_row_alloc_ok; i<nrows; i++)
191    {
192       i_here = result->icols[i]-result->alloc_icol;
193       i_last = i_here + result->row_nnz[i];
194       if (result->row_nnz[i]>0)
195       {
196          if (*n_alloc_used > i_here || i_last > n_alloc)
197          {
198             n_rescue += result->row_nnz[i];
199             n_row_rescue++;
200             //printf("     Rescue row %d: %d nonzeros\n",i,result->row_nnz[i]);
201             //printf("        (New start at %d, old start at %d)\n",
202             //             *n_alloc_used, i_here);
203          }
204 
205          if (i<i_row) {  *n_alloc_used += result->row_nnz[i];}
206          else         {  *n_alloc_used += max_row_nnz[i];}
207       }
208    }
209 
210 #ifdef garbage_debug
211    printf(" + %d arrays will have to be rescued:  %d nnz\n",
212              n_row_rescue, n_rescue);
213 #endif
214 
215    // Allocate rescue arrays
216    ierr = PetscMalloc( n_rescue * sizeof(int), &icol_rescue);
217    CHKERRQ(ierr);
218    ierr = PetscMalloc( n_rescue * sizeof(PetscScalar), &val_rescue);
219    CHKERRQ(ierr);
220 
221    // Rescue the arrays which need rescuing
222    n_row_rescue = 0; n_rescue = 0;
223    if (*n_row_alloc_ok==0) { *n_alloc_used = 0; }
224    else
225    {
226       i = *n_row_alloc_ok - 1;
227       *n_alloc_used = (result->icols[i]-result->alloc_icol) +
228                        result->row_nnz[i];
229    }
230 
231    for (i=*n_row_alloc_ok; i<nrows; i++)
232    {
233       i_here = result->icols[i]-result->alloc_icol;
234       i_last = i_here + result->row_nnz[i];
235       if (result->row_nnz[i]>0)
236       {
237          if (*n_alloc_used > i_here || i_last > n_alloc)
238          {
239             //printf("     Rescuing row %d to array[%d..]\n",i,n_rescue);
240             memcpy((void*) &icol_rescue[n_rescue],
241                    (void*) result->icols[i],
242                     result->row_nnz[i] * sizeof(int));
243             memcpy((void*) &val_rescue[n_rescue],
244                    (void*) result->values[i],
245                    result->row_nnz[i] * sizeof(PetscScalar));
246             n_rescue += result->row_nnz[i];
247             n_row_rescue++;
248          }
249 
250          if (i<i_row) {  *n_alloc_used += result->row_nnz[i];}
251          else         {  *n_alloc_used += max_row_nnz[i];}
252       }
253    }
254 
255    //*********************************************************
256    // 3. Reallocate and correct administration
257 
258    if (n_alloc != result->n_alloc_icol)
259    {
260       n_copy = MIN(n_alloc,result->n_alloc_icol);
261 
262       // PETSC knows no REALLOC, so we'll REALLOC ourselves.
263 
264       // Allocate new icol-data, copy old contents
265       ierr = PetscMalloc(  n_alloc * sizeof(int),
266                            &result->alloc_icol); CHKERRQ(ierr);
267       memcpy(result->alloc_icol, alloc_icol_old, n_copy*sizeof(int));
268 
269       // Update administration, Reset pointers to new arrays
270       result->n_alloc_icol = n_alloc;
271       for (i=0; i<nrows; i++)
272       {
273           result->icols[i] =  result->alloc_icol +
274                   (result->icols[i]  - alloc_icol_old);
275           result->values[i] =  result->alloc_val +
276                   (result->icols[i]  - result->alloc_icol);
277       }
278 
279       // Delete old array
280       ierr = PetscFree(alloc_icol_old);  CHKERRQ(ierr);
281 
282       // Allocate new value-data, copy old contents
283       ierr = PetscMalloc(  n_alloc * sizeof(PetscScalar),
284                            &result->alloc_val); CHKERRQ(ierr);
285       memcpy(result->alloc_val, alloc_val_old, n_copy*sizeof(PetscScalar));
286 
287       // Update administration, Reset pointers to new arrays
288       result->n_alloc_val  = n_alloc;
289       for (i=0; i<nrows; i++)
290       {
291           result->values[i] =  result->alloc_val +
292                   (result->icols[i]  - result->alloc_icol);
293       }
294 
295       // Delete old array
296       ierr = PetscFree(alloc_val_old);  CHKERRQ(ierr);
297    }
298 
299 
300    //*********************************************************
301    // 4. Copy all the arrays to their proper places
302    n_row_rescue = 0; n_rescue = 0;
303    if (*n_row_alloc_ok==0) { *n_alloc_used = 0; }
304    else
305    {
306       i = *n_row_alloc_ok - 1;
307       *n_alloc_used = (result->icols[i]-result->alloc_icol) +
308                        result->row_nnz[i];
309    }
310 
311    for (i=*n_row_alloc_ok; i<nrows; i++)
312    {
313       i_here = result->icols[i]-result->alloc_icol;
314       i_last = i_here + result->row_nnz[i];
315 
316       result->icols[i] = result->alloc_icol + *n_alloc_used;
317       result->values[i]= result->alloc_val  + *n_alloc_used;
318 
319       if (result->row_nnz[i]>0)
320       {
321          if (*n_alloc_used > i_here || i_last > n_alloc)
322          {
323             //printf("     Copying rescued array[%d..] to row %d\n",
324             //       n_rescue,i);
325             //fflush(stdout);
326             memcpy((void*) result->icols[i],
327                    (void*) &icol_rescue[n_rescue],
328                     result->row_nnz[i] * sizeof(int));
329             memcpy((void*) result->values[i],
330                    (void*) &val_rescue[n_rescue],
331                     result->row_nnz[i] * sizeof(PetscScalar));
332             n_rescue += result->row_nnz[i];
333             n_row_rescue++;
334          }
335          else
336          {
337             for (j=0; j<result->row_nnz[i]; j++)
338             {
339                 result->icols[i][j]   = result->alloc_icol[i_here+j];
340                 result->values[i][j]  = result->alloc_val[ i_here+j];
341             }
342          }
343          if (i<i_row) {  *n_alloc_used += result->row_nnz[i];}
344          else         {  *n_alloc_used += max_row_nnz[i];}
345       }
346    }
347 
348    // Delete the rescue arrays
349    ierr = PetscFree(icol_rescue); CHKERRQ(ierr);
350    ierr = PetscFree(val_rescue);  CHKERRQ(ierr);
351    *n_row_alloc_ok = i_row;
352 
353    PetscFunctionReturn(0);
354 
355 }
356 
357 /*
358   spbas_incomplete_cholesky:
359      incomplete Cholesky decomposition of a square, symmetric,
360      positive definite matrix.
361 
362      In case negative diagonals are encountered, function returns
363      NEGATIVE_DIAGONAL. When this happens, call this function again
364      with a larger epsdiag_in, a less sparse pattern, and/or a smaller
365      droptol
366 */
367 #undef __FUNCT__
368 #define __FUNCT__ "spbas_incomplete_cholesky"
369 PetscErrorCode spbas_incomplete_cholesky(
370         Mat A, const PetscInt *rip, const PetscInt *riip,
371         spbas_matrix pattern,
372         PetscScalar droptol, PetscScalar epsdiag_in,
373         spbas_matrix * matrix_L)
374 {
375 
376 #undef cholesky_debug
377 
378 #ifdef cholesky_debug
379    PetscInt idebug=0;
380    const char * fmt= "   (%d,%d) -> %12.8e\n";
381    PetscScalar tmp;
382 #endif
383 
384    PetscInt jL;
385    Mat_SeqAIJ     *a=(Mat_SeqAIJ*)A->data;
386    PetscInt       *ai=a->i,*aj=a->j;
387    MatScalar      *aa=a->a;
388    PetscInt nrows, ncols;
389    PetscInt *max_row_nnz;
390    PetscErrorCode ierr;
391    spbas_matrix retval;
392    PetscScalar * diag;
393    PetscScalar * val;
394    PetscScalar * lvec;
395    PetscScalar epsdiag;
396    PetscInt i,j,k;
397    const PetscTruth do_values = PETSC_TRUE;
398    PetscInt * r1_icol; PetscScalar *r1_val;
399    PetscInt * r_icol; PetscInt r_nnz; PetscScalar *r_val;
400    PetscInt * A_icol; PetscInt A_nnz; PetscScalar *A_val;
401    PetscInt * p_icol; PetscInt p_nnz;
402    PetscInt n_row_alloc_ok = 0;  // number of rows which have been stored
403                             // correctly in the matrix
404    PetscInt n_alloc_used = 0;    // part of result->icols and result->values
405                             // which is currently being used
406    PetscFunctionBegin;
407 
408    // Convert the Manteuffel shift from 'fraction of average diagonal' to
409    // dimensioned value
410    ierr = MatGetSize(A, &nrows, &ncols); CHKERRQ(ierr);
411    ierr = MatGetTrace(A, &epsdiag); CHKERRQ(ierr);
412    epsdiag *= epsdiag_in / nrows;
413    printf("   Dimensioned Manteuffel shift=%e\n", epsdiag);
414    printf("   Drop tolerance              =%e\n",droptol);
415 
416    if (droptol<1e-10) {droptol=1e-10;}
417 
418    // Check dimensions
419    if ( (nrows != pattern.nrows) ||
420         (ncols != pattern.ncols) ||
421         (ncols != nrows) )
422    {
423       SETERRQ( PETSC_ERR_ARG_INCOMP,
424                "Dimension error in spbas_incomplete_cholesky\n");
425    }
426 
427    // Set overall values
428    retval.nrows = nrows;
429    retval.ncols = nrows;
430    retval.nnz   = pattern.nnz/10;
431    retval.col_idx_type = SPBAS_COLUMN_NUMBERS;
432    retval.block_data = PETSC_TRUE;
433 
434    // Allocate sparseness pattern
435    ierr =  spbas_allocate_pattern(&retval, do_values); CHKERRQ(ierr);
436    // Initial allocation of data
437    memset((void*) retval.row_nnz, 0, nrows*sizeof(int));
438    ierr =  spbas_allocate_data(&retval); CHKERRQ(ierr);
439    retval.nnz   = 0;
440 
441    // Allocate work arrays
442    ierr = PetscMalloc(nrows*sizeof(PetscScalar), &diag); CHKERRQ(ierr);
443    ierr = PetscMalloc(nrows*sizeof(PetscScalar), &val);  CHKERRQ(ierr);
444    ierr = PetscMalloc(nrows*sizeof(PetscScalar), &lvec); CHKERRQ(ierr);
445    ierr = PetscMalloc(nrows*sizeof(int), &max_row_nnz);  CHKERRQ(ierr);
446    if (!(diag && val && lvec && max_row_nnz))
447    {
448       SETERRQ( PETSC_ERR_MEM,
449               "Allocation error in spbas_incomplete_cholesky\n");
450    }
451 
452    memset((void*) val, 0, nrows*sizeof(PetscScalar));
453    memset((void*) lvec, 0, nrows*sizeof(PetscScalar));
454    memset((void*) max_row_nnz, 0, nrows*sizeof(int));
455 
456    // Check correct format of sparseness pattern
457    if (pattern.col_idx_type != SPBAS_DIAGONAL_OFFSETS)
458    {
459       SETERRQ( PETSC_ERR_SUP_SYS,
460                "Error in spbas_incomplete_cholesky: must have diagonal offsets in pattern\n");
461    }
462 
463    // Count the nonzeros on transpose of pattern
464    for (i = 0; i<nrows; i++)
465    {
466       p_nnz  = pattern.row_nnz[i];
467       p_icol = pattern.icols[i];
468       for (j=0; j<p_nnz; j++)
469       {
470          max_row_nnz[i+p_icol[j]]++;
471       }
472    }
473 
474    // Calculate rows of L
475    for (i = 0; i<nrows; i++)
476    {
477       p_nnz  = pattern.row_nnz[i];
478       p_icol = pattern.icols[i];
479 
480       r_nnz  = retval.row_nnz[i];
481       r_icol = retval.icols[i];
482       r_val  = retval.values[i];
483       A_nnz  = ai[rip[i]+1] - ai[rip[i]];
484       A_icol = &aj[ai[rip[i]]];
485       A_val  = &aa[ai[rip[i]]];
486 
487       // Calculate  val += A(i,i:n)';
488       for (j=0; j<A_nnz; j++)
489       {
490          k = riip[A_icol[j]];
491          if (k>=i) { val[k] = A_val[j]; }
492       }
493 
494       // Add regularization
495       val[i] += epsdiag;
496 
497       // Calculate lvec   = diag(D(0:i-1)) * L(0:i-1,i);
498       //           val(i) = A(i,i) - L(0:i-1,i)' * lvec
499       for ( j=0; j<r_nnz; j++)
500       {
501          k           = r_icol[j];
502          lvec[k]     = diag[k] * r_val[j];
503          val[i]     -= r_val[j] * lvec[k];
504       }
505 
506       // Calculate the new diagonal
507       diag[i] = val[i];
508       if (diag[i]<droptol)
509       {
510          printf("Error in spbas_incomplete_cholesky:\n");
511          printf("Negative diagonal in row %d\n",i+1);
512 
513          // Delete the whole matrix at once.
514          ierr = spbas_delete(retval);
515          return NEGATIVE_DIAGONAL;
516       }
517 
518 #ifdef cholesky_debug
519       if (idebug>=3)
520       {
521          printf("diagD * L (%d,:)=\n",i+1);
522          for (j=0; j<r_nnz; j++)
523          {
524             printf(fmt,r_icol[j]+1,i+1,lvec[r_icol[j]]);
525          }
526          printf("\n\n");
527       }
528 
529       if (idebug>=1) { printf("Diag = %12.8e\n\n\n",diag[i]);}
530 
531       if (idebug>=2)
532       {
533          printf("L^T * diagD * L=\n");
534          tmp = 0.0;
535          for ( j=0; j<r_nnz; j++)
536          {
537             k     =  r_icol[j];
538             tmp   += r_val[j] * lvec[k];
539          }
540          printf(fmt, i+1,i+1,tmp);
541       }
542 #endif
543 
544       // If necessary, allocate arrays
545       if (r_nnz==0)
546       {
547          ierr = spbas_cholesky_row_alloc( retval, i, 1, &n_alloc_used);
548          if (ierr == PETSC_ERR_MEM)
549          {
550             ierr = spbas_cholesky_garbage_collect( &retval,  i, &n_row_alloc_ok,
551                                &n_alloc_used, max_row_nnz);
552             CHKERRQ(ierr);
553             ierr = spbas_cholesky_row_alloc( retval, i, 1, &n_alloc_used);
554          }
555          CHKERRQ(ierr);
556          r_icol = retval.icols[i];
557          r_val  = retval.values[i];
558       }
559 
560       // Now, fill in
561       r_icol[r_nnz] = i;
562       r_val [r_nnz] = 1.0;
563       r_nnz++;
564       retval.row_nnz[i]++;
565 
566       retval.nnz += r_nnz;
567 
568       // Calculate
569       //   val(i+1:n) = (A(i,i+1:n)- L(0:i-1,i+1:n)' * lvec)/diag(i)
570       for (j=1; j<p_nnz; j++)
571       {
572          k = i+p_icol[j];
573          r1_icol = retval.icols[k];
574          r1_val  = retval.values[k];
575 #ifdef cholesky_debug
576          tmp = 0.0;
577 #endif
578          for (jL=0; jL<retval.row_nnz[k]; jL++)
579          {
580             val[k] -= r1_val[jL] * lvec[r1_icol[jL]];
581 #ifdef cholesky_debug
582             tmp    += r1_val[jL] * lvec[r1_icol[jL]];
583 #endif
584          }
585 #ifdef cholesky_debug
586          if (idebug>=2 && tmp!=0) {printf(fmt, k+1,i+1,tmp);}
587 #endif
588          val[k] /= diag[i];
589 
590          if (val[k] > droptol || val[k]< -droptol)
591          {
592             // If necessary, allocate arrays
593             if (retval.row_nnz[k]==0)
594             {
595                ierr = spbas_cholesky_row_alloc( retval, k, max_row_nnz[k],
596                             &n_alloc_used);
597                if (ierr == PETSC_ERR_MEM)
598                {
599                   ierr = spbas_cholesky_garbage_collect(
600                                      &retval,  i, &n_row_alloc_ok,
601                                      &n_alloc_used, max_row_nnz);
602                   CHKERRQ(ierr);
603                   ierr = spbas_cholesky_row_alloc( retval, k, max_row_nnz[k],
604                             &n_alloc_used);
605                   r_icol = retval.icols[i];
606                   r_val  = retval.values[i];
607                }
608                CHKERRQ(ierr);
609             }
610 
611             // Now, fill in
612             retval.icols[k][retval.row_nnz[k]] = i;
613             retval.values[k][retval.row_nnz[k]] = val[k];
614             retval.row_nnz[k]++;
615          }
616          val[k] = 0;
617       }
618 #ifdef cholesky_debug
619       if (idebug>=2) {printf("\n\n");}
620 #endif
621 
622       // Erase the values used in the work arrays
623       for (j=0; j<r_nnz; j++) { lvec[r_icol[j]] = 0; }
624 
625 #ifdef cholesky_debug
626       if (idebug>=2)
627       {
628          printf("new L=\n");
629          for (jL=i; jL<nrows; jL++)
630          {
631              j = retval.row_nnz[jL]-1;
632              if (j>=0)
633              {
634                 k = retval.icols[jL][j];
635                 if (k==i) printf(fmt, jL+1,i+1,retval.values[jL][j]);
636              }
637          }
638          printf("\n\n");
639       }
640 #endif
641    }
642 
643    ierr=PetscFree(lvec); CHKERRQ(ierr); ierr=PetscFree(val); CHKERRQ(ierr);
644 
645    ierr = spbas_cholesky_garbage_collect( &retval, nrows, &n_row_alloc_ok,
646                                      &n_alloc_used, max_row_nnz);
647    ierr=PetscFree(max_row_nnz); CHKERRQ(ierr);
648 
649    // Place the inverse of the diagonals in the matrix
650    for (i=0; i<nrows; i++)
651    {
652       r_nnz = retval.row_nnz[i];
653       retval.values[i][r_nnz-1] = 1.0 / diag[i];
654       for (j=0; j<r_nnz-1; j++)
655       {
656          retval.values[i][j] *= -1;
657       }
658    }
659    ierr=PetscFree(diag); CHKERRQ(ierr);
660 
661 
662    // Return successfully
663    *matrix_L = retval;
664 
665 #ifdef cholesky_debug
666    for (i = 0; i<nrows; i++)
667    {
668       printf("Row %d\n",i+1);
669       for (j=0; j<retval.row_nnz[i]; j++)
670       {
671          printf(fmt, i+1,retval.icols[i][j]+1,retval.values[i][j]);
672       }
673    }
674 #endif
675    PetscFunctionReturn(0);
676 }
677 
678