├── .github
└── workflows
│ └── rust.yml
├── .gitignore
├── CITATION.cff
├── Cargo.toml
├── LICENSE
├── README.md
├── src
├── data.rs
└── lib.rs
└── tests
├── assets
├── allsol_1.sprs
├── allsol_2.sprs
├── cholsol_1.sprs
├── cholsol_2.sprs
├── cholsol_5.sprs
├── cholsol_7.sprs
├── lusol_3.sprs
├── lusol_4.sprs
├── lusol_6.sprs
├── qrsol_3.sprs
├── qrsol_4.sprs
├── qrsol_5.sprs
├── qrsol_8.sprs
└── qrsol_9.sprs
├── basic_tests.rs
├── save_load_tests.rs
├── solver_tests.rs
└── utils.rs
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Build
20 | run: cargo build --verbose
21 | - name: Run tests
22 | run: cargo test --release --verbose
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /.vscode
3 |
4 | # Ignored file types
5 | *.lock
6 | *.sprs
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | title: rsparse
3 | message: >-
4 | If you use this software, please cite it using the
5 | metadata from this file.
6 | type: software
7 | authors:
8 | - family-names: Lado-Roigé
9 | given-names: Ricard
10 | orcid: 'https://orcid.org/0000-0002-6421-7351'
11 | repository-code: 'https://github.com/RLado/rsparse'
12 | url: 'https://crates.io/crates/rsparse'
13 | abstract: >-
14 | A Rust library for solving sparse linear systems using
15 | direct methods.
16 | keywords:
17 | - sparse-matrices
18 | - linear-algebra
19 | - math
20 | - rust
21 | license: MIT
22 | version: 1.2.1
23 | date-released: '2025-03-29'
24 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rsparse"
3 | version = "1.2.1"
4 | authors = ["Ricard Lado"]
5 |
6 | description = "A Rust library for solving sparse linear systems using direct methods."
7 | categories = ["mathematics", "science"]
8 | keywords = ["math", "linear", "algebra", "sparse", "matrix"]
9 | license = "MIT"
10 | repository = "https://github.com/rlado/rsparse"
11 | readme = "README.md"
12 | edition = "2021"
13 | exclude = [".github/",]
14 |
15 | [lib]
16 | name = "rsparse"
17 | path = "src/lib.rs"
18 |
19 | [dependencies]
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Ricard Lado
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rsparse
2 |
3 | A Rust library for solving sparse linear systems using direct methods.
4 |
5 |
6 |  [](https://crates.io/crates/rsparse) [](https://crates.io/crates/rsparse)
7 |
8 | ---
9 |
10 | ## Data structures
11 | - CSC matrix (`Sprs`)
12 | - Triplet matrix (`Trpl`)
13 |
14 | ## Features
15 | - Convert from dense `[Vec]` or `Vec>` matrix to CSC sparse matrix `Sprs`
16 | - Convert from sparse to dense `Vec>`
17 | - Convert from a triplet format matrix `Trpl` to CSC `Sprs`
18 | - Sparse matrix addition [C=A+B]
19 | - Sparse matrix multiplication [C=A*B]
20 | - Transpose sparse matrices
21 | - Solve sparse linear systems
22 |
23 | ### Solvers
24 | - **lsolve**: Solve a lower triangular system. Solves Lx=b where x and b are dense.
25 | - **ltsolve**: Solve L’x=b where x and b are dense.
26 | - **usolve**: Solve an upper triangular system. Solves Ux=b where x and b are dense
27 | - **utsolve**: Solve U’x=b where x and b are dense
28 | - **cholsol**: A\b solver using Cholesky factorization. Where A is a defined positive `Sprs` matrix and b is a dense vector
29 | - **lusol**: A\b solver using LU factorization. Where A is a square `Sprs` matrix and b is a dense vector
30 | - **qrsol**: A\b solver using QR factorization. Where A is a rectangular `Sprs` matrix and b is a dense vector
31 |
32 | ## Examples
33 | ### Basic matrix operations
34 | ```rust
35 | use rsparse;
36 |
37 | fn main() {
38 | // Create a CSC sparse matrix A
39 | let a = rsparse::data::Sprs{
40 | // Maximum number of entries
41 | nzmax: 5,
42 | // number of rows
43 | m: 3,
44 | // number of columns
45 | n: 3,
46 | // Values
47 | x: vec![1., 9., 9., 2., 9.],
48 | // Indices
49 | i: vec![1, 2, 2, 0, 2],
50 | // Pointers
51 | p: vec![0, 2, 3, 5]
52 | };
53 |
54 | // Import the same matrix from a dense structure
55 | let mut a2 = rsparse::data::Sprs::new_from_vec(
56 | &[
57 | vec![0., 0., 2.],
58 | vec![1., 0., 0.],
59 | vec![9., 9., 9.]
60 | ]
61 | );
62 |
63 | // Check if they are the same
64 | assert_eq!(a.nzmax, a2.nzmax);
65 | assert_eq!(a.m,a2.m);
66 | assert_eq!(a.n,a2.n);
67 | assert_eq!(a.x,a2.x);
68 | assert_eq!(a.i,a2.i);
69 | assert_eq!(a.p,a2.p);
70 |
71 | // Transform A to dense and print result
72 | println!("\nA");
73 | print_matrix(&a.to_dense());
74 |
75 | // Transpose A
76 | let at = rsparse::transpose(&a);
77 | // Transform to dense and print result
78 | println!("\nAt");
79 | print_matrix(&at.to_dense());
80 |
81 | // B = A + A'
82 | let b = &a + &at;
83 | // Transform to dense and print result
84 | println!("\nB");
85 | print_matrix(&b.to_dense());
86 |
87 | // C = A * B
88 | let c = &a * &b;
89 | // Transform to dense and print result
90 | println!("\nC");
91 | print_matrix(&c.to_dense());
92 | }
93 |
94 | fn print_matrix(vec: &[Vec]) {
95 | for row in vec {
96 | println!("{:?}", row);
97 | }
98 | }
99 | ```
100 |
101 | Output:
102 |
103 | ```
104 | A
105 | 0 0 2
106 | 1 0 0
107 | 9 9 9
108 |
109 | At
110 | 0 1 9
111 | 0 0 9
112 | 2 0 9
113 |
114 | B
115 | 0 1 11
116 | 1 0 9
117 | 11 9 18
118 |
119 | C
120 | 22 18 36
121 | 0 1 11
122 | 108 90 342
123 | ```
124 |
125 |
126 | ### Solve a linear system
127 | ```rust
128 | use rsparse;
129 |
130 | fn main() {
131 | // Arbitrary A matrix (dense)
132 | let a = [
133 | vec![8.2541e-01, 9.5622e-01, 4.6698e-01, 8.4410e-03, 6.3193e-01, 7.5741e-01, 5.3584e-01, 3.9448e-01],
134 | vec![7.4808e-01, 2.0403e-01, 9.4649e-01, 2.5086e-01, 2.6931e-01, 5.5866e-01, 3.1827e-01, 2.9819e-02],
135 | vec![6.3980e-01, 9.1615e-01, 8.5515e-01, 9.5323e-01, 7.8323e-01, 8.6003e-01, 7.5761e-01, 8.9255e-01],
136 | vec![1.8726e-01, 8.9339e-01, 9.9796e-01, 5.0506e-01, 6.1439e-01, 4.3617e-01, 7.3369e-01, 1.5565e-01],
137 | vec![2.8015e-02, 6.3404e-01, 8.4771e-01, 8.6419e-01, 2.7555e-01, 3.5909e-01, 7.6644e-01, 8.9905e-02],
138 | vec![9.1817e-01, 8.6629e-01, 5.9917e-01, 1.9346e-01, 2.1960e-01, 1.8676e-01, 8.7020e-01, 2.7891e-01],
139 | vec![3.1999e-01, 5.9988e-01, 8.7402e-01, 5.5710e-01, 2.4707e-01, 7.5652e-01, 8.3682e-01, 6.3145e-01],
140 | vec![9.3807e-01, 7.5985e-02, 7.8758e-01, 3.6881e-01, 4.4553e-01, 5.5005e-02, 3.3908e-01, 3.4573e-01],
141 | ];
142 |
143 | // Convert A to sparse
144 | let mut a_sparse = rsparse::data::Sprs::new();
145 | a_sparse.from_vec(&a);
146 |
147 | // Generate arbitrary b vector
148 | let mut b = [
149 | 0.4377,
150 | 0.7328,
151 | 0.1227,
152 | 0.1817,
153 | 0.2634,
154 | 0.6876,
155 | 0.8711,
156 | 0.4201
157 | ];
158 |
159 | // Known solution:
160 | /*
161 | 0.264678,
162 | -1.228118,
163 | -0.035452,
164 | -0.676711,
165 | -0.066194,
166 | 0.761495,
167 | 1.852384,
168 | -0.282992
169 | */
170 |
171 | // A*x=b -> solve for x -> place x in b
172 | rsparse::lusol(&a_sparse, &mut b, 1, 1e-6);
173 | println!("\nX");
174 | println!("{:?}", &b);
175 | }
176 | ```
177 |
178 | Output:
179 |
180 | ```
181 | X
182 | [0.2646806068156303, -1.2280777288645675, -0.035491404094236435, -0.6766064748053932, -0.06619898266432682, 0.7615102544801993, 1.8522970972589123, -0.2830302118359591]
183 | ```
184 |
185 | ## Documentation
186 | Documentation is available at [docs.rs](https://docs.rs/rsparse).
187 |
188 | ## Sources
189 | - Davis, T. (2006). Direct Methods for Sparse Linear Systems. Society for Industrial and Applied Mathematics. [https://doi.org/10.1137/1.9780898718881](https://doi.org/10.1137/1.9780898718881)
190 | - [CSparse](https://people.math.sc.edu/Burkardt/c_src/csparse/csparse.html): A Concise Sparse Matrix Package in C
--------------------------------------------------------------------------------
/src/data.rs:
--------------------------------------------------------------------------------
1 | //! Data structures for rsparse
2 |
3 | use crate::{add, multiply, scpmat, scxmat};
4 | use std::fmt;
5 | use std::fs::File;
6 | use std::io::{BufRead, BufReader, Write};
7 | use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
8 |
9 | // Define a generic Numeric trait compatible with `Sprs` matrices
10 | /// Define zero trait for generic Numeric type
11 | pub trait Zero {
12 | fn zero() -> Self;
13 | }
14 |
15 | impl Zero for i8 {
16 | fn zero() -> Self {
17 | 0
18 | }
19 | }
20 |
21 | impl Zero for i16 {
22 | fn zero() -> Self {
23 | 0
24 | }
25 | }
26 |
27 | impl Zero for i32 {
28 | fn zero() -> Self {
29 | 0
30 | }
31 | }
32 |
33 | impl Zero for i64 {
34 | fn zero() -> Self {
35 | 0
36 | }
37 | }
38 |
39 | impl Zero for isize {
40 | fn zero() -> Self {
41 | 0
42 | }
43 | }
44 |
45 | impl Zero for f32 {
46 | fn zero() -> Self {
47 | 0.0
48 | }
49 | }
50 |
51 | impl Zero for f64 {
52 | fn zero() -> Self {
53 | 0.0
54 | }
55 | }
56 |
57 | /// Define one trait for generic Numeric type
58 | pub trait One {
59 | fn one() -> Self;
60 | }
61 |
62 | impl One for i8 {
63 | fn one() -> Self {
64 | 1
65 | }
66 | }
67 |
68 | impl One for i16 {
69 | fn one() -> Self {
70 | 1
71 | }
72 | }
73 |
74 | impl One for i32 {
75 | fn one() -> Self {
76 | 1
77 | }
78 | }
79 |
80 | impl One for i64 {
81 | fn one() -> Self {
82 | 1
83 | }
84 | }
85 |
86 | impl One for isize {
87 | fn one() -> Self {
88 | 1
89 | }
90 | }
91 |
92 | impl One for f32 {
93 | fn one() -> Self {
94 | 1.0
95 | }
96 | }
97 |
98 | impl One for f64 {
99 | fn one() -> Self {
100 | 1.0
101 | }
102 | }
103 |
104 | /// Aggregate trait representing numeric values
105 | pub trait Numeric:
106 | Add
107 | + AddAssign
108 | + Sub
109 | + SubAssign
110 | + Neg
111 | + Mul
112 | + MulAssign
113 | + Div
114 | + DivAssign
115 | + Copy
116 | + PartialEq
117 | + PartialOrd
118 | + Default
119 | + Zero
120 | + One
121 | + Add