1use crate::models::graph::VertexCovering;
7use crate::models::set::SetCovering;
8use crate::rules::traits::{ReduceTo, ReductionResult};
9use crate::traits::Problem;
10use crate::types::ProblemSize;
11use num_traits::{Num, Zero};
12use std::ops::AddAssign;
13
14#[derive(Debug, Clone)]
16pub struct ReductionVCToSC<W> {
17 target: SetCovering<W>,
18 source_size: ProblemSize,
19}
20
21impl<W> ReductionResult for ReductionVCToSC<W>
22where
23 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
24{
25 type Source = VertexCovering<W>;
26 type Target = SetCovering<W>;
27
28 fn target_problem(&self) -> &Self::Target {
29 &self.target
30 }
31
32 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<SetCovering<W>> for VertexCovering<W>
48where
49 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
50{
51 type Result = ReductionVCToSC<W>;
52
53 fn reduce_to(&self) -> Self::Result {
54 let edges = self.edges();
55 let num_edges = edges.len();
56 let num_vertices = self.num_vertices();
57
58 let sets: Vec<Vec<usize>> = (0..num_vertices)
61 .map(|vertex| {
62 edges
63 .iter()
64 .enumerate()
65 .filter(|(_, (u, v))| *u == vertex || *v == vertex)
66 .map(|(edge_idx, _)| edge_idx)
67 .collect()
68 })
69 .collect();
70
71 let target = SetCovering::with_weights(num_edges, sets, self.weights_ref().clone());
72
73 ReductionVCToSC {
74 target,
75 source_size: self.problem_size(),
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::solvers::{BruteForce, Solver};
84 use crate::traits::ConstraintSatisfactionProblem;
85
86 #[test]
87 fn test_vc_to_sc_basic() {
88 let vc_problem = VertexCovering::<i32>::new(3, vec![(0, 1), (1, 2)]);
93 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
94 let sc_problem = reduction.target_problem();
95
96 assert_eq!(sc_problem.universe_size(), 2); assert_eq!(sc_problem.num_sets(), 3); assert_eq!(sc_problem.get_set(0), Some(&vec![0]));
102 assert_eq!(sc_problem.get_set(1), Some(&vec![0, 1]));
104 assert_eq!(sc_problem.get_set(2), Some(&vec![1]));
106 }
107
108 #[test]
109 fn test_vc_to_sc_triangle() {
110 let vc_problem = VertexCovering::<i32>::new(3, vec![(0, 1), (1, 2), (0, 2)]);
113 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
114 let sc_problem = reduction.target_problem();
115
116 assert_eq!(sc_problem.universe_size(), 3);
117 assert_eq!(sc_problem.num_sets(), 3);
118
119 for i in 0..3 {
121 let set = sc_problem.get_set(i).unwrap();
122 assert_eq!(set.len(), 2);
123 }
124 }
125
126 #[test]
127 fn test_vc_to_sc_solution_extraction() {
128 let vc_problem = VertexCovering::<i32>::new(3, vec![(0, 1), (1, 2)]);
129 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
130 let sc_problem = reduction.target_problem();
131
132 let solver = BruteForce::new();
134 let sc_solutions = solver.find_best(sc_problem);
135
136 let vc_solutions: Vec<_> = sc_solutions
138 .iter()
139 .map(|s| reduction.extract_solution(s))
140 .collect();
141
142 for sol in &vc_solutions {
144 assert!(vc_problem.solution_size(sol).is_valid);
145 }
146
147 let min_size: usize = vc_solutions[0].iter().sum();
149 assert_eq!(min_size, 1);
150 }
151
152 #[test]
153 fn test_vc_to_sc_optimality_preservation() {
154 let vc_problem = VertexCovering::<i32>::new(4, vec![(0, 1), (1, 2), (2, 3)]);
156 let solver = BruteForce::new();
157
158 let direct_solutions = solver.find_best(&vc_problem);
160 let direct_size = direct_solutions[0].iter().sum::<usize>();
161
162 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
164 let sc_solutions = solver.find_best(reduction.target_problem());
165 let reduced_solutions: Vec<_> = sc_solutions
166 .iter()
167 .map(|s| reduction.extract_solution(s))
168 .collect();
169 let reduced_size = reduced_solutions[0].iter().sum::<usize>();
170
171 assert_eq!(direct_size, reduced_size);
173 }
174
175 #[test]
176 fn test_vc_to_sc_weighted() {
177 let vc_problem = VertexCovering::with_weights(3, vec![(0, 1), (1, 2)], vec![10, 1, 10]);
179 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
180 let sc_problem = reduction.target_problem();
181
182 assert_eq!(sc_problem.weights(), vec![10, 1, 10]);
184
185 let solver = BruteForce::new();
187 let vc_solutions = solver.find_best(&vc_problem);
188 let sc_solutions = solver.find_best(sc_problem);
189
190 assert_eq!(vc_solutions[0], vec![0, 1, 0]);
192 assert_eq!(sc_solutions[0], vec![0, 1, 0]);
193 }
194
195 #[test]
196 fn test_vc_to_sc_empty_graph() {
197 let vc_problem = VertexCovering::<i32>::new(3, vec![]);
199 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
200 let sc_problem = reduction.target_problem();
201
202 assert_eq!(sc_problem.universe_size(), 0);
203 assert_eq!(sc_problem.num_sets(), 3);
204
205 for i in 0..3 {
207 assert!(sc_problem.get_set(i).unwrap().is_empty());
208 }
209 }
210
211 #[test]
212 fn test_vc_to_sc_source_target_size() {
213 let vc_problem = VertexCovering::<i32>::new(5, vec![(0, 1), (1, 2), (2, 3), (3, 4)]);
214 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
215
216 let source_size = reduction.source_size();
217 let target_size = reduction.target_size();
218
219 assert_eq!(source_size.get("num_vertices"), Some(5));
220 assert_eq!(source_size.get("num_edges"), Some(4));
221 assert_eq!(target_size.get("universe_size"), Some(4)); assert_eq!(target_size.get("num_sets"), Some(5)); }
224
225 #[test]
226 fn test_vc_to_sc_star_graph() {
227 let vc_problem = VertexCovering::<i32>::new(4, vec![(0, 1), (0, 2), (0, 3)]);
230 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
231 let sc_problem = reduction.target_problem();
232
233 assert_eq!(sc_problem.get_set(0), Some(&vec![0, 1, 2]));
235 assert_eq!(sc_problem.get_set(1), Some(&vec![0]));
237 assert_eq!(sc_problem.get_set(2), Some(&vec![1]));
238 assert_eq!(sc_problem.get_set(3), Some(&vec![2]));
239
240 let solver = BruteForce::new();
242 let solutions = solver.find_best(&vc_problem);
243 assert_eq!(solutions[0], vec![1, 0, 0, 0]);
244 }
245
246 #[test]
247 fn test_vc_to_sc_all_solutions_valid() {
248 let vc_problem = VertexCovering::<i32>::new(4, vec![(0, 1), (1, 2), (0, 2), (2, 3)]);
250 let reduction = ReduceTo::<SetCovering<i32>>::reduce_to(&vc_problem);
251 let sc_problem = reduction.target_problem();
252
253 let solver = BruteForce::new();
254 let sc_solutions = solver.find_best(sc_problem);
255
256 for sc_sol in &sc_solutions {
257 let vc_sol = reduction.extract_solution(sc_sol);
258 let sol_size = vc_problem.solution_size(&vc_sol);
259 assert!(
260 sol_size.is_valid,
261 "Extracted solution {:?} should be valid",
262 vc_sol
263 );
264 }
265 }
266}
267
268use crate::poly;
270use crate::rules::registry::{ReductionEntry, ReductionOverhead};
271
272inventory::submit! {
273 ReductionEntry {
274 source_name: "VertexCovering",
275 target_name: "SetCovering",
276 source_graph: "SimpleGraph",
277 target_graph: "SetSystem",
278 overhead_fn: || ReductionOverhead::new(vec![
279 ("num_sets", poly!(num_vertices)),
280 ("num_elements", poly!(num_edges)),
281 ]),
282 }
283}