problemreductions/rules/
sequencingtominimizeweightedtardiness_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
8use crate::models::misc::SequencingToMinimizeWeightedTardiness;
9use crate::reduction;
10use crate::rules::traits::{ReduceTo, ReductionResult};
11
12#[derive(Debug, Clone)]
21pub struct ReductionSTMWTToILP {
22 target: ILP<i32>,
23 num_tasks: usize,
24 num_order_vars: usize,
25}
26
27impl ReductionSTMWTToILP {
28 fn encode_schedule_as_lehmer(schedule: &[usize]) -> Vec<usize> {
29 let mut available: Vec<usize> = (0..schedule.len()).collect();
30 let mut config = Vec::with_capacity(schedule.len());
31 for &task in schedule {
32 let digit = available
33 .iter()
34 .position(|&c| c == task)
35 .expect("schedule must be a permutation");
36 config.push(digit);
37 available.remove(digit);
38 }
39 config
40 }
41}
42
43impl ReductionResult for ReductionSTMWTToILP {
44 type Source = SequencingToMinimizeWeightedTardiness;
45 type Target = ILP<i32>;
46
47 fn target_problem(&self) -> &ILP<i32> {
48 &self.target
49 }
50
51 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
53 let n = self.num_tasks;
54 let c_offset = self.num_order_vars;
55 let mut jobs: Vec<usize> = (0..n).collect();
56 jobs.sort_by_key(|&j| (target_solution.get(c_offset + j).copied().unwrap_or(0), j));
57 Self::encode_schedule_as_lehmer(&jobs)
58 }
59}
60
61#[reduction(overhead = {
62 num_vars = "num_tasks * (num_tasks - 1) / 2 + 2 * num_tasks",
63 num_constraints = "num_tasks * (num_tasks - 1) / 2 + num_tasks + num_tasks * (num_tasks - 1) + 2 * num_tasks + 1",
64})]
65impl ReduceTo<ILP<i32>> for SequencingToMinimizeWeightedTardiness {
66 type Result = ReductionSTMWTToILP;
67
68 fn reduce_to(&self) -> Self::Result {
69 let n = self.num_tasks();
70 let num_order_vars = n * n.saturating_sub(1) / 2;
71 let num_vars = num_order_vars + 2 * n;
72
73 let order_var = |i: usize, j: usize| -> usize {
74 debug_assert!(i < j);
75 i * (2 * n - i - 1) / 2 + (j - i - 1)
76 };
77 let c_var = |j: usize| -> usize { num_order_vars + j };
78 let t_var = |j: usize| -> usize { num_order_vars + n + j };
79
80 let lengths = self.lengths();
81 let deadlines = self.deadlines();
82 let weights = self.weights();
83 let bound = self.bound();
84
85 let big_m: f64 = lengths.iter().sum::<u64>() as f64;
87
88 let mut constraints = Vec::new();
89
90 for i in 0..n {
92 for j in (i + 1)..n {
93 constraints.push(LinearConstraint::le(vec![(order_var(i, j), 1.0)], 1.0));
94 constraints.push(LinearConstraint::ge(vec![(order_var(i, j), 1.0)], 0.0));
95 }
96 }
97
98 for (j, &l_j) in lengths.iter().enumerate() {
100 constraints.push(LinearConstraint::ge(vec![(c_var(j), 1.0)], l_j as f64));
101 }
102
103 for i in 0..n {
105 for (j, &l_j) in lengths.iter().enumerate() {
106 if i == j {
107 continue;
108 }
109 if i < j {
110 constraints.push(LinearConstraint::ge(
114 vec![(c_var(j), 1.0), (c_var(i), -1.0), (order_var(i, j), -big_m)],
115 l_j as f64 - big_m,
116 ));
117 } else {
118 constraints.push(LinearConstraint::ge(
122 vec![(c_var(j), 1.0), (c_var(i), -1.0), (order_var(j, i), big_m)],
123 l_j as f64,
124 ));
125 }
126 }
127 }
128
129 for (j, &d_j) in deadlines.iter().enumerate() {
131 constraints.push(LinearConstraint::ge(
132 vec![(t_var(j), 1.0), (c_var(j), -1.0)],
133 -(d_j as f64),
134 ));
135 }
136
137 for j in 0..n {
139 constraints.push(LinearConstraint::ge(vec![(t_var(j), 1.0)], 0.0));
140 }
141
142 let terms: Vec<(usize, f64)> = (0..n).map(|j| (t_var(j), weights[j] as f64)).collect();
144 constraints.push(LinearConstraint::le(terms, bound as f64));
145
146 ReductionSTMWTToILP {
147 target: ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize),
148 num_tasks: n,
149 num_order_vars,
150 }
151 }
152}
153
154#[cfg(feature = "example-db")]
155pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
156 vec![crate::example_db::specs::RuleExampleSpec {
157 id: "sequencingtominimizeweightedtardiness_to_ilp",
158 build: || {
159 let source = SequencingToMinimizeWeightedTardiness::new(
160 vec![3, 4, 2],
161 vec![2, 3, 1],
162 vec![5, 8, 4],
163 10,
164 );
165 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
166 },
167 }]
168}
169
170#[cfg(test)]
171#[path = "../unit_tests/rules/sequencingtominimizeweightedtardiness_ilp.rs"]
172mod tests;