1use crate::models::algebraic::{Bounded, IntegerVariable, LinearConstraint, ObjectiveSense, ILP};
19use crate::models::graph::MinimumEdgeCostFlow;
20use crate::reduction;
21use crate::rules::traits::{ReduceTo, ReductionResult};
22
23#[derive(Debug, Clone)]
29pub struct ReductionMECFToILP {
30 target: ILP<i64, i64, Bounded>,
31 num_edges: usize,
32}
33
34impl ReductionResult for ReductionMECFToILP {
35 type Source = MinimumEdgeCostFlow;
36 type Target = ILP<i64, i64, Bounded>;
37
38 fn target_problem(&self) -> &ILP<i64, i64, Bounded> {
39 &self.target
40 }
41
42 fn extract_solution(
44 &self,
45 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
46 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
47 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
48
49 crate::rules::ilp_helpers::decode_usize_values(&target_solution[..self.num_edges])
50 }
51}
52
53#[reduction(transform = {
56 exact {
57 num_vars = "2 * num_edges",
58 },
59 upper_bound {
60 max_constraint_magnitude_bits = "max_capacity * (num_edges + 1) + 2",
61 num_constraints = "2 * num_edges + num_vertices - 1",
62 num_nonzeros = "5 * num_edges",
63 },
64})]
65impl ReduceTo<ILP<i64, i64, Bounded>> for MinimumEdgeCostFlow {
66 type Result = ReductionMECFToILP;
67
68 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
69 let arcs = self.graph().arcs();
70 let m = arcs.len();
71 let n = self.num_vertices();
72 let num_vars = 2 * m;
73
74 let f = |a: usize| a; let y = |a: usize| m + a; let mut constraints = Vec::new();
78
79 for a in 0..m {
81 constraints.push(LinearConstraint::le(
82 vec![(f(a), 1), (y(a), -self.capacities()[a])],
83 0,
84 ));
85 }
86
87 for a in 0..m {
89 constraints.push(LinearConstraint::le(vec![(y(a), 1)], 1));
90 }
91
92 for vertex in 0..n {
94 if vertex == self.source() || vertex == self.sink() {
95 continue;
96 }
97
98 let mut terms: Vec<(usize, i64)> = Vec::new();
99 for (a, &(u, v)) in arcs.iter().enumerate() {
100 if vertex == u {
101 terms.push((f(a), -1)); } else if vertex == v {
103 terms.push((f(a), 1)); }
105 }
106
107 if !terms.is_empty() {
108 constraints.push(LinearConstraint::eq(terms, 0));
109 }
110 }
111
112 let sink = self.sink();
114 let mut sink_terms: Vec<(usize, i64)> = Vec::new();
115 for (a, &(u, v)) in arcs.iter().enumerate() {
116 if v == sink {
117 sink_terms.push((f(a), 1));
118 } else if u == sink {
119 sink_terms.push((f(a), -1));
120 }
121 }
122 constraints.push(LinearConstraint::ge(
123 sink_terms,
124 crate::rules::ilp_helpers::bounded_flow_requirement(
125 self.required_flow(),
126 self.capacities().iter().copied(),
127 ),
128 ));
129
130 let objective: Vec<(usize, i64)> = (0..m).map(|a| (y(a), self.prices()[a])).collect();
132
133 let mut variables = self
134 .capacities()
135 .iter()
136 .map(|&capacity| IntegerVariable::new(Some(0), Some(capacity)))
137 .collect::<Result<Vec<_>, _>>()
138 .map_err(Self::target_construction)?;
139 variables.resize(num_vars, IntegerVariable::binary());
140
141 Ok(ReductionMECFToILP {
142 target: ILP::with_variables(
143 variables,
144 constraints,
145 objective,
146 ObjectiveSense::Minimize,
147 )
148 .map_err(Self::target_construction)?,
149 num_edges: m,
150 })
151 }
152}
153
154#[cfg(feature = "example-db")]
155pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
156 use crate::topology::DirectedGraph;
157
158 vec![crate::example_db::specs::RuleExampleSpec {
159 id: "minimumedgecostflow_to_ilp",
160 build: || {
161 let source = MinimumEdgeCostFlow::new(
162 DirectedGraph::new(5, vec![(0, 1), (0, 2), (0, 3), (1, 4), (2, 4), (3, 4)]),
163 vec![3, 1, 2, 0, 0, 0],
164 vec![2, 2, 2, 2, 2, 2],
165 0,
166 4,
167 3,
168 );
169 crate::example_db::specs::rule_example_via_bounded_ilp::<_>(source)
170 },
171 }]
172}
173
174#[cfg(test)]
175#[path = "../unit_tests/rules/minimumedgecostflow_ilp.rs"]
176mod tests;