problemreductions/rules/
sequencingwithreleasetimesanddeadlines_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
8use crate::models::misc::SequencingWithReleaseTimesAndDeadlines;
9use crate::reduction;
10use crate::rules::traits::{ReduceTo, ReductionResult};
11
12#[derive(Debug, Clone)]
17pub struct ReductionSWRTDToILP {
18 target: ILP<bool>,
19 num_tasks: usize,
20 time_horizon: usize,
21}
22
23impl ReductionResult for ReductionSWRTDToILP {
24 type Source = SequencingWithReleaseTimesAndDeadlines;
25 type Target = ILP<bool>;
26
27 fn target_problem(&self) -> &ILP<bool> {
28 &self.target
29 }
30
31 fn extract_solution(
33 &self,
34 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
35 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
36 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
37
38 Ok({
39 let n = self.num_tasks;
40 let horizon = self.time_horizon;
41 let starts =
43 crate::rules::ilp_helpers::one_hot_decode_rows(target_solution, n, horizon, 0)?;
44 let mut start_times: Vec<_> = starts.into_iter().enumerate().collect();
45 start_times.sort_by_key(|&(j, t)| (t, j));
47 let schedule: Vec<usize> = start_times.iter().map(|&(j, _)| j).collect();
48 schedule
49 })
50 }
51}
52
53#[reduction(transform = upper_bound {
54 num_vars = "num_tasks * time_horizon",
55 num_constraints = "num_tasks * time_horizon + num_tasks + time_horizon",
56},
57 unavailable = {
58 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
59 }
60)]
61impl ReduceTo<ILP<bool>> for SequencingWithReleaseTimesAndDeadlines {
62 type Result = ReductionSWRTDToILP;
63
64 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
65 let n = self.num_tasks();
66 let horizon = self.time_horizon() as usize;
67 let num_vars = n * horizon;
68
69 let var = |j: usize, t: usize| -> usize { j * horizon + t };
70
71 let lengths = self.lengths();
72 let release_times = self.release_times();
73 let deadlines = self.deadlines();
74
75 let mut constraints = Vec::new();
76
77 for j in 0..n {
82 let r = release_times[j] as usize;
83 let last_start = deadlines[j]
84 .checked_sub(lengths[j])
85 .and_then(|time| usize::try_from(time).ok());
86 let terms: Vec<(usize, i64)> = last_start
87 .filter(|&last| r <= last)
88 .into_iter()
89 .flat_map(|last| r..=last)
90 .filter(|&t| t < horizon)
91 .map(|t| (var(j, t), 1))
92 .collect();
93 constraints.push(LinearConstraint::eq(terms, 1));
94
95 for t in 0..horizon {
97 if t < r || last_start.is_none_or(|last| t > last) {
98 constraints.push(LinearConstraint::eq(vec![(var(j, t), 1)], 0));
99 }
100 }
101 }
102
103 for tau in 0..horizon {
106 let mut terms: Vec<(usize, i64)> = Vec::new();
107 for (j, &len_j) in lengths.iter().enumerate() {
108 let p = len_j as usize;
109 let t_min = (tau + 1).saturating_sub(p);
112 let t_max = tau;
113 for t in t_min..=t_max {
114 if t < horizon {
115 terms.push((var(j, t), 1));
116 }
117 }
118 }
119 constraints.push(LinearConstraint::le(terms, 1));
120 }
121
122 Ok(ReductionSWRTDToILP {
123 target: ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize)
124 .map_err(Self::target_construction)?,
125 num_tasks: n,
126 time_horizon: horizon,
127 })
128 }
129}
130
131#[cfg(feature = "example-db")]
132pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
133 vec![crate::example_db::specs::RuleExampleSpec {
134 id: "sequencingwithreleasetimesanddeadlines_to_ilp",
135 build: || {
136 let source = SequencingWithReleaseTimesAndDeadlines::new(
137 vec![1, 2, 1],
138 vec![0, 0, 2],
139 vec![3, 3, 4],
140 );
141 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
142 },
143 }]
144}
145
146#[cfg(test)]
147#[path = "../unit_tests/rules/sequencingwithreleasetimesanddeadlines_ilp.rs"]
148mod tests;