1use crate::models::graph::{IndependentSet, VertexCovering};
6use crate::rules::traits::{ReduceTo, ReductionResult};
7use crate::traits::Problem;
8use crate::types::ProblemSize;
9use num_traits::{Num, Zero};
10use std::ops::AddAssign;
11
12#[derive(Debug, Clone)]
14pub struct ReductionISToVC<W> {
15 target: VertexCovering<W>,
16 source_size: ProblemSize,
17}
18
19impl<W> ReductionResult for ReductionISToVC<W>
20where
21 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
22{
23 type Source = IndependentSet<W>;
24 type Target = VertexCovering<W>;
25
26 fn target_problem(&self) -> &Self::Target {
27 &self.target
28 }
29
30 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
33 target_solution.iter().map(|&x| 1 - x).collect()
34 }
35
36 fn source_size(&self) -> ProblemSize {
37 self.source_size.clone()
38 }
39
40 fn target_size(&self) -> ProblemSize {
41 self.target.problem_size()
42 }
43}
44
45impl<W> ReduceTo<VertexCovering<W>> for IndependentSet<W>
46where
47 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
48{
49 type Result = ReductionISToVC<W>;
50
51 fn reduce_to(&self) -> Self::Result {
52 let target = VertexCovering::with_weights(
53 self.num_vertices(),
54 self.edges(),
55 self.weights_ref().clone(),
56 );
57 ReductionISToVC {
58 target,
59 source_size: self.problem_size(),
60 }
61 }
62}
63
64#[derive(Debug, Clone)]
66pub struct ReductionVCToIS<W> {
67 target: IndependentSet<W>,
68 source_size: ProblemSize,
69}
70
71impl<W> ReductionResult for ReductionVCToIS<W>
72where
73 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
74{
75 type Source = VertexCovering<W>;
76 type Target = IndependentSet<W>;
77
78 fn target_problem(&self) -> &Self::Target {
79 &self.target
80 }
81
82 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
84 target_solution.iter().map(|&x| 1 - x).collect()
85 }
86
87 fn source_size(&self) -> ProblemSize {
88 self.source_size.clone()
89 }
90
91 fn target_size(&self) -> ProblemSize {
92 self.target.problem_size()
93 }
94}
95
96impl<W> ReduceTo<IndependentSet<W>> for VertexCovering<W>
97where
98 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
99{
100 type Result = ReductionVCToIS<W>;
101
102 fn reduce_to(&self) -> Self::Result {
103 let target = IndependentSet::with_weights(
104 self.num_vertices(),
105 self.edges(),
106 self.weights_ref().clone(),
107 );
108 ReductionVCToIS {
109 target,
110 source_size: self.problem_size(),
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::solvers::{BruteForce, Solver};
119
120 #[test]
121 fn test_is_to_vc_reduction() {
122 let is_problem = IndependentSet::<i32>::new(3, vec![(0, 1), (1, 2), (0, 2)]);
124 let reduction = ReduceTo::<VertexCovering<i32>>::reduce_to(&is_problem);
125 let vc_problem = reduction.target_problem();
126
127 let solver = BruteForce::new();
129 let vc_solutions = solver.find_best(vc_problem);
130
131 let is_solutions: Vec<_> = vc_solutions
133 .iter()
134 .map(|s| reduction.extract_solution(s))
135 .collect();
136
137 for sol in &is_solutions {
139 let size: usize = sol.iter().sum();
140 assert_eq!(size, 1, "Max IS in triangle should be 1");
141 }
142 }
143
144 #[test]
145 fn test_vc_to_is_reduction() {
146 let vc_problem = VertexCovering::<i32>::new(3, vec![(0, 1), (1, 2)]);
148 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&vc_problem);
149 let is_problem = reduction.target_problem();
150
151 let solver = BruteForce::new();
152 let is_solutions = solver.find_best(is_problem);
153
154 let vc_solutions: Vec<_> = is_solutions
155 .iter()
156 .map(|s| reduction.extract_solution(s))
157 .collect();
158
159 for sol in &vc_solutions {
161 let size: usize = sol.iter().sum();
162 assert_eq!(size, 1, "Min VC in path should be 1");
163 }
164 }
165
166 #[test]
167 fn test_roundtrip_is_vc_is() {
168 let original = IndependentSet::<i32>::new(4, vec![(0, 1), (1, 2), (2, 3)]);
169 let solver = BruteForce::new();
170 let original_solutions = solver.find_best(&original);
171
172 let reduction1 = ReduceTo::<VertexCovering<i32>>::reduce_to(&original);
174 let vc = reduction1.target_problem().clone();
175 let reduction2 = ReduceTo::<IndependentSet<i32>>::reduce_to(&vc);
176 let roundtrip = reduction2.target_problem();
177
178 let roundtrip_solutions = solver.find_best(roundtrip);
179
180 let orig_size: usize = original_solutions[0].iter().sum();
182 let rt_size: usize = roundtrip_solutions[0].iter().sum();
183 assert_eq!(orig_size, rt_size);
184 }
185
186 #[test]
187 fn test_weighted_reduction() {
188 let is_problem =
190 IndependentSet::with_weights(3, vec![(0, 1), (1, 2)], vec![10, 20, 30]);
191 let reduction = ReduceTo::<VertexCovering<i32>>::reduce_to(&is_problem);
192 let vc_problem = reduction.target_problem();
193
194 assert_eq!(vc_problem.weights_ref(), &vec![10, 20, 30]);
196 }
197
198 #[test]
199 fn test_source_and_target_size() {
200 let is_problem = IndependentSet::<i32>::new(5, vec![(0, 1), (1, 2), (2, 3), (3, 4)]);
201 let reduction = ReduceTo::<VertexCovering<i32>>::reduce_to(&is_problem);
202
203 let source_size = reduction.source_size();
204 let target_size = reduction.target_size();
205
206 assert_eq!(source_size.get("num_vertices"), Some(5));
207 assert_eq!(target_size.get("num_vertices"), Some(5));
208 }
209}
210
211use crate::poly;
213use crate::rules::registry::{ReductionEntry, ReductionOverhead};
214
215inventory::submit! {
216 ReductionEntry {
217 source_name: "IndependentSet",
218 target_name: "VertexCovering",
219 source_graph: "SimpleGraph",
220 target_graph: "SimpleGraph",
221 overhead_fn: || ReductionOverhead::new(vec![
222 ("num_vertices", poly!(num_vertices)),
223 ("num_edges", poly!(num_edges)),
224 ]),
225 }
226}
227
228inventory::submit! {
229 ReductionEntry {
230 source_name: "VertexCovering",
231 target_name: "IndependentSet",
232 source_graph: "SimpleGraph",
233 target_graph: "SimpleGraph",
234 overhead_fn: || ReductionOverhead::new(vec![
235 ("num_vertices", poly!(num_vertices)),
236 ("num_edges", poly!(num_edges)),
237 ]),
238 }
239}