problemreductions/models/misc/
flow_shop_scheduling.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "FlowShopScheduling",
14 display_name: "Flow Shop Scheduling",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Determine if a flow-shop schedule for jobs on m processors meets a deadline",
19 fields: &[
20 FieldInfo { name: "num_processors", type_name: "usize", description: "Number of machines m" },
21 FieldInfo { name: "task_lengths", type_name: "Vec<Vec<u64>>", description: "task_lengths[j][i] = length of job j's task on machine i" },
22 FieldInfo { name: "deadline", type_name: "u64", description: "Global deadline D" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct FlowShopScheduling {
61 num_processors: usize,
63 task_lengths: Vec<Vec<u64>>,
65 deadline: u64,
67}
68
69impl FlowShopScheduling {
70 pub fn new(num_processors: usize, task_lengths: Vec<Vec<u64>>, deadline: u64) -> Self {
81 for (j, tasks) in task_lengths.iter().enumerate() {
82 assert_eq!(
83 tasks.len(),
84 num_processors,
85 "Job {} has {} tasks, expected {}",
86 j,
87 tasks.len(),
88 num_processors
89 );
90 }
91 Self {
92 num_processors,
93 task_lengths,
94 deadline,
95 }
96 }
97
98 pub fn num_processors(&self) -> usize {
100 self.num_processors
101 }
102
103 pub fn task_lengths(&self) -> &[Vec<u64>] {
105 &self.task_lengths
106 }
107
108 pub fn deadline(&self) -> u64 {
110 self.deadline
111 }
112
113 pub fn num_jobs(&self) -> usize {
115 self.task_lengths.len()
116 }
117
118 pub fn compute_makespan(&self, job_order: &[usize]) -> u64 {
123 let n = job_order.len();
124 let m = self.num_processors;
125 assert_eq!(
126 n,
127 self.task_lengths.len(),
128 "job_order length ({}) does not match num_jobs ({})",
129 n,
130 self.task_lengths.len()
131 );
132 for (k, &job) in job_order.iter().enumerate() {
133 assert!(
134 job < self.task_lengths.len(),
135 "job_order[{}] = {} is out of range (num_jobs = {})",
136 k,
137 job,
138 self.task_lengths.len()
139 );
140 }
141 if n == 0 || m == 0 {
142 return 0;
143 }
144
145 let mut completion = vec![vec![0u64; m]; n];
147
148 for (k, &job) in job_order.iter().enumerate() {
149 for i in 0..m {
150 let prev_machine = if i == 0 { 0 } else { completion[k][i - 1] };
151 let prev_job = if k == 0 { 0 } else { completion[k - 1][i] };
152 let start = prev_machine.max(prev_job);
153 completion[k][i] = start + self.task_lengths[job][i];
154 }
155 }
156
157 completion[n - 1][m - 1]
158 }
159}
160
161impl Problem for FlowShopScheduling {
162 const NAME: &'static str = "FlowShopScheduling";
163 type Value = crate::types::Or;
164
165 fn variant() -> Vec<(&'static str, &'static str)> {
166 crate::variant_params![]
167 }
168
169 fn dims(&self) -> Vec<usize> {
170 super::lehmer_dims(self.num_jobs())
171 }
172
173 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
174 crate::types::Or({
175 let Some(job_order) = super::decode_lehmer(config, self.num_jobs()) else {
176 return crate::types::Or(false);
177 };
178
179 let makespan = self.compute_makespan(&job_order);
180 makespan <= self.deadline
181 })
182 }
183}
184
185crate::declare_variants! {
186 default FlowShopScheduling => "factorial(num_jobs)",
187}
188
189#[cfg(feature = "example-db")]
190pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
191 vec![crate::example_db::specs::ModelExampleSpec {
192 id: "flow_shop_scheduling",
193 instance: Box::new(FlowShopScheduling::new(
194 3,
195 vec![
196 vec![3, 4, 2],
197 vec![2, 3, 5],
198 vec![4, 1, 3],
199 vec![1, 5, 4],
200 vec![3, 2, 3],
201 ],
202 25,
203 )),
204 optimal_config: vec![3, 0, 2, 1, 0],
206 optimal_value: serde_json::json!(true),
207 }]
208}
209
210#[cfg(test)]
211#[path = "../../unit_tests/models/misc/flow_shop_scheduling.rs"]
212mod tests;