Skip to content

Commit dfcc313

Browse files
zazabapclaudeGiggleLiu
authored
Fix #219: [Model] SequencingWithinIntervals (#222)
* Add plan for #212: [Model] MultiprocessorScheduling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add plan for #219: [Model] SequencingWithinIntervals Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: implement SequencingWithinIntervals model for #219 Add a satisfaction problem model for scheduling tasks within time windows without overlap (Garey & Johnson SS1, NP-complete). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update SequencingWithinIntervals for new API Add missing display_name, aliases, dimensions fields to ProblemSchemaEntry. Use 'default sat' prefix in declare_variants! macro. Rename task_lengths CLI field to lengths to avoid clash with FlowShopScheduling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Copilot review comments - Use checked_add to prevent u64 overflow in constructor - Rename test functions to snake_case (test_sequencing_within_intervals_*) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing structural items for SequencingWithinIntervals - Add canonical model example in example_db (satisfaction_example) - Add trait_consistency test entry - Add paper display-name and problem-def block in reductions.typ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: regenerate JSON artifacts for SequencingWithinIntervals Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: consolidate SequencingWithinIntervals re-exports into existing use blocks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: trigger CI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: apply rustfmt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove unreachable deadline check in evaluate() The deadline guard `start + l[i] > d[i]` in evaluate() was dead code: since c < dim = d[i] - r[i] - l[i] + 1, start + l[i] <= d[i] holds by construction. Removing it fixes the 1-line codecov gap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: align with PR #192 standard — use load-model-example, remove unrelated files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add Gantt chart figure, remove plan doc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add CLI tests, inline dims computation in evaluate() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: GiggleLiu <cacate0129@gmail.com>
1 parent abfebe0 commit dfcc313

10 files changed

Lines changed: 573 additions & 3 deletions

File tree

docs/paper/reductions.typ

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
"PartitionIntoTriangles": [Partition Into Triangles],
111111
"FlowShopScheduling": [Flow Shop Scheduling],
112112
"MinimumTardinessSequencing": [Minimum Tardiness Sequencing],
113+
"SequencingWithinIntervals": [Sequencing Within Intervals],
113114
"DirectedTwoCommodityIntegralFlow": [Directed Two-Commodity Integral Flow],
114115
)
115116

@@ -2108,6 +2109,100 @@ NP-completeness was established by Garey, Johnson, and Stockmeyer @gareyJohnsonS
21082109
) <fig:flowshop>
21092110
]
21102111

2112+
#{
2113+
let x = load-model-example("SequencingWithinIntervals")
2114+
let ntasks = x.instance.lengths.len()
2115+
let release = x.instance.release_times
2116+
let deadline = x.instance.deadlines
2117+
let lengths = x.instance.lengths
2118+
let sol = x.optimal.at(0)
2119+
// Compute start times from config offsets: start_i = release_i + config_i
2120+
let starts = range(ntasks).map(i => release.at(i) + sol.config.at(i))
2121+
// Identify the enforcer task: the one with the tightest window (deadline - release == length)
2122+
let enforcer = range(ntasks).filter(i => deadline.at(i) - release.at(i) == lengths.at(i)).at(0)
2123+
let regular = range(ntasks).filter(i => i != enforcer)
2124+
// Partition sum B = total length of regular tasks
2125+
let B = regular.map(i => lengths.at(i)).sum()
2126+
[
2127+
#problem-def("SequencingWithinIntervals")[
2128+
Given a finite set $T$ of tasks and, for each $t in T$, a release time $r(t) >= 0$, a deadline $d(t) >= 0$, and a processing length $ell(t) in ZZ^+$ satisfying $r(t) + ell(t) <= d(t)$, determine whether there exists a feasible schedule $sigma: T -> ZZ_(>= 0)$ such that for each $t in T$: (1) $sigma(t) >= r(t)$, (2) $sigma(t) + ell(t) <= d(t)$, and (3) for all $t' in T backslash {t}$, either $sigma(t') + ell(t') <= sigma(t)$ or $sigma(t') >= sigma(t) + ell(t)$.
2129+
][
2130+
Sequencing Within Intervals is problem SS1 in Garey & Johnson @garey1979, proved NP-complete via reduction from Partition (Theorem 3.8). Each task $t$ must execute non-preemptively during the interval $[r(t), d(t))$, occupying $ell(t)$ consecutive time units, and no two tasks may overlap. The problem is a canonical single-machine scheduling problem and one of the earliest NP-completeness results for scheduling theory.
2131+
2132+
The NP-completeness proof uses an "enforcer" task pinned at the midpoint of the time horizon, forcing the remaining tasks to split into two balanced groups --- directly encoding the Partition problem.
2133+
2134+
*Example.* Consider #ntasks tasks derived from a Partition instance with $A = {#regular.map(i => str(lengths.at(i))).join(", ")}$ (sum $B = #B$):
2135+
#align(center, table(
2136+
columns: ntasks + 1,
2137+
align: center,
2138+
table.header([$"Task"$], ..regular.map(i => [$t_#(i + 1)$]), [$overline(t)$]),
2139+
[$r(t)$], ..regular.map(i => [#release.at(i)]), [#release.at(enforcer)],
2140+
[$d(t)$], ..regular.map(i => [#deadline.at(i)]), [#deadline.at(enforcer)],
2141+
[$ell(t)$], ..regular.map(i => [#lengths.at(i)]), [#lengths.at(enforcer)],
2142+
))
2143+
The enforcer task $overline(t)$ must run in $[#release.at(enforcer), #deadline.at(enforcer))$, splitting the schedule into $[0, #release.at(enforcer))$ and $[#deadline.at(enforcer), #deadline.at(0))$. Each side has #(B / 2) time units, and tasks with total length $#(B / 2)$ must fill each side --- corresponding to a partition of $A$.
2144+
2145+
#figure(
2146+
canvas(length: 1cm, {
2147+
import draw: *
2148+
let colors = (rgb("#4e79a7"), rgb("#e15759"), rgb("#76b7b2"), rgb("#f28e2b"))
2149+
let enforcer-color = rgb("#b07aa1")
2150+
let task-labels = regular.map(i => "$t_" + str(i + 1) + "$") + ("$overline(t)$",)
2151+
let task-order = regular + (enforcer,)
2152+
let scale = 0.7
2153+
let row-h = 0.6
2154+
2155+
// Single-row Gantt chart: all tasks on one timeline
2156+
for (k, i) in task-order.enumerate() {
2157+
let s = starts.at(i)
2158+
let e = s + lengths.at(i)
2159+
let x0 = s * scale
2160+
let x1 = e * scale
2161+
let col = if i == enforcer { enforcer-color } else { colors.at(regular.position(j => j == i)) }
2162+
rect((x0, -row-h / 2), (x1, row-h / 2),
2163+
fill: col.transparentize(30%), stroke: 0.4pt + col)
2164+
content(((x0 + x1) / 2, 0), text(6pt, task-labels.at(k)))
2165+
}
2166+
2167+
// Release-time and deadline markers for each task
2168+
for (k, i) in task-order.enumerate() {
2169+
let col = if i == enforcer { enforcer-color } else { colors.at(regular.position(j => j == i)) }
2170+
// Release time: upward triangle below axis
2171+
let rx = release.at(i) * scale
2172+
line((rx, -row-h / 2 - 0.05), (rx, -row-h / 2 - 0.18), stroke: 0.5pt + col)
2173+
// Deadline: downward tick above axis
2174+
let dx = deadline.at(i) * scale
2175+
line((dx, row-h / 2 + 0.05), (dx, row-h / 2 + 0.18), stroke: 0.5pt + col)
2176+
}
2177+
2178+
// Release / deadline group labels
2179+
content((-0.5, -row-h / 2 - 0.12), text(5pt)[$r$])
2180+
content((-0.5, row-h / 2 + 0.12), text(5pt)[$d$])
2181+
2182+
// Time axis
2183+
let max-t = 11
2184+
let y-axis = -row-h / 2 - 0.35
2185+
line((0, y-axis), (max-t * scale, y-axis), stroke: 0.4pt)
2186+
for t in range(max-t + 1) {
2187+
let x = t * scale
2188+
line((x, y-axis), (x, y-axis - 0.08), stroke: 0.4pt)
2189+
if calc.rem(t, 2) == 0 or t == max-t {
2190+
content((x, y-axis - 0.22), text(5pt, str(t)))
2191+
}
2192+
}
2193+
content((max-t * scale / 2, y-axis - 0.45), text(7pt)[$t$])
2194+
2195+
// Enforcer region highlight
2196+
let ex0 = release.at(enforcer) * scale
2197+
let ex1 = deadline.at(enforcer) * scale
2198+
line((ex0, row-h / 2 + 0.3), (ex0, y-axis), stroke: (paint: enforcer-color, thickness: 0.6pt, dash: "dashed"))
2199+
line((ex1, row-h / 2 + 0.3), (ex1, y-axis), stroke: (paint: enforcer-color, thickness: 0.6pt, dash: "dashed"))
2200+
}),
2201+
caption: [Feasible schedule for the SWI instance. The enforcer task $overline(t)$ (purple) is pinned at $[#release.at(enforcer), #deadline.at(enforcer))$, splitting the timeline into two halves of #(B / 2) time units each.],
2202+
) <fig:swi>
2203+
]
2204+
]
2205+
}
21112206
#{
21122207
let x = load-model-example("MinimumTardinessSequencing")
21132208
let ntasks = x.instance.num_tasks

problemreductions-cli/src/cli.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ Flags by problem type:
239239
BMF --matrix (0/1), --rank
240240
SteinerTree --graph, --edge-weights, --terminals
241241
CVP --basis, --target-vec [--bounds]
242+
SequencingWithinIntervals --release-times, --deadlines, --lengths
242243
OptimalLinearArrangement --graph, --bound
243244
RuralPostman (RPP) --graph, --edge-weights, --required-edges, --bound
244245
MultipleChoiceBranching --arcs [--weights] --partition --bound [--num-vertices]
@@ -410,6 +411,12 @@ pub struct CreateArgs {
410411
/// Variable bounds for CVP as "lower,upper" (e.g., "-10,10") [default: -10,10]
411412
#[arg(long, allow_hyphen_values = true)]
412413
pub bounds: Option<String>,
414+
/// Release times for SequencingWithinIntervals (comma-separated, e.g., "0,0,5")
415+
#[arg(long)]
416+
pub release_times: Option<String>,
417+
/// Processing lengths for SequencingWithinIntervals (comma-separated, e.g., "3,1,1")
418+
#[arg(long)]
419+
pub lengths: Option<String>,
413420
/// Terminal vertices for SteinerTree (comma-separated indices, e.g., "0,2,4")
414421
#[arg(long)]
415422
pub terminals: Option<String>,

problemreductions-cli/src/commands/create.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ use problemreductions::export::{ModelExample, ProblemRef, ProblemSide, RuleExamp
1010
use problemreductions::models::algebraic::{ClosestVectorProblem, BMF};
1111
use problemreductions::models::graph::{
1212
GraphPartitioning, HamiltonianPath, LengthBoundedDisjointPaths, MultipleChoiceBranching,
13+
SteinerTree,
1314
};
1415
use problemreductions::models::misc::{
1516
BinPacking, FlowShopScheduling, LongestCommonSubsequence, MinimumTardinessSequencing,
16-
PaintShop, ShortestCommonSupersequence, SubsetSum,
17+
PaintShop, SequencingWithinIntervals, ShortestCommonSupersequence, SubsetSum,
1718
};
1819
use problemreductions::prelude::*;
1920
use problemreductions::registry::collect_schemas;
@@ -66,6 +67,9 @@ fn all_data_flags_empty(args: &CreateArgs) -> bool {
6667
&& args.basis.is_none()
6768
&& args.target_vec.is_none()
6869
&& args.bounds.is_none()
70+
&& args.release_times.is_none()
71+
&& args.deadlines.is_none()
72+
&& args.lengths.is_none()
6973
&& args.terminals.is_none()
7074
&& args.tree.is_none()
7175
&& args.required_edges.is_none()
@@ -229,6 +233,7 @@ fn type_format_hint(type_name: &str, graph_type: Option<&str>) -> &'static str {
229233
"Vec<Vec<usize>>" => "semicolon-separated groups: \"0,1;2,3\"",
230234
"usize" => "integer",
231235
"u64" => "integer",
236+
"Vec<u64>" => "comma-separated integers: 0,0,5",
232237
"i64" => "integer",
233238
"BigUint" => "nonnegative decimal integer",
234239
"Vec<BigUint>" => "comma-separated nonnegative decimal integers: 3,7,1,8",
@@ -274,6 +279,7 @@ fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str {
274279
}
275280
"PartitionIntoTriangles" => "--graph 0-1,1-2,0-2",
276281
"Factoring" => "--target 15 --m 4 --n 4",
282+
"SequencingWithinIntervals" => "--release-times 0,0,5 --deadlines 11,11,6 --lengths 3,1,1",
277283
"SteinerTree" => "--graph 0-1,1-2,1-3,3-4 --edge-weights 2,2,1,1 --terminals 0,2,4",
278284
"OptimalLinearArrangement" => "--graph 0-1,1-2,2-3 --bound 5",
279285
"DirectedTwoCommodityIntegralFlow" => {
@@ -1212,6 +1218,31 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
12121218
)
12131219
}
12141220

1221+
// SequencingWithinIntervals
1222+
"SequencingWithinIntervals" => {
1223+
let usage = "Usage: pred create SequencingWithinIntervals --release-times 0,0,5 --deadlines 11,11,6 --lengths 3,1,1";
1224+
let rt_str = args.release_times.as_deref().ok_or_else(|| {
1225+
anyhow::anyhow!("SequencingWithinIntervals requires --release-times\n\n{usage}")
1226+
})?;
1227+
let dl_str = args.deadlines.as_deref().ok_or_else(|| {
1228+
anyhow::anyhow!("SequencingWithinIntervals requires --deadlines\n\n{usage}")
1229+
})?;
1230+
let len_str = args.lengths.as_deref().ok_or_else(|| {
1231+
anyhow::anyhow!("SequencingWithinIntervals requires --lengths\n\n{usage}")
1232+
})?;
1233+
let release_times: Vec<u64> = util::parse_comma_list(rt_str)?;
1234+
let deadlines: Vec<u64> = util::parse_comma_list(dl_str)?;
1235+
let lengths: Vec<u64> = util::parse_comma_list(len_str)?;
1236+
(
1237+
ser(SequencingWithinIntervals::new(
1238+
release_times,
1239+
deadlines,
1240+
lengths,
1241+
))?,
1242+
resolved_variant.clone(),
1243+
)
1244+
}
1245+
12151246
// OptimalLinearArrangement — graph + bound
12161247
"OptimalLinearArrangement" => {
12171248
let usage = "Usage: pred create OptimalLinearArrangement --graph 0-1,1-2,2-3 --bound 5";

problemreductions-cli/tests/cli_tests.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4776,3 +4776,76 @@ fn test_create_weighted_mis_round_trips_into_solve() {
47764776
let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
47774777
assert_eq!(json["evaluation"], "Valid(5)");
47784778
}
4779+
4780+
#[test]
4781+
fn test_create_sequencing_within_intervals() {
4782+
let output_file =
4783+
std::env::temp_dir().join("pred_test_create_sequencing_within_intervals.json");
4784+
let output = pred()
4785+
.args([
4786+
"-o",
4787+
output_file.to_str().unwrap(),
4788+
"create",
4789+
"SequencingWithinIntervals",
4790+
"--release-times",
4791+
"0,0,0,0,5",
4792+
"--deadlines",
4793+
"11,11,11,11,6",
4794+
"--lengths",
4795+
"3,1,2,4,1",
4796+
])
4797+
.output()
4798+
.unwrap();
4799+
assert!(
4800+
output.status.success(),
4801+
"stderr: {}",
4802+
String::from_utf8_lossy(&output.stderr)
4803+
);
4804+
let content = std::fs::read_to_string(&output_file).unwrap();
4805+
let json: serde_json::Value = serde_json::from_str(&content).unwrap();
4806+
assert_eq!(json["type"], "SequencingWithinIntervals");
4807+
assert_eq!(
4808+
json["data"]["release_times"],
4809+
serde_json::json!([0, 0, 0, 0, 5])
4810+
);
4811+
assert_eq!(
4812+
json["data"]["deadlines"],
4813+
serde_json::json!([11, 11, 11, 11, 6])
4814+
);
4815+
assert_eq!(json["data"]["lengths"], serde_json::json!([3, 1, 2, 4, 1]));
4816+
std::fs::remove_file(&output_file).ok();
4817+
}
4818+
4819+
#[test]
4820+
fn test_create_model_example_sequencing_within_intervals() {
4821+
let output = pred()
4822+
.args(["create", "--example", "SequencingWithinIntervals"])
4823+
.output()
4824+
.unwrap();
4825+
assert!(
4826+
output.status.success(),
4827+
"stderr: {}",
4828+
String::from_utf8_lossy(&output.stderr)
4829+
);
4830+
let stdout = String::from_utf8(output.stdout).unwrap();
4831+
let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
4832+
assert_eq!(json["type"], "SequencingWithinIntervals");
4833+
}
4834+
4835+
#[test]
4836+
fn test_create_sequencing_within_intervals_rejects_empty_window() {
4837+
let output = pred()
4838+
.args([
4839+
"create",
4840+
"SequencingWithinIntervals",
4841+
"--release-times",
4842+
"5",
4843+
"--deadlines",
4844+
"3",
4845+
"--lengths",
4846+
"2",
4847+
])
4848+
.output()
4849+
.unwrap();
4850+
assert!(!output.status.success());
4851+
}

src/example_db/fixtures/examples.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
{"problem":"PartitionIntoTriangles","variant":{"graph":"SimpleGraph"},"instance":{"graph":{"inner":{"edge_property":"undirected","edges":[[0,1,null],[0,2,null],[1,2,null],[3,4,null],[3,5,null],[4,5,null],[0,3,null]],"node_holes":[],"nodes":[null,null,null,null,null,null]}}},"samples":[{"config":[0,0,0,1,1,1],"metric":true}],"optimal":[{"config":[0,0,0,1,1,1],"metric":true},{"config":[1,1,1,0,0,0],"metric":true}]},
3333
{"problem":"QUBO","variant":{"weight":"f64"},"instance":{"matrix":[[-1.0,2.0,0.0],[0.0,-1.0,2.0],[0.0,0.0,-1.0]],"num_vars":3},"samples":[{"config":[1,0,1],"metric":{"Valid":-2.0}}],"optimal":[{"config":[1,0,1],"metric":{"Valid":-2.0}}]},
3434
{"problem":"Satisfiability","variant":{},"instance":{"clauses":[{"literals":[1,2]},{"literals":[-1,3]},{"literals":[-2,-3]}],"num_vars":3},"samples":[{"config":[1,0,1],"metric":true}],"optimal":[{"config":[0,1,0],"metric":true},{"config":[1,0,1],"metric":true}]},
35+
{"problem":"SequencingWithinIntervals","variant":{},"instance":{"deadlines":[11,11,11,11,6],"lengths":[3,1,2,4,1],"release_times":[0,0,0,0,5]},"samples":[{"config":[0,6,3,7,0],"metric":true}],"optimal":[{"config":[0,6,3,7,0],"metric":true},{"config":[0,10,3,6,0],"metric":true},{"config":[2,6,0,7,0],"metric":true},{"config":[2,10,0,6,0],"metric":true},{"config":[6,0,9,1,0],"metric":true},{"config":[6,4,9,0,0],"metric":true},{"config":[8,0,6,1,0],"metric":true},{"config":[8,4,6,0,0],"metric":true}]},
3536
{"problem":"SetBasis","variant":{},"instance":{"collection":[[0,1],[1,2],[0,2],[0,1,2]],"k":3,"universe_size":4},"samples":[{"config":[1,0,0,0,0,1,0,0,0,0,1,0],"metric":true}],"optimal":[{"config":[0,0,1,0,0,1,0,0,1,0,0,0],"metric":true},{"config":[0,0,1,0,1,0,0,0,0,1,0,0],"metric":true},{"config":[0,1,0,0,0,0,1,0,1,0,0,0],"metric":true},{"config":[0,1,0,0,1,0,0,0,0,0,1,0],"metric":true},{"config":[0,1,1,0,1,0,1,0,1,1,0,0],"metric":true},{"config":[0,1,1,0,1,1,0,0,1,0,1,0],"metric":true},{"config":[1,0,0,0,0,0,1,0,0,1,0,0],"metric":true},{"config":[1,0,0,0,0,1,0,0,0,0,1,0],"metric":true},{"config":[1,0,1,0,0,1,1,0,1,1,0,0],"metric":true},{"config":[1,0,1,0,1,1,0,0,0,1,1,0],"metric":true},{"config":[1,1,0,0,0,1,1,0,1,0,1,0],"metric":true},{"config":[1,1,0,0,1,0,1,0,0,1,1,0],"metric":true}]},
3637
{"problem":"ShortestCommonSupersequence","variant":{},"instance":{"alphabet_size":3,"bound":4,"strings":[[0,1,2],[1,0,2]]},"samples":[{"config":[1,0,1,2],"metric":true}],"optimal":[{"config":[0,1,0,2],"metric":true},{"config":[1,0,1,2],"metric":true}]},
3738
{"problem":"SpinGlass","variant":{"graph":"SimpleGraph","weight":"i32"},"instance":{"couplings":[1,1,1,1,1,1,1],"fields":[0,0,0,0,0],"graph":{"inner":{"edge_property":"undirected","edges":[[0,1,null],[1,2,null],[3,4,null],[0,3,null],[1,3,null],[1,4,null],[2,4,null]],"node_holes":[],"nodes":[null,null,null,null,null]}}},"samples":[{"config":[1,0,1,1,0],"metric":{"Valid":-3}}],"optimal":[{"config":[0,0,1,1,0],"metric":{"Valid":-3}},{"config":[0,1,0,0,1],"metric":{"Valid":-3}},{"config":[0,1,0,1,0],"metric":{"Valid":-3}},{"config":[0,1,1,1,0],"metric":{"Valid":-3}},{"config":[1,0,0,0,1],"metric":{"Valid":-3}},{"config":[1,0,1,0,1],"metric":{"Valid":-3}},{"config":[1,0,1,1,0],"metric":{"Valid":-3}},{"config":[1,1,0,0,1],"metric":{"Valid":-3}}]},

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ pub mod prelude {
5858
};
5959
pub use crate::models::misc::{
6060
BinPacking, Factoring, FlowShopScheduling, Knapsack, LongestCommonSubsequence,
61-
MinimumTardinessSequencing, PaintShop, ShortestCommonSupersequence, SubsetSum,
61+
MinimumTardinessSequencing, PaintShop, SequencingWithinIntervals,
62+
ShortestCommonSupersequence, SubsetSum,
6263
};
6364
pub use crate::models::set::{
6465
ExactCoverBy3Sets, MaximumSetPacking, MinimumSetCovering, SetBasis,

src/models/misc/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//! - [`LongestCommonSubsequence`]: Longest Common Subsequence
99
//! - [`MinimumTardinessSequencing`]: Minimize tardy tasks in single-machine scheduling
1010
//! - [`PaintShop`]: Minimize color switches in paint shop scheduling
11+
//! - [`SequencingWithinIntervals`]: Schedule tasks within time windows
1112
//! - [`ShortestCommonSupersequence`]: Find a common supersequence of bounded length
1213
//! - [`SubsetSum`]: Find a subset summing to exactly a target value
1314
@@ -18,6 +19,7 @@ mod knapsack;
1819
mod longest_common_subsequence;
1920
mod minimum_tardiness_sequencing;
2021
pub(crate) mod paintshop;
22+
mod sequencing_within_intervals;
2123
pub(crate) mod shortest_common_supersequence;
2224
mod subset_sum;
2325

@@ -28,6 +30,7 @@ pub use knapsack::Knapsack;
2830
pub use longest_common_subsequence::LongestCommonSubsequence;
2931
pub use minimum_tardiness_sequencing::MinimumTardinessSequencing;
3032
pub use paintshop::PaintShop;
33+
pub use sequencing_within_intervals::SequencingWithinIntervals;
3134
pub use shortest_common_supersequence::ShortestCommonSupersequence;
3235
pub use subset_sum::SubsetSum;
3336

@@ -36,6 +39,7 @@ pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::M
3639
let mut specs = Vec::new();
3740
specs.extend(factoring::canonical_model_example_specs());
3841
specs.extend(paintshop::canonical_model_example_specs());
42+
specs.extend(sequencing_within_intervals::canonical_model_example_specs());
3943
specs.extend(shortest_common_supersequence::canonical_model_example_specs());
4044
specs.extend(minimum_tardiness_sequencing::canonical_model_example_specs());
4145
specs

0 commit comments

Comments
 (0)