1use crate::{Error, Result};
2
3#[allow(unused_imports)]
4use super::matrix::DistanceMatrix;
5
6#[derive(Debug, Clone)]
16pub struct SparseDistanceMatrix {
17 pub(super) n: usize,
18 pub(super) offsets: Vec<usize>,
21 pub(super) indices: Vec<u32>,
24 pub(super) values: Vec<f64>,
26 pub(super) max_distance: f64,
28}
29
30impl SparseDistanceMatrix {
31 pub(super) fn from_lower_rows(n: usize, rows: &[Vec<(usize, f64)>]) -> Result<Self> {
32 debug_assert_eq!(rows.len(), n);
33 let mut degree = vec![0usize; n];
34 let mut max_distance = 0.0f64;
35 for (i, row) in rows.iter().enumerate() {
36 debug_assert!(row.is_sorted_by_key(|&(j, _)| j));
37 for &(j, distance) in row {
38 debug_assert!(j < i);
39 degree[i] += 1;
40 degree[j] += 1;
41 max_distance = max_distance.max(distance);
42 }
43 }
44 let mut offsets = vec![0usize; n + 1];
45 let mut total = 0usize;
46 for (vertex, &count) in degree.iter().enumerate() {
47 offsets[vertex] = total;
48 total = total
49 .checked_add(count)
50 .ok_or_else(|| Error::InvalidInput("sparse edge storage overflows usize".into()))?;
51 }
52 offsets[n] = total;
53 let mut indices = vec![0u32; total];
54 let mut values = vec![0.0; total];
55 let mut cursor = offsets[..n].to_vec();
56 for (i, row) in rows.iter().enumerate() {
57 for &(j, distance) in row {
58 indices[cursor[i]] = j as u32;
59 values[cursor[i]] = distance;
60 cursor[i] += 1;
61 indices[cursor[j]] = i as u32;
62 values[cursor[j]] = distance;
63 cursor[j] += 1;
64 }
65 }
66 debug_assert!((0..n).all(|v| {
69 let start = offsets[v];
70 let end = offsets[v + 1];
71 indices[start..end].is_sorted()
72 }));
73 Ok(Self {
74 n,
75 offsets,
76 indices,
77 values,
78 max_distance,
79 })
80 }
81
82 pub fn from_triplets(n: usize, triplets: &[(usize, usize, f64)]) -> Result<Self> {
87 let degree = validate_triplets(n, triplets)?;
88 let mut offsets = offsets_from_degrees(°ree);
89 let (mut indices, mut values) = fill_neighbor_storage(triplets, &offsets);
90 let write = compact_neighbor_storage(n, °ree, &mut offsets, &mut indices, &mut values)?;
91 offsets[n] = write;
92 indices.truncate(write);
93 values.truncate(write);
94
95 let max_distance = triplets.iter().fold(0.0f64, |m, &(_, _, d)| m.max(d));
96 Ok(Self {
97 n,
98 offsets,
99 indices,
100 values,
101 max_distance,
102 })
103 }
104
105 pub fn len(&self) -> usize {
107 self.n
108 }
109
110 pub fn is_empty(&self) -> bool {
112 self.n == 0
113 }
114
115 pub fn num_edges(&self) -> usize {
117 self.indices.len() / 2
118 }
119
120 #[inline]
122 pub(super) fn span(&self, v: usize) -> (usize, usize) {
123 (self.offsets[v], self.offsets[v + 1])
124 }
125
126 #[cfg(test)]
128 #[inline]
129 pub(super) fn degree(&self, v: usize) -> usize {
130 self.offsets[v + 1] - self.offsets[v]
131 }
132
133 #[inline]
135 pub fn get(&self, i: usize, j: usize) -> f64 {
136 debug_assert!(i < self.n && j < self.n);
137 if i == j {
138 return 0.0;
139 }
140 let (start, end) = self.span(i);
141 match self.indices[start..end].binary_search(&(j as u32)) {
142 Ok(pos) => self.values[start + pos],
143 Err(_) => f64::INFINITY,
144 }
145 }
146
147 pub fn edges(&self) -> impl Iterator<Item = (usize, usize, f64)> + '_ {
150 (0..self.n).flat_map(move |u| {
151 let (start, end) = self.span(u);
152 self.indices[start..end]
153 .iter()
154 .zip(&self.values[start..end])
155 .filter(move |&(&v, _)| u < v as usize)
156 .map(move |(&v, &d)| (u, v as usize, d))
157 })
158 }
159}
160
161fn validate_triplets(n: usize, triplets: &[(usize, usize, f64)]) -> Result<Vec<usize>> {
162 if n > u32::MAX as usize {
163 return Err(Error::InvalidInput(format!(
164 "sparse matrix holds at most {} points, got {n}",
165 u32::MAX
166 )));
167 }
168 let mut degree = vec![0usize; n];
169 for (index, &(i, j, distance)) in triplets.iter().enumerate() {
170 validate_triplet(index, i, j, distance, n)?;
171 degree[i] += 1;
172 degree[j] += 1;
173 }
174 Ok(degree)
175}
176
177fn validate_triplet(index: usize, i: usize, j: usize, distance: f64, n: usize) -> Result<()> {
178 if i >= n || j >= n {
179 return Err(Error::InvalidInput(format!(
180 "triplet {index}: vertex out of range ({i}, {j}) for n = {n}"
181 )));
182 }
183 if i == j {
184 return Err(Error::InvalidInput(format!(
185 "triplet {index}: self-distance for vertex {i}"
186 )));
187 }
188 if !distance.is_finite() || distance < 0.0 {
189 return Err(Error::InvalidDistance(format!(
190 "triplet {index}: distance must be finite and non-negative, got {distance}"
191 )));
192 }
193 Ok(())
194}
195
196fn offsets_from_degrees(degree: &[usize]) -> Vec<usize> {
197 let mut offsets = vec![0usize; degree.len() + 1];
198 let mut total = 0usize;
199 for (vertex, &value) in degree.iter().enumerate() {
200 offsets[vertex] = total;
201 total += value;
202 }
203 offsets[degree.len()] = total;
204 offsets
205}
206
207fn fill_neighbor_storage(
208 triplets: &[(usize, usize, f64)],
209 offsets: &[usize],
210) -> (Vec<u32>, Vec<f64>) {
211 let total = offsets.last().copied().unwrap_or(0);
212 let mut indices = vec![0u32; total];
213 let mut values = vec![0.0f64; total];
214 let mut cursor = offsets[..offsets.len() - 1].to_vec();
215 for &(i, j, distance) in triplets {
216 let distance = if distance == 0.0 { 0.0 } else { distance };
217 indices[cursor[i]] = j as u32;
218 values[cursor[i]] = distance;
219 cursor[i] += 1;
220 indices[cursor[j]] = i as u32;
221 values[cursor[j]] = distance;
222 cursor[j] += 1;
223 }
224 (indices, values)
225}
226
227fn compact_neighbor_storage(
228 n: usize,
229 degree: &[usize],
230 offsets: &mut [usize],
231 indices: &mut [u32],
232 values: &mut [f64],
233) -> Result<usize> {
234 let widest = degree.iter().copied().max().unwrap_or(0);
235 let mut list = Vec::<(u32, f64)>::with_capacity(widest);
236 let mut write = 0usize;
237 for vertex in 0..n {
238 let (start, end) = (offsets[vertex], offsets[vertex + 1]);
239 offsets[vertex] = write;
240 if indices[start..end].is_sorted_by(|a, b| a < b) {
241 copy_sorted_neighbors(start, end, write, indices, values);
242 write += end - start;
243 } else {
244 write = sort_and_copy_neighbors(vertex, start, end, write, indices, values, &mut list)?;
245 }
246 }
247 Ok(write)
248}
249
250fn copy_sorted_neighbors(
251 start: usize,
252 end: usize,
253 write: usize,
254 indices: &mut [u32],
255 values: &mut [f64],
256) {
257 if start != write {
258 indices.copy_within(start..end, write);
259 values.copy_within(start..end, write);
260 }
261}
262
263fn sort_and_copy_neighbors(
264 vertex: usize,
265 start: usize,
266 end: usize,
267 mut write: usize,
268 indices: &mut [u32],
269 values: &mut [f64],
270 list: &mut Vec<(u32, f64)>,
271) -> Result<usize> {
272 list.clear();
273 list.extend(
274 indices[start..end]
275 .iter()
276 .zip(&values[start..end])
277 .map(|(&neighbor, &distance)| (neighbor, distance)),
278 );
279 list.sort_unstable_by(|left, right| left.0.cmp(&right.0).then(left.1.total_cmp(&right.1)));
280 reject_conflicting_neighbors(vertex, list)?;
281 list.dedup_by(|left, right| left.0 == right.0 && left.1 == right.1);
282 for &(neighbor, distance) in list.iter() {
283 indices[write] = neighbor;
284 values[write] = distance;
285 write += 1;
286 }
287 Ok(write)
288}
289
290fn reject_conflicting_neighbors(vertex: usize, neighbors: &[(u32, f64)]) -> Result<()> {
291 for pair in neighbors.windows(2) {
292 if pair[0].0 == pair[1].0 && pair[0].1 != pair[1].1 {
293 return Err(Error::InvalidInput(format!(
294 "conflicting distances for pair ({vertex}, {}): {} vs {}",
295 pair[0].0, pair[0].1, pair[1].1
296 )));
297 }
298 }
299 Ok(())
300}