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