Skip to main content

problemreductions/models/misc/
sequencing_within_intervals.rs

1//! Sequencing Within Intervals problem implementation.
2//!
3//! Given a set of tasks, each with a release time, deadline, and processing length,
4//! determine whether all tasks can be scheduled non-overlappingly such that each
5//! task runs entirely within its allowed time window.
6
7use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12    ProblemSchemaEntry {
13        name: "SequencingWithinIntervals",
14        display_name: "Sequencing Within Intervals",
15        aliases: &[],
16        dimensions: &[],
17        module_path: module_path!(),
18        description: "Schedule tasks non-overlappingly within their time windows",
19        fields: &[
20            FieldInfo { name: "release_times", type_name: "Vec<u64>", description: "Release time r(t) for each task" },
21            FieldInfo { name: "deadlines", type_name: "Vec<u64>", description: "Deadline d(t) for each task" },
22            FieldInfo { name: "lengths", type_name: "Vec<u64>", description: "Processing length l(t) for each task" },
23        ],
24    }
25}
26
27/// Sequencing Within Intervals problem.
28///
29/// Given `n` tasks, each with release time `r(t)`, deadline `d(t)`, and processing
30/// length `l(t)`, determine whether there exists a schedule `sigma: T -> Z_>=0`
31/// such that:
32/// - `sigma(t) >= r(t)` (task starts no earlier than its release time)
33/// - `sigma(t) + l(t) <= d(t)` (task finishes by its deadline)
34/// - No two tasks overlap in time
35///
36/// This is problem SS1 from Garey & Johnson (1979), NP-complete via Theorem 3.8.
37///
38/// # Representation
39///
40/// Each task has a variable representing its start time offset from the release time.
41/// Variable `i` takes values in `{0, ..., d(i) - r(i) - l(i)}`, so the actual start
42/// time is `r(i) + config[i]`.
43///
44/// # Example
45///
46/// ```
47/// use problemreductions::models::misc::SequencingWithinIntervals;
48/// use problemreductions::{Problem, Solver, BruteForce};
49///
50/// // 3 tasks: release_times = [0, 2, 4], deadlines = [3, 5, 7], lengths = [2, 2, 2]
51/// let problem = SequencingWithinIntervals::new(vec![0, 2, 4], vec![3, 5, 7], vec![2, 2, 2]);
52/// let solver = BruteForce::new();
53/// let solution = solver.find_witness(&problem);
54/// assert!(solution.is_some());
55/// ```
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct SequencingWithinIntervals {
58    /// Release times for each task.
59    release_times: Vec<u64>,
60    /// Deadlines for each task.
61    deadlines: Vec<u64>,
62    /// Processing lengths for each task.
63    lengths: Vec<u64>,
64}
65
66impl SequencingWithinIntervals {
67    /// Create a new SequencingWithinIntervals problem.
68    ///
69    /// # Panics
70    /// Panics if the three vectors have different lengths, or if any task has
71    /// `r(i) + l(i) > d(i)` (empty time window).
72    pub fn new(release_times: Vec<u64>, deadlines: Vec<u64>, lengths: Vec<u64>) -> Self {
73        assert_eq!(
74            release_times.len(),
75            deadlines.len(),
76            "release_times and deadlines must have the same length"
77        );
78        assert_eq!(
79            release_times.len(),
80            lengths.len(),
81            "release_times and lengths must have the same length"
82        );
83        for i in 0..release_times.len() {
84            let sum = release_times[i]
85                .checked_add(lengths[i])
86                .expect("overflow computing r(i) + l(i)");
87            assert!(
88                sum <= deadlines[i],
89                "Task {i}: r({}) + l({}) > d({}), time window is empty",
90                release_times[i],
91                lengths[i],
92                deadlines[i]
93            );
94        }
95        Self {
96            release_times,
97            deadlines,
98            lengths,
99        }
100    }
101
102    /// Returns the release times.
103    pub fn release_times(&self) -> &[u64] {
104        &self.release_times
105    }
106
107    /// Returns the deadlines.
108    pub fn deadlines(&self) -> &[u64] {
109        &self.deadlines
110    }
111
112    /// Returns the processing lengths.
113    pub fn lengths(&self) -> &[u64] {
114        &self.lengths
115    }
116
117    /// Returns the number of tasks.
118    pub fn num_tasks(&self) -> usize {
119        self.release_times.len()
120    }
121}
122
123impl Problem for SequencingWithinIntervals {
124    const NAME: &'static str = "SequencingWithinIntervals";
125    type Value = crate::types::Or;
126
127    fn variant() -> Vec<(&'static str, &'static str)> {
128        crate::variant_params![]
129    }
130
131    fn dims(&self) -> Vec<usize> {
132        (0..self.num_tasks())
133            .map(|i| (self.deadlines[i] - self.release_times[i] - self.lengths[i] + 1) as usize)
134            .collect()
135    }
136
137    fn evaluate(&self, config: &[usize]) -> crate::types::Or {
138        crate::types::Or({
139            let n = self.num_tasks();
140            if config.len() != n {
141                return crate::types::Or(false);
142            }
143
144            // Check each variable is within range and compute start times
145            let mut starts = Vec::with_capacity(n);
146            for (i, &c) in config.iter().enumerate() {
147                let dim =
148                    (self.deadlines[i] - self.release_times[i] - self.lengths[i] + 1) as usize;
149                if c >= dim {
150                    return crate::types::Or(false);
151                }
152                // start = r[i] + c, and c < dim = d[i] - r[i] - l[i] + 1,
153                // so start + l[i] <= d[i] is guaranteed by construction.
154                let start = self.release_times[i] + c as u64;
155                starts.push(start);
156            }
157
158            // Check no two tasks overlap
159            for i in 0..n {
160                for j in (i + 1)..n {
161                    let end_i = starts[i] + self.lengths[i];
162                    let end_j = starts[j] + self.lengths[j];
163                    // Tasks overlap if neither finishes before the other starts
164                    if !(end_i <= starts[j] || end_j <= starts[i]) {
165                        return crate::types::Or(false);
166                    }
167                }
168            }
169
170            true
171        })
172    }
173}
174
175crate::declare_variants! {
176    default SequencingWithinIntervals => "2^num_tasks",
177}
178
179#[cfg(feature = "example-db")]
180pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
181    vec![crate::example_db::specs::ModelExampleSpec {
182        id: "sequencing_within_intervals",
183        instance: Box::new(SequencingWithinIntervals::new(
184            vec![0, 1, 3, 6, 0],
185            vec![5, 8, 9, 12, 12],
186            vec![2, 2, 2, 3, 2],
187        )),
188        optimal_config: vec![0, 1, 1, 0, 9],
189        optimal_value: serde_json::json!(true),
190    }]
191}
192
193#[cfg(test)]
194#[path = "../../unit_tests/models/misc/sequencing_within_intervals.rs"]
195mod tests;