problemreductions/models/misc/
scheduling_with_individual_deadlines.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "SchedulingWithIndividualDeadlines",
15 display_name: "Scheduling With Individual Deadlines",
16 aliases: &[],
17 dimensions: &[],
18 category: crate::registry::ProblemCategory::Misc,
19 module_path: module_path!(),
20 description: "Determine whether unit-length tasks can be scheduled on m processors while meeting individual deadlines",
21 fields: SchedulingWithIndividualDeadlinesCreateSpec::FIELDS,
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct SchedulingWithIndividualDeadlines {
33 num_tasks: usize,
34 num_processors: usize,
35 deadlines: Vec<i64>,
36 precedences: Vec<(usize, usize)>,
37}
38
39#[derive(Debug, Deserialize, crate::CreateSpec)]
40struct SchedulingWithIndividualDeadlinesCreateSpec {
41 num_tasks: usize,
43 num_processors: usize,
45 deadlines: Vec<i64>,
47 precedences: Option<Vec<(usize, usize)>>,
49}
50impl TryFrom<SchedulingWithIndividualDeadlinesCreateSpec> for SchedulingWithIndividualDeadlines {
51 type Error = crate::registry::ConstructionError;
52 fn try_from(spec: SchedulingWithIndividualDeadlinesCreateSpec) -> Result<Self, Self::Error> {
53 if spec.deadlines.len() != spec.num_tasks {
54 return Err(format!(
55 "deadlines has {} entries, expected {}",
56 spec.deadlines.len(),
57 spec.num_tasks
58 )
59 .into());
60 }
61 if spec.deadlines.iter().any(|&deadline| deadline < 0) {
62 return Err("deadlines must be nonnegative".to_string().into());
63 }
64 if spec
65 .deadlines
66 .iter()
67 .any(|&deadline| usize::try_from(deadline).is_err())
68 {
69 return Err("deadlines must fit usize to define schedule slots"
70 .to_string()
71 .into());
72 }
73 let precedences = spec.precedences.unwrap_or_default();
74 if let Some(&(pred, succ)) = precedences
75 .iter()
76 .find(|&&(p, s)| p >= spec.num_tasks || s >= spec.num_tasks)
77 {
78 return Err(format!(
79 "precedence ({pred}, {succ}) is out of range for {} tasks",
80 spec.num_tasks
81 )
82 .into());
83 }
84 Ok(Self::new(
85 spec.num_tasks,
86 spec.num_processors,
87 spec.deadlines,
88 precedences,
89 ))
90 }
91}
92
93impl SchedulingWithIndividualDeadlines {
94 pub fn new(
95 num_tasks: usize,
96 num_processors: usize,
97 deadlines: Vec<i64>,
98 precedences: Vec<(usize, usize)>,
99 ) -> Self {
100 assert_eq!(
101 deadlines.len(),
102 num_tasks,
103 "deadlines length must equal num_tasks"
104 );
105 assert!(
106 deadlines.iter().all(|&deadline| deadline >= 0),
107 "deadlines must be nonnegative"
108 );
109 assert!(
110 deadlines
111 .iter()
112 .all(|&deadline| usize::try_from(deadline).is_ok()),
113 "deadlines must fit usize to define schedule slots"
114 );
115 for &(pred, succ) in &precedences {
116 assert!(
117 pred < num_tasks,
118 "predecessor index {} out of range (num_tasks = {})",
119 pred,
120 num_tasks
121 );
122 assert!(
123 succ < num_tasks,
124 "successor index {} out of range (num_tasks = {})",
125 succ,
126 num_tasks
127 );
128 }
129
130 Self {
131 num_tasks,
132 num_processors,
133 deadlines,
134 precedences,
135 }
136 }
137
138 pub fn num_tasks(&self) -> usize {
139 self.num_tasks
140 }
141
142 pub fn num_processors(&self) -> usize {
143 self.num_processors
144 }
145
146 pub fn deadlines(&self) -> &[i64] {
147 &self.deadlines
148 }
149
150 pub fn precedences(&self) -> &[(usize, usize)] {
151 &self.precedences
152 }
153
154 pub fn num_precedences(&self) -> usize {
155 self.precedences.len()
156 }
157
158 pub fn max_deadline(&self) -> i64 {
159 self.deadlines.iter().copied().max().unwrap_or(0)
160 }
161}
162
163impl Problem for SchedulingWithIndividualDeadlines {
164 const NAME: &'static str = "SchedulingWithIndividualDeadlines";
165 type Solution = Vec<usize>;
166 type Value = crate::types::Or;
167
168 crate::problem_parameters![
169 ("max_deadline", max_deadline),
170 ("num_precedences", num_precedences),
171 ("num_tasks", num_tasks),
172 ];
173
174 fn variant() -> Vec<(&'static str, &'static str)> {
175 crate::variant_params![]
176 }
177
178 fn evaluate(
179 &self,
180 config: &Self::Solution,
181 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
182 Ok({
183 crate::types::Or({
184 if config.len() != self.num_tasks {
185 return Err(crate::traits::EvaluationError::InvalidConfiguration(
186 "schedule length does not match the tasks".into(),
187 ));
188 }
189
190 for (&start, &deadline) in config.iter().zip(&self.deadlines) {
191 let deadline =
192 usize::try_from(deadline).expect("validated deadline must fit usize");
193 if start >= deadline {
194 return Ok(crate::types::Or(false));
195 }
196 }
197
198 for &(pred, succ) in &self.precedences {
199 if config[pred] + 1 > config[succ] {
200 return Ok(crate::types::Or(false));
201 }
202 }
203
204 let mut slot_loads = BTreeMap::new();
205 for &start in config {
206 let load = slot_loads.entry(start).or_insert(0usize);
207 *load += 1;
208 if *load > self.num_processors {
209 return Ok(crate::types::Or(false));
210 }
211 }
212
213 true
214 })
215 })
216 }
217}
218
219impl crate::solvers::BruteForceProblem for SchedulingWithIndividualDeadlines {
220 fn dimensions(&self) -> Vec<usize> {
221 self.deadlines
222 .iter()
223 .map(|&deadline| usize::try_from(deadline).expect("validated deadline must fit usize"))
224 .collect()
225 }
226}
227
228crate::declare_variants! {
229 default SchedulingWithIndividualDeadlines => "max_deadline^num_tasks" create SchedulingWithIndividualDeadlinesCreateSpec,
230}
231
232crate::register_brute_force! {
233 SchedulingWithIndividualDeadlines,
234}
235
236#[cfg(feature = "example-db")]
237pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
238 vec![crate::example_db::specs::ModelExampleSpec {
239 id: "scheduling_with_individual_deadlines",
240 instance: Box::new(SchedulingWithIndividualDeadlines::new(
241 7,
242 3,
243 vec![2, 1, 2, 2, 3, 3, 2],
244 vec![(0, 3), (1, 3), (1, 4), (2, 4), (2, 5)],
245 )),
246 optimal_config: serde_json::json!(vec![0, 0, 0, 1, 2, 1, 1]),
247 optimal_value: serde_json::json!(true),
248 }]
249}
250
251#[cfg(test)]
252#[path = "../../unit_tests/models/misc/scheduling_with_individual_deadlines.rs"]
253mod tests;