problemreductions/rules/
independentset_setpacking.rs1use crate::models::graph::IndependentSet;
7use crate::models::set::SetPacking;
8use crate::rules::traits::{ReduceTo, ReductionResult};
9use crate::traits::Problem;
10use crate::types::ProblemSize;
11use num_traits::{Num, Zero};
12use std::collections::HashSet;
13use std::ops::AddAssign;
14
15#[derive(Debug, Clone)]
17pub struct ReductionISToSP<W> {
18 target: SetPacking<W>,
19 source_size: ProblemSize,
20}
21
22impl<W> ReductionResult for ReductionISToSP<W>
23where
24 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
25{
26 type Source = IndependentSet<W>;
27 type Target = SetPacking<W>;
28
29 fn target_problem(&self) -> &Self::Target {
30 &self.target
31 }
32
33 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
35 target_solution.to_vec()
36 }
37
38 fn source_size(&self) -> ProblemSize {
39 self.source_size.clone()
40 }
41
42 fn target_size(&self) -> ProblemSize {
43 self.target.problem_size()
44 }
45}
46
47impl<W> ReduceTo<SetPacking<W>> for IndependentSet<W>
48where
49 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
50{
51 type Result = ReductionISToSP<W>;
52
53 fn reduce_to(&self) -> Self::Result {
54 let edges = self.edges();
55 let n = self.num_vertices();
56
57 let mut sets: Vec<Vec<usize>> = vec![Vec::new(); n];
59 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
60 sets[u].push(edge_idx);
61 sets[v].push(edge_idx);
62 }
63
64 let target = SetPacking::with_weights(sets, self.weights_ref().clone());
65
66 ReductionISToSP {
67 target,
68 source_size: self.problem_size(),
69 }
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct ReductionSPToIS<W> {
76 target: IndependentSet<W>,
77 source_size: ProblemSize,
78}
79
80impl<W> ReductionResult for ReductionSPToIS<W>
81where
82 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
83{
84 type Source = SetPacking<W>;
85 type Target = IndependentSet<W>;
86
87 fn target_problem(&self) -> &Self::Target {
88 &self.target
89 }
90
91 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
93 target_solution.to_vec()
94 }
95
96 fn source_size(&self) -> ProblemSize {
97 self.source_size.clone()
98 }
99
100 fn target_size(&self) -> ProblemSize {
101 self.target.problem_size()
102 }
103}
104
105impl<W> ReduceTo<IndependentSet<W>> for SetPacking<W>
106where
107 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
108{
109 type Result = ReductionSPToIS<W>;
110
111 fn reduce_to(&self) -> Self::Result {
112 let sets = self.sets();
113 let n = sets.len();
114
115 let mut edges = Vec::new();
117 for (i, set_i_vec) in sets.iter().enumerate() {
118 let set_i: HashSet<_> = set_i_vec.iter().collect();
119 for (j, set_j) in sets.iter().enumerate().skip(i + 1) {
120 if set_j.iter().any(|elem| set_i.contains(elem)) {
122 edges.push((i, j));
123 }
124 }
125 }
126
127 let target = IndependentSet::with_weights(n, edges, self.weights_ref().clone());
128
129 ReductionSPToIS {
130 target,
131 source_size: self.problem_size(),
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::solvers::{BruteForce, Solver};
140
141 #[test]
142 fn test_is_to_setpacking() {
143 let is_problem = IndependentSet::<i32>::new(3, vec![(0, 1), (1, 2), (0, 2)]);
145 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&is_problem);
146 let sp_problem = reduction.target_problem();
147
148 let solver = BruteForce::new();
149 let sp_solutions = solver.find_best(sp_problem);
150
151 let is_solutions: Vec<_> = sp_solutions
153 .iter()
154 .map(|s| reduction.extract_solution(s))
155 .collect();
156
157 for sol in &is_solutions {
159 let size: usize = sol.iter().sum();
160 assert_eq!(size, 1);
161 }
162 }
163
164 #[test]
165 fn test_setpacking_to_is() {
166 let sets = vec![
168 vec![0, 1],
169 vec![2, 3],
170 vec![1, 2], ];
172 let sp_problem = SetPacking::<i32>::new(sets);
173 let reduction: ReductionSPToIS<i32> =
174 ReduceTo::<IndependentSet<i32>>::reduce_to(&sp_problem);
175 let is_problem = reduction.target_problem();
176
177 let solver = BruteForce::new();
178 let is_solutions = solver.find_best(is_problem);
179
180 for sol in &is_solutions {
182 let size: usize = sol.iter().sum();
183 assert_eq!(size, 2);
184 }
185 }
186
187 #[test]
188 fn test_roundtrip_is_sp_is() {
189 let original = IndependentSet::<i32>::new(4, vec![(0, 1), (1, 2), (2, 3)]);
190 let solver = BruteForce::new();
191 let original_solutions = solver.find_best(&original);
192
193 let reduction1 = ReduceTo::<SetPacking<i32>>::reduce_to(&original);
195 let sp = reduction1.target_problem().clone();
196 let reduction2: ReductionSPToIS<i32> =
197 ReduceTo::<IndependentSet<i32>>::reduce_to(&sp);
198 let roundtrip = reduction2.target_problem();
199
200 let roundtrip_solutions = solver.find_best(roundtrip);
201
202 let orig_size: usize = original_solutions[0].iter().sum();
204 let rt_size: usize = roundtrip_solutions[0].iter().sum();
205 assert_eq!(orig_size, rt_size);
206 }
207
208 #[test]
209 fn test_weighted_reduction() {
210 let is_problem =
211 IndependentSet::with_weights(3, vec![(0, 1), (1, 2)], vec![10, 20, 30]);
212 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&is_problem);
213 let sp_problem = reduction.target_problem();
214
215 assert_eq!(sp_problem.weights_ref(), &vec![10, 20, 30]);
217 }
218
219 #[test]
220 fn test_empty_graph() {
221 let is_problem = IndependentSet::<i32>::new(3, vec![]);
223 let reduction = ReduceTo::<SetPacking<i32>>::reduce_to(&is_problem);
224 let sp_problem = reduction.target_problem();
225
226 assert_eq!(sp_problem.num_sets(), 3);
228
229 let solver = BruteForce::new();
230 let solutions = solver.find_best(sp_problem);
231
232 assert_eq!(solutions[0].iter().sum::<usize>(), 3);
234 }
235
236 #[test]
237 fn test_disjoint_sets() {
238 let sets = vec![vec![0], vec![1], vec![2]];
240 let sp_problem = SetPacking::<i32>::new(sets);
241 let reduction: ReductionSPToIS<i32> =
242 ReduceTo::<IndependentSet<i32>>::reduce_to(&sp_problem);
243 let is_problem = reduction.target_problem();
244
245 assert_eq!(is_problem.num_edges(), 0);
247 }
248}
249
250use crate::poly;
252use crate::rules::registry::{ReductionEntry, ReductionOverhead};
253
254inventory::submit! {
255 ReductionEntry {
256 source_name: "IndependentSet",
257 target_name: "SetPacking",
258 source_graph: "SimpleGraph",
259 target_graph: "SetSystem",
260 overhead_fn: || ReductionOverhead::new(vec![
261 ("num_sets", poly!(num_vertices)),
262 ("num_elements", poly!(num_vertices)),
263 ]),
264 }
265}
266
267inventory::submit! {
268 ReductionEntry {
269 source_name: "SetPacking",
270 target_name: "IndependentSet",
271 source_graph: "SetSystem",
272 target_graph: "SimpleGraph",
273 overhead_fn: || ReductionOverhead::new(vec![
274 ("num_vertices", poly!(num_sets)),
275 ("num_edges", poly!(num_sets)),
276 ]),
277 }
278}