problemreductions/rules/
directedtwocommodityintegralflow_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
16use crate::models::graph::DirectedTwoCommodityIntegralFlow;
17use crate::reduction;
18use crate::rules::traits::{ReduceTo, ReductionResult};
19
20#[derive(Debug, Clone)]
26pub struct ReductionD2CIFToILP {
27 target: ILP<i32>,
28 num_arcs: usize,
29}
30
31impl ReductionResult for ReductionD2CIFToILP {
32 type Source = DirectedTwoCommodityIntegralFlow;
33 type Target = ILP<i32>;
34
35 fn target_problem(&self) -> &ILP<i32> {
36 &self.target
37 }
38
39 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
41 target_solution[..2 * self.num_arcs].to_vec()
42 }
43}
44
45#[reduction(
46 overhead = {
47 num_vars = "2 * num_arcs",
48 num_constraints = "num_arcs + 2 * num_vertices + 2",
49 }
50)]
51impl ReduceTo<ILP<i32>> for DirectedTwoCommodityIntegralFlow {
52 type Result = ReductionD2CIFToILP;
53
54 fn reduce_to(&self) -> Self::Result {
55 let arcs = self.graph().arcs();
56 let m = arcs.len();
57 let n = self.num_vertices();
58 let num_vars = 2 * m;
59
60 let f1 = |a: usize| a;
61 let f2 = |a: usize| m + a;
62
63 let mut constraints = Vec::new();
64
65 for a in 0..m {
67 constraints.push(LinearConstraint::le(
68 vec![(f1(a), 1.0), (f2(a), 1.0)],
69 self.capacities()[a] as f64,
70 ));
71 }
72
73 for vertex in 0..n {
75 let mut terms_c1: Option<Vec<(usize, f64)>> = None;
77 let mut terms_c2: Option<Vec<(usize, f64)>> = None;
79
80 if vertex != self.source_1() && vertex != self.sink_1() {
81 terms_c1 = Some(Vec::new());
82 }
83 if vertex != self.source_2() && vertex != self.sink_2() {
84 terms_c2 = Some(Vec::new());
85 }
86
87 for (a, &(u, v)) in arcs.iter().enumerate() {
88 if vertex == u {
89 if let Some(terms) = &mut terms_c1 {
91 terms.push((f1(a), -1.0));
92 }
93 if let Some(terms) = &mut terms_c2 {
94 terms.push((f2(a), -1.0));
95 }
96 } else if vertex == v {
97 if let Some(terms) = &mut terms_c1 {
99 terms.push((f1(a), 1.0));
100 }
101 if let Some(terms) = &mut terms_c2 {
102 terms.push((f2(a), 1.0));
103 }
104 }
105 }
106
107 if let Some(terms_c1) = terms_c1.filter(|terms| !terms.is_empty()) {
108 constraints.push(LinearConstraint::eq(terms_c1, 0.0));
109 }
110 if let Some(terms_c2) = terms_c2.filter(|terms| !terms.is_empty()) {
111 constraints.push(LinearConstraint::eq(terms_c2, 0.0));
112 }
113 }
114
115 let sink_1 = self.sink_1();
117 let mut sink1_terms: Vec<(usize, f64)> = Vec::new();
118 for (a, &(u, v)) in arcs.iter().enumerate() {
119 if v == sink_1 {
120 sink1_terms.push((f1(a), 1.0));
121 } else if u == sink_1 {
122 sink1_terms.push((f1(a), -1.0));
123 }
124 }
125 constraints.push(LinearConstraint::ge(
126 sink1_terms,
127 self.requirement_1() as f64,
128 ));
129
130 let sink_2 = self.sink_2();
132 let mut sink2_terms: Vec<(usize, f64)> = Vec::new();
133 for (a, &(u, v)) in arcs.iter().enumerate() {
134 if v == sink_2 {
135 sink2_terms.push((f2(a), 1.0));
136 } else if u == sink_2 {
137 sink2_terms.push((f2(a), -1.0));
138 }
139 }
140 constraints.push(LinearConstraint::ge(
141 sink2_terms,
142 self.requirement_2() as f64,
143 ));
144
145 ReductionD2CIFToILP {
146 target: ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize),
147 num_arcs: m,
148 }
149 }
150}
151
152#[cfg(feature = "example-db")]
153pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
154 use crate::topology::DirectedGraph;
155
156 vec![crate::example_db::specs::RuleExampleSpec {
157 id: "directedtwocommodityintegralflow_to_ilp",
158 build: || {
159 let source = DirectedTwoCommodityIntegralFlow::new(
163 DirectedGraph::new(
164 6,
165 vec![
166 (0, 2),
167 (0, 3),
168 (1, 2),
169 (1, 3),
170 (2, 4),
171 (2, 5),
172 (3, 4),
173 (3, 5),
174 ],
175 ),
176 vec![1; 8],
177 0,
178 4,
179 1,
180 5,
181 1,
182 1,
183 );
184 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
185 },
186 }]
187}
188
189#[cfg(test)]
190#[path = "../unit_tests/rules/directedtwocommodityintegralflow_ilp.rs"]
191mod tests;