problemreductions/models/misc/
sequencing_to_minimize_maximum_cumulative_cost.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::de::Error as _;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "SequencingToMinimizeMaximumCumulativeCost",
15 display_name: "Sequencing to Minimize Maximum Cumulative Cost",
16 aliases: &[],
17 dimensions: &[],
18 module_path: module_path!(),
19 description: "Schedule tasks with precedence constraints to minimize the maximum cumulative cost prefix",
20 fields: &[
21 FieldInfo { name: "costs", type_name: "Vec<i64>", description: "Task costs in schedule order-independent indexing" },
22 FieldInfo { name: "precedences", type_name: "Vec<(usize, usize)>", description: "Precedence pairs (predecessor, successor)" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize)]
38pub struct SequencingToMinimizeMaximumCumulativeCost {
39 costs: Vec<i64>,
40 precedences: Vec<(usize, usize)>,
41}
42
43#[derive(Debug, Deserialize)]
44struct SequencingToMinimizeMaximumCumulativeCostUnchecked {
45 costs: Vec<i64>,
46 precedences: Vec<(usize, usize)>,
47}
48
49impl SequencingToMinimizeMaximumCumulativeCost {
50 pub fn new(costs: Vec<i64>, precedences: Vec<(usize, usize)>) -> Self {
56 validate_precedences(&precedences, costs.len());
57 Self { costs, precedences }
58 }
59
60 pub fn costs(&self) -> &[i64] {
62 &self.costs
63 }
64
65 pub fn precedences(&self) -> &[(usize, usize)] {
67 &self.precedences
68 }
69
70 pub fn num_tasks(&self) -> usize {
72 self.costs.len()
73 }
74
75 pub fn num_precedences(&self) -> usize {
77 self.precedences.len()
78 }
79
80 fn decode_schedule(&self, config: &[usize]) -> Option<Vec<usize>> {
81 super::decode_lehmer(config, self.num_tasks())
82 }
83}
84
85impl<'de> Deserialize<'de> for SequencingToMinimizeMaximumCumulativeCost {
86 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
87 where
88 D: serde::Deserializer<'de>,
89 {
90 let unchecked =
91 SequencingToMinimizeMaximumCumulativeCostUnchecked::deserialize(deserializer)?;
92 if let Some(message) =
93 precedence_validation_error(&unchecked.precedences, unchecked.costs.len())
94 {
95 return Err(D::Error::custom(message));
96 }
97 Ok(Self {
98 costs: unchecked.costs,
99 precedences: unchecked.precedences,
100 })
101 }
102}
103
104fn validate_precedences(precedences: &[(usize, usize)], num_tasks: usize) {
105 if let Some(message) = precedence_validation_error(precedences, num_tasks) {
106 panic!("{message}");
107 }
108}
109
110fn precedence_validation_error(precedences: &[(usize, usize)], num_tasks: usize) -> Option<String> {
111 for &(pred, succ) in precedences {
112 if pred >= num_tasks {
113 return Some(format!(
114 "predecessor index {} out of range (num_tasks = {})",
115 pred, num_tasks
116 ));
117 }
118 if succ >= num_tasks {
119 return Some(format!(
120 "successor index {} out of range (num_tasks = {})",
121 succ, num_tasks
122 ));
123 }
124 }
125 None
126}
127
128impl Problem for SequencingToMinimizeMaximumCumulativeCost {
129 const NAME: &'static str = "SequencingToMinimizeMaximumCumulativeCost";
130 type Value = crate::types::Min<i64>;
131
132 fn variant() -> Vec<(&'static str, &'static str)> {
133 crate::variant_params![]
134 }
135
136 fn dims(&self) -> Vec<usize> {
137 super::lehmer_dims(self.num_tasks())
138 }
139
140 fn evaluate(&self, config: &[usize]) -> crate::types::Min<i64> {
141 let Some(schedule) = self.decode_schedule(config) else {
142 return crate::types::Min(None);
143 };
144
145 let mut positions = vec![0usize; self.num_tasks()];
146 for (position, &task) in schedule.iter().enumerate() {
147 positions[task] = position;
148 }
149 for &(pred, succ) in &self.precedences {
150 if positions[pred] >= positions[succ] {
151 return crate::types::Min(None);
152 }
153 }
154
155 let mut cumulative = 0i64;
156 let mut max_cumulative = 0i64;
157 for &task in &schedule {
158 cumulative += self.costs[task];
159 if cumulative > max_cumulative {
160 max_cumulative = cumulative;
161 }
162 }
163 crate::types::Min(Some(max_cumulative))
164 }
165}
166
167crate::declare_variants! {
168 default SequencingToMinimizeMaximumCumulativeCost => "factorial(num_tasks)",
169}
170
171#[cfg(feature = "example-db")]
172pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
173 vec![crate::example_db::specs::ModelExampleSpec {
174 id: "sequencing_to_minimize_maximum_cumulative_cost",
175 instance: Box::new(SequencingToMinimizeMaximumCumulativeCost::new(
176 vec![2, -1, 3, -2, 1, -3],
177 vec![(0, 2), (1, 2), (1, 3), (2, 4), (3, 5), (4, 5)],
178 )),
179 optimal_config: vec![1, 0, 1, 0, 0, 0],
180 optimal_value: serde_json::json!(3),
181 }]
182}
183
184#[cfg(test)]
185#[path = "../../unit_tests/models/misc/sequencing_to_minimize_maximum_cumulative_cost.rs"]
186mod tests;