problemreductions/rules/
preemptivescheduling_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
25use crate::models::misc::PreemptiveScheduling;
26use crate::reduction;
27use crate::rules::traits::{ReduceTo, ReductionResult};
28
29#[derive(Debug, Clone)]
37pub struct ReductionPSToILP {
38 target: ILP<i64>,
39 num_tasks: usize,
40 d_max: usize,
41}
42
43impl ReductionResult for ReductionPSToILP {
44 type Source = PreemptiveScheduling;
45 type Target = ILP<i64>;
46
47 fn target_problem(&self) -> &ILP<i64> {
48 &self.target
49 }
50
51 fn extract_solution(
55 &self,
56 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
57 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
58 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
59
60 Ok((0..self.num_tasks)
61 .map(|task| {
62 (0..self.d_max)
63 .map(|time| target_solution[task * self.d_max + time] == 1)
64 .collect()
65 })
66 .collect())
67 }
68}
69
70#[reduction(
71 transform = exact {
72 num_vars = "num_tasks * d_max + 1",
73 num_constraints = "num_tasks + d_max + num_precedences * d_max + 2 * num_tasks * d_max",
74 },
75 unavailable = {
76 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
77 }
78)]
79impl ReduceTo<ILP<i64>> for PreemptiveScheduling {
80 type Result = ReductionPSToILP;
81
82 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
83 let n = self.num_tasks();
84 let d = self.d_max();
85 let num_task_vars = n * d;
86 let m_var = num_task_vars; let num_vars = num_task_vars + 1;
88 let lengths = self.lengths();
89 let processor_count =
90 Self::exact_i64(self.num_processors(), "encoding the processor capacity")?;
91
92 let x = |t: usize, u: usize| t * d + u;
93
94 let mut constraints = Vec::new();
95
96 for (t, &length) in lengths.iter().enumerate() {
98 let terms: Vec<(usize, i64)> = (0..d).map(|u| (x(t, u), 1)).collect();
99 constraints.push(LinearConstraint::eq(terms, length));
100 }
101
102 for u in 0..d {
104 let terms: Vec<(usize, i64)> = (0..n).map(|t| (x(t, u), 1)).collect();
105 constraints.push(LinearConstraint::le(terms, processor_count));
106 }
107
108 for &(pred, succ) in self.precedences() {
115 let l_pred = lengths[pred];
116 for u in 0..d {
117 let mut terms: Vec<(usize, i64)> = Vec::new();
120 for v in 0..u {
122 terms.push((x(pred, v), -1));
123 }
124 terms.push((x(succ, u), l_pred));
125 constraints.push(LinearConstraint::le(terms, 0));
126 }
127 }
128
129 for t in 0..n {
131 for u in 0..d {
132 constraints.push(LinearConstraint::ge(
133 vec![
134 (m_var, 1),
135 (x(t, u), -Self::exact_i64(u + 1, "encoding a time slot")?),
136 ],
137 0,
138 ));
139 }
140 }
141
142 for t in 0..n {
144 for u in 0..d {
145 constraints.push(LinearConstraint::le(vec![(x(t, u), 1)], 1));
146 }
147 }
148
149 let objective = vec![(m_var, 1)];
151
152 Ok(ReductionPSToILP {
153 target: ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize)
154 .map_err(Self::target_construction)?,
155 num_tasks: n,
156 d_max: d,
157 })
158 }
159}
160
161#[cfg(feature = "example-db")]
162pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
163 vec![crate::example_db::specs::RuleExampleSpec {
164 id: "preemptivescheduling_to_ilp",
165 build: || {
166 let source = PreemptiveScheduling::new(vec![2, 1, 2], 2, vec![(0, 2)]).unwrap();
168 crate::example_db::specs::rule_example_via_ilp::<_, i64>(source)
169 },
170 }]
171}
172
173#[cfg(test)]
174#[path = "../unit_tests/rules/preemptivescheduling_ilp.rs"]
175mod tests;