File: cholmod2.c

package info (click to toggle)
suitesparse 1%3A7.11.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 258,172 kB
  • sloc: ansic: 1,153,566; cpp: 48,145; makefile: 4,997; fortran: 2,087; java: 1,826; sh: 1,113; ruby: 725; python: 676; asm: 371; sed: 166; awk: 44
file content (301 lines) | stat: -rw-r--r-- 9,968 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
//------------------------------------------------------------------------------
// CHOLMOD/MATLAB/cholmod2: MATLAB interface to CHOLMOD x=A\b
//------------------------------------------------------------------------------

// CHOLMOD/MATLAB Module.  Copyright (C) 2005-2023, Timothy A. Davis.
// All Rights Reserved.
// SPDX-License-Identifier: GPL-2.0+

//------------------------------------------------------------------------------

// Supernodal sparse Cholesky backslash, x = A\b.  Factorizes PAP' in LL' then
// solves a sparse linear system.  Uses the diagonal and upper triangular part
// of A only.  A must be sparse.  b can be sparse or dense.
//
// Usage:
//
//      x = cholmod2 (A, b)
//      [x stats] = cholmod2 (A, b, ordering)   % a scalar: 0,-1,-2, or -3
//      [x stats] = cholmod2 (A, b, p)          % a permutation vector
//
// The 3rd argument select the ordering method to use.  If not present or -1,
// the default ordering strategy is used (AMD, and then try METIS if AMD finds
// an ordering with high fill-in, and use the best method tried).
//
// A final string argument determines the precision to use: 'double' for
// double precision (real or complex) or 'single' for single precision
// (either real or complex).  The default is 'double', even if all inputs
// are single.
//
// Other options for the ordering parameter:
//
//      0   natural (no etree postordering)
//      -1  use CHOLMOD's default ordering strategy (AMD, then try METIS)
//      -2  AMD, and then try NESDIS (not METIS) if AMD has high fill-in
//      -3  use AMD only
//      -4  use METIS only
//      -5  use NESDIS only
//      -6  natural, but with etree postordering
//      p   user permutation (vector of size n, with a permutation of 1:n)
//
// stats(1)     estimate of the reciprocal of the condition number
// stats(2)     ordering used:
//                  0: natural, 1: given, 2:amd, 3:metis, 4:nesdis, 5:colamd,
//                  6: natural but postordered.
// stats(3)     nnz(L)
// stats(4)     flop count in Cholesky factorization.  Excludes solution
//                  of upper/lower triangular systems, which can be easily
//                  computed from stats(3) (roughly 4*nnz(L)*size(b,2)).
// stats(5)     memory usage in MB.

#include "sputil2.h"

void mexFunction
(
    int nargout,
    mxArray *pargout [ ],
    int nargin,
    const mxArray *pargin [ ]
)
{
    double dummy = 0, rcond, *p ;
    cholmod_sparse Amatrix, Bspmatrix, *A, *Bs, *Xs ;
    cholmod_dense Bmatrix, *X, *B ;
    cholmod_factor *L ;
    cholmod_common Common, *cm ;
    int64_t n, B_is_sparse, ordering, k, *Perm ;

    //--------------------------------------------------------------------------
    // start CHOLMOD and set parameters
    //--------------------------------------------------------------------------

    cm = &Common ;
    cholmod_l_start (cm) ;
    sputil2_config (SPUMONI, cm) ;

    // There is no supernodal LDL'.  If cm->final_ll = FALSE (the default), then
    // this mexFunction will use a simplicial LDL' when flops/lnz < 40, and a
    // supernodal LL' otherwise.  This may give suprising results to the MATLAB
    // user, so always perform an LL' factorization by setting cm->final_ll
    // to TRUE.

    cm->final_ll = TRUE ;
    cm->quick_return_if_not_posdef = TRUE ;

    //--------------------------------------------------------------------------
    // get inputs
    //--------------------------------------------------------------------------

    // get the precision option
    int dtype = CHOLMOD_DOUBLE ;
    mxClassID mxdtype = mxDOUBLE_CLASS ;
    if (nargin > 1 && mxIsChar (pargin [nargin-1]))
    {
        char str [LEN] ;
        str [0] = '\0' ;
        mxGetString (pargin [nargin-1], str, LEN) ;
        if (str [0] == 's')
        {
            dtype = CHOLMOD_SINGLE ;
            mxdtype = mxSINGLE_CLASS ;
        }
        nargin-- ;
    }

    if (nargout > 2 || nargin < 2 || nargin > 3)
    {
        mexErrMsgTxt ("usage: [x,rcond] = cholmod2 (A,b,ordering,prec)") ;
    }
    n = mxGetM (pargin [0]) ;
    if (!mxIsSparse (pargin [0]) || (n != mxGetN (pargin [0])))
    {
        mexErrMsgTxt ("A must be square and sparse") ;
    }
    if (n != mxGetM (pargin [1]))
    {
        mexErrMsgTxt ("# of rows of A and B must match") ;
    }

    // get sparse matrix A.  Use triu(A) only.
    size_t A_xsize = 0 ;
    A = sputil2_get_sparse (pargin [0], 1, dtype, &Amatrix, &A_xsize, cm) ;

    // get sparse or dense matrix B
    B = NULL ;
    Bs = NULL ;
    B_is_sparse = mxIsSparse (pargin [1]) ;
    size_t B_xsize = 0 ;
    if (B_is_sparse)
    {
        // get sparse matrix B (unsymmetric)
        Bs = sputil2_get_sparse (pargin [1], 0, dtype, &Bspmatrix, &B_xsize,
            cm) ;
    }
    else
    {
        // get dense matrix B
        B = sputil2_get_dense (pargin [1], dtype, &Bmatrix, &B_xsize, cm) ;
    }

    // get the ordering option
    if (nargin < 3)
    {
        // use default ordering
        ordering = -1 ;
    }
    else
    {
        // use a non-default option
        ordering = mxGetScalar (pargin [2]) ;
    }

    p = NULL ;
    Perm = NULL ;

    if (ordering == 0)
    {
        // natural ordering
        cm->nmethods = 1 ;
        cm->method [0].ordering = CHOLMOD_NATURAL ;
        cm->postorder = FALSE ;
    }
    else if (ordering == -1)
    {
        // default strategy ... nothing to change
    }
    else if (ordering == -2)
    {
        // default strategy, but with NESDIS in place of METIS
        cm->default_nesdis = TRUE ;
    }
    else if (ordering == -3)
    {
        // use AMD only
        cm->nmethods = 1 ;
        cm->method [0].ordering = CHOLMOD_AMD ;
        cm->postorder = TRUE ;
    }
    else if (ordering == -4)
    {
        // use METIS only
        cm->nmethods = 1 ;
        cm->method [0].ordering = CHOLMOD_METIS ;
        cm->postorder = TRUE ;
    }
    else if (ordering == -5)
    {
        // use NESDIS only
        cm->nmethods = 1 ;
        cm->method [0].ordering = CHOLMOD_NESDIS ;
        cm->postorder = TRUE ;
    }
    else if (ordering == -6)
    {
        // natural ordering, but with etree postordering
        cm->nmethods = 1 ;
        cm->method [0].ordering = CHOLMOD_NATURAL ;
        cm->postorder = TRUE ;
    }
    else if (ordering == -7)
    {
        // always try both AMD and METIS, and pick the best
        cm->nmethods = 2 ;
        cm->method [0].ordering = CHOLMOD_AMD ;
        cm->method [1].ordering = CHOLMOD_METIS ;
        cm->postorder = TRUE ;
    }
    else if (ordering >= 1)
    {
        // assume the 3rd argument is a user-provided permutation of 1:n
        if (mxGetNumberOfElements (pargin [2]) != n)
        {
            mexErrMsgTxt ("invalid input permutation") ;
        }
        // copy from double to integer, and convert to 0-based
        p = (double *) mxGetData (pargin [2]) ;
        Perm = cholmod_l_malloc (n, sizeof (int64_t), cm) ;
        for (k = 0 ; k < n ; k++)
        {
            Perm [k] = p [k] - 1 ;
        }
        // check the permutation
        if (!cholmod_l_check_perm (Perm, n, n, cm))
        {
            mexErrMsgTxt ("invalid input permutation") ;
        }
        // use only the given permutation
        cm->nmethods = 1 ;
        cm->method [0].ordering = CHOLMOD_GIVEN ;
        cm->postorder = FALSE ;
    }
    else
    {
        mexErrMsgTxt ("invalid ordering option") ;
    }

    //--------------------------------------------------------------------------
    // analyze and factorize
    //--------------------------------------------------------------------------

    L = cholmod_l_analyze_p (A, Perm, NULL, 0, cm) ;
    cholmod_l_free (n, sizeof (int64_t), Perm, cm) ;
    cholmod_l_factorize (A, L, cm) ;

    rcond = cholmod_l_rcond (L, cm) ;
    if (rcond == 0)
    {
        mexWarnMsgTxt ("Matrix is indefinite or singular to working precision");
    }
    else if (rcond < DBL_EPSILON)
    {
        mexWarnMsgTxt ("Matrix is close to singular or badly scaled.") ;
        mexPrintf ("         Results may be inaccurate. RCOND = %g.\n", rcond) ;
    }

    //--------------------------------------------------------------------------
    // solve and return solution to MATLAB
    //--------------------------------------------------------------------------

    if (B_is_sparse)
    {
        // solve AX=B with sparse X and B; return sparse X to MATLAB.
        // The sparse X must be returned to MATLAB as double since MATLAB
        // does not (yet) support sparse single precision matrices.
        // cholmod_l_spsolve returns Xs with no explicit zeros.
        Xs = cholmod_l_spsolve (CHOLMOD_A, L, Bs, cm) ;
        pargout [0] = sputil2_put_sparse (&Xs, mxDOUBLE_CLASS,
            /* already done by cholmod_l_spsolve: */ false, cm) ;
    }
    else
    {
        // solve AX=B with dense X and B; return dense X to MATLAB
        X = cholmod_l_solve (CHOLMOD_A, L, B, cm) ;
        // the dense X can be returned in its current type
        pargout [0] = sputil2_put_dense (&X, mxdtype, cm) ;
    }

    // return statistics, if requested
    if (nargout > 1)
    {
        pargout [1] = mxCreateDoubleMatrix (1, 5, mxREAL) ;
        p = (double *) mxGetData (pargout [1]) ;
        p [0] = rcond ;
        p [1] = L->ordering ;
        p [2] = cm->lnz ;
        p [3] = cm->fl ;
        p [4] = cm->memory_usage / 1048576. ;
    }

    //--------------------------------------------------------------------------
    // free workspace and return result
    //--------------------------------------------------------------------------

    sputil2_free_sparse (&A,  &Amatrix,   A_xsize, cm) ;
    sputil2_free_sparse (&Bs, &Bspmatrix, B_xsize, cm) ;
    sputil2_free_dense  (&B,  &Bmatrix,   B_xsize, cm) ;
    cholmod_l_free_factor (&L, cm) ;
    cholmod_l_finish (cm) ;
    if (SPUMONI > 0) cholmod_l_print_common (" ", cm) ;
    if (SPUMONI > 1) cholmod_l_gpu_stats (cm) ;
}