problemreductions/rules/
sequencingtominimizemaximumcumulativecost_ilp.rs1use crate::models::algebraic::{Bounded, IntegerVariable, LinearConstraint, ObjectiveSense, ILP};
8use crate::models::misc::SequencingToMinimizeMaximumCumulativeCost;
9use crate::reduction;
10use crate::rules::ilp_helpers::one_hot_decode;
11use crate::rules::traits::{ReduceTo, ReductionResult};
12
13#[derive(Debug, Clone)]
20pub struct ReductionSTMMCCToILP {
21 target: ILP<i64, i64, Bounded>,
22 num_tasks: usize,
23}
24
25impl ReductionResult for ReductionSTMMCCToILP {
26 type Source = SequencingToMinimizeMaximumCumulativeCost;
27 type Target = ILP<i64, i64, Bounded>;
28
29 fn target_problem(&self) -> &ILP<i64, i64, Bounded> {
30 &self.target
31 }
32
33 fn extract_solution(
35 &self,
36 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
37 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
38 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
39
40 Ok({
41 let n = self.num_tasks;
42
43 one_hot_decode(target_solution, n, n, 0)?
44 })
45 }
46}
47
48#[reduction(transform = {
49 exact {
50 num_vars = "num_tasks^2 + 1",
51 num_constraints = "num_tasks^2 + 3 * num_tasks + num_precedences + 1",
52 },
53 upper_bound {
54 max_constraint_magnitude_bits = "max_cost_bits + num_tasks",
55 num_nonzeros = "(num_tasks^2 + 1) * (num_tasks^2 + 3 * num_tasks + num_precedences + 1)",
56 },
57})]
58impl ReduceTo<ILP<i64, i64, Bounded>> for SequencingToMinimizeMaximumCumulativeCost {
59 type Result = ReductionSTMMCCToILP;
60
61 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
62 let n = self.num_tasks();
63 let z_var = n * n;
65 let num_vars = n * n + 1;
66
67 let x_var = |j: usize, p: usize| -> usize { j * n + p };
68
69 let mut constraints = Vec::new();
70
71 for j in 0..n {
73 let terms: Vec<(usize, i64)> = (0..n).map(|p| (x_var(j, p), 1)).collect();
74 constraints.push(LinearConstraint::eq(terms, 1));
75 }
76
77 for p in 0..n {
79 let terms: Vec<(usize, i64)> = (0..n).map(|j| (x_var(j, p), 1)).collect();
80 constraints.push(LinearConstraint::eq(terms, 1));
81 }
82
83 for &(i, j) in self.precedences() {
85 let mut terms: Vec<(usize, i64)> = Vec::new();
86 for p in 0..n {
87 let p_i64 = Self::exact_i64(p, "encoding a task position")?;
88 terms.push((x_var(j, p), p_i64));
89 terms.push((x_var(i, p), -p_i64));
90 }
91 constraints.push(LinearConstraint::ge(terms, 1));
92 }
93
94 for j in 0..n {
96 for p in 0..n {
97 constraints.push(LinearConstraint::le(vec![(x_var(j, p), 1)], 1));
98 }
99 }
100
101 let costs = self.costs();
104 for q in 0..n {
105 let mut terms: Vec<(usize, i64)> = Vec::new();
106 for (j, &c_j) in costs.iter().enumerate() {
107 for p in 0..=q {
108 terms.push((x_var(j, p), c_j));
109 }
110 }
111 terms.push((z_var, -1));
112 constraints.push(LinearConstraint::le(terms, 0));
113 }
114
115 let z_upper = costs.iter().try_fold(0_i64, |total, &cost| {
117 let magnitude = cost.checked_abs().ok_or_else(|| {
118 crate::rules::ReductionError::integer_overflow::<
119 SequencingToMinimizeMaximumCumulativeCost,
120 ILP<i64, i64, Bounded>,
121 >("taking the absolute value of a task cost")
122 })?;
123 total.checked_add(magnitude).ok_or_else(|| {
124 crate::rules::ReductionError::integer_overflow::<
125 SequencingToMinimizeMaximumCumulativeCost,
126 ILP<i64, i64, Bounded>,
127 >("summing absolute task costs")
128 })
129 })?;
130 constraints.push(LinearConstraint::le(vec![(z_var, 1)], z_upper));
131
132 let objective = vec![(z_var, 1)];
134
135 let mut variables = vec![IntegerVariable::binary(); num_vars];
136 variables[z_var] =
137 IntegerVariable::new(Some(0), Some(z_upper)).map_err(Self::target_construction)?;
138
139 Ok(ReductionSTMMCCToILP {
140 target: ILP::with_variables(
141 variables,
142 constraints,
143 objective,
144 ObjectiveSense::Minimize,
145 )
146 .map_err(Self::target_construction)?,
147 num_tasks: n,
148 })
149 }
150}
151
152#[cfg(feature = "example-db")]
153pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
154 vec![crate::example_db::specs::RuleExampleSpec {
155 id: "sequencingtominimizemaximumcumulativecost_to_ilp",
156 build: || {
157 let source =
158 SequencingToMinimizeMaximumCumulativeCost::new(vec![2, -1, 3, -2], vec![(0, 2)]);
159 crate::example_db::specs::rule_example_via_bounded_ilp::<_>(source)
160 },
161 }]
162}
163
164#[cfg(test)]
165#[path = "../unit_tests/rules/sequencingtominimizemaximumcumulativecost_ilp.rs"]
166mod tests;