problemreductions/models/misc/
sequencing_to_minimize_tardy_task_weight.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use crate::types::Min;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "SequencingToMinimizeTardyTaskWeight",
15 display_name: "Sequencing to Minimize Tardy Task Weight",
16 aliases: &[],
17 dimensions: &[],
18 module_path: module_path!(),
19 description: "Schedule tasks with lengths, weights, and deadlines to minimize total weight of tardy tasks",
20 fields: &[
21 FieldInfo { name: "lengths", type_name: "Vec<u64>", description: "Processing time for each task" },
22 FieldInfo { name: "weights", type_name: "Vec<u64>", description: "Weight w(t) for each task" },
23 FieldInfo { name: "deadlines", type_name: "Vec<u64>", description: "Deadline d(t) for each task" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, Serialize)]
41pub struct SequencingToMinimizeTardyTaskWeight {
42 lengths: Vec<u64>,
43 weights: Vec<u64>,
44 deadlines: Vec<u64>,
45}
46
47#[derive(Deserialize)]
48struct SequencingToMinimizeTardyTaskWeightSerde {
49 lengths: Vec<u64>,
50 weights: Vec<u64>,
51 deadlines: Vec<u64>,
52}
53
54impl SequencingToMinimizeTardyTaskWeight {
55 fn validate(lengths: &[u64], weights: &[u64], deadlines: &[u64]) -> Result<(), String> {
56 if lengths.len() != weights.len() {
57 return Err("lengths length must equal weights length".to_string());
58 }
59 if lengths.len() != deadlines.len() {
60 return Err("lengths length must equal deadlines length".to_string());
61 }
62 if lengths.contains(&0) {
63 return Err("task lengths must be positive".to_string());
64 }
65 if weights.contains(&0) {
66 return Err("task weights must be positive".to_string());
67 }
68 Ok(())
69 }
70
71 pub fn new(lengths: Vec<u64>, weights: Vec<u64>, deadlines: Vec<u64>) -> Self {
78 Self::validate(&lengths, &weights, &deadlines).unwrap_or_else(|err| panic!("{err}"));
79 Self {
80 lengths,
81 weights,
82 deadlines,
83 }
84 }
85
86 pub fn num_tasks(&self) -> usize {
88 self.lengths.len()
89 }
90
91 pub fn lengths(&self) -> &[u64] {
93 &self.lengths
94 }
95
96 pub fn weights(&self) -> &[u64] {
98 &self.weights
99 }
100
101 pub fn deadlines(&self) -> &[u64] {
103 &self.deadlines
104 }
105
106 fn tardy_task_weight(&self, schedule: &[usize]) -> Min<u64> {
107 let mut elapsed: u64 = 0;
108 let mut total: u64 = 0;
109 for &task in schedule {
110 elapsed = elapsed
111 .checked_add(self.lengths[task])
112 .expect("total processing time overflowed u64");
113 if elapsed > self.deadlines[task] {
114 total = total
115 .checked_add(self.weights[task])
116 .expect("tardy task weight overflowed u64");
117 }
118 }
119 Min(Some(total))
120 }
121}
122
123impl TryFrom<SequencingToMinimizeTardyTaskWeightSerde> for SequencingToMinimizeTardyTaskWeight {
124 type Error = String;
125
126 fn try_from(value: SequencingToMinimizeTardyTaskWeightSerde) -> Result<Self, Self::Error> {
127 Self::validate(&value.lengths, &value.weights, &value.deadlines)?;
128 Ok(Self {
129 lengths: value.lengths,
130 weights: value.weights,
131 deadlines: value.deadlines,
132 })
133 }
134}
135
136impl<'de> Deserialize<'de> for SequencingToMinimizeTardyTaskWeight {
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: serde::Deserializer<'de>,
140 {
141 let value = SequencingToMinimizeTardyTaskWeightSerde::deserialize(deserializer)?;
142 Self::try_from(value).map_err(serde::de::Error::custom)
143 }
144}
145
146impl Problem for SequencingToMinimizeTardyTaskWeight {
147 const NAME: &'static str = "SequencingToMinimizeTardyTaskWeight";
148 type Value = Min<u64>;
149
150 fn variant() -> Vec<(&'static str, &'static str)> {
151 crate::variant_params![]
152 }
153
154 fn dims(&self) -> Vec<usize> {
155 let n = self.num_tasks();
156 vec![n; n]
157 }
158
159 fn evaluate(&self, config: &[usize]) -> Min<u64> {
160 let n = self.num_tasks();
161 let Some(schedule) = super::decode_permutation(config, n) else {
162 return Min(None);
163 };
164 self.tardy_task_weight(&schedule)
165 }
166}
167
168crate::declare_variants! {
169 default SequencingToMinimizeTardyTaskWeight => "factorial(num_tasks)",
170}
171
172#[cfg(feature = "example-db")]
173pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
174 vec![crate::example_db::specs::ModelExampleSpec {
175 id: "sequencing_to_minimize_tardy_task_weight",
176 instance: Box::new(SequencingToMinimizeTardyTaskWeight::new(
185 vec![3, 2, 4, 1, 2],
186 vec![5, 3, 7, 2, 4],
187 vec![6, 4, 10, 2, 8],
188 )),
189 optimal_config: vec![3, 0, 4, 2, 1],
190 optimal_value: serde_json::json!(3),
191 }]
192}
193
194#[cfg(test)]
195#[path = "../../unit_tests/models/misc/sequencing_to_minimize_tardy_task_weight.rs"]
196mod tests;