//------------------------------------------------------------------------------ // 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) ; }