1 //------------------------------------------------------------------------------
2 // SLIP_LU/SLIP_LU_factorize: exact sparse LU factorization
3 //------------------------------------------------------------------------------
4 
5 // SLIP_LU: (c) 2019-2020, Chris Lourenco, Jinhao Chen, Erick Moreno-Centeno,
6 // Timothy A. Davis, Texas A&M University.  All Rights Reserved.  See
7 // SLIP_LU/License for the license.
8 
9 //------------------------------------------------------------------------------
10 
11 /* Purpose: This function performs the SLIP LU factorization. This factorization
12  * is done via n iterations of the sparse REF triangular solve function. The
13  * overall factorization is PAQ = LDU
14  * The determinant of A can be obtained as determinant = rhos[n-1]
15  *
16  *  L: undefined on input, created on output
17  *  U: undefined on input, created on output
18  *  rhos: undefined on input, created on output
19  *  pinv: undefined on input, created on output
20  *
21  *  A: input only, not modified
22  *  S: input only, not modified
23  *  option: input only, not modified
24  */
25 
26 #define SLIP_FREE_WORK              \
27     SLIP_matrix_free(&x, NULL);     \
28     SLIP_FREE(xi);                  \
29     SLIP_FREE(h);                   \
30     SLIP_FREE(pivs);                \
31     SLIP_FREE(row_perm);            \
32 
33 #define SLIP_FREE_ALL               \
34     SLIP_FREE_WORK                  \
35     SLIP_matrix_free(&L, NULL);     \
36     SLIP_matrix_free(&U, NULL);     \
37     SLIP_matrix_free(&rhos, NULL);  \
38     SLIP_FREE(pinv);
39 
40 #include "slip_internal.h"
41 
SLIP_LU_factorize(SLIP_matrix ** L_handle,SLIP_matrix ** U_handle,SLIP_matrix ** rhos_handle,int64_t ** pinv_handle,const SLIP_matrix * A,const SLIP_LU_analysis * S,const SLIP_options * option)42 SLIP_info SLIP_LU_factorize
43 (
44     // output:
45     SLIP_matrix **L_handle,    // lower triangular matrix
46     SLIP_matrix **U_handle,    // upper triangular matrix
47     SLIP_matrix **rhos_handle, // sequence of pivots
48     int64_t **pinv_handle,     // inverse row permutation
49     // input:
50     const SLIP_matrix *A,      // matrix to be factored
51     const SLIP_LU_analysis *S, // column permutation and estimates
52                                // of nnz in L and U
53     const SLIP_options* option // command options
54 )
55 {
56 
57     //--------------------------------------------------------------------------
58     // check inputs
59     //--------------------------------------------------------------------------
60 
61     if (!slip_initialized ( )) return (SLIP_PANIC) ;
62 
63     SLIP_REQUIRE (A, SLIP_CSC, SLIP_MPZ) ;
64     int64_t anz = SLIP_matrix_nnz (A, option) ;
65 
66     if (!L_handle || !U_handle || !rhos_handle || !pinv_handle || !S || anz < 0)
67     {
68         return SLIP_INCORRECT_INPUT;
69     }
70 
71     (*L_handle) = NULL ;
72     (*U_handle) = NULL ;
73     (*rhos_handle) = NULL ;
74     (*pinv_handle) = NULL ;
75 
76     //--------------------------------------------------------------------------
77     // Declare and initialize workspace
78     //--------------------------------------------------------------------------
79 
80     SLIP_matrix *L = NULL ;
81     SLIP_matrix *U = NULL ;
82     SLIP_matrix *rhos = NULL ;
83     int64_t *pinv = NULL ;
84     int64_t *xi = NULL ;
85     int64_t *h = NULL ;
86     int64_t *pivs = NULL ;
87     int64_t *row_perm = NULL ;
88     SLIP_matrix *x = NULL ;
89 
90     SLIP_info info ;
91     int64_t n = A->n ;
92 
93     int64_t k = 0, top, i, j, col, loc, lnz = 0, unz = 0, pivot, jnew ;
94     size_t size ;
95 
96     // Inverse pivot ordering
97     pinv = (int64_t *) SLIP_malloc (n * sizeof (int64_t)) ;
98 
99     // Indicator of which rows have been pivotal
100     // pivs[i] = 1 if row i has been selected as a pivot
101     // row, otherwise, pivs[i] < 0
102     pivs = (int64_t*) SLIP_malloc(n* sizeof(int64_t));
103 
104     // h is the history vector utilized for the sparse REF
105     // triangular solve algorithm. h serves as a global
106     // vector which is repeatedly passed into the triangular
107     // solve algorithm
108     h = (int64_t*) SLIP_malloc(n* sizeof(int64_t));
109 
110     // xi is the global nonzero pattern vector. It stores
111     // the pattern of nonzeros of the kth column of L and U
112     // for the triangular solve.
113     xi = (int64_t*) SLIP_malloc(2*n* sizeof(int64_t));
114 
115     // Actual row permutation, the inverse of pinv. This
116     // is used for sorting
117     row_perm = (int64_t*) SLIP_malloc(n* sizeof(int64_t));
118 
119     if (!pivs || !h || !xi || !row_perm || !pinv)
120     {
121         // out of memory: free everything and return
122         SLIP_FREE_ALL  ;
123         return SLIP_OUT_OF_MEMORY;
124     }
125 
126     // initialize workspace and pivot status
127     for (i = 0; i < n; i++)
128     {
129         h[i] = -1;
130         pivs[i] = -1;
131         // Initialize location based vectors
132         pinv[i] = i;
133         row_perm[i] = i;
134     }
135 
136     //--------------------------------------------------------------------------
137     // Declare memory for rhos, L, and U
138     //--------------------------------------------------------------------------
139 
140     // Create rhos, a global dense mpz_t matrix of dimension n*1
141     SLIP_CHECK (SLIP_matrix_allocate(&rhos, SLIP_DENSE, SLIP_MPZ, n, 1, n,
142         false, false, option));
143 
144     // Allocate L and U without initializing each entry.
145     // L and U are allocated to have nnz(L) which is estimated by the symbolic
146     // analysis. However, unlike traditional matrix allocation, the second
147     // boolean parameter here is set to false, so the individual values of
148     // L and U are not allocated. Instead, a more efficient method to
149     // allocate these values is done in the factorization to reduce
150     // memory usage.
151     SLIP_CHECK (SLIP_matrix_allocate(&L, SLIP_CSC, SLIP_MPZ, n, n, S->lnz,
152         false, false, option));
153     SLIP_CHECK (SLIP_matrix_allocate(&U, SLIP_CSC, SLIP_MPZ, n, n, S->unz,
154         false, false, option));
155 
156     //--------------------------------------------------------------------------
157     // allocate and initialize the workspace x
158     //--------------------------------------------------------------------------
159 
160     // SLIP LU utilizes arbitrary sized integers which can grow beyond the
161     // default 64 bits allocated by GMP. If the integers frequently grow, GMP
162     // can get bogged down by performing intermediate reallocations. Instead,
163     // we utilize a larger estimate on the workspace x vector so that computing
164     // the values in L and U do not require too many extra intemediate calls to
165     // realloc.
166     //
167     // Note that the estimate presented here is not an upper bound nor a lower
168     // bound.  It is still possible that more bits will be required which is
169     // correctly handled internally.
170     int64_t estimate = 64 * SLIP_MAX (2, ceil (log2 ((double) n))) ;
171 
172     // Create x, a global dense mpz_t matrix of dimension n*1. Unlike rhos, the
173     // second boolean parameter is set to false to avoid initializing
174     // each mpz entry of x with default size.  It is intialized below.
175     SLIP_CHECK (SLIP_matrix_allocate(&x, SLIP_DENSE, SLIP_MPZ, n, 1, n,
176         false, /* do not initialize the entries of x: */ false, option));
177 
178     // initialize the entries of x
179     for (i = 0; i < n; i++)
180     {
181         // Allocate memory for entries of x
182         SLIP_CHECK(SLIP_mpz_init2(x->x.mpz[i], estimate));
183     }
184 
185     //--------------------------------------------------------------------------
186     // Iterations 0:n-1 (1:n in standard)
187     //--------------------------------------------------------------------------
188 
189     for (k = 0; k < n; k++)
190     {
191         // Column pointers for column k of L and U
192         L->p[k] = lnz;
193         U->p[k] = unz;
194         col = S->q[k];
195 
196         //----------------------------------------------------------------------
197         // Reallocate memory if necessary
198         // if lnz+n > L->nzmax, L needs to expand to accomodate new nonzeros.
199         // To do so, we double the size of the L and U matrices.
200         //----------------------------------------------------------------------
201         if (lnz + n > L->nzmax)
202         {
203             // Double the size of L
204             SLIP_CHECK(slip_sparse_realloc(L));
205         }
206         if (unz + n > U->nzmax)
207         {
208             // Double the size of U
209             SLIP_CHECK(slip_sparse_realloc(U));
210         }
211 
212         //----------------------------------------------------------------------
213         // Triangular solve to compute LDx = A(:,k)
214         //----------------------------------------------------------------------
215         SLIP_CHECK(slip_ref_triangular_solve(&top, L, A, k, xi,
216             (const int64_t *) (S->q),
217             rhos,
218             (const int64_t *) pinv,
219             (const int64_t *) row_perm,
220             h, x)) ;
221 
222         //----------------------------------------------------------------------
223         // Obtain pivot
224         //----------------------------------------------------------------------
225         SLIP_CHECK(slip_get_pivot(&pivot, x, pivs, n, top, xi,
226             col, k, rhos, pinv, row_perm, option));
227 
228         //----------------------------------------------------------------------
229         // Populate L and U. We iterate across all nonzeros in x
230         //----------------------------------------------------------------------
231         for (j = top; j < n; j++)
232         {
233             jnew = xi[j];
234             // Location of x[j] in final matrix
235             loc = pinv[jnew];
236 
237             //------------------------------------------------------------------
238             // loc <= k are rows above k, thus go to U
239             //------------------------------------------------------------------
240             if (loc <= k)
241             {
242                 // Place the i location of the unz nonzero
243                 U->i[unz] = jnew;
244                 // Find the size in bits of x[j]
245                 SLIP_CHECK(SLIP_mpz_sizeinbase(&size, x->x.mpz[jnew], 2));
246                 // GMP manual: Allocated size should be size+2
247                 SLIP_CHECK(SLIP_mpz_init2(U->x.mpz[unz], size+2));
248                 // Place the x value of the unz nonzero
249                 SLIP_CHECK(SLIP_mpz_set(U->x.mpz[unz], x->x.mpz[jnew]));
250                 // Increment unz
251                 unz++;
252             }
253 
254             //------------------------------------------------------------------
255             // loc >= k are rows below k, thus go to L
256             //------------------------------------------------------------------
257             if (loc >= k)
258             {
259                 // Place the i location of the lnz nonzero
260                 L->i[lnz] = jnew;
261                 // Set the size of x[j]
262                 SLIP_CHECK(SLIP_mpz_sizeinbase(&size, x->x.mpz[jnew], 2));
263                 // GMP manual: Allocated size should be size+2
264                 SLIP_CHECK(SLIP_mpz_init2(L->x.mpz[lnz], size+2));
265                 // Place the x value of the lnz nonzero
266                 SLIP_CHECK(SLIP_mpz_set(L->x.mpz[lnz], x->x.mpz[jnew]));
267                 // Increment lnz
268                 lnz++;
269             }
270         }
271     }
272 
273     // Finalize L->p, U->p
274     L->p[n] = lnz;
275     U->p[n] = unz;
276 
277     //--------------------------------------------------------------------------
278     // Free memory
279     //--------------------------------------------------------------------------
280 
281     // free everything, but keep L, U, rhos, and pinv
282     SLIP_FREE_WORK ;
283 
284     // This cannot fail since the size of L and U are shrinking.
285     // Collapse L
286     slip_sparse_collapse(L);
287     // Collapse U
288     slip_sparse_collapse(U);
289 
290     //--------------------------------------------------------------------------
291     // finalize the row indices in L and U
292     //--------------------------------------------------------------------------
293 
294     // Permute entries in L
295     for (i = 0; i < lnz; i++)
296     {
297         L->i[i] = pinv[L->i[i]];
298     }
299     // Permute entries in U
300     for (i = 0; i < unz; i++)
301     {
302         U->i[i] = pinv[U->i[i]];
303     }
304 
305     //--------------------------------------------------------------------------
306     // check the LU factorization (debugging only)
307     //--------------------------------------------------------------------------
308 
309     #if 0
310     SLIP_CHECK (SLIP_matrix_check (L, option)) ;
311     SLIP_CHECK (SLIP_matrix_check (U, option)) ;
312     #endif
313 
314     //--------------------------------------------------------------------------
315     // return result
316     //--------------------------------------------------------------------------
317 
318     (*L_handle) = L ;
319     (*U_handle) = U ;
320     (*rhos_handle) = rhos ;
321     (*pinv_handle) = pinv ;
322     return (SLIP_OK) ;
323 }
324 
325