Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"SubsetSum": [Subset Sum],
"MinimumFeedbackArcSet": [Minimum Feedback Arc Set],
"MinimumFeedbackVertexSet": [Minimum Feedback Vertex Set],
"SequencingWithReleaseTimesAndDeadlines": [Sequencing with Release Times and Deadlines],
"ShortestCommonSupersequence": [Shortest Common Supersequence],
"MinimumSumMulticenter": [Minimum Sum Multicenter],
"SubgraphIsomorphism": [Subgraph Isomorphism],
Expand Down Expand Up @@ -1624,6 +1625,15 @@ NP-completeness was established by Garey, Johnson, and Stockmeyer @gareyJohnsonS
*Example.* Let $A = {3, 7, 1, 8, 2, 4}$ ($n = 6$) and target $B = 11$. Selecting $A' = {3, 8}$ gives sum $3 + 8 = 11 = B$. Another solution: $A' = {7, 4}$ with sum $7 + 4 = 11 = B$.
]

#problem-def("SequencingWithReleaseTimesAndDeadlines")[
Given a set $T$ of $n$ tasks and, for each task $t in T$, a processing time $ell(t) in ZZ^+$, a release time $r(t) in ZZ^(>=0)$, and a deadline $d(t) in ZZ^+$, determine whether there exists a one-processor schedule $sigma: T -> ZZ^(>=0)$ such that for all $t in T$: $sigma(t) >= r(t)$, $sigma(t) + ell(t) <= d(t)$, and no two tasks overlap (i.e., $sigma(t) > sigma(t')$ implies $sigma(t) >= sigma(t') + ell(t')$).
][
Problem SS1 in Garey and Johnson's appendix @garey1979, and a fundamental single-machine scheduling feasibility problem. It is strongly NP-complete by reduction from 3-Partition, so no pseudo-polynomial time algorithm exists unless P = NP. The problem becomes polynomial-time solvable when: (1) all task lengths equal 1, (2) preemption is allowed, or (3) all release times are zero. The best known exact algorithm for the general case runs in $O^*(2^n dot n)$ time via dynamic programming on task subsets.

*Example.* Let $T = {t_1, t_2, t_3}$ with $ell(t_1) = 2$, $r(t_1) = 0$, $d(t_1) = 5$; $ell(t_2) = 3$, $r(t_2) = 1$, $d(t_2) = 6$; $ell(t_3) = 1$, $r(t_3) = 2$, $d(t_3) = 4$. A feasible schedule: $sigma(t_1) = 0$ (runs $[0, 2)$), $sigma(t_3) = 2$ (runs $[2, 3)$), $sigma(t_2) = 3$ (runs $[3, 6)$). All release and deadline constraints are satisfied with no overlap.
]
Comment on lines +1628 to +1634


#{
let x = load-model-example("ShortestCommonSupersequence")
let alpha-size = x.instance.alphabet_size
Expand Down
32 changes: 32 additions & 0 deletions docs/src/reductions/problem_schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,17 @@
}
]
},
{
"name": "LongestCommonSubsequence",
"description": "Find the longest string that is a subsequence of every input string",
"fields": [
{
"name": "strings",
"type_name": "Vec<Vec<u8>>",
"description": "The input strings"
}
]
},
{
"name": "MaxCut",
"description": "Find maximum weight cut in a graph",
Expand Down Expand Up @@ -594,6 +605,27 @@
}
]
},
{
"name": "SequencingWithReleaseTimesAndDeadlines",
"description": "Single-machine scheduling feasibility: can all tasks be scheduled within their release-deadline windows without overlap?",
"fields": [
{
"name": "lengths",
"type_name": "Vec<u64>",
"description": "Processing time l(t) for each task (positive)"
},
{
"name": "release_times",
"type_name": "Vec<u64>",
"description": "Release time r(t) for each task (non-negative)"
},
{
"name": "deadlines",
"type_name": "Vec<u64>",
"description": "Deadline d(t) for each task (positive)"
}
]
},
{
"name": "ShortestCommonSupersequence",
"description": "Find a common supersequence of bounded length for a set of strings",
Expand Down
8 changes: 7 additions & 1 deletion problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,13 @@ pub struct CreateArgs {
/// Directed arcs for directed graph problems (e.g., 0>1,1>2,2>0)
#[arg(long)]
pub arcs: Option<String>,
/// Deadlines for MinimumTardinessSequencing (comma-separated, e.g., "5,5,5,3,3")
/// Task processing times for scheduling problems (comma-separated, e.g., "3,2,4")
#[arg(long)]
pub lengths: Option<String>,
/// Task release times for scheduling problems (comma-separated, e.g., "0,1,5")
#[arg(long)]
pub release_times: Option<String>,
/// Deadlines for scheduling problems (comma-separated, e.g., "5,5,5,3,3")
#[arg(long)]
pub deadlines: Option<String>,
/// Precedence pairs for MinimumTardinessSequencing (e.g., "0>3,1>3,1>4,2>4")
Expand Down
45 changes: 44 additions & 1 deletion problemreductions-cli/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use problemreductions::models::algebraic::{ClosestVectorProblem, BMF};
use problemreductions::models::graph::{GraphPartitioning, HamiltonianPath};
use problemreductions::models::misc::{
BinPacking, FlowShopScheduling, LongestCommonSubsequence, MinimumTardinessSequencing,
PaintShop, ShortestCommonSupersequence, SubsetSum,
PaintShop, SequencingWithReleaseTimesAndDeadlines, ShortestCommonSupersequence, SubsetSum,
};
use problemreductions::prelude::*;
use problemreductions::registry::collect_schemas;
Expand Down Expand Up @@ -57,6 +57,8 @@ fn all_data_flags_empty(args: &CreateArgs) -> bool {
&& args.pattern.is_none()
&& args.strings.is_none()
&& args.arcs.is_none()
&& args.lengths.is_none()
&& args.release_times.is_none()
&& args.deadlines.is_none()
&& args.precedence_pairs.is_none()
&& args.task_lengths.is_none()
Expand Down Expand Up @@ -1086,6 +1088,47 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
)
}

// SequencingWithReleaseTimesAndDeadlines
"SequencingWithReleaseTimesAndDeadlines" => {
let lengths_str = args.lengths.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"SequencingWithReleaseTimesAndDeadlines requires --lengths, --release-times, and --deadlines\n\n\
Usage: pred create SequencingWithReleaseTimesAndDeadlines --lengths 3,2,4 --release-times 0,1,5 --deadlines 5,6,10"
)
})?;
let release_str = args.release_times.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"SequencingWithReleaseTimesAndDeadlines requires --release-times\n\n\
Usage: pred create SequencingWithReleaseTimesAndDeadlines --lengths 3,2,4 --release-times 0,1,5 --deadlines 5,6,10"
)
})?;
let deadlines_str = args.deadlines.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"SequencingWithReleaseTimesAndDeadlines requires --deadlines\n\n\
Usage: pred create SequencingWithReleaseTimesAndDeadlines --lengths 3,2,4 --release-times 0,1,5 --deadlines 5,6,10"
)
})?;
let lengths: Vec<u64> = util::parse_comma_list(lengths_str)?;
let release_times: Vec<u64> = util::parse_comma_list(release_str)?;
let deadlines: Vec<u64> = util::parse_comma_list(deadlines_str)?;
if lengths.len() != release_times.len() || lengths.len() != deadlines.len() {
bail!(
"All three lists must have the same length: lengths={}, release_times={}, deadlines={}",
lengths.len(),
release_times.len(),
deadlines.len()
);
}
(
ser(SequencingWithReleaseTimesAndDeadlines::new(
lengths,
release_times,
deadlines,
))?,
resolved_variant.clone(),
)
}

_ => bail!("{}", crate::problem_name::unknown_problem_error(canonical)),
};

Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ pub mod prelude {
};
pub use crate::models::misc::{
BinPacking, Factoring, FlowShopScheduling, Knapsack, LongestCommonSubsequence,
MinimumTardinessSequencing, PaintShop, ShortestCommonSupersequence, SubsetSum,
MinimumTardinessSequencing, PaintShop, SequencingWithReleaseTimesAndDeadlines,
ShortestCommonSupersequence, SubsetSum,
};
pub use crate::models::set::{ExactCoverBy3Sets, MaximumSetPacking, MinimumSetCovering};

Expand Down
4 changes: 4 additions & 0 deletions src/models/misc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//! - [`LongestCommonSubsequence`]: Longest Common Subsequence
//! - [`MinimumTardinessSequencing`]: Minimize tardy tasks in single-machine scheduling
//! - [`PaintShop`]: Minimize color switches in paint shop scheduling
//! - [`SequencingWithReleaseTimesAndDeadlines`]: Single-machine scheduling feasibility
//! - [`ShortestCommonSupersequence`]: Find a common supersequence of bounded length
//! - [`SubsetSum`]: Find a subset summing to exactly a target value

Expand All @@ -18,6 +19,7 @@ mod knapsack;
mod longest_common_subsequence;
mod minimum_tardiness_sequencing;
pub(crate) mod paintshop;
mod sequencing_with_release_times_and_deadlines;
pub(crate) mod shortest_common_supersequence;
mod subset_sum;

Expand All @@ -28,6 +30,7 @@ pub use knapsack::Knapsack;
pub use longest_common_subsequence::LongestCommonSubsequence;
pub use minimum_tardiness_sequencing::MinimumTardinessSequencing;
pub use paintshop::PaintShop;
pub use sequencing_with_release_times_and_deadlines::SequencingWithReleaseTimesAndDeadlines;
pub use shortest_common_supersequence::ShortestCommonSupersequence;
pub use subset_sum::SubsetSum;

Expand All @@ -38,5 +41,6 @@ pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::M
specs.extend(paintshop::canonical_model_example_specs());
specs.extend(shortest_common_supersequence::canonical_model_example_specs());
specs.extend(minimum_tardiness_sequencing::canonical_model_example_specs());
specs.extend(sequencing_with_release_times_and_deadlines::canonical_model_example_specs());
specs
}
177 changes: 177 additions & 0 deletions src/models/misc/sequencing_with_release_times_and_deadlines.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//! Sequencing with Release Times and Deadlines problem implementation.
//!
//! Given a set of tasks each with a processing time, release time, and deadline,
//! determine whether all tasks can be non-preemptively scheduled on one processor
//! such that each task starts after its release time and finishes by its deadline.
//! Strongly NP-complete (Garey & Johnson, A5 SS1).

use crate::registry::{FieldInfo, ProblemSchemaEntry};
use crate::traits::{Problem, SatisfactionProblem};
use serde::{Deserialize, Serialize};

inventory::submit! {
ProblemSchemaEntry {
name: "SequencingWithReleaseTimesAndDeadlines",
display_name: "Sequencing with Release Times and Deadlines",
aliases: &[],
dimensions: &[],
module_path: module_path!(),
description: "Single-machine scheduling feasibility: can all tasks be scheduled within their release-deadline windows without overlap?",
fields: &[
FieldInfo { name: "lengths", type_name: "Vec<u64>", description: "Processing time l(t) for each task (positive)" },
FieldInfo { name: "release_times", type_name: "Vec<u64>", description: "Release time r(t) for each task (non-negative)" },
FieldInfo { name: "deadlines", type_name: "Vec<u64>", description: "Deadline d(t) for each task (positive)" },
],
}
}

/// Sequencing with Release Times and Deadlines.
///
/// Given a set of `n` tasks, each with a processing time `l(t)`, release time
/// `r(t)`, and deadline `d(t)`, determine whether there exists a one-processor
/// schedule where each task starts no earlier than its release time and finishes
/// by its deadline, with no two tasks overlapping.
///
/// # Representation
///
/// Uses a permutation encoding (Lehmer code), where `config[i]` selects which
/// remaining task to schedule next from the pool of unscheduled tasks.
/// `dims() = [n, n-1, ..., 2, 1]`. Tasks are scheduled left-to-right: each
/// task starts at `max(release_time, current_time)`. The schedule is feasible
/// iff every task finishes by its deadline.
///
/// # Example
///
/// ```
/// use problemreductions::models::misc::SequencingWithReleaseTimesAndDeadlines;
/// use problemreductions::{Problem, Solver, BruteForce};
///
/// let problem = SequencingWithReleaseTimesAndDeadlines::new(
/// vec![1, 2, 1],
/// vec![0, 0, 2],
/// vec![3, 3, 4],
/// );
/// let solver = BruteForce::new();
/// let solution = solver.find_satisfying(&problem);
/// assert!(solution.is_some());
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SequencingWithReleaseTimesAndDeadlines {
lengths: Vec<u64>,
release_times: Vec<u64>,
deadlines: Vec<u64>,
}

impl SequencingWithReleaseTimesAndDeadlines {
/// Create a new instance.
///
/// # Panics
///
/// Panics if the three vectors have different lengths.
pub fn new(lengths: Vec<u64>, release_times: Vec<u64>, deadlines: Vec<u64>) -> Self {
assert_eq!(lengths.len(), release_times.len());
assert_eq!(lengths.len(), deadlines.len());
Self {
lengths,
release_times,
deadlines,
}
}

/// Returns the processing times.
pub fn lengths(&self) -> &[u64] {
&self.lengths
}

/// Returns the release times.
pub fn release_times(&self) -> &[u64] {
&self.release_times
}

/// Returns the deadlines.
pub fn deadlines(&self) -> &[u64] {
&self.deadlines
}

/// Returns the number of tasks.
pub fn num_tasks(&self) -> usize {
self.lengths.len()
}

/// Returns the time horizon (maximum deadline).
pub fn time_horizon(&self) -> u64 {
self.deadlines.iter().copied().max().unwrap_or(0)
}
}

impl Problem for SequencingWithReleaseTimesAndDeadlines {
const NAME: &'static str = "SequencingWithReleaseTimesAndDeadlines";
type Metric = bool;

fn variant() -> Vec<(&'static str, &'static str)> {
crate::variant_params![]
}

fn dims(&self) -> Vec<usize> {
let n = self.num_tasks();
(0..n).rev().map(|i| i + 1).collect()
}

fn evaluate(&self, config: &[usize]) -> bool {
let n = self.num_tasks();
if config.len() != n {
return false;
}

// Decode Lehmer code into a permutation of task indices.
let mut available: Vec<usize> = (0..n).collect();
let mut schedule = Vec::with_capacity(n);
for &c in config.iter() {
if c >= available.len() {
return false;
}
schedule.push(available.remove(c));
}

// Schedule tasks left-to-right: each task starts at max(release_time, current_time).
let mut current_time: u64 = 0;
for &task in &schedule {
let start = current_time.max(self.release_times[task]);
let finish = start + self.lengths[task];
if finish > self.deadlines[task] {
return false;
}
current_time = finish;
}

true
}
}

impl SatisfactionProblem for SequencingWithReleaseTimesAndDeadlines {}

crate::declare_variants! {
default sat SequencingWithReleaseTimesAndDeadlines => "2^num_tasks * num_tasks",
}

#[cfg(feature = "example-db")]
pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
vec![crate::example_db::specs::ModelExampleSpec {
id: "sequencing_with_release_times_and_deadlines",
build: || {
// 5 tasks from issue example.
// Feasible schedule order: t3, t0, t1, t2, t4
let problem = SequencingWithReleaseTimesAndDeadlines::new(
vec![3, 2, 4, 1, 2],
vec![0, 1, 5, 0, 8],
vec![5, 6, 10, 3, 12],
);
// Lehmer code [3,0,0,0,0] = permutation [3,0,1,2,4]
crate::example_db::specs::satisfaction_example(problem, vec![vec![3, 0, 0, 0, 0]])
},
}]
}

#[cfg(test)]
#[path = "../../unit_tests/models/misc/sequencing_with_release_times_and_deadlines.rs"]
mod tests;
3 changes: 2 additions & 1 deletion src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub use graph::{
};
pub use misc::{
BinPacking, Factoring, FlowShopScheduling, Knapsack, LongestCommonSubsequence,
MinimumTardinessSequencing, PaintShop, ShortestCommonSupersequence, SubsetSum,
MinimumTardinessSequencing, PaintShop, SequencingWithReleaseTimesAndDeadlines,
ShortestCommonSupersequence, SubsetSum,
};
pub use set::{ExactCoverBy3Sets, MaximumSetPacking, MinimumSetCovering};
Loading
Loading