1use crate::graph_types::SimpleGraph;
7use crate::traits::{ConstraintSatisfactionProblem, Problem};
8use crate::types::{EnergyMode, LocalConstraint, LocalSolutionSize, ProblemSize, SolutionSize};
9use petgraph::graph::{NodeIndex, UnGraph};
10use petgraph::visit::EdgeRef;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct Matching<W = i32> {
38 num_vertices: usize,
40 graph: UnGraph<(), W>,
42 edge_weights: Vec<W>,
44}
45
46impl<W: Clone + Default> Matching<W> {
47 pub fn new(num_vertices: usize, edges: Vec<(usize, usize, W)>) -> Self {
53 let mut graph = UnGraph::new_undirected();
54 for _ in 0..num_vertices {
55 graph.add_node(());
56 }
57 let mut edge_weights = Vec::new();
58 for (u, v, w) in edges {
59 graph.add_edge(NodeIndex::new(u), NodeIndex::new(v), w.clone());
60 edge_weights.push(w);
61 }
62 Self {
63 num_vertices,
64 graph,
65 edge_weights,
66 }
67 }
68
69 pub fn unweighted(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self
71 where
72 W: From<i32>,
73 {
74 Self::new(
75 num_vertices,
76 edges.into_iter().map(|(u, v)| (u, v, W::from(1))).collect(),
77 )
78 }
79
80 pub fn num_vertices(&self) -> usize {
82 self.num_vertices
83 }
84
85 pub fn num_edges(&self) -> usize {
87 self.graph.edge_count()
88 }
89
90 pub fn edge_endpoints(&self, edge_idx: usize) -> Option<(usize, usize)> {
92 let edge_ref = self.graph.edge_references().nth(edge_idx)?;
93 Some((edge_ref.source().index(), edge_ref.target().index()))
94 }
95
96 pub fn edges(&self) -> Vec<(usize, usize, W)> {
98 self.graph
99 .edge_references()
100 .map(|e| (e.source().index(), e.target().index(), e.weight().clone()))
101 .collect()
102 }
103
104 pub fn vertex_to_edges(&self) -> HashMap<usize, Vec<usize>> {
106 let mut v2e: HashMap<usize, Vec<usize>> = HashMap::new();
107 for (idx, edge) in self.graph.edge_references().enumerate() {
108 let u = edge.source().index();
109 let v = edge.target().index();
110 v2e.entry(u).or_default().push(idx);
111 v2e.entry(v).or_default().push(idx);
112 }
113 v2e
114 }
115
116 fn is_valid_matching(&self, config: &[usize]) -> bool {
118 let mut vertex_used = vec![false; self.num_vertices];
119
120 for (idx, &selected) in config.iter().enumerate() {
121 if selected == 1 {
122 if let Some((u, v)) = self.edge_endpoints(idx) {
123 if vertex_used[u] || vertex_used[v] {
124 return false;
125 }
126 vertex_used[u] = true;
127 vertex_used[v] = true;
128 }
129 }
130 }
131 true
132 }
133}
134
135impl<W> Problem for Matching<W>
136where
137 W: Clone + Default + PartialOrd + num_traits::Num + num_traits::Zero + std::ops::AddAssign + 'static,
138{
139 const NAME: &'static str = "Matching";
140 type GraphType = SimpleGraph;
141 type Weight = W;
142 type Size = W;
143
144 fn num_variables(&self) -> usize {
145 self.graph.edge_count() }
147
148 fn num_flavors(&self) -> usize {
149 2 }
151
152 fn problem_size(&self) -> ProblemSize {
153 ProblemSize::new(vec![
154 ("num_vertices", self.num_vertices),
155 ("num_edges", self.graph.edge_count()),
156 ])
157 }
158
159 fn energy_mode(&self) -> EnergyMode {
160 EnergyMode::LargerSizeIsBetter }
162
163 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
164 let is_valid = self.is_valid_matching(config);
165 let mut total = W::zero();
166 for (idx, &selected) in config.iter().enumerate() {
167 if selected == 1 {
168 if let Some(w) = self.edge_weights.get(idx) {
169 total += w.clone();
170 }
171 }
172 }
173 SolutionSize::new(total, is_valid)
174 }
175}
176
177impl<W> ConstraintSatisfactionProblem for Matching<W>
178where
179 W: Clone + Default + PartialOrd + num_traits::Num + num_traits::Zero + std::ops::AddAssign + 'static,
180{
181 fn constraints(&self) -> Vec<LocalConstraint> {
182 let v2e = self.vertex_to_edges();
183 let mut constraints = Vec::new();
184
185 for (_v, incident_edges) in v2e {
187 if incident_edges.len() < 2 {
188 continue; }
190
191 let num_edges = incident_edges.len();
192 let num_configs = 2usize.pow(num_edges as u32);
193
194 let spec: Vec<bool> = (0..num_configs)
196 .map(|config_idx| {
197 let count = (0..num_edges)
198 .filter(|&i| (config_idx >> i) & 1 == 1)
199 .count();
200 count <= 1
201 })
202 .collect();
203
204 constraints.push(LocalConstraint::new(2, incident_edges, spec));
205 }
206
207 constraints
208 }
209
210 fn objectives(&self) -> Vec<LocalSolutionSize<Self::Size>> {
211 self.edge_weights
212 .iter()
213 .enumerate()
214 .map(|(i, w)| LocalSolutionSize::new(2, vec![i], vec![W::zero(), w.clone()]))
215 .collect()
216 }
217
218 fn weights(&self) -> Vec<Self::Size> {
219 self.edge_weights.clone()
220 }
221
222 fn set_weights(&mut self, weights: Vec<Self::Size>) {
223 assert_eq!(weights.len(), self.num_variables());
224 self.edge_weights = weights;
225 }
226
227 fn is_weighted(&self) -> bool {
228 if self.edge_weights.is_empty() {
229 return false;
230 }
231 let first = &self.edge_weights[0];
232 !self.edge_weights.iter().all(|w| w == first)
233 }
234}
235
236pub fn is_matching(num_vertices: usize, edges: &[(usize, usize)], selected: &[bool]) -> bool {
238 if selected.len() != edges.len() {
239 return false;
240 }
241
242 let mut vertex_used = vec![false; num_vertices];
243 for (idx, &sel) in selected.iter().enumerate() {
244 if sel {
245 let (u, v) = edges[idx];
246 if u >= num_vertices || v >= num_vertices {
247 return false;
248 }
249 if vertex_used[u] || vertex_used[v] {
250 return false;
251 }
252 vertex_used[u] = true;
253 vertex_used[v] = true;
254 }
255 }
256 true
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::solvers::{BruteForce, Solver};
263
264 #[test]
265 fn test_matching_creation() {
266 let problem = Matching::new(4, vec![(0, 1, 1), (1, 2, 2), (2, 3, 3)]);
267 assert_eq!(problem.num_vertices(), 4);
268 assert_eq!(problem.num_edges(), 3);
269 assert_eq!(problem.num_variables(), 3);
270 }
271
272 #[test]
273 fn test_matching_unweighted() {
274 let problem = Matching::<i32>::unweighted(3, vec![(0, 1), (1, 2)]);
275 assert_eq!(problem.num_edges(), 2);
276 }
277
278 #[test]
279 fn test_edge_endpoints() {
280 let problem = Matching::new(3, vec![(0, 1, 1), (1, 2, 2)]);
281 assert_eq!(problem.edge_endpoints(0), Some((0, 1)));
282 assert_eq!(problem.edge_endpoints(1), Some((1, 2)));
283 assert_eq!(problem.edge_endpoints(2), None);
284 }
285
286 #[test]
287 fn test_is_valid_matching() {
288 let problem = Matching::new(4, vec![(0, 1, 1), (1, 2, 1), (2, 3, 1)]);
289
290 assert!(problem.is_valid_matching(&[1, 0, 0]));
292
293 assert!(problem.is_valid_matching(&[1, 0, 1]));
295
296 assert!(!problem.is_valid_matching(&[1, 1, 0]));
298 }
299
300 #[test]
301 fn test_solution_size() {
302 let problem = Matching::new(4, vec![(0, 1, 5), (1, 2, 10), (2, 3, 3)]);
303
304 let sol = problem.solution_size(&[1, 0, 1]);
305 assert!(sol.is_valid);
306 assert_eq!(sol.size, 8); let sol = problem.solution_size(&[0, 1, 0]);
309 assert!(sol.is_valid);
310 assert_eq!(sol.size, 10);
311 }
312
313 #[test]
314 fn test_brute_force_path() {
315 let problem = Matching::<i32>::unweighted(4, vec![(0, 1), (1, 2), (2, 3)]);
317 let solver = BruteForce::new();
318
319 let solutions = solver.find_best(&problem);
320 assert!(solutions.contains(&vec![1, 0, 1]));
322 for sol in &solutions {
323 assert_eq!(problem.solution_size(sol).size, 2);
324 }
325 }
326
327 #[test]
328 fn test_brute_force_triangle() {
329 let problem = Matching::<i32>::unweighted(3, vec![(0, 1), (1, 2), (0, 2)]);
330 let solver = BruteForce::new();
331
332 let solutions = solver.find_best(&problem);
333 for sol in &solutions {
335 assert_eq!(sol.iter().sum::<usize>(), 1);
336 assert!(problem.solution_size(sol).is_valid);
337 }
338 }
339
340 #[test]
341 fn test_brute_force_weighted() {
342 let problem = Matching::new(4, vec![(0, 1, 100), (0, 2, 1), (1, 3, 1)]);
344 let solver = BruteForce::new();
345
346 let solutions = solver.find_best(&problem);
347 assert!(solutions.contains(&vec![1, 0, 0]));
349 }
350
351 #[test]
352 fn test_is_matching_function() {
353 let edges = vec![(0, 1), (1, 2), (2, 3)];
354
355 assert!(is_matching(4, &edges, &[true, false, true])); assert!(is_matching(4, &edges, &[false, true, false])); assert!(!is_matching(4, &edges, &[true, true, false])); assert!(is_matching(4, &edges, &[false, false, false])); }
360
361 #[test]
362 fn test_energy_mode() {
363 let problem = Matching::<i32>::unweighted(2, vec![(0, 1)]);
364 assert!(problem.energy_mode().is_maximization());
365 }
366
367 #[test]
368 fn test_empty_graph() {
369 let problem = Matching::<i32>::unweighted(3, vec![]);
370 let sol = problem.solution_size(&[]);
371 assert!(sol.is_valid);
372 assert_eq!(sol.size, 0);
373 }
374
375 #[test]
376 fn test_constraints() {
377 let problem = Matching::<i32>::unweighted(3, vec![(0, 1), (1, 2)]);
378 let constraints = problem.constraints();
379 assert_eq!(constraints.len(), 1);
381 }
382
383 #[test]
384 fn test_edges() {
385 let problem = Matching::new(3, vec![(0, 1, 5), (1, 2, 10)]);
386 let edges = problem.edges();
387 assert_eq!(edges.len(), 2);
388 }
389
390 #[test]
391 fn test_perfect_matching() {
392 let problem = Matching::<i32>::unweighted(
394 4,
395 vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
396 );
397 let solver = BruteForce::new();
398
399 let solutions = solver.find_best(&problem);
400 for sol in &solutions {
402 assert_eq!(problem.solution_size(sol).size, 2);
403 let mut used = [false; 4];
405 for (idx, &sel) in sol.iter().enumerate() {
406 if sel == 1 {
407 if let Some((u, v)) = problem.edge_endpoints(idx) {
408 used[u] = true;
409 used[v] = true;
410 }
411 }
412 }
413 assert!(used.iter().all(|&u| u)); }
415 }
416
417 #[test]
418 fn test_is_satisfied() {
419 let problem = Matching::<i32>::unweighted(4, vec![(0, 1), (1, 2), (2, 3)]);
420
421 assert!(problem.is_satisfied(&[1, 0, 1])); assert!(problem.is_satisfied(&[0, 1, 0])); assert!(!problem.is_satisfied(&[1, 1, 0])); }
425
426 #[test]
427 fn test_objectives() {
428 let problem = Matching::new(3, vec![(0, 1, 5), (1, 2, 10)]);
429 let objectives = problem.objectives();
430 assert_eq!(objectives.len(), 2);
431 }
432
433 #[test]
434 fn test_set_weights() {
435 let mut problem = Matching::<i32>::unweighted(3, vec![(0, 1), (1, 2)]);
436 assert!(!problem.is_weighted()); problem.set_weights(vec![1, 2]);
438 assert!(problem.is_weighted());
439 assert_eq!(problem.weights(), vec![1, 2]);
440 }
441
442 #[test]
443 fn test_is_weighted_empty() {
444 let problem = Matching::<i32>::unweighted(2, vec![]);
445 assert!(!problem.is_weighted());
446 }
447
448 #[test]
449 fn test_is_matching_wrong_len() {
450 let edges = vec![(0, 1), (1, 2)];
451 assert!(!is_matching(3, &edges, &[true])); }
453
454 #[test]
455 fn test_is_matching_out_of_bounds() {
456 let edges = vec![(0, 5)]; assert!(!is_matching(3, &edges, &[true]));
458 }
459
460 #[test]
461 fn test_problem_size() {
462 let problem = Matching::<i32>::unweighted(5, vec![(0, 1), (1, 2), (2, 3)]);
463 let size = problem.problem_size();
464 assert_eq!(size.get("num_vertices"), Some(5));
465 assert_eq!(size.get("num_edges"), Some(3));
466 }
467}