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