scirs2_sparse/
convert.rs

1//! Conversion utilities for sparse matrices
2//!
3//! This module provides functions for converting between different sparse matrix
4//! formats and between sparse and dense representations.
5
6use crate::coo::CooMatrix;
7use crate::csc::CscMatrix;
8use crate::csr::CsrMatrix;
9use crate::error::SparseResult;
10use scirs2_core::ndarray::Array2;
11
12/// Convert a dense matrix to CSR format
13///
14/// # Arguments
15///
16/// * `dense` - Dense matrix as 2D array
17///
18/// # Returns
19///
20/// * Sparse matrix in CSR format
21///
22/// # Example
23///
24/// ```
25/// use scirs2_core::ndarray::Array2;
26/// use scirs2_sparse::convert::dense_to_csr;
27///
28/// let dense = Array2::from_shape_vec((3, 3), vec![
29///     1.0, 0.0, 2.0,
30///     0.0, 0.0, 3.0,
31///     4.0, 5.0, 0.0,
32/// ]).expect("Operation failed");
33///
34/// let sparse = dense_to_csr(&dense).expect("Operation failed");
35/// ```
36#[allow(dead_code)]
37pub fn dense_to_csr(dense: &Array2<f64>) -> SparseResult<CsrMatrix<f64>> {
38    let shape = dense.dim();
39    let (rows, cols) = (shape.0, shape.1);
40
41    let mut data = Vec::new();
42    let mut row_indices = Vec::new();
43    let mut col_indices = Vec::new();
44
45    for i in 0..rows {
46        for j in 0..cols {
47            let val = dense[[i, j]];
48            if val != 0.0 {
49                data.push(val);
50                row_indices.push(i);
51                col_indices.push(j);
52            }
53        }
54    }
55
56    CsrMatrix::new(data, row_indices, col_indices, (rows, cols))
57}
58
59/// Convert a dense matrix to CSC format
60///
61/// # Arguments
62///
63/// * `dense` - Dense matrix as 2D array
64///
65/// # Returns
66///
67/// * Sparse matrix in CSC format
68#[allow(dead_code)]
69pub fn dense_to_csc(dense: &Array2<f64>) -> SparseResult<CscMatrix<f64>> {
70    let shape = dense.dim();
71    let (rows, cols) = (shape.0, shape.1);
72
73    let mut data = Vec::new();
74    let mut row_indices = Vec::new();
75    let mut col_indices = Vec::new();
76
77    for i in 0..rows {
78        for j in 0..cols {
79            let val = dense[[i, j]];
80            if val != 0.0 {
81                data.push(val);
82                row_indices.push(i);
83                col_indices.push(j);
84            }
85        }
86    }
87
88    CscMatrix::new(data, row_indices, col_indices, (rows, cols))
89}
90
91/// Convert a dense matrix to COO format
92///
93/// # Arguments
94///
95/// * `dense` - Dense matrix as 2D array
96///
97/// # Returns
98///
99/// * Sparse matrix in COO format
100#[allow(dead_code)]
101pub fn dense_to_coo(dense: &Array2<f64>) -> SparseResult<CooMatrix<f64>> {
102    let shape = dense.dim();
103    let (rows, cols) = (shape.0, shape.1);
104
105    let mut data = Vec::new();
106    let mut row_indices = Vec::new();
107    let mut col_indices = Vec::new();
108
109    for i in 0..rows {
110        for j in 0..cols {
111            let val = dense[[i, j]];
112            if val != 0.0 {
113                data.push(val);
114                row_indices.push(i);
115                col_indices.push(j);
116            }
117        }
118    }
119
120    CooMatrix::new(data, row_indices, col_indices, (rows, cols))
121}
122
123/// Convert a CSR matrix to dense format
124///
125/// # Arguments
126///
127/// * `sparse` - Sparse matrix in CSR format
128///
129/// # Returns
130///
131/// * Dense matrix as 2D array
132#[allow(dead_code)]
133pub fn csr_to_dense(sparse: &CsrMatrix<f64>) -> Array2<f64> {
134    let (rows, cols) = sparse.shape();
135    let mut dense = Array2::zeros((rows, cols));
136
137    let dense_vec = sparse.to_dense();
138    for i in 0..rows {
139        for j in 0..cols {
140            dense[[i, j]] = dense_vec[i][j];
141        }
142    }
143
144    dense
145}
146
147/// Convert a CSC matrix to dense format
148///
149/// # Arguments
150///
151/// * `sparse` - Sparse matrix in CSC format
152///
153/// # Returns
154///
155/// * Dense matrix as 2D array
156#[allow(dead_code)]
157pub fn csc_to_dense(sparse: &CscMatrix<f64>) -> Array2<f64> {
158    let (rows, cols) = sparse.shape();
159    let mut dense = Array2::zeros((rows, cols));
160
161    let dense_vec = sparse.to_dense();
162    for i in 0..rows {
163        for j in 0..cols {
164            dense[[i, j]] = dense_vec[i][j];
165        }
166    }
167
168    dense
169}
170
171/// Convert a COO matrix to dense format
172///
173/// # Arguments
174///
175/// * `sparse` - Sparse matrix in COO format
176///
177/// # Returns
178///
179/// * Dense matrix as 2D array
180#[allow(dead_code)]
181pub fn coo_to_dense(sparse: &CooMatrix<f64>) -> Array2<f64> {
182    let (rows, cols) = sparse.shape();
183    let mut dense = Array2::zeros((rows, cols));
184
185    let dense_vec = sparse.to_dense();
186    for i in 0..rows {
187        for j in 0..cols {
188            dense[[i, j]] = dense_vec[i][j];
189        }
190    }
191
192    dense
193}
194
195/// Convert a CSR matrix to COO format
196///
197/// # Arguments
198///
199/// * `csr` - Sparse matrix in CSR format
200///
201/// # Returns
202///
203/// * Sparse matrix in COO format
204#[allow(dead_code)]
205pub fn csr_to_coo(csr: &CsrMatrix<f64>) -> CooMatrix<f64> {
206    let (rows, cols) = csr.shape();
207    let dense = csr.to_dense();
208
209    let mut data = Vec::new();
210    let mut row_indices = Vec::new();
211    let mut col_indices = Vec::new();
212
213    for (i, row) in dense.iter().enumerate().take(rows) {
214        for (j, &val) in row.iter().enumerate().take(cols) {
215            if val != 0.0 {
216                data.push(val);
217                row_indices.push(i);
218                col_indices.push(j);
219            }
220        }
221    }
222
223    CooMatrix::new(data, row_indices, col_indices, (rows, cols)).expect("Operation failed")
224}
225
226/// Convert a CSC matrix to COO format
227///
228/// # Arguments
229///
230/// * `csc` - Sparse matrix in CSC format
231///
232/// # Returns
233///
234/// * Sparse matrix in COO format
235#[allow(dead_code)]
236pub fn csc_to_coo(csc: &CscMatrix<f64>) -> CooMatrix<f64> {
237    let (rows, cols) = csc.shape();
238    let dense = csc.to_dense();
239
240    let mut data = Vec::new();
241    let mut row_indices = Vec::new();
242    let mut col_indices = Vec::new();
243
244    for (i, row) in dense.iter().enumerate().take(rows) {
245        for (j, &val) in row.iter().enumerate().take(cols) {
246            if val != 0.0 {
247                data.push(val);
248                row_indices.push(i);
249                col_indices.push(j);
250            }
251        }
252    }
253
254    CooMatrix::new(data, row_indices, col_indices, (rows, cols)).expect("Operation failed")
255}
256
257/// Convert a COO matrix to CSR format
258///
259/// # Arguments
260///
261/// * `coo` - Sparse matrix in COO format
262///
263/// # Returns
264///
265/// * Sparse matrix in CSR format
266#[allow(dead_code)]
267pub fn coo_to_csr(coo: &CooMatrix<f64>) -> CsrMatrix<f64> {
268    let (rows, cols) = coo.shape();
269    let dense = coo.to_dense();
270
271    let mut data = Vec::new();
272    let mut row_indices = Vec::new();
273    let mut col_indices = Vec::new();
274
275    for (i, row) in dense.iter().enumerate().take(rows) {
276        for (j, &val) in row.iter().enumerate().take(cols) {
277            if val != 0.0 {
278                data.push(val);
279                row_indices.push(i);
280                col_indices.push(j);
281            }
282        }
283    }
284
285    CsrMatrix::new(data, row_indices, col_indices, (rows, cols)).expect("Operation failed")
286}
287
288/// Convert a COO matrix to CSC format
289///
290/// # Arguments
291///
292/// * `coo` - Sparse matrix in COO format
293///
294/// # Returns
295///
296/// * Sparse matrix in CSC format
297#[allow(dead_code)]
298pub fn coo_to_csc(coo: &CooMatrix<f64>) -> CscMatrix<f64> {
299    let (rows, cols) = coo.shape();
300    let dense = coo.to_dense();
301
302    let mut data = Vec::new();
303    let mut row_indices = Vec::new();
304    let mut col_indices = Vec::new();
305
306    for (i, row) in dense.iter().enumerate().take(rows) {
307        for (j, &val) in row.iter().enumerate().take(cols) {
308            if val != 0.0 {
309                data.push(val);
310                row_indices.push(i);
311                col_indices.push(j);
312            }
313        }
314    }
315
316    CscMatrix::new(data, row_indices, col_indices, (rows, cols)).expect("Operation failed")
317}
318
319/// Convert a CSR matrix to CSC format
320///
321/// # Arguments
322///
323/// * `csr` - Sparse matrix in CSR format
324///
325/// # Returns
326///
327/// * Sparse matrix in CSC format
328#[allow(dead_code)]
329pub fn csr_to_csc<F>(csr: &CsrMatrix<F>) -> SparseResult<CscMatrix<F>>
330where
331    F: Clone
332        + Copy
333        + std::fmt::Debug
334        + PartialEq
335        + scirs2_core::numeric::Zero
336        + scirs2_core::SparseElement,
337{
338    // Start with CSR in triplet format
339    let (rows, cols) = csr.shape();
340    let mut row_indices = Vec::new();
341    let mut col_indices = Vec::new();
342    let mut values = Vec::new();
343
344    // Extract all non-zero entries from CSR into COO format triplets
345    for i in 0..rows {
346        for j in csr.indptr[i]..csr.indptr[i + 1] {
347            if j < csr.indices.len() {
348                let col = csr.indices[j];
349                let val = csr.data[j];
350
351                row_indices.push(i);
352                col_indices.push(col);
353                values.push(val);
354            }
355        }
356    }
357
358    // Create a COO matrix from the triplets
359    let coo = CooMatrix::new(values, row_indices, col_indices, (rows, cols))?;
360
361    // Convert COO to CSC (which basically just sorts by column, then row)
362    Ok(coo.to_csc())
363}
364
365#[cfg(test)]
366mod tests {
367    use super::*;
368    use approx::assert_relative_eq;
369    use scirs2_core::ndarray::Array2;
370
371    #[test]
372    fn test_dense_to_csr_to_dense() {
373        // Create a dense matrix
374        let dense =
375            Array2::from_shape_vec((3, 3), vec![1.0, 0.0, 2.0, 0.0, 0.0, 3.0, 4.0, 5.0, 0.0])
376                .expect("Operation failed");
377
378        // Convert to CSR
379        let csr = dense_to_csr(&dense).expect("Operation failed");
380
381        // Convert back to dense
382        let dense2 = csr_to_dense(&csr);
383
384        // Check equality
385        for i in 0..3 {
386            for j in 0..3 {
387                assert_relative_eq!(dense[[i, j]], dense2[[i, j]], epsilon = 1e-10);
388            }
389        }
390    }
391
392    #[test]
393    fn test_dense_to_csc_to_dense() {
394        // Create a dense matrix
395        let dense =
396            Array2::from_shape_vec((3, 3), vec![1.0, 0.0, 2.0, 0.0, 0.0, 3.0, 4.0, 5.0, 0.0])
397                .expect("Operation failed");
398
399        // Convert to CSC
400        let csc = dense_to_csc(&dense).expect("Operation failed");
401
402        // Convert back to dense
403        let dense2 = csc_to_dense(&csc);
404
405        // Check equality
406        for i in 0..3 {
407            for j in 0..3 {
408                assert_relative_eq!(dense[[i, j]], dense2[[i, j]], epsilon = 1e-10);
409            }
410        }
411    }
412
413    #[test]
414    fn test_dense_to_coo_to_dense() {
415        // Create a dense matrix
416        let dense =
417            Array2::from_shape_vec((3, 3), vec![1.0, 0.0, 2.0, 0.0, 0.0, 3.0, 4.0, 5.0, 0.0])
418                .expect("Operation failed");
419
420        // Convert to COO
421        let coo = dense_to_coo(&dense).expect("Operation failed");
422
423        // Convert back to dense
424        let dense2 = coo_to_dense(&coo);
425
426        // Check equality
427        for i in 0..3 {
428            for j in 0..3 {
429                assert_relative_eq!(dense[[i, j]], dense2[[i, j]], epsilon = 1e-10);
430            }
431        }
432    }
433
434    #[test]
435    fn test_format_conversions() {
436        // Create a COO matrix
437        let rows = vec![0, 0, 1, 2, 2];
438        let cols = vec![0, 2, 2, 0, 1];
439        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
440        let shape = (3, 3);
441
442        let coo = CooMatrix::new(data, rows, cols, shape).expect("Operation failed");
443
444        // Convert to CSR
445        let csr = coo_to_csr(&coo);
446
447        // Convert CSR to COO
448        let coo2 = csr_to_coo(&csr);
449
450        // Check that the conversions preserved the data
451        let dense1 = coo.to_dense();
452        let dense2 = coo2.to_dense();
453
454        assert_eq!(dense1, dense2);
455    }
456}