1 //------------------------------------------------------------------------------
2 // SLIP_LU/SLIP_LU_analyze: symbolic ordering and analysis for sparse LU
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 symbolic ordering for SLIP LU.
12 * Currently, there are three options: user-defined order, COLAMD, or AMD.
13 *
14 * Input/output arguments:
15 *
16 * S: Symbolic analysis struct. Undefined on input; contains column
17 * permutation and estimates of nnz(L) and nnz(U) nnz on output
18 *
19 * A: Input matrix, unmodified on input/output
20 *
21 * option: option->order tells the function which ordering scheme to use
22 *
23 */
24
25 // SLIP_LU_analyze creates the SLIP_LU_analysis object S. Use
26 // SLIP_delete_LU_analysis to delete it.
27
28 #include "slip_internal.h"
29
SLIP_LU_analyze(SLIP_LU_analysis ** S_handle,const SLIP_matrix * A,const SLIP_options * option)30 SLIP_info SLIP_LU_analyze
31 (
32 SLIP_LU_analysis** S_handle, // symbolic analysis (column perm. and nnz L,U)
33 const SLIP_matrix *A, // Input matrix
34 const SLIP_options *option // Control parameters, if NULL, use default
35 )
36 {
37
38 //--------------------------------------------------------------------------
39 // check inputs
40 //--------------------------------------------------------------------------
41
42 if (!slip_initialized ( )) return (SLIP_PANIC) ;
43
44 // A can have any data type, but must be in sparse CSC format
45 SLIP_REQUIRE_KIND (A, SLIP_CSC) ;
46
47 if (!S_handle || A->n != A->m)
48 {
49 return SLIP_INCORRECT_INPUT;
50 }
51 (*S_handle) = NULL ;
52
53 //--------------------------------------------------------------------------
54 // allocate symbolic analysis object
55 //--------------------------------------------------------------------------
56
57 SLIP_LU_analysis *S = NULL ;
58 int64_t i, n = A->n, anz = SLIP_matrix_nnz(A, option);
59 // ALlocate memory for S
60 S = (SLIP_LU_analysis*) SLIP_malloc(sizeof(SLIP_LU_analysis));
61 if (S == NULL) {return SLIP_OUT_OF_MEMORY;}
62
63 // Allocate memory for column permutation
64 S->q = (int64_t*) SLIP_malloc((n+1) * sizeof(int64_t));
65 if (S->q == NULL)
66 {
67 SLIP_FREE(S);
68 return SLIP_OUT_OF_MEMORY;
69 }
70
71 //--------------------------------------------------------------------------
72 // No ordering is used. S->q is set to [0 ... n] and the number of nonzeros
73 // in L and U is estimated to be 10 times the number of nonzeros in A. This
74 // is a very crude estimate on the nnz(L) and nnz(U)
75 //--------------------------------------------------------------------------
76
77 SLIP_col_order order = SLIP_OPTION_ORDER (option) ;
78 int pr = SLIP_OPTION_PRINT_LEVEL (option) ;
79
80 if (order == SLIP_NO_ORDERING)
81 {
82 for (i = 0; i < n+1; i++)
83 {
84 S->q[i] = i;
85 }
86 // estimates for number of L and U nonzeros
87 S->lnz = S->unz = 10*anz;
88 }
89
90 //--------------------------------------------------------------------------
91 // The AMD ordering is used. S->q is set to AMD's column ordering on
92 // A+A'. The number of nonzeros in L and U is given as AMD's computed
93 // number of nonzeros in the Cholesky factor L of A+A'
94 //--------------------------------------------------------------------------
95 else if (order == SLIP_AMD)
96 {
97 double Control [AMD_CONTROL]; // Declare AMD control
98 amd_l_defaults (Control) ; // Set AMD defaults
99 double Info [AMD_INFO];
100 // Perform AMD
101 amd_l_order(n, (SuiteSparse_long *) A->p, (SuiteSparse_long *) A->i,
102 (SuiteSparse_long *) S->q, Control, Info) ;
103 S->lnz = S->unz = Info[AMD_LNZ]; // estimate for unz and lnz
104 if (pr > 0) // Output AMD info if desired
105 {
106 SLIP_PRINTF ("\n****Column Ordering Information****\n") ;
107 amd_l_control (Control) ;
108 amd_l_info (Info) ;
109 }
110 }
111
112 //--------------------------------------------------------------------------
113 // The COLAMD ordering is used. S->q is set as COLAMD's column ordering.
114 // The number of nonzeros in L and U is set as 10 times the number of
115 // nonzeros in A. This is a crude estimate.
116 //--------------------------------------------------------------------------
117 else
118 {
119 // Declared as per COLAMD documentation
120 int64_t Alen = 2*anz + 6 *(n+1) + 6*(n+1) + n;
121 int64_t* A2 = (int64_t*) SLIP_malloc(Alen* sizeof(int64_t));
122 if (!A2)
123 {
124 // out of memory
125 SLIP_LU_analysis_free (&S, option) ;
126 return (SLIP_OUT_OF_MEMORY) ;
127 }
128 // Initialize S->q as per COLAMD documentation
129 for (i = 0; i < n+1; i++)
130 {
131 S->q[i] = A->p[i];
132 }
133 // Initialize A2 per COLAMD documentation
134 for (i = 0; i < anz; i++)
135 {
136 A2[i] = A->i[i];
137 }
138 int64_t stats [COLAMD_STATS];
139 colamd_l (n, n, Alen, (SuiteSparse_long *) A2,
140 (SuiteSparse_long *) S->q, (double *) NULL,
141 (SuiteSparse_long *) stats) ;
142 // estimate for lnz and unz
143 S->lnz = S->unz = 10*anz;
144
145 // Print stats if desired
146 if (pr > 0)
147 {
148 SLIP_PRINTF ("\n****Column Ordering Information****\n") ;
149 colamd_l_report ((SuiteSparse_long *) stats) ;
150 SLIP_PRINTF ("\nEstimated L and U nonzeros: %" PRId64 "\n", S->lnz);
151 }
152 SLIP_FREE(A2);
153 }
154
155 //--------------------------------------------------------------------------
156 // Make sure appropriate space is allocated. It's possible to return
157 // estimates which exceed the dimension of L and U or estimates which are
158 // too small for L U. In this case, this block of code ensures that the
159 // estimates on nnz(L) and nnz(U) are at least n and no more than n*n.
160 //--------------------------------------------------------------------------
161 // estimate exceeds max number of nnz in A
162 if (S->lnz > (double) n*n)
163 {
164 int64_t nnz = ceil(0.5*n*n);
165 S->lnz = S->unz = nnz;
166 }
167 // If estimate < n, first column of triangular solve may fail
168 if (S->lnz < n)
169 {
170 S->lnz = S->lnz + n;
171 }
172 if (S->unz < n)
173 {
174 S->unz = S->unz + n;
175 }
176
177 //--------------------------------------------------------------------------
178 // return result
179 //--------------------------------------------------------------------------
180
181 (*S_handle) = S ;
182 return SLIP_OK;
183 }
184
185