Skip to content

Commit 28126fa

Browse files
added from_coo method to csr matrix
1 parent 30e1d5f commit 28126fa

3 files changed

Lines changed: 96 additions & 3 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "extended_matrix"
3-
version = "0.9.6"
3+
version = "0.9.7"
44
authors = ["Roman Shushakov <roman.a.shushakov1@gmail.com>"]
55
edition = "2024"
66
description = "A matrix calculation module."

src/matrix/csr_matrix.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,67 @@ where
114114
CsrMatrix::create(n_rows, n_cols, values, col_index, row_ptr)
115115
}
116116

117+
pub fn from_coo(
118+
n_rows: usize,
119+
n_cols: usize,
120+
triplets: &[(usize, usize, V)],
121+
) -> Result<Self, String> {
122+
if n_rows == 0 || n_cols == 0 {
123+
return Err("CsrMatrix::from_coo: empty shape".to_string());
124+
}
125+
126+
// Copy + validate
127+
let mut entries: Vec<(usize, usize, V)> = Vec::with_capacity(triplets.len());
128+
for &(r, c, v) in triplets {
129+
if r >= n_rows || c >= n_cols {
130+
return Err(format!(
131+
"CsrMatrix::from_coo: index out of bounds: ({},{}) for {}x{}",
132+
r, c, n_rows, n_cols
133+
));
134+
}
135+
entries.push((r, c, v));
136+
}
137+
138+
// Sort by (row, col)
139+
entries.sort_by(|a, b| (a.0, a.1).cmp(&(b.0, b.1)));
140+
141+
// Compress duplicates
142+
let mut cols: Vec<usize> = Vec::new();
143+
let mut vals: Vec<V> = Vec::new();
144+
let mut row_ptr = vec![0usize; n_rows + 1];
145+
146+
let mut last_rc: Option<(usize, usize)> = None;
147+
148+
for (r, c, v) in entries {
149+
match last_rc {
150+
Some((lr, lc)) if lr == r && lc == c => {
151+
// duplicate -> sum
152+
let last = vals.last_mut().unwrap();
153+
*last = *last + v;
154+
}
155+
_ => {
156+
cols.push(c);
157+
vals.push(v);
158+
row_ptr[r + 1] += 1; // count nnz in row r
159+
last_rc = Some((r, c));
160+
}
161+
}
162+
}
163+
164+
// Prefix sum: counts -> row pointers
165+
for i in 0..n_rows {
166+
row_ptr[i + 1] += row_ptr[i];
167+
}
168+
169+
Ok(CsrMatrix {
170+
n_rows,
171+
n_cols,
172+
values: vals,
173+
col_index: cols,
174+
row_ptr,
175+
})
176+
}
177+
117178
pub fn spmv(&self, x: &[V]) -> Result<Vec<V>, String> {
118179
if x.len() != self.n_cols {
119180
return Err(format!(

src/tests/matrix/test_csr_matrix.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
use crate::{BasicOperationsTrait, CsrMatrix, Position, SquareMatrix};
44

5+
const ABS_TOL: f64 = 1e-12;
6+
57
fn mat2x2(a11: f64, a12: f64, a21: f64, a22: f64) -> SquareMatrix<f64> {
68
SquareMatrix::create(2, &[a11, a12, a21, a22])
79
}
810

911
#[test]
1012
fn test_csr_from_square_matrix_and_spmv() {
11-
const ABS_TOL: f64 = 1e-12;
12-
1313
// A = [[4, 0],
1414
// [1, 3]]
1515
let a = mat2x2(4.0, 0.0, 1.0, 3.0);
@@ -23,3 +23,35 @@ fn test_csr_from_square_matrix_and_spmv() {
2323
assert!((y[0] - 4.0).abs() < ABS_TOL);
2424
assert!((y[1] - 7.0).abs() < ABS_TOL);
2525
}
26+
27+
#[test]
28+
fn test_from_coo_basic_spmv() {
29+
// A = [4 1 0;
30+
// 1 3 1;
31+
// 0 1 2]
32+
let trip = vec![
33+
(0, 0, 4.0f64),
34+
(0, 1, 1.0),
35+
(1, 0, 1.0),
36+
(1, 1, 3.0),
37+
(1, 2, 1.0),
38+
(2, 1, 1.0),
39+
(2, 2, 2.0),
40+
];
41+
42+
let csr = CsrMatrix::from_coo(3, 3, &trip).unwrap();
43+
let y = csr.spmv(&[1.0, 2.0, 3.0]).unwrap();
44+
assert!((y[0] - 6.0).abs() < ABS_TOL);
45+
assert!((y[1] - 10.0).abs() < ABS_TOL);
46+
assert!((y[2] - 8.0).abs() < ABS_TOL);
47+
}
48+
49+
#[test]
50+
fn test_from_coo_sums_duplicates() {
51+
// A(0,0) = 1 + 2 = 3, A(1,1)=4
52+
let trip = vec![(0usize, 0usize, 1.0f64), (0, 0, 2.0), (1, 1, 4.0)];
53+
let csr = CsrMatrix::from_coo(2, 2, &trip).unwrap();
54+
let y = csr.spmv(&[1.0, 1.0]).unwrap();
55+
assert!((y[0] - 3.0).abs() < ABS_TOL);
56+
assert!((y[1] - 4.0).abs() < ABS_TOL);
57+
}

0 commit comments

Comments
 (0)