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<i32>,
41 num_edges: usize,
42}
43
44impl ReductionResult for ReductionUFLBToILP {
45 type Source = UndirectedFlowLowerBounds;
46 type Target = ILP<i32>;
47
48 fn target_problem(&self) -> &ILP<i32> {
49 &self.target
50 }
51
52 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
58 let e = self.num_edges;
59 target_solution[2 * e..3 * e]
60 .iter()
61 .map(|&z| 1 - z)
62 .collect()
63 }
64}
65
66#[reduction(
67 overhead = {
68 num_vars = "3 * num_edges",
69 num_constraints = "4 * num_edges + num_vertices + 1",
70 }
71)]
72impl ReduceTo<ILP<i32>> for UndirectedFlowLowerBounds {
73 type Result = ReductionUFLBToILP;
74
75 fn reduce_to(&self) -> Self::Result {
76 let edges = self.graph().edges();
77 let e = edges.len();
78 let n = self.num_vertices();
79 let num_vars = 3 * e;
80
81 let f_uv = |edge: usize| 2 * edge;
82 let f_vu = |edge: usize| 2 * edge + 1;
83 let z = |edge: usize| 2 * e + edge;
84
85 let mut constraints = Vec::new();
86
87 for (edge_idx, _) in edges.iter().enumerate() {
88 let cap = self.capacities()[edge_idx] as f64;
89 let lower = self.lower_bounds()[edge_idx] as f64;
90
91 constraints.push(LinearConstraint::le(vec![(z(edge_idx), 1.0)], 1.0));
93
94 constraints.push(LinearConstraint::le(
96 vec![(f_uv(edge_idx), 1.0), (z(edge_idx), -cap)],
97 0.0,
98 ));
99
100 constraints.push(LinearConstraint::le(
102 vec![(f_vu(edge_idx), 1.0), (z(edge_idx), cap)],
103 cap,
104 ));
105
106 if lower > 0.0 {
107 constraints.push(LinearConstraint::ge(
109 vec![(f_uv(edge_idx), 1.0), (z(edge_idx), -lower)],
110 0.0,
111 ));
112
113 constraints.push(LinearConstraint::ge(
115 vec![(f_vu(edge_idx), 1.0), (z(edge_idx), lower)],
116 lower,
117 ));
118 }
119 }
120
121 for vertex in 0..n {
123 if vertex == self.source() || vertex == self.sink() {
124 continue;
125 }
126
127 let mut terms: Vec<(usize, f64)> = Vec::new();
128 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
129 if vertex == u {
130 terms.push((f_uv(edge_idx), -1.0));
132 terms.push((f_vu(edge_idx), 1.0));
133 } else if vertex == v {
134 terms.push((f_uv(edge_idx), 1.0));
136 terms.push((f_vu(edge_idx), -1.0));
137 }
138 }
139
140 if !terms.is_empty() {
141 constraints.push(LinearConstraint::eq(terms, 0.0));
142 }
143 }
144
145 let sink = self.sink();
147 let mut sink_terms: Vec<(usize, f64)> = Vec::new();
148 for (edge_idx, &(u, v)) in edges.iter().enumerate() {
149 if v == sink {
150 sink_terms.push((f_uv(edge_idx), 1.0));
152 sink_terms.push((f_vu(edge_idx), -1.0));
153 } else if u == sink {
154 sink_terms.push((f_uv(edge_idx), -1.0));
156 sink_terms.push((f_vu(edge_idx), 1.0));
157 }
158 }
159 constraints.push(LinearConstraint::ge(sink_terms, self.requirement() as f64));
160
161 ReductionUFLBToILP {
162 target: ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize),
163 num_edges: e,
164 }
165 }
166}
167
168#[cfg(feature = "example-db")]
169pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
170 use crate::topology::SimpleGraph;
171
172 vec![crate::example_db::specs::RuleExampleSpec {
173 id: "undirectedflowlowerbounds_to_ilp",
174 build: || {
175 let source = UndirectedFlowLowerBounds::new(
178 SimpleGraph::new(3, vec![(0, 1), (1, 2)]),
179 vec![2, 2],
180 vec![1, 1],
181 0,
182 2,
183 1,
184 );
185 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
186 },
187 }]
188}
189
190#[cfg(test)]
191#[path = "../unit_tests/rules/undirectedflowlowerbounds_ilp.rs"]
192mod tests;