problemreductions/models/misc/
multiprocessor_scheduling.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "MultiprocessorScheduling",
14 display_name: "Multiprocessor Scheduling",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Assign tasks to processors so that no processor's load exceeds a deadline",
19 fields: &[
20 FieldInfo { name: "lengths", type_name: "Vec<u64>", description: "Processing time l(t) for each task" },
21 FieldInfo { name: "num_processors", type_name: "usize", description: "Number of identical processors m" },
22 FieldInfo { name: "deadline", type_name: "u64", description: "Global deadline D" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct MultiprocessorScheduling {
57 lengths: Vec<u64>,
59 #[serde(deserialize_with = "positive_usize::deserialize")]
61 num_processors: usize,
62 deadline: u64,
64}
65
66impl MultiprocessorScheduling {
67 pub fn new(lengths: Vec<u64>, num_processors: usize, deadline: u64) -> Self {
72 assert!(num_processors > 0, "num_processors must be positive");
73 Self {
74 lengths,
75 num_processors,
76 deadline,
77 }
78 }
79
80 pub fn lengths(&self) -> &[u64] {
82 &self.lengths
83 }
84
85 pub fn num_processors(&self) -> usize {
87 self.num_processors
88 }
89
90 pub fn deadline(&self) -> u64 {
92 self.deadline
93 }
94
95 pub fn num_tasks(&self) -> usize {
97 self.lengths.len()
98 }
99
100 pub fn total_length(&self) -> u64 {
102 self.lengths.iter().sum()
103 }
104}
105
106impl Problem for MultiprocessorScheduling {
107 const NAME: &'static str = "MultiprocessorScheduling";
108 type Value = crate::types::Or;
109
110 fn variant() -> Vec<(&'static str, &'static str)> {
111 crate::variant_params![]
112 }
113
114 fn dims(&self) -> Vec<usize> {
115 vec![self.num_processors; self.num_tasks()]
116 }
117
118 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
119 crate::types::Or({
120 if config.len() != self.num_tasks() {
121 return crate::types::Or(false);
122 }
123 let m = self.num_processors;
124 if config.iter().any(|&p| p >= m) {
125 return crate::types::Or(false);
126 }
127 let mut loads = vec![0u64; m];
128 for (i, &processor) in config.iter().enumerate() {
129 loads[processor] += self.lengths[i];
130 }
131 loads.iter().all(|&load| load <= self.deadline)
132 })
133 }
134}
135
136crate::declare_variants! {
137 default MultiprocessorScheduling => "2^num_tasks",
138}
139
140#[cfg(feature = "example-db")]
141pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
142 vec![crate::example_db::specs::ModelExampleSpec {
143 id: "multiprocessor_scheduling",
144 instance: Box::new(MultiprocessorScheduling::new(vec![4, 5, 3, 2, 6], 2, 10)),
145 optimal_config: vec![0, 1, 1, 1, 0],
146 optimal_value: serde_json::json!(true),
147 }]
148}
149
150mod positive_usize {
151 use serde::de::Error;
152 use serde::{Deserialize, Deserializer};
153
154 pub fn deserialize<'de, D>(deserializer: D) -> Result<usize, D::Error>
155 where
156 D: Deserializer<'de>,
157 {
158 let value = usize::deserialize(deserializer)?;
159 if value == 0 {
160 return Err(D::Error::custom("expected positive integer, got 0"));
161 }
162 Ok(value)
163 }
164}
165
166#[cfg(test)]
167#[path = "../../unit_tests/models/misc/multiprocessor_scheduling.rs"]
168mod tests;