problemreductions/rules/threepartition_sequencingwithreleasetimesanddeadlines.rs
1//! Reduction from ThreePartition to SequencingWithReleaseTimesAndDeadlines.
2//!
3//! Given a 3-Partition instance with 3m elements of sizes s(a_i) and bound B,
4//! construct a single-machine scheduling instance with:
5//! - 3m element tasks: length = s(a_i), release = 0, deadline = m*B + (m-1)
6//! - (m-1) filler tasks: length = 1, release = (j+1)*B + j, deadline = (j+1)*B + j + 1
7//!
8//! The filler tasks partition the timeline into m slots of width B each. Since
9//! B/4 < s(a_i) < B/2, exactly 3 element tasks must fit in each slot, yielding
10//! a valid 3-partition iff the schedule is feasible.
11//!
12//! Reference: Garey & Johnson, *Computers and Intractability*, Section 4.2.
13
14use crate::models::misc::{SequencingWithReleaseTimesAndDeadlines, ThreePartition};
15use crate::reduction;
16use crate::rules::traits::{ReduceTo, ReductionResult};
17
18/// Number of element tasks (= source.num_elements() = 3m).
19fn num_element_tasks(source: &ThreePartition) -> usize {
20 source.num_elements()
21}
22
23/// Number of filler tasks (= m - 1).
24fn num_filler_tasks(source: &ThreePartition) -> usize {
25 source.num_groups() - 1
26}
27
28/// Result of reducing ThreePartition to SequencingWithReleaseTimesAndDeadlines.
29#[derive(Debug, Clone)]
30pub struct ReductionThreePartitionToSRTD {
31 target: SequencingWithReleaseTimesAndDeadlines,
32 /// Number of element tasks (3m) — first 3m tasks in the target are element tasks.
33 num_element_tasks: usize,
34 /// The bound B from the source.
35 bound: u64,
36}
37
38impl ReductionResult for ReductionThreePartitionToSRTD {
39 type Source = ThreePartition;
40 type Target = SequencingWithReleaseTimesAndDeadlines;
41
42 fn target_problem(&self) -> &Self::Target {
43 &self.target
44 }
45
46 /// Extract a ThreePartition config from a target schedule config.
47 ///
48 /// Decode the Lehmer code to a task permutation, simulate the schedule to
49 /// find each task's start time, then assign each element task to its slot
50 /// based on start_time / (B + 1).
51 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
52 let n = self.target.num_tasks();
53 // Decode Lehmer code to permutation
54 let schedule = crate::models::misc::decode_lehmer(target_solution, n)
55 .expect("target_solution must be a valid Lehmer code");
56
57 // Simulate the schedule to find start times
58 let mut current_time: u64 = 0;
59 let mut slot_assignment = vec![0usize; self.num_element_tasks];
60 let slot_width = self.bound + 1; // B + 1 (slot width including the filler gap)
61
62 for &task in &schedule {
63 let start = current_time.max(self.target.release_times()[task]);
64 let finish = start + self.target.lengths()[task];
65 current_time = finish;
66
67 // Only element tasks (indices 0..3m) contribute to the partition
68 if task < self.num_element_tasks {
69 let slot = (start / slot_width) as usize;
70 slot_assignment[task] = slot;
71 }
72 }
73
74 slot_assignment
75 }
76}
77
78#[reduction(overhead = {
79 num_tasks = "num_elements + num_groups - 1",
80})]
81impl ReduceTo<SequencingWithReleaseTimesAndDeadlines> for ThreePartition {
82 type Result = ReductionThreePartitionToSRTD;
83
84 fn reduce_to(&self) -> Self::Result {
85 let n_elem = num_element_tasks(self);
86 let n_fill = num_filler_tasks(self);
87 let m = self.num_groups();
88 let b = self.bound();
89 let total_tasks = n_elem + n_fill;
90
91 // Time horizon: m*B + (m-1) = m*(B+1) - 1
92 let horizon = (m as u64) * (b + 1) - 1;
93
94 let mut lengths = Vec::with_capacity(total_tasks);
95 let mut release_times = Vec::with_capacity(total_tasks);
96 let mut deadlines = Vec::with_capacity(total_tasks);
97
98 // Element tasks (indices 0..3m)
99 for &size in self.sizes() {
100 lengths.push(size);
101 release_times.push(0);
102 deadlines.push(horizon);
103 }
104
105 // Filler tasks (indices 3m..4m-1)
106 for j in 0..n_fill {
107 // Filler j separates slot j from slot j+1
108 // Release = (j+1)*B + j, Deadline = (j+1)*B + j + 1
109 let release = ((j + 1) as u64) * b + (j as u64);
110 lengths.push(1);
111 release_times.push(release);
112 deadlines.push(release + 1);
113 }
114
115 ReductionThreePartitionToSRTD {
116 target: SequencingWithReleaseTimesAndDeadlines::new(lengths, release_times, deadlines),
117 num_element_tasks: n_elem,
118 bound: b,
119 }
120 }
121}
122
123#[cfg(feature = "example-db")]
124pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
125 use crate::export::SolutionPair;
126
127 vec![crate::example_db::specs::RuleExampleSpec {
128 id: "threepartition_to_sequencingwithreleasetimesanddeadlines",
129 build: || {
130 // ThreePartition: sizes=[4,5,6,4,6,5], bound=15, m=2
131 // Groups: {4,5,6}=15, {4,6,5}=15
132 // Source config: [0,0,0,1,1,1] (elements 0,1,2 in group 0; 3,4,5 in group 1)
133 //
134 // Target: 6 element tasks + 1 filler = 7 tasks
135 // Schedule for source config [0,0,0,1,1,1]:
136 // Slot 0 [0,15): tasks 0(len=4), 1(len=5), 2(len=6) -> times [0,4), [4,9), [9,15)
137 // Filler [15,16): task 6(len=1)
138 // Slot 1 [16,31): tasks 3(len=4), 4(len=6), 5(len=5) -> times [16,20), [20,26), [26,31)
139 // Permutation: [0,1,2,6,3,4,5]
140 // Lehmer code: [0,0,0,3,0,0,0]
141 // remaining=[0,1,2,3,4,5,6], pick 0 -> 0, remaining=[1,2,3,4,5,6]
142 // remaining=[1,2,3,4,5,6], pick 0 -> 1, remaining=[2,3,4,5,6]
143 // remaining=[2,3,4,5,6], pick 0 -> 2, remaining=[3,4,5,6]
144 // remaining=[3,4,5,6], pick 3 -> 6, remaining=[3,4,5]
145 // remaining=[3,4,5], pick 0 -> 3, remaining=[4,5]
146 // remaining=[4,5], pick 0 -> 4, remaining=[5]
147 // remaining=[5], pick 0 -> 5
148 crate::example_db::specs::rule_example_with_witness::<
149 _,
150 SequencingWithReleaseTimesAndDeadlines,
151 >(
152 ThreePartition::new(vec![4, 5, 6, 4, 6, 5], 15),
153 SolutionPair {
154 source_config: vec![0, 0, 0, 1, 1, 1],
155 target_config: vec![0, 0, 0, 3, 0, 0, 0],
156 },
157 )
158 },
159 }]
160}
161
162#[cfg(test)]
163#[path = "../unit_tests/rules/threepartition_sequencingwithreleasetimesanddeadlines.rs"]
164mod tests;