1use crate::models::graph::Matching;
7use crate::models::set::SetPacking;
8use crate::rules::traits::{ReduceTo, ReductionResult};
9use crate::traits::{ConstraintSatisfactionProblem, Problem};
10use crate::types::ProblemSize;
11use num_traits::{Num, Zero};
12use std::ops::AddAssign;
13
14#[derive(Debug, Clone)]
16pub struct ReductionMatchingToSP<W> {
17 target: SetPacking<W>,
18 source_size: ProblemSize,
19}
20
21impl<W> ReductionResult for ReductionMatchingToSP<W>
22where
23 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
24{
25 type Source = Matching<W>;
26 type Target = SetPacking<W>;
27
28 fn target_problem(&self) -> &Self::Target {
29 &self.target
30 }
31
32 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
34 target_solution.to_vec()
35 }
36
37 fn source_size(&self) -> ProblemSize {
38 self.source_size.clone()
39 }
40
41 fn target_size(&self) -> ProblemSize {
42 self.target.problem_size()
43 }
44}
45
46impl<W> ReduceTo<SetPacking<W>> for Matching<W>
47where
48 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
49{
50 type Result = ReductionMatchingToSP<W>;
51
52 fn reduce_to(&self) -> Self::Result {
53 let edges = self.edges();
54
55 let sets: Vec<Vec<usize>> = edges.iter().map(|&(u, v, _)| vec![u, v]).collect();
57
58 let weights = self.weights();
60
61 let target = SetPacking::with_weights(sets, weights);
62
63 ReductionMatchingToSP {
64 target,
65 source_size: self.problem_size(),
66 }
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::solvers::{BruteForce, Solver};
74
75 #[test]
76 fn test_matching_to_setpacking_structure() {
77 let matching = Matching::<i32>::unweighted(3, vec![(0, 1), (1, 2)]);
79 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
80 let sp = reduction.target_problem();
81
82 assert_eq!(sp.num_sets(), 2);
84
85 let sets = sp.sets();
87 assert_eq!(sets[0], vec![0, 1]);
88 assert_eq!(sets[1], vec![1, 2]);
89 }
90
91 #[test]
92 fn test_matching_to_setpacking_path() {
93 let matching = Matching::<i32>::unweighted(4, vec![(0, 1), (1, 2), (2, 3)]);
95 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
96 let sp = reduction.target_problem();
97
98 let solver = BruteForce::new();
99 let sp_solutions = solver.find_best(sp);
100
101 let _matching_solutions: Vec<_> = sp_solutions
103 .iter()
104 .map(|s| reduction.extract_solution(s))
105 .collect();
106
107 let direct_solutions = solver.find_best(&matching);
109
110 let sp_size: usize = sp_solutions[0].iter().sum();
112 let direct_size: usize = direct_solutions[0].iter().sum();
113 assert_eq!(sp_size, direct_size);
114 assert_eq!(sp_size, 2); }
116
117 #[test]
118 fn test_matching_to_setpacking_triangle() {
119 let matching = Matching::<i32>::unweighted(3, vec![(0, 1), (1, 2), (0, 2)]);
121 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
122 let sp = reduction.target_problem();
123
124 let solver = BruteForce::new();
125 let sp_solutions = solver.find_best(sp);
126
127 for sol in &sp_solutions {
129 assert_eq!(sol.iter().sum::<usize>(), 1);
130 }
131
132 assert_eq!(sp_solutions.len(), 3);
134 }
135
136 #[test]
137 fn test_matching_to_setpacking_weighted() {
138 let matching = Matching::new(4, vec![(0, 1, 100), (0, 2, 1), (1, 3, 1)]);
140 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
141 let sp = reduction.target_problem();
142
143 assert_eq!(sp.weights_ref(), &vec![100, 1, 1]);
145
146 let solver = BruteForce::new();
147 let sp_solutions = solver.find_best(sp);
148
149 assert!(sp_solutions.contains(&vec![1, 0, 0]));
151
152 let direct_solutions = solver.find_best(&matching);
154 assert_eq!(matching.solution_size(&sp_solutions[0]).size, 100);
155 assert_eq!(matching.solution_size(&direct_solutions[0]).size, 100);
156 }
157
158 #[test]
159 fn test_matching_to_setpacking_solution_extraction() {
160 let matching = Matching::<i32>::unweighted(4, vec![(0, 1), (1, 2), (2, 3)]);
161 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
162
163 let sp_solution = vec![1, 0, 1];
165 let matching_solution = reduction.extract_solution(&sp_solution);
166 assert_eq!(matching_solution, vec![1, 0, 1]);
167
168 assert!(matching.solution_size(&matching_solution).is_valid);
170 }
171
172 #[test]
173 fn test_matching_to_setpacking_k4() {
174 let matching = Matching::<i32>::unweighted(
176 4,
177 vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
178 );
179 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
180 let sp = reduction.target_problem();
181
182 let solver = BruteForce::new();
183 let sp_solutions = solver.find_best(sp);
184 let direct_solutions = solver.find_best(&matching);
185
186 let sp_size: usize = sp_solutions[0].iter().sum();
188 let direct_size: usize = direct_solutions[0].iter().sum();
189 assert_eq!(sp_size, 2);
190 assert_eq!(direct_size, 2);
191 }
192
193 #[test]
194 fn test_matching_to_setpacking_empty() {
195 let matching = Matching::<i32>::unweighted(3, vec![]);
197 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
198 let sp = reduction.target_problem();
199
200 assert_eq!(sp.num_sets(), 0);
201 }
202
203 #[test]
204 fn test_matching_to_setpacking_single_edge() {
205 let matching = Matching::<i32>::unweighted(2, vec![(0, 1)]);
206 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
207 let sp = reduction.target_problem();
208
209 assert_eq!(sp.num_sets(), 1);
210 assert_eq!(sp.sets()[0], vec![0, 1]);
211
212 let solver = BruteForce::new();
213 let sp_solutions = solver.find_best(sp);
214
215 assert_eq!(sp_solutions, vec![vec![1]]);
217 }
218
219 #[test]
220 fn test_matching_to_setpacking_disjoint_edges() {
221 let matching = Matching::<i32>::unweighted(4, vec![(0, 1), (2, 3)]);
223 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
224 let sp = reduction.target_problem();
225
226 let solver = BruteForce::new();
227 let sp_solutions = solver.find_best(sp);
228
229 assert_eq!(sp_solutions, vec![vec![1, 1]]);
231 }
232
233 #[test]
234 fn test_reduction_sizes() {
235 let matching = Matching::<i32>::unweighted(5, vec![(0, 1), (1, 2), (2, 3)]);
236 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
237
238 let source_size = reduction.source_size();
239 let target_size = reduction.target_size();
240
241 assert_eq!(source_size.get("num_vertices"), Some(5));
242 assert_eq!(source_size.get("num_edges"), Some(3));
243 assert_eq!(target_size.get("num_sets"), Some(3));
244 }
245
246 #[test]
247 fn test_matching_to_setpacking_star() {
248 let matching = Matching::<i32>::unweighted(4, vec![(0, 1), (0, 2), (0, 3)]);
250 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&matching);
251 let sp = reduction.target_problem();
252
253 let solver = BruteForce::new();
254 let sp_solutions = solver.find_best(sp);
255
256 for sol in &sp_solutions {
258 assert_eq!(sol.iter().sum::<usize>(), 1);
259 }
260 assert_eq!(sp_solutions.len(), 3);
262 }
263}
264
265use crate::poly;
267use crate::rules::registry::{ReductionEntry, ReductionOverhead};
268
269inventory::submit! {
270 ReductionEntry {
271 source_name: "Matching",
272 target_name: "SetPacking",
273 source_graph: "SimpleGraph",
274 target_graph: "SetSystem",
275 overhead_fn: || ReductionOverhead::new(vec![
276 ("num_sets", poly!(num_edges)),
277 ("num_elements", poly!(num_vertices)),
278 ]),
279 }
280}