problemreductions/topology/
triangular_subgraph.rs1use super::graph::Graph;
7use super::unit_disk_graph::UnitDiskGraph;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct TriangularSubgraph {
23 positions: Vec<(i32, i32)>,
25}
26
27const TRIANGULAR_RADIUS: f64 = 1.1;
29
30impl TriangularSubgraph {
31 pub fn new(positions: Vec<(i32, i32)>) -> Self {
33 Self { positions }
34 }
35
36 pub fn positions(&self) -> &[(i32, i32)] {
38 &self.positions
39 }
40
41 pub fn num_positions(&self) -> usize {
43 self.positions.len()
44 }
45
46 #[allow(unknown_lints, clippy::manual_is_multiple_of)]
52 fn physical_position(row: i32, col: i32) -> (f64, f64) {
53 let y = col as f64 * (3.0_f64.sqrt() / 2.0);
54 let offset = if col % 2 == 0 { 0.5 } else { 0.0 };
55 let x = row as f64 + offset;
56 (x, y)
57 }
58
59 fn distance(p1: (f64, f64), p2: (f64, f64)) -> f64 {
61 let dx = p1.0 - p2.0;
62 let dy = p1.1 - p2.1;
63 (dx * dx + dy * dy).sqrt()
64 }
65}
66
67impl Graph for TriangularSubgraph {
68 const NAME: &'static str = "TriangularSubgraph";
69
70 fn num_vertices(&self) -> usize {
71 self.positions.len()
72 }
73
74 fn num_edges(&self) -> usize {
75 let n = self.positions.len();
76 let mut count = 0;
77 for i in 0..n {
78 let pi = Self::physical_position(self.positions[i].0, self.positions[i].1);
79 for j in (i + 1)..n {
80 let pj = Self::physical_position(self.positions[j].0, self.positions[j].1);
81 if Self::distance(pi, pj) < TRIANGULAR_RADIUS {
82 count += 1;
83 }
84 }
85 }
86 count
87 }
88
89 fn edges(&self) -> Vec<(usize, usize)> {
90 let n = self.positions.len();
91 let mut edges = Vec::new();
92 for i in 0..n {
93 let pi = Self::physical_position(self.positions[i].0, self.positions[i].1);
94 for j in (i + 1)..n {
95 let pj = Self::physical_position(self.positions[j].0, self.positions[j].1);
96 if Self::distance(pi, pj) < TRIANGULAR_RADIUS {
97 edges.push((i, j));
98 }
99 }
100 }
101 edges
102 }
103
104 fn has_edge(&self, u: usize, v: usize) -> bool {
105 if u >= self.positions.len() || v >= self.positions.len() || u == v {
106 return false;
107 }
108 let pu = Self::physical_position(self.positions[u].0, self.positions[u].1);
109 let pv = Self::physical_position(self.positions[v].0, self.positions[v].1);
110 Self::distance(pu, pv) < TRIANGULAR_RADIUS
111 }
112
113 fn neighbors(&self, v: usize) -> Vec<usize> {
114 if v >= self.positions.len() {
115 return Vec::new();
116 }
117 let pv = Self::physical_position(self.positions[v].0, self.positions[v].1);
118 (0..self.positions.len())
119 .filter(|&u| {
120 u != v && {
121 let pu = Self::physical_position(self.positions[u].0, self.positions[u].1);
122 Self::distance(pv, pu) < TRIANGULAR_RADIUS
123 }
124 })
125 .collect()
126 }
127}
128
129impl crate::variant::VariantParam for TriangularSubgraph {
130 const CATEGORY: &'static str = "graph";
131 const VALUE: &'static str = "TriangularSubgraph";
132 const PARENT_VALUE: Option<&'static str> = Some("UnitDiskGraph");
133}
134impl crate::variant::CastToParent for TriangularSubgraph {
135 type Parent = UnitDiskGraph;
136 fn cast_to_parent(&self) -> UnitDiskGraph {
137 let positions: Vec<(f64, f64)> = self
138 .positions
139 .iter()
140 .map(|&(r, c)| Self::physical_position(r, c))
141 .collect();
142 UnitDiskGraph::new(positions, TRIANGULAR_RADIUS)
143 }
144}