problemreductions/rules/
multiplechoicebranching_ilp.rs1use crate::models::algebraic::{Bounded, IntegerVariable, LinearConstraint, ObjectiveSense, ILP};
4use crate::models::graph::MultipleChoiceBranching;
5use crate::reduction;
6use crate::rules::traits::{ReduceTo, ReductionResult};
7
8#[derive(Debug, Clone)]
9pub struct ReductionMultipleChoiceBranchingToILP {
10 target: ILP<i64, i64, Bounded>,
11 num_arcs: usize,
12}
13
14impl ReductionResult for ReductionMultipleChoiceBranchingToILP {
15 type Source = MultipleChoiceBranching<i64>;
16 type Target = ILP<i64, i64, Bounded>;
17
18 fn target_problem(&self) -> &Self::Target {
19 &self.target
20 }
21
22 fn extract_solution(
23 &self,
24 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
25 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
26 crate::rules::traits::validate_target_witness(
27 self.target_problem(),
28 target_solution,
29 |value| value.value.is_some(),
30 "target ILP assignment is infeasible",
31 )?;
32 Ok(target_solution[..self.num_arcs]
33 .iter()
34 .map(|&selected| selected == 1)
35 .collect())
36 }
37}
38
39#[crate::aggregate_reduction(ilp_feasibility)]
40impl crate::rules::AggregateReductionResult for ReductionMultipleChoiceBranchingToILP {}
41
42#[reduction(transform = {
43 exact {
44 num_vars = "num_arcs + num_vertices",
45 num_constraints = "2 * num_arcs + 2 * num_vertices + num_partition_groups + 1",
46 },
47 upper_bound {
48 max_constraint_magnitude_bits = "max_weight_bits + num_vertices",
49 num_nonzeros = "(num_arcs + num_vertices) * (2 * num_arcs + 2 * num_vertices + num_partition_groups + 1)",
50 },
51})]
52impl ReduceTo<ILP<i64, i64, Bounded>> for MultipleChoiceBranching<i64> {
53 type Result = ReductionMultipleChoiceBranchingToILP;
54
55 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
56 let num_arcs = self.num_arcs();
57 let num_vertices = self.num_vertices();
58 let order = |vertex: usize| num_arcs + vertex;
59 let mut constraints = Vec::new();
60
61 for arc in 0..num_arcs {
62 constraints.push(LinearConstraint::le(vec![(arc, 1)], 1));
63 }
64 if num_vertices > 0 {
65 let big_m = <Self as ReduceTo<ILP<i64, i64, Bounded>>>::exact_i64(
66 num_vertices,
67 "encoding topological-order constraints",
68 )?;
69 for vertex in 0..num_vertices {
70 constraints.push(LinearConstraint::le(vec![(order(vertex), 1)], big_m - 1));
71 }
72 for (arc, &(source, target)) in self.graph().arcs().iter().enumerate() {
73 constraints.push(LinearConstraint::le(
74 vec![(order(source), 1), (order(target), -1), (arc, big_m)],
75 big_m - 1,
76 ));
77 }
78 }
79 for group in self.partition() {
80 constraints.push(LinearConstraint::le(
81 group.iter().map(|&arc| (arc, 1)).collect(),
82 1,
83 ));
84 }
85 for vertex in 0..num_vertices {
86 constraints.push(LinearConstraint::le(
87 self.graph()
88 .arcs()
89 .iter()
90 .enumerate()
91 .filter_map(|(arc, &(_, target))| (target == vertex).then_some((arc, 1)))
92 .collect(),
93 1,
94 ));
95 }
96 constraints.push(LinearConstraint::ge(
97 self.weights()
98 .iter()
99 .enumerate()
100 .map(|(arc, &weight)| (arc, weight))
101 .collect(),
102 *self.threshold(),
103 ));
104
105 let mut variables = vec![IntegerVariable::binary(); num_arcs + num_vertices];
106 variables[num_arcs..].fill(
107 IntegerVariable::new(
108 Some(0),
109 Some(Self::exact_i64(
110 num_vertices.saturating_sub(1),
111 "bounding topological labels",
112 )?),
113 )
114 .map_err(Self::target_construction)?,
115 );
116
117 let target = ILP::with_variables(variables, constraints, vec![], ObjectiveSense::Minimize)
118 .map_err(<Self as ReduceTo<ILP<i64, i64, Bounded>>>::target_construction)?;
119 Ok(ReductionMultipleChoiceBranchingToILP { target, num_arcs })
120 }
121}
122
123#[cfg(feature = "example-db")]
124pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
125 use crate::topology::DirectedGraph;
126
127 vec![crate::example_db::specs::RuleExampleSpec {
128 id: "multiplechoicebranching_to_ilp",
129 build: || {
130 let source = MultipleChoiceBranching::new(
131 DirectedGraph::new(3, vec![(0, 1), (1, 2)]),
132 vec![2, 3],
133 vec![vec![0], vec![1]],
134 5,
135 );
136 crate::example_db::specs::rule_example_via_bounded_ilp::<_>(source)
137 },
138 }]
139}
140
141#[cfg(test)]
142#[path = "../unit_tests/rules/multiplechoicebranching_ilp.rs"]
143mod tests;