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