problemreductions/rules/
minimummultiwaycut_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
10use crate::models::graph::MinimumMultiwayCut;
11use crate::reduction;
12use crate::rules::traits::{ReduceTo, ReductionResult};
13use crate::topology::{Graph, SimpleGraph};
14
15#[derive(Debug, Clone)]
24pub struct ReductionMMCToILP {
25 target: ILP<bool>,
26 n: usize,
28 m: usize,
30 k: usize,
32}
33
34impl ReductionResult for ReductionMMCToILP {
35 type Source = MinimumMultiwayCut<SimpleGraph, i64>;
36 type Target = ILP<bool>;
37
38 fn target_problem(&self) -> &ILP<bool> {
39 &self.target
40 }
41
42 fn extract_solution(
46 &self,
47 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
48 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
49 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
50
51 Ok({
52 let offset = self.k * self.n;
53 (0..self.m)
54 .map(|e| target_solution[offset + e] == 1)
55 .collect()
56 })
57 }
58}
59
60#[reduction(transform = {
61 exact {
62 num_vars = "num_terminals * num_vertices + num_edges",
63 num_constraints = "num_vertices + 2 * num_terminals * num_edges + num_terminals * num_terminals",
64 },
65 upper_bound {
66 max_constraint_magnitude_bits = "1",
67 num_nonzeros = "(num_terminals * num_vertices + num_edges) * (num_vertices + 2 * num_terminals * num_edges + num_terminals * num_terminals)",
68 },
69})]
70impl ReduceTo<ILP<bool>> for MinimumMultiwayCut<SimpleGraph, i64> {
71 type Result = ReductionMMCToILP;
72
73 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
74 let n = self.num_vertices();
75 let m = self.num_edges();
76 let k = self.num_terminals();
77 let terminals = self.terminals();
78 let edges = self.graph().edges();
79 let weights = self.edge_weights();
80 let num_vars = k * n + m;
81
82 let num_terminal_fixes = k * k;
86 let num_constraints = n + 2 * k * m + num_terminal_fixes;
87 let mut constraints = Vec::with_capacity(num_constraints);
88
89 for (i, &t) in terminals.iter().enumerate() {
91 constraints.push(LinearConstraint::eq(vec![(i * n + t, 1)], 1));
92 }
93
94 for (i, &t) in terminals.iter().enumerate() {
96 for j in 0..k {
97 if j != i {
98 constraints.push(LinearConstraint::eq(vec![(j * n + t, 1)], 0));
99 }
100 }
101 }
102
103 for v in 0..n {
105 let terms: Vec<(usize, i64)> = (0..k).map(|i| (i * n + v, 1)).collect();
106 constraints.push(LinearConstraint::eq(terms, 1));
107 }
108
109 for (e_idx, (u, v)) in edges.iter().enumerate() {
113 let x_var = k * n + e_idx;
114 for i in 0..k {
115 let y_iu = i * n + u;
116 let y_iv = i * n + v;
117 constraints.push(LinearConstraint::ge(
119 vec![(x_var, 1), (y_iu, -1), (y_iv, 1)],
120 0,
121 ));
122 constraints.push(LinearConstraint::ge(
124 vec![(x_var, 1), (y_iu, 1), (y_iv, -1)],
125 0,
126 ));
127 }
128 }
129
130 let objective: Vec<(usize, i64)> = weights
132 .iter()
133 .enumerate()
134 .map(|(e_idx, &weight)| (k * n + e_idx, weight))
135 .collect();
136
137 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize)
138 .map_err(<Self as ReduceTo<ILP<bool>>>::target_construction)?;
139
140 Ok(ReductionMMCToILP { target, n, m, k })
141 }
142}
143
144#[cfg(feature = "example-db")]
145pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
146 vec![crate::example_db::specs::RuleExampleSpec {
147 id: "minimummultiwaycut_to_ilp",
148 build: || {
149 let graph = SimpleGraph::new(5, vec![(0, 1), (1, 2), (2, 3), (3, 4), (0, 4), (1, 3)]);
150 let problem = MinimumMultiwayCut::new(graph, vec![0, 2, 4], vec![2, 3, 1, 2, 4, 5]);
151 crate::example_db::specs::rule_example_via_ilp::<_, bool>(problem)
152 },
153 }]
154}
155
156#[cfg(test)]
157#[path = "../unit_tests/rules/minimummultiwaycut_ilp.rs"]
158mod tests;