1#![allow(non_snake_case)]
18use serde::{Deserialize, Serialize};
19use std::collections::{HashMap, HashSet};
20
21#[derive(Clone, Debug, Serialize, Deserialize)]
23pub struct FleetAgent {
24 pub id: u64,
25 pub position: [f64; 2],
26 pub neighbors: Vec<u64>,
27 pub capabilities: Vec<String>,
28}
29
30#[derive(Clone, Debug, Serialize, Deserialize)]
32pub struct FleetGraph {
33 agents: Vec<FleetAgent>,
34 agent_index: HashMap<u64, usize>,
36 adjacency: HashMap<u64, HashSet<u64>>,
37 edge_count: usize,
38}
39
40impl FleetGraph {
41 pub fn new() -> Self {
42 Self {
43 agents: Vec::new(),
44 agent_index: HashMap::new(),
45 adjacency: HashMap::new(),
46 edge_count: 0,
47 }
48 }
49
50 pub fn add_agent(&mut self, id: u64, position: [f64; 2], capabilities: Vec<String>) {
51 if let std::collections::hash_map::Entry::Vacant(e) = self.adjacency.entry(id) {
52 e.insert(HashSet::new());
53 let idx = self.agents.len();
54 self.agent_index.insert(id, idx);
55 self.agents.push(FleetAgent { id, position, neighbors: vec![], capabilities });
56 }
57 }
58
59 pub fn add_edge(&mut self, a: u64, b: u64) {
60 let mut new_edge = false;
62 if let Some(set) = self.adjacency.get_mut(&a) {
63 if set.insert(b) {
64 new_edge = true;
65 if let Some(agent) = self.agents.iter_mut().find(|ag| ag.id == a) {
66 if !agent.neighbors.contains(&b) {
67 agent.neighbors.push(b);
68 }
69 }
70 }
71 }
72 if let Some(set) = self.adjacency.get_mut(&b) {
73 if set.insert(a) {
74 if new_edge {
75 self.edge_count += 1;
76 }
77 if let Some(agent) = self.agents.iter_mut().find(|ag| ag.id == b) {
78 if !agent.neighbors.contains(&a) {
79 agent.neighbors.push(a);
80 }
81 }
82 }
83 }
84 }
85
86 pub fn V(&self) -> usize {
87 self.agents.len()
88 }
89
90 pub fn E(&self) -> usize {
91 self.edge_count
92 }
93
94 pub fn check_laman_rigidity(&self) -> RigidityResult {
102 let V = self.V();
103 let E = self.E();
104
105 let expected_E = 2 * V - 3;
107 let e_ratio = if expected_E > 0 { E as f64 / expected_E as f64 } else { 1.0 };
108
109 let subgraph_check = if V < 3 {
111 true
112 } else {
113 let mut ok = true;
114 for agent in &self.agents {
115 let v_prime = agent.neighbors.len() + 1;
116 let e_prime = agent.neighbors.len();
117 if v_prime >= 2 && e_prime > 2 * v_prime - 3 {
118 ok = false;
119 break;
120 }
121 }
122 ok
123 };
124
125 let is_rigid = (e_ratio - 1.0).abs() < 0.05 && subgraph_check; let H1 = if E >= V { E - V + 1 } else { 0 };
129
130 RigidityResult {
131 is_rigid,
132 V,
133 E,
134 expected_E,
135 h1_dimension: H1,
136 critical_subgraphs: vec![],
137 max_neighbors: self.agents.iter().map(|a| a.neighbors.len()).max().unwrap_or(0),
138 }
139 }
140
141 pub fn max_neighbors(&self) -> usize {
143 self.agents.iter().map(|a| a.neighbors.len()).max().unwrap_or(0)
144 }
145
146 pub fn get_agent(&self, id: u64) -> Option<&FleetAgent> {
148 self.agent_index.get(&id).and_then(|&idx| self.agents.get(idx))
149 }
150
151 pub fn get_neighbors(&self, id: u64) -> Vec<u64> {
153 self.adjacency.get(&id).map(|s| s.iter().copied().collect::<Vec<u64>>()).unwrap_or_default()
154 }
155}
156
157impl Default for FleetGraph {
158 fn default() -> Self {
159 Self::new()
160 }
161}
162
163#[derive(Clone, Debug, Serialize, Deserialize)]
165pub struct RigidityResult {
166 pub is_rigid: bool,
168 pub V: usize,
169 pub E: usize,
170 pub expected_E: usize,
171 pub h1_dimension: usize,
173 pub critical_subgraphs: Vec<u64>,
175 pub max_neighbors: usize,
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_laman_rigid_triangle() {
185 let mut graph = FleetGraph::new();
187 graph.add_agent(1, [0.0, 0.0], vec![]);
188 graph.add_agent(2, [1.0, 0.0], vec![]);
189 graph.add_agent(3, [0.5, 0.87], vec![]);
190 graph.add_edge(1, 2);
191 graph.add_edge(2, 3);
192 graph.add_edge(3, 1);
193
194 let result = graph.check_laman_rigidity();
195 assert!(result.is_rigid);
196 assert_eq!(result.V, 3);
197 assert_eq!(result.E, 3);
198 assert_eq!(result.max_neighbors, 2);
199 }
200
201 #[test]
202 fn test_laman_indeterminate_square() {
203 let mut graph = FleetGraph::new();
205 graph.add_agent(1, [0.0, 0.0], vec![]);
206 graph.add_agent(2, [1.0, 0.0], vec![]);
207 graph.add_agent(3, [1.0, 1.0], vec![]);
208 graph.add_agent(4, [0.0, 1.0], vec![]);
209 graph.add_edge(1, 2);
210 graph.add_edge(2, 3);
211 graph.add_edge(3, 4);
212 graph.add_edge(4, 1);
213
214 let result = graph.check_laman_rigidity();
215 assert!(!result.is_rigid);
216 assert_eq!(result.max_neighbors, 2);
217 }
218
219 #[test]
220 fn test_k12_complete_graph() {
221 let mut graph = FleetGraph::new();
224 for i in 0..12 {
225 graph.add_agent(i as u64, [i as f64, 0.0], vec![]);
226 }
227 for i in 0..12 {
228 for j in (i+1)..12 {
229 graph.add_edge(i as u64, j as u64);
230 }
231 }
232
233 let result = graph.check_laman_rigidity();
234 assert!(!result.is_rigid); assert_eq!(result.max_neighbors, 11);
236 }
237
238 #[test]
239 fn test_h1_dimension_cycle() {
240 let mut graph = FleetGraph::new();
242 graph.add_agent(0, [0.0, 0.0], vec![]);
243 graph.add_agent(1, [1.0, 0.0], vec![]);
244 graph.add_agent(2, [0.0, 1.0], vec![]);
245 graph.add_edge(0, 1);
246 graph.add_edge(1, 2);
247 graph.add_edge(2, 0);
248
249 let result = graph.check_laman_rigidity();
250 assert_eq!(result.h1_dimension, 1);
251 assert!(result.is_rigid); }
253
254 #[test]
255 fn test_o1_agent_lookup() {
256 let mut graph = FleetGraph::new();
258 for i in 0..100 {
259 graph.add_agent(i, [i as f64, 0.0], vec![format!("cap_{}", i)]);
260 }
261 let start = std::time::Instant::now();
263 for i in 0..100 {
264 let agent = graph.get_agent(i);
265 assert!(agent.is_some());
266 assert_eq!(agent.unwrap().id, i);
267 }
268 let elapsed = start.elapsed();
269 assert!(elapsed.as_secs_f64() < 0.001, "O(1) lookup took {}s for 100 agents", elapsed.as_secs_f64());
271 }
272
273 #[test]
274 fn test_o1_lookup_not_found() {
275 let mut graph = FleetGraph::new();
276 graph.add_agent(42, [0.0, 0.0], vec![]);
277 assert!(graph.get_agent(999).is_none());
278 }
279
280 #[test]
281 fn test_agent_index_consistency() {
282 let mut graph = FleetGraph::new();
284 for i in 0..50 {
285 graph.add_agent(i * 2, [i as f64, 0.0], vec![]);
286 }
287 for i in 0..50 {
288 let agent = graph.get_agent(i * 2).expect(&format!("agent {} should exist", i * 2));
289 assert_eq!(agent.id, i * 2);
290 }
291 }
292}