problemreductions/rules/
maxcut_minimumcutintoboundedsets.rs1use crate::models::graph::{MaxCut, MinimumCutIntoBoundedSets};
11use crate::reduction;
12use crate::rules::traits::{ReduceTo, ReductionResult};
13use crate::topology::{Graph, SimpleGraph};
14
15#[derive(Debug, Clone)]
17pub struct ReductionMaxCutToMinCutBounded {
18 target: MinimumCutIntoBoundedSets<SimpleGraph, i64>,
19 original_n: usize,
21}
22
23impl ReductionResult for ReductionMaxCutToMinCutBounded {
24 type Source = MaxCut<SimpleGraph, i64>;
25 type Target = MinimumCutIntoBoundedSets<SimpleGraph, i64>;
26
27 fn target_problem(&self) -> &Self::Target {
28 &self.target
29 }
30
31 fn extract_solution(
34 &self,
35 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
36 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
37 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
38
39 Ok(target_solution[..self.original_n].to_vec())
40 }
41}
42
43#[reduction(
44 transform = exact {
45 num_vertices = "2 * num_vertices + 2",
46 num_edges = "(num_vertices + 1) * (2 * num_vertices + 1)",
47 }
48)]
49impl ReduceTo<MinimumCutIntoBoundedSets<SimpleGraph, i64>> for MaxCut<SimpleGraph, i64> {
50 type Result = ReductionMaxCutToMinCutBounded;
51
52 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
53 let n = self.graph().num_vertices();
54
55 let n_prime = n.checked_add(n % 2).ok_or_else(|| {
58 crate::rules::ReductionError::integer_overflow::<
59 MaxCut<SimpleGraph, i64>,
60 MinimumCutIntoBoundedSets<SimpleGraph, i64>,
61 >("padding the source vertex count")
62 })?;
63 let big_n = n_prime.checked_mul(2).ok_or_else(|| {
64 crate::rules::ReductionError::integer_overflow::<
65 MaxCut<SimpleGraph, i64>,
66 MinimumCutIntoBoundedSets<SimpleGraph, i64>,
67 >("computing the target vertex count")
68 })?;
69
70 let w_max = self
72 .edge_weights()
73 .iter()
74 .copied()
75 .max()
76 .unwrap_or(0)
77 .checked_add(1)
78 .ok_or_else(|| {
79 crate::rules::ReductionError::integer_overflow::<
80 MaxCut<SimpleGraph, i64>,
81 MinimumCutIntoBoundedSets<SimpleGraph, i64>,
82 >("computing the inverted-weight ceiling")
83 })?;
84
85 let orig_edges = self.graph().edges();
87 let mut edge_weight_map: std::collections::HashMap<(usize, usize), i64> =
88 std::collections::HashMap::new();
89 for (&(u, v), w) in orig_edges.iter().zip(self.edge_weights()) {
90 let (a, b) = if u < v { (u, v) } else { (v, u) };
91 edge_weight_map.insert((a, b), w);
92 }
93
94 let mut edges = Vec::new();
96 let mut weights = Vec::new();
97 for i in 0..big_n {
98 for j in (i + 1)..big_n {
99 edges.push((i, j));
100 if let Some(&w) = edge_weight_map.get(&(i, j)) {
101 weights.push(w_max.checked_sub(w).ok_or_else(|| {
102 crate::rules::ReductionError::integer_overflow::<
103 MaxCut<SimpleGraph, i64>,
104 MinimumCutIntoBoundedSets<SimpleGraph, i64>,
105 >("inverting an edge weight")
106 })?);
107 } else {
108 weights.push(w_max);
109 }
110 }
111 }
112
113 let source_vertex = n_prime;
115 let sink_vertex = n_prime.checked_add(1).ok_or_else(|| {
116 crate::rules::ReductionError::integer_overflow::<
117 MaxCut<SimpleGraph, i64>,
118 MinimumCutIntoBoundedSets<SimpleGraph, i64>,
119 >("computing the target sink vertex")
120 })?;
121 let size_bound = n_prime;
122
123 let target = MinimumCutIntoBoundedSets::new(
124 SimpleGraph::new(big_n, edges),
125 weights,
126 source_vertex,
127 sink_vertex,
128 size_bound,
129 );
130
131 Ok(ReductionMaxCutToMinCutBounded {
132 target,
133 original_n: n,
134 })
135 }
136}
137
138#[cfg(feature = "example-db")]
139pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
140 use crate::export::SolutionPair;
141 use crate::solvers::BruteForce;
142
143 vec![crate::example_db::specs::RuleExampleSpec {
144 id: "maxcut_to_minimumcutintoboundedsets",
145 build: || {
146 let source = MaxCut::new(
148 SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]),
149 vec![1i64, 1, 1],
150 );
151 let reduction =
152 ReduceTo::<MinimumCutIntoBoundedSets<SimpleGraph, i64>>::reduce_to(&source)
153 .expect("reduction should succeed");
154
155 let solver = BruteForce::new();
157 let source_witness = solver
158 .solve(&source)
159 .expect("canonical source evaluation must succeed")
160 .expect("canonical source must have an optimum");
161 let target_witness = solver
162 .solve(reduction.target_problem())
163 .expect("canonical target evaluation must succeed")
164 .expect("canonical target must have an optimum");
165
166 crate::example_db::specs::assemble_rule_example(
167 &source,
168 reduction.target_problem(),
169 vec![SolutionPair {
170 source_config: serde_json::json!(source_witness),
171 target_config: serde_json::json!(target_witness),
172 }],
173 )
174 },
175 }]
176}
177
178#[cfg(test)]
179#[path = "../unit_tests/rules/maxcut_minimumcutintoboundedsets.rs"]
180mod tests;