problemreductions/topology/
unit_disk_graph.rs1use super::graph::Graph;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub struct UnitDiskGraph {
34 positions: Vec<(f64, f64)>,
36 radius: f64,
38 edges: Vec<(usize, usize)>,
40}
41
42impl UnitDiskGraph {
43 pub fn new(positions: Vec<(f64, f64)>, radius: f64) -> Self {
50 let n = positions.len();
51 let mut edges = Vec::new();
52
53 for i in 0..n {
55 for j in (i + 1)..n {
56 if Self::distance(&positions[i], &positions[j]) <= radius {
57 edges.push((i, j));
58 }
59 }
60 }
61
62 Self {
63 positions,
64 radius,
65 edges,
66 }
67 }
68
69 pub fn unit(positions: Vec<(f64, f64)>) -> Self {
71 Self::new(positions, 1.0)
72 }
73
74 fn distance(p1: &(f64, f64), p2: &(f64, f64)) -> f64 {
76 let dx = p1.0 - p2.0;
77 let dy = p1.1 - p2.1;
78 (dx * dx + dy * dy).sqrt()
79 }
80
81 pub fn num_vertices(&self) -> usize {
83 self.positions.len()
84 }
85
86 pub fn num_edges(&self) -> usize {
88 self.edges.len()
89 }
90
91 pub fn radius(&self) -> f64 {
93 self.radius
94 }
95
96 pub fn position(&self, v: usize) -> Option<(f64, f64)> {
98 self.positions.get(v).copied()
99 }
100
101 pub fn positions(&self) -> &[(f64, f64)] {
103 &self.positions
104 }
105
106 pub fn edges(&self) -> &[(usize, usize)] {
108 &self.edges
109 }
110
111 pub fn has_edge(&self, u: usize, v: usize) -> bool {
113 let (u, v) = if u < v { (u, v) } else { (v, u) };
114 self.edges.contains(&(u, v))
115 }
116
117 pub fn vertex_distance(&self, u: usize, v: usize) -> Option<f64> {
119 match (self.positions.get(u), self.positions.get(v)) {
120 (Some(p1), Some(p2)) => Some(Self::distance(p1, p2)),
121 _ => None,
122 }
123 }
124
125 pub fn neighbors(&self, v: usize) -> Vec<usize> {
127 self.edges
128 .iter()
129 .filter_map(|&(u1, u2)| {
130 if u1 == v {
131 Some(u2)
132 } else if u2 == v {
133 Some(u1)
134 } else {
135 None
136 }
137 })
138 .collect()
139 }
140
141 pub fn degree(&self, v: usize) -> usize {
143 self.neighbors(v).len()
144 }
145
146 pub fn bounding_box(&self) -> Option<((f64, f64), (f64, f64))> {
148 if self.positions.is_empty() {
149 return None;
150 }
151
152 let min_x = self.positions.iter().map(|p| p.0).fold(f64::INFINITY, f64::min);
153 let max_x = self.positions.iter().map(|p| p.0).fold(f64::NEG_INFINITY, f64::max);
154 let min_y = self.positions.iter().map(|p| p.1).fold(f64::INFINITY, f64::min);
155 let max_y = self.positions.iter().map(|p| p.1).fold(f64::NEG_INFINITY, f64::max);
156
157 Some(((min_x, min_y), (max_x, max_y)))
158 }
159
160 pub fn grid(rows: usize, cols: usize, spacing: f64, radius: f64) -> Self {
169 let mut positions = Vec::with_capacity(rows * cols);
170 for r in 0..rows {
171 for c in 0..cols {
172 positions.push((c as f64 * spacing, r as f64 * spacing));
173 }
174 }
175 Self::new(positions, radius)
176 }
177}
178
179impl Graph for UnitDiskGraph {
180 fn num_vertices(&self) -> usize {
181 self.positions.len()
182 }
183
184 fn num_edges(&self) -> usize {
185 self.edges.len()
186 }
187
188 fn edges(&self) -> Vec<(usize, usize)> {
189 self.edges.clone()
190 }
191
192 fn has_edge(&self, u: usize, v: usize) -> bool {
193 let (u, v) = if u < v { (u, v) } else { (v, u) };
194 self.edges.contains(&(u, v))
195 }
196
197 fn neighbors(&self, v: usize) -> Vec<usize> {
198 self.edges
199 .iter()
200 .filter_map(|&(u1, u2)| {
201 if u1 == v {
202 Some(u2)
203 } else if u2 == v {
204 Some(u1)
205 } else {
206 None
207 }
208 })
209 .collect()
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_udg_basic() {
219 let udg = UnitDiskGraph::new(
220 vec![(0.0, 0.0), (1.0, 0.0), (3.0, 0.0)],
221 1.0,
222 );
223 assert_eq!(udg.num_vertices(), 3);
224 assert_eq!(udg.num_edges(), 1); }
226
227 #[test]
228 fn test_udg_unit() {
229 let udg = UnitDiskGraph::unit(vec![(0.0, 0.0), (0.5, 0.5)]);
230 assert_eq!(udg.radius(), 1.0);
231 assert_eq!(udg.num_edges(), 1);
233 }
234
235 #[test]
236 fn test_udg_has_edge() {
237 let udg = UnitDiskGraph::new(
238 vec![(0.0, 0.0), (1.0, 0.0), (3.0, 0.0)],
239 1.0,
240 );
241 assert!(udg.has_edge(0, 1));
242 assert!(udg.has_edge(1, 0)); assert!(!udg.has_edge(0, 2));
244 assert!(!udg.has_edge(1, 2));
245 }
246
247 #[test]
248 fn test_udg_neighbors() {
249 let udg = UnitDiskGraph::new(
250 vec![(0.0, 0.0), (1.0, 0.0), (0.5, 0.5)],
251 1.0,
252 );
253 let neighbors = udg.neighbors(0);
254 assert!(neighbors.contains(&1));
256 assert!(neighbors.contains(&2));
257 }
258
259 #[test]
260 fn test_udg_degree() {
261 let udg = UnitDiskGraph::new(
262 vec![(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (5.0, 5.0)],
263 1.5,
264 );
265 assert_eq!(udg.degree(0), 2);
267 assert_eq!(udg.degree(3), 0);
269 }
270
271 #[test]
272 fn test_udg_vertex_distance() {
273 let udg = UnitDiskGraph::new(
274 vec![(0.0, 0.0), (3.0, 4.0)],
275 10.0,
276 );
277 let dist = udg.vertex_distance(0, 1);
278 assert_eq!(dist, Some(5.0)); }
280
281 #[test]
282 fn test_udg_position() {
283 let udg = UnitDiskGraph::new(
284 vec![(1.0, 2.0), (3.0, 4.0)],
285 1.0,
286 );
287 assert_eq!(udg.position(0), Some((1.0, 2.0)));
288 assert_eq!(udg.position(1), Some((3.0, 4.0)));
289 assert_eq!(udg.position(2), None);
290 }
291
292 #[test]
293 fn test_udg_bounding_box() {
294 let udg = UnitDiskGraph::new(
295 vec![(1.0, 2.0), (3.0, 4.0), (-1.0, 0.0)],
296 1.0,
297 );
298 let bbox = udg.bounding_box();
299 assert!(bbox.is_some());
300 let ((min_x, min_y), (max_x, max_y)) = bbox.unwrap();
301 assert_eq!(min_x, -1.0);
302 assert_eq!(max_x, 3.0);
303 assert_eq!(min_y, 0.0);
304 assert_eq!(max_y, 4.0);
305 }
306
307 #[test]
308 fn test_udg_empty_bounding_box() {
309 let udg = UnitDiskGraph::new(vec![], 1.0);
310 assert!(udg.bounding_box().is_none());
311 }
312
313 #[test]
314 fn test_udg_grid() {
315 let udg = UnitDiskGraph::grid(2, 3, 1.0, 1.0);
316 assert_eq!(udg.num_vertices(), 6);
317 assert_eq!(udg.num_edges(), 7);
322 }
323
324 #[test]
325 fn test_udg_grid_diagonal() {
326 let udg = UnitDiskGraph::grid(2, 2, 1.0, 1.5);
328 assert_eq!(udg.num_vertices(), 4);
329 assert_eq!(udg.num_edges(), 6);
332 }
333
334 #[test]
335 fn test_udg_edges_list() {
336 let udg = UnitDiskGraph::new(
337 vec![(0.0, 0.0), (1.0, 0.0)],
338 1.0,
339 );
340 let edges = udg.edges();
341 assert_eq!(edges.len(), 1);
342 assert_eq!(edges[0], (0, 1));
343 }
344}