problemreductions/rules/
sequencingtominimizemaximumcumulativecost_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
8use crate::models::misc::SequencingToMinimizeMaximumCumulativeCost;
9use crate::reduction;
10use crate::rules::ilp_helpers::{one_hot_decode, permutation_to_lehmer};
11use crate::rules::traits::{ReduceTo, ReductionResult};
12
13#[derive(Debug, Clone)]
20pub struct ReductionSTMMCCToILP {
21 target: ILP<i32>,
22 num_tasks: usize,
23}
24
25impl ReductionResult for ReductionSTMMCCToILP {
26 type Source = SequencingToMinimizeMaximumCumulativeCost;
27 type Target = ILP<i32>;
28
29 fn target_problem(&self) -> &ILP<i32> {
30 &self.target
31 }
32
33 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
35 let n = self.num_tasks;
36 let schedule = one_hot_decode(target_solution, n, n, 0);
37 permutation_to_lehmer(&schedule)
38 }
39}
40
41#[reduction(overhead = {
42 num_vars = "num_tasks * num_tasks + 1",
43 num_constraints = "2 * num_tasks + num_precedences + num_tasks + num_tasks * num_tasks",
44})]
45impl ReduceTo<ILP<i32>> for SequencingToMinimizeMaximumCumulativeCost {
46 type Result = ReductionSTMMCCToILP;
47
48 fn reduce_to(&self) -> Self::Result {
49 let n = self.num_tasks();
50 let z_var = n * n;
52 let num_vars = n * n + 1;
53
54 let x_var = |j: usize, p: usize| -> usize { j * n + p };
55
56 let mut constraints = Vec::new();
57
58 for j in 0..n {
60 let terms: Vec<(usize, f64)> = (0..n).map(|p| (x_var(j, p), 1.0)).collect();
61 constraints.push(LinearConstraint::eq(terms, 1.0));
62 }
63
64 for p in 0..n {
66 let terms: Vec<(usize, f64)> = (0..n).map(|j| (x_var(j, p), 1.0)).collect();
67 constraints.push(LinearConstraint::eq(terms, 1.0));
68 }
69
70 for &(i, j) in self.precedences() {
72 let mut terms: Vec<(usize, f64)> = Vec::new();
73 for p in 0..n {
74 terms.push((x_var(j, p), p as f64));
75 terms.push((x_var(i, p), -(p as f64)));
76 }
77 constraints.push(LinearConstraint::ge(terms, 1.0));
78 }
79
80 for j in 0..n {
82 for p in 0..n {
83 constraints.push(LinearConstraint::le(vec![(x_var(j, p), 1.0)], 1.0));
84 }
85 }
86
87 let costs = self.costs();
90 for q in 0..n {
91 let mut terms: Vec<(usize, f64)> = Vec::new();
92 for (j, &c_j) in costs.iter().enumerate() {
93 for p in 0..=q {
94 terms.push((x_var(j, p), c_j as f64));
95 }
96 }
97 terms.push((z_var, -1.0));
98 constraints.push(LinearConstraint::le(terms, 0.0));
99 }
100
101 let z_upper: f64 = costs.iter().map(|&c| (c as f64).abs()).sum();
103 constraints.push(LinearConstraint::le(vec![(z_var, 1.0)], z_upper));
104
105 let objective = vec![(z_var, 1.0)];
107
108 ReductionSTMMCCToILP {
109 target: ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize),
110 num_tasks: n,
111 }
112 }
113}
114
115#[cfg(feature = "example-db")]
116pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
117 vec![crate::example_db::specs::RuleExampleSpec {
118 id: "sequencingtominimizemaximumcumulativecost_to_ilp",
119 build: || {
120 let source =
121 SequencingToMinimizeMaximumCumulativeCost::new(vec![2, -1, 3, -2], vec![(0, 2)]);
122 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
123 },
124 }]
125}
126
127#[cfg(test)]
128#[path = "../unit_tests/rules/sequencingtominimizemaximumcumulativecost_ilp.rs"]
129mod tests;