problemreductions/models/misc/
precedence_constrained_scheduling.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "PrecedenceConstrainedScheduling",
14 display_name: "Precedence Constrained Scheduling",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Schedule unit-length tasks on m processors by deadline D respecting precedence constraints",
19 fields: &[
20 FieldInfo { name: "num_tasks", type_name: "usize", description: "Number of tasks n = |T|" },
21 FieldInfo { name: "num_processors", type_name: "usize", description: "Number of processors m" },
22 FieldInfo { name: "deadline", type_name: "usize", description: "Global deadline D" },
23 FieldInfo { name: "precedences", type_name: "Vec<(usize, usize)>", description: "Precedence pairs (i, j) meaning task i must finish before task j starts" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct PrecedenceConstrainedScheduling {
55 num_tasks: usize,
56 num_processors: usize,
57 deadline: usize,
58 precedences: Vec<(usize, usize)>,
59}
60
61impl PrecedenceConstrainedScheduling {
62 pub fn new(
69 num_tasks: usize,
70 num_processors: usize,
71 deadline: usize,
72 precedences: Vec<(usize, usize)>,
73 ) -> Self {
74 if num_tasks > 0 {
75 assert!(
76 num_processors > 0,
77 "num_processors must be > 0 when there are tasks"
78 );
79 assert!(deadline > 0, "deadline must be > 0 when there are tasks");
80 }
81 for &(i, j) in &precedences {
82 assert!(
83 i < num_tasks && j < num_tasks,
84 "Precedence ({}, {}) out of bounds for {} tasks",
85 i,
86 j,
87 num_tasks
88 );
89 }
90 Self {
91 num_tasks,
92 num_processors,
93 deadline,
94 precedences,
95 }
96 }
97
98 pub fn num_tasks(&self) -> usize {
100 self.num_tasks
101 }
102
103 pub fn num_processors(&self) -> usize {
105 self.num_processors
106 }
107
108 pub fn deadline(&self) -> usize {
110 self.deadline
111 }
112
113 pub fn precedences(&self) -> &[(usize, usize)] {
115 &self.precedences
116 }
117}
118
119impl Problem for PrecedenceConstrainedScheduling {
120 const NAME: &'static str = "PrecedenceConstrainedScheduling";
121 type Value = crate::types::Or;
122
123 fn variant() -> Vec<(&'static str, &'static str)> {
124 crate::variant_params![]
125 }
126
127 fn dims(&self) -> Vec<usize> {
128 vec![self.deadline; self.num_tasks]
129 }
130
131 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
132 crate::types::Or({
133 if config.len() != self.num_tasks {
134 return crate::types::Or(false);
135 }
136 if config.iter().any(|&v| v >= self.deadline) {
138 return crate::types::Or(false);
139 }
140 let mut slot_count = vec![0usize; self.deadline];
142 for &slot in config {
143 slot_count[slot] += 1;
144 if slot_count[slot] > self.num_processors {
145 return crate::types::Or(false);
146 }
147 }
148 for &(i, j) in &self.precedences {
150 if config[j] < config[i] + 1 {
151 return crate::types::Or(false);
152 }
153 }
154 true
155 })
156 }
157}
158
159crate::declare_variants! {
160 default PrecedenceConstrainedScheduling => "2^num_tasks",
161}
162
163#[cfg(feature = "example-db")]
164pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
165 vec![crate::example_db::specs::ModelExampleSpec {
166 id: "precedence_constrained_scheduling",
167 instance: Box::new(PrecedenceConstrainedScheduling::new(
169 8,
170 3,
171 4,
172 vec![
173 (0, 2),
174 (0, 3),
175 (1, 3),
176 (1, 4),
177 (2, 5),
178 (3, 6),
179 (4, 6),
180 (5, 7),
181 (6, 7),
182 ],
183 )),
184 optimal_config: vec![0, 0, 1, 1, 1, 2, 2, 3],
186 optimal_value: serde_json::json!(true),
187 }]
188}
189
190#[cfg(test)]
191#[path = "../../unit_tests/models/misc/precedence_constrained_scheduling.rs"]
192mod tests;