Skip to content

Commit 8a50d52

Browse files
Initial CXSparse.jl: cs_qr + cs_lu wrappers
Wraps `CXSparse_jll`'s `cs_di_*`, `cs_dl_*`, `cs_ci_*`, `cs_cl_*` symbols to expose Tim Davis's textbook QR and LU sparse solvers (the CSparse counterparts to KLU's small-problem fast-path philosophy) as a Julian API: cs_qr(A::SparseMatrixCSC) → CSQR cs_lu(A::SparseMatrixCSC) → CSLU F \ b, ldiv!(x, F, b) Element types: Float64 and ComplexF64. Index types: Int32 and Int64. C-side memory is freed by a Julia finalizer. cs_qr requires m ≥ n; cs_lu requires square. cs_qr is not rank-revealing — on rank-deficient matrices it returns x with non-finite components (the back-solve divides through a zero R diagonal); the residual ||A x − b|| is correct iff the non-finite entries fall in null(A) and cancel. Tests pass: 154/154 across all (Tv, Ti) variants, finalizer/GC stress, and overdetermined cases. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
0 parents  commit 8a50d52

6 files changed

Lines changed: 765 additions & 0 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Manifest.toml
2+
*.jl.cov
3+
*.jl.*.cov
4+
*.jl.mem
5+
deps/deps.jl
6+
.DS_Store

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Chris Rackauckas and contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Project.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name = "CXSparse"
2+
uuid = "27bc1049-af5b-4e6d-9573-9751c3395f6e"
3+
authors = ["Chris Rackauckas <accounts@chrisrackauckas.com> and contributors"]
4+
version = "0.1.0"
5+
6+
[deps]
7+
CXSparse_jll = "c77e7b6a-7cf9-58ed-a396-e1da12b05d87"
8+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
9+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
10+
11+
[compat]
12+
CXSparse_jll = "4"
13+
LinearAlgebra = "1"
14+
SparseArrays = "1"
15+
julia = "1.10"
16+
17+
[extras]
18+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
19+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
20+
21+
[targets]
22+
test = ["Random", "Test"]

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# CXSparse.jl
2+
3+
Julia wrapper around the [CXSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse/tree/dev/CXSparse) library from SuiteSparse — the lightweight, textbook-style sparse direct solvers from Tim Davis's *Direct Methods for Sparse Linear Systems* (CSparse), extended to support `ComplexF64` values and both 32- and 64-bit indices.
4+
5+
CXSparse is the QR / LU counterpart to **KLU's design philosophy**: a small symbolic phase, very little per-call overhead, well-suited to small-to-medium sparse problems (n up to a few thousand). It complements the multifrontal solvers (UMFPACK, SPQR, CHOLMOD) which pay a heavier symbolic tax in exchange for BLAS-3 speedups on larger fronts.
6+
7+
This package wraps the `cs_qr` and `cs_lu` factorizations from `CXSparse_jll` and exposes a Julian API.
8+
9+
## Status
10+
11+
- [x] `cs_qr` for `SparseMatrixCSC{Float64, Int32 | Int64}` and `SparseMatrixCSC{ComplexF64, Int32 | Int64}`
12+
- [x] `cs_lu` for the same four element/index combinations
13+
- [x] `LinearAlgebra.ldiv!` and `\` for both factorizations
14+
- [x] Finalizer-managed C-side memory (no manual `cs_*_sfree` calls required)
15+
- [ ] `cs_cholsol` for symmetric positive definite — not yet wrapped
16+
17+
## Quick start
18+
19+
```julia
20+
using CXSparse, SparseArrays
21+
22+
A = sparse([2.0 1.0 0.0;
23+
1.0 3.0 1.0;
24+
0.0 1.0 2.0])
25+
b = [1.0, 2.0, 3.0]
26+
27+
# QR factorization (works for square or overdetermined m ≥ n):
28+
F = cs_qr(A)
29+
x = F \ b
30+
# or in-place:
31+
x_pre = zeros(3)
32+
ldiv!(x_pre, F, b)
33+
34+
# LU factorization (square only):
35+
G = cs_lu(A)
36+
y = G \ b
37+
```
38+
39+
## Supported element/index combinations
40+
41+
| element type | index type | CXSparse name |
42+
|----------------|------------|---------------|
43+
| `Float64` | `Int32` | `cs_di_*` |
44+
| `Float64` | `Int64` | `cs_dl_*` |
45+
| `ComplexF64` | `Int32` | `cs_ci_*` |
46+
| `ComplexF64` | `Int64` | `cs_cl_*` |
47+
48+
CXSparse does **not** ship `Float32` variants — `cs_si_*` / `cs_sl_*` don't exist upstream.
49+
50+
## When to use this vs SPQR / UMFPACK
51+
52+
CXSparse `cs_qr` and `cs_lu` are textbook implementations: simple, low symbolic-phase overhead, no BLAS-3. They sit alongside KLU in the "small/medium fast path" niche:
53+
54+
| | KLU | UMFPACK | SPQR | CXSparse `cs_qr` / `cs_lu` |
55+
|---|---|---|---|---|
56+
| factorization | LU | LU | QR | LU / QR |
57+
| algorithm | BTF + Gilbert–Peierls partial-pivot LU | multifrontal BLAS-3 LU | multifrontal BLAS-3 QR | textbook Householder QR / Gilbert–Peierls LU |
58+
| sweet spot | n ≲ few thousand | n ≳ 1k | n ≳ 1k, least-squares | n ≲ few thousand |
59+
| pivoting | partial | row + column | sparsity-only (rank-revealing if `tol ≥ 0`) | partial (LU), sparsity-only (QR) |
60+
| BLAS-3 | no | yes | yes | no |
61+
62+
On 199×199 sparse matrices we observed `cs_qr` running in ~325 µs vs SPQR's ~570 µs (1.7× faster). For rank-deficient inputs, however, `cs_qr` is **not rank-revealing**`x` may contain non-finite entries from the back-solve dividing through near-zero `R` diagonals. If you need a clean solution on rank-deficient systems, fall back to `qr(A)` (SPQR) or LAPACK's column-pivoted QR on `Matrix(A)`.
63+
64+
## Internals
65+
66+
CXSparse uses 0-based indexing. The wrapper allocates 0-based `colptr` / `rowval` buffers when constructing the `cs_*_sparse` view; `nzval` is shared with the user's `SparseMatrixCSC` directly (CXSparse doesn't mutate it during factorization of a CSC matrix).
67+
68+
Symbolic and numeric C-side structs (`cs_*_symbolic`, `cs_*_numeric`) are owned by the Julia factorization object and freed by a finalizer. The struct internals are accessed via raw pointer offsets matching the layout in `cs.h`; we never mirror the symbolic/numeric structs in full.
69+
70+
## License
71+
72+
The wrapper is MIT-licensed. CXSparse itself is dual-licensed (LGPL-2.1+ / GPL-2.0+) — see the upstream [SuiteSparse repository](https://github.com/DrTimothyAldenDavis/SuiteSparse).

0 commit comments

Comments
 (0)