1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//! Diagonal (DIA) matrix format
//!
//! This module provides the DIA matrix format implementation, which is
//! efficient for matrices with values concentrated on a small number of diagonals.
use crate::error::{SparseError, SparseResult};
use scirs2_core::numeric::{SparseElement, Zero};
/// Diagonal (DIA) matrix
///
/// A sparse matrix format that stores diagonals, making it efficient for
/// matrices with values concentrated on a small number of diagonals.
pub struct DiaMatrix<T> {
/// Number of rows
rows: usize,
/// Number of columns
cols: usize,
/// Diagonals data (n_diags x max(rows, cols))
data: Vec<Vec<T>>,
/// Diagonal offsets from the main diagonal
offsets: Vec<isize>,
}
impl<T> DiaMatrix<T>
where
T: Clone + Copy + Zero + std::cmp::PartialEq + SparseElement,
{
/// Create a new DIA matrix from raw data
///
/// # Arguments
///
/// * `data` - Diagonals data (n_diags x max(rows, cols))
/// * `offsets` - Diagonal offsets from the main diagonal
/// * `shape` - Tuple containing the matrix dimensions (rows, cols)
///
/// # Returns
///
/// * A new DIA matrix
///
/// # Examples
///
/// ```
/// use scirs2_sparse::dia::DiaMatrix;
///
/// // Create a 3x3 sparse matrix with main diagonal and upper diagonal
/// let data = vec![
/// vec![1.0, 2.0, 3.0], // Main diagonal
/// vec![4.0, 5.0, 0.0], // Upper diagonal (k=1)
/// ];
/// let offsets = vec![0, 1]; // Main diagonal and k=1
/// let shape = (3, 3);
///
/// let matrix = DiaMatrix::new(data, offsets, shape).expect("Operation failed");
/// ```
pub fn new(
data: Vec<Vec<T>>,
offsets: Vec<isize>,
shape: (usize, usize),
) -> SparseResult<Self> {
let (rows, cols) = shape;
let max_dim = rows.max(cols);
// Validate input data
if data.len() != offsets.len() {
return Err(SparseError::DimensionMismatch {
expected: data.len(),
found: offsets.len(),
});
}
for diag in data.iter() {
if diag.len() != max_dim {
return Err(SparseError::DimensionMismatch {
expected: max_dim,
found: diag.len(),
});
}
}
Ok(DiaMatrix {
rows,
cols,
data,
offsets,
})
}
/// Create a new empty DIA matrix
///
/// # Arguments
///
/// * `shape` - Tuple containing the matrix dimensions (rows, cols)
///
/// # Returns
///
/// * A new empty DIA matrix
pub fn empty(shape: (usize, usize)) -> Self {
let (rows, cols) = shape;
DiaMatrix {
rows,
cols,
data: Vec::new(),
offsets: Vec::new(),
}
}
/// Get the number of rows in the matrix
pub fn rows(&self) -> usize {
self.rows
}
/// Get the number of columns in the matrix
pub fn cols(&self) -> usize {
self.cols
}
/// Get the shape (dimensions) of the matrix
pub fn shape(&self) -> (usize, usize) {
(self.rows, self.cols)
}
/// Get the number of non-zero elements in the matrix
pub fn nnz(&self) -> usize {
let mut count = 0;
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
// Calculate valid range for this diagonal
let mut start = 0;
let mut end = self.rows.min(self.cols);
if offset < 0 {
start = (-offset) as usize;
}
if offset > 0 {
end = (self.rows as isize - offset) as usize;
}
// Count non-zeros in the valid range
for val in diag.iter().skip(start).take(end - start) {
if *val != T::sparse_zero() {
count += 1;
}
}
}
count
}
/// Convert to dense matrix (as `Vec<Vec<T>>`)
pub fn to_dense(&self) -> Vec<Vec<T>>
where
T: Zero + Copy + SparseElement,
{
let mut result = vec![vec![T::sparse_zero(); self.cols]; self.rows];
for (diag_idx, &offset) in self.offsets.iter().enumerate() {
let diag = &self.data[diag_idx];
if offset >= 0 {
// Upper diagonal
let offset = offset as usize;
for i in 0..self.rows.min(self.cols.saturating_sub(offset)) {
result[i][i + offset] = diag[i];
}
} else {
// Lower diagonal
let offset = (-offset) as usize;
for i in 0..self.cols.min(self.rows.saturating_sub(offset)) {
result[i + offset][i] = diag[i];
}
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dia_create() {
// Create a 3x3 sparse matrix with main diagonal and upper diagonal
let data = vec![
vec![1.0, 2.0, 3.0], // Main diagonal
vec![4.0, 5.0, 0.0], // Upper diagonal (k=1)
];
let offsets = vec![0, 1]; // Main diagonal and k=1
let shape = (3, 3);
let matrix = DiaMatrix::new(data, offsets, shape).expect("Operation failed");
assert_eq!(matrix.shape(), (3, 3));
assert_eq!(matrix.nnz(), 5); // 3 on main diagonal, 2 on upper diagonal
}
#[test]
fn test_dia_to_dense() {
// Create a 3x3 sparse matrix with main diagonal and upper diagonal
let data = vec![
vec![1.0, 2.0, 3.0], // Main diagonal
vec![4.0, 5.0, 0.0], // Upper diagonal (k=1)
];
let offsets = vec![0, 1]; // Main diagonal and k=1
let shape = (3, 3);
let matrix = DiaMatrix::new(data, offsets, shape).expect("Operation failed");
let dense = matrix.to_dense();
let expected = vec![
vec![1.0, 4.0, 0.0],
vec![0.0, 2.0, 5.0],
vec![0.0, 0.0, 3.0],
];
assert_eq!(dense, expected);
}
}