problemreductions/rules/
undirectedflowlowerbounds_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
27use crate::models::graph::UndirectedFlowLowerBounds;
28use crate::reduction;
29use crate::rules::traits::{ReduceTo, ReductionResult};
30use crate::topology::Graph;
31
32#[derive(Debug, Clone)]
39pub struct ReductionUFLBToILP {
40 target: ILP<i64>,
41 num_edges: usize,
42}
43
44impl ReductionResult for ReductionUFLBToILP {
45 type Source = UndirectedFlowLowerBounds;
46 type Target = ILP<i64>;
47
48 fn target_problem(&self) -> &ILP<i64> {
49 &self.target
50 }
51
52 fn extract_solution(
58 &self,
59 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
60 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
61 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
62
63 Ok({
64 let e = self.num_edges;
65 target_solution[2 * e..3 * e]
66 .iter()
67 .map(|&z| z == 0)
68 .collect()
69 })
70 }
71}
72
73#[reduction(
74 transform = exact {
75 num_vars = "3 * num_edges",
76 num_constraints = "4 * num_edges + num_vertices + 1",
77 },
78 unavailable = {
79 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
80 }
81)]
82impl ReduceTo<ILP<i64>> for UndirectedFlowLowerBounds {
83 type Result = ReductionUFLBToILP;
84
85 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
86 let edges = self.graph().edges();
87 let e = edges.len();
88 let n = self.num_vertices();
89 let num_vars = 3 * e;
90 let f_uv = |edge: usize| 2 * edge;
91 let f_vu = |edge: usize| 2 * edge + 1;
92 let z = |edge: usize| 2 * e + edge;
93
94 let mut constraints = Vec::new();
95
96 for (edge_idx, _) in edges.iter().enumerate() {
97 let cap = self.capacities()[edge_idx];
98 let lower = self.lower_bounds()[edge_idx];
99
100 constraints.push(LinearConstraint::le(vec![(z(edge_idx), 1)], 1));
102
103 constraints.push(LinearConstraint::le(
105 vec![(f_uv(edge_idx), 1), (z(edge_idx), -cap)],
106 0,
107 ));
108
109 constraints.push(LinearConstraint::le(
111 vec![(f_vu(edge_idx), 1), (z(edge_idx), cap)],
112 cap,
113 ));
114
115 if lower > 0 {
116 constraints.push(LinearConstraint::ge(
118 vec![(f_uv(edge_idx), 1), (z(edge_idx), -lower)],
119 0,
120 ));
121
122 constraints.push(LinearConstraint::ge(
124 vec![(f_vu(edge_idx), 1), (z(edge_idx), lower)],
125 lower,
126 ));
127 }
128 }
129
130 for vertex in 0..n {
132 if vertex == self.source() || vertex == self.sink() {
133 continue;
134 }
135
136 let mut terms: Vec<(usize, i64)> = Vec::new();
137 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
138 if vertex == u {
139 terms.push((f_uv(edge_idx), -1));
141 terms.push((f_vu(edge_idx), 1));
142 } else if vertex == v {
143 terms.push((f_uv(edge_idx), 1));
145 terms.push((f_vu(edge_idx), -1));
146 }
147 }
148
149 if !terms.is_empty() {
150 constraints.push(LinearConstraint::eq(terms, 0));
151 }
152 }
153
154 let sink = self.sink();
156 let mut sink_terms: Vec<(usize, i64)> = Vec::new();
157 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
158 if v == sink {
159 sink_terms.push((f_uv(edge_idx), 1));
161 sink_terms.push((f_vu(edge_idx), -1));
162 } else if u == sink {
163 sink_terms.push((f_uv(edge_idx), -1));
165 sink_terms.push((f_vu(edge_idx), 1));
166 }
167 }
168 constraints.push(LinearConstraint::ge(sink_terms, self.requirement()));
169
170 Ok(ReductionUFLBToILP {
171 target: ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize)
172 .map_err(Self::target_construction)?,
173 num_edges: e,
174 })
175 }
176}
177
178#[cfg(feature = "example-db")]
179pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
180 use crate::topology::SimpleGraph;
181
182 vec![crate::example_db::specs::RuleExampleSpec {
183 id: "undirectedflowlowerbounds_to_ilp",
184 build: || {
185 let source = UndirectedFlowLowerBounds::new(
188 SimpleGraph::new(3, vec![(0, 1), (1, 2)]),
189 vec![2, 2],
190 vec![1, 1],
191 0,
192 2,
193 1,
194 );
195 crate::example_db::specs::rule_example_via_ilp::<_, i64>(source)
196 },
197 }]
198}
199
200#[cfg(test)]
201#[path = "../unit_tests/rules/undirectedflowlowerbounds_ilp.rs"]
202mod tests;