Skip to main content

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;