Skip to content

Commit e70b661

Browse files
GiggleLiuisPANNclaude
authored
Fix #503: [Model] SchedulingWithIndividualDeadlines (#678)
* Add plan for #503: [Model] SchedulingWithIndividualDeadlines * Implement #503: [Model] SchedulingWithIndividualDeadlines * chore: remove plan file after implementation * chore: minimize example fixture updates for #503 * chore: tighten scheduling CLI contract for #503 * fix: address review feedback for PR #678 * Fix formatting after merge with main Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Revert unrelated changes: README.md and pred inspect docs Reverts the README.md build instruction change and the pred inspect output format change in cli.md, which are outside the scope of this model PR. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Xiwei Pan <xiwei.pan@connect.hkust-gz.edu.cn> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 418222f commit e70b661

13 files changed

Lines changed: 694 additions & 34 deletions

File tree

docs/paper/reductions.typ

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,10 @@
131131
"PartitionIntoTriangles": [Partition Into Triangles],
132132
"PrimeAttributeName": [Prime Attribute Name],
133133
"FlowShopScheduling": [Flow Shop Scheduling],
134-
"StaffScheduling": [Staff Scheduling],
135134
"MultiprocessorScheduling": [Multiprocessor Scheduling],
136135
"PrecedenceConstrainedScheduling": [Precedence Constrained Scheduling],
136+
"SchedulingWithIndividualDeadlines": [Scheduling With Individual Deadlines],
137+
"StaffScheduling": [Staff Scheduling],
137138
"MinimumTardinessSequencing": [Minimum Tardiness Sequencing],
138139
"SequencingToMinimizeWeightedTardiness": [Sequencing to Minimize Weighted Tardiness],
139140
"SequencingToMinimizeMaximumCumulativeCost": [Sequencing to Minimize Maximum Cumulative Cost],
@@ -3408,6 +3409,82 @@ A classical NP-complete problem from Garey and Johnson @garey1979[Ch.~3, p.~76],
34083409
]
34093410
}
34103411

3412+
#{
3413+
let x = load-model-example("SchedulingWithIndividualDeadlines")
3414+
let ntasks = x.instance.num_tasks
3415+
let nproc = x.instance.num_processors
3416+
let deadlines = x.instance.deadlines
3417+
let precs = x.instance.precedences
3418+
let sample = x.samples.at(0)
3419+
let start = sample.config
3420+
let horizon = deadlines.fold(0, (acc, d) => if d > acc { d } else { acc })
3421+
let slot-groups = range(horizon).map(slot => range(ntasks).filter(t => start.at(t) == slot))
3422+
let tight-tasks = range(ntasks).filter(t => start.at(t) + 1 == deadlines.at(t))
3423+
let start-label = start.map(v => str(v)).join(", ")
3424+
let deadline-pairs = deadlines.enumerate().map(((t, d)) => [$d(t_#(t + 1)) = #d$])
3425+
let slot-summaries = slot-groups.enumerate().map(((slot, tasks)) => [slot #slot: #tasks.map(task => $t_#(task + 1)$).join(", ")])
3426+
let tight-task-labels = tight-tasks.map(task => $t_#(task + 1)$)
3427+
[
3428+
#problem-def("SchedulingWithIndividualDeadlines")[
3429+
Given a set $T$ of $n$ unit-length tasks, a number $m in ZZ^+$ of identical processors, a deadline function $d: T -> ZZ^+$, and a partial order $prec.eq$ on $T$, determine whether there exists a schedule $sigma: T -> {0, 1, dots, D - 1}$, where $D = max_(t in T) d(t)$, such that every task meets its own deadline ($sigma(t) + 1 <= d(t)$), every precedence constraint is respected (if $t_i prec.eq t_j$ then $sigma(t_i) + 1 <= sigma(t_j)$), and at most $m$ tasks are scheduled in each time slot.
3430+
][
3431+
Scheduling With Individual Deadlines is the parallel-machine feasibility problem catalogued as A5 SS11 in Garey & Johnson @garey1979. Garey & Johnson record NP-completeness via reduction from Vertex Cover, and Brucker, Garey, and Johnson sharpen the complexity picture: the problem remains NP-complete for out-tree precedence constraints, but becomes polynomial-time solvable for in-trees @bruckerGareyJohnson1977. The two-processor case is also polynomial-time solvable @garey1979.
3432+
3433+
The direct encoding in this library uses one start-time variable per task, with each variable ranging over its allowable deadline window. If $D = max_t d(t)$, exhaustive search over that encoding yields an $O^*(D^n)$ brute-force bound.#footnote[This is the worst-case search bound induced by the implementation's configuration space; deadlines can be smaller on individual tasks, so practical instances may enumerate fewer than $D^n$ assignments.]
3434+
3435+
*Example.* Consider $n = #ntasks$ tasks on $m = #nproc$ processors with deadlines #{deadline-pairs.join(", ")} and precedence constraints #{precs.map(p => [$t_#(p.at(0) + 1) prec.eq t_#(p.at(1) + 1)$]).join(", ")}. The sample schedule $sigma = [#start-label]$ assigns #{slot-summaries.join("; ")}. Every slot uses at most #nproc processors, and the tight tasks #{tight-task-labels.join(", ")} finish exactly at their deadlines.
3436+
3437+
#figure(
3438+
canvas(length: 1cm, {
3439+
import draw: *
3440+
let colors = (
3441+
rgb("#4e79a7"),
3442+
rgb("#e15759"),
3443+
rgb("#76b7b2"),
3444+
rgb("#f28e2b"),
3445+
rgb("#59a14f"),
3446+
rgb("#edc948"),
3447+
rgb("#b07aa1"),
3448+
)
3449+
let scale = 1.25
3450+
let row-h = 0.58
3451+
let gap = 0.18
3452+
3453+
for lane in range(nproc) {
3454+
let y = -lane * (row-h + gap)
3455+
content((-0.8, y), text(7pt, "P" + str(lane + 1)))
3456+
}
3457+
3458+
for (slot, tasks) in slot-groups.enumerate() {
3459+
for (lane, task) in tasks.enumerate() {
3460+
let x0 = slot * scale
3461+
let x1 = (slot + 1) * scale
3462+
let y = -lane * (row-h + gap)
3463+
let color = colors.at(calc.rem(task, colors.len()))
3464+
rect(
3465+
(x0, y - row-h / 2),
3466+
(x1, y + row-h / 2),
3467+
fill: color.transparentize(30%),
3468+
stroke: 0.4pt + color,
3469+
)
3470+
content(((x0 + x1) / 2, y), text(7pt)[$t_#(task + 1)$])
3471+
}
3472+
}
3473+
3474+
let y-axis = -(nproc - 1) * (row-h + gap) - row-h / 2 - 0.2
3475+
line((0, y-axis), (horizon * scale, y-axis), stroke: 0.4pt)
3476+
for t in range(horizon + 1) {
3477+
let x = t * scale
3478+
line((x, y-axis), (x, y-axis - 0.1), stroke: 0.4pt)
3479+
content((x, y-axis - 0.24), text(6pt, str(t)))
3480+
}
3481+
content((horizon * scale / 2, y-axis - 0.46), text(7pt)[time slot])
3482+
}),
3483+
caption: [A feasible 3-processor schedule for Scheduling With Individual Deadlines. Tasks sharing a column run in the same unit-length time slot; the sample assignment uses slots $0, 1, 2$ and meets every deadline.],
3484+
) <fig:scheduling-with-individual-deadlines>
3485+
]
3486+
]
3487+
}
34113488
#{
34123489
let x = load-model-example("SequencingWithinIntervals")
34133490
let ntasks = x.instance.lengths.len()

docs/paper/references.bib

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,16 @@ @book{garey1979
137137
year = {1979}
138138
}
139139

140+
@article{bruckerGareyJohnson1977,
141+
author = {Peter Brucker and Michael R. Garey and David S. Johnson},
142+
title = {Scheduling equal-length tasks under tree-like precedence constraints to minimize maximum lateness},
143+
journal = {Mathematics of Operations Research},
144+
volume = {2},
145+
number = {3},
146+
pages = {275--284},
147+
year = {1977}
148+
}
149+
140150
@article{eswarantarjan1976,
141151
author = {K. P. Eswaran and Robert E. Tarjan},
142152
title = {Augmentation Problems},

docs/src/cli.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,13 +357,18 @@ pred create Factoring --target 21 --bits-m 3 --bits-n 3 -o factoring2.json
357357
pred create X3C --universe 9 --sets "0,1,2;0,2,4;3,4,5;3,5,7;6,7,8;1,4,6;2,5,8" -o x3c.json
358358
pred create MinimumCardinalityKey --num-attributes 6 --dependencies "0,1>2;0,2>3;1,3>4;2,4>5" --k 2 -o mck.json
359359
pred create MinimumTardinessSequencing --n 5 --deadlines 5,5,5,3,3 --precedence-pairs "0>3,1>3,1>4,2>4" -o mts.json
360+
pred create SchedulingWithIndividualDeadlines --n 7 --deadlines 2,1,2,2,3,3,2 --num-processors 3 --precedence-pairs "0>3,1>3,1>4,2>4,2>5" -o swid.json
361+
pred solve swid.json --solver brute-force
360362
pred create StringToStringCorrection --source-string "0,1,2,3,1,0" --target-string "0,1,3,2,1" --bound 2 | pred solve - --solver brute-force
361363
pred create StrongConnectivityAugmentation --arcs "0>1,1>2,2>0,3>4,4>3,2>3,4>5,5>3" --candidate-arcs "3>0:5,3>1:3,3>2:4,4>0:6,4>1:2,4>2:7,5>0:4,5>1:3,5>2:1,0>3:8,0>4:3,0>5:2,1>3:6,1>4:4,1>5:5,2>4:3,2>5:7,1>0:2" --bound 1 -o sca.json
362364
```
363365
364366
For `LengthBoundedDisjointPaths`, the CLI flag `--bound` maps to the JSON field
365367
`max_length`.
366368
369+
For problem-specific create help, run `pred create <PROBLEM>` with no additional flags.
370+
The generic `pred create --help` output lists all flags across all problem types.
371+
367372
Canonical examples are useful when you want a known-good instance from the paper/example database.
368373
For model examples, `pred create --example <PROBLEM_SPEC>` emits the canonical instance for that
369374
graph node.

problemreductions-cli/src/cli.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,10 @@ Flags by problem type:
271271
FlowShopScheduling --task-lengths, --deadline [--num-processors]
272272
StaffScheduling --schedules, --requirements, --num-workers, --k
273273
MinimumTardinessSequencing --n, --deadlines [--precedence-pairs]
274+
RectilinearPictureCompression --matrix (0/1), --k
275+
SchedulingWithIndividualDeadlines --n, --num-processors/--m, --deadlines [--precedence-pairs]
274276
SequencingToMinimizeMaximumCumulativeCost --costs, --bound [--precedence-pairs]
275277
SequencingToMinimizeWeightedTardiness --sizes, --weights, --deadlines, --bound
276-
RectilinearPictureCompression --matrix (0/1), --k
277278
SCS --strings, --bound [--alphabet-size]
278279
StringToStringCorrection --source-string, --target-string, --bound [--alphabet-size]
279280
D2CIF --arcs, --capacities, --source-1, --sink-1, --source-2, --sink-2, --requirement-1, --requirement-2
@@ -375,7 +376,7 @@ pub struct CreateArgs {
375376
/// Target value (for Factoring and SubsetSum)
376377
#[arg(long)]
377378
pub target: Option<String>,
378-
/// Bits for first factor (for Factoring)
379+
/// Bits for first factor (for Factoring); also accepted as a processor-count alias for scheduling create commands
379380
#[arg(long)]
380381
pub m: Option<usize>,
381382
/// Bits for second factor (for Factoring)
@@ -516,10 +517,10 @@ pub struct CreateArgs {
516517
/// Storage costs for MultipleCopyFileAllocation (comma-separated, e.g., "1,1,1,1")
517518
#[arg(long)]
518519
pub storage: Option<String>,
519-
/// Deadlines for scheduling problems (comma-separated, e.g., "5,5,5,3,3")
520+
/// Deadlines for MinimumTardinessSequencing or SchedulingWithIndividualDeadlines (comma-separated, e.g., "5,5,5,3,3")
520521
#[arg(long)]
521522
pub deadlines: Option<String>,
522-
/// Precedence pairs for MinimumTardinessSequencing (e.g., "0>3,1>3,1>4,2>4")
523+
/// Precedence pairs for MinimumTardinessSequencing or SchedulingWithIndividualDeadlines (e.g., "0>3,1>3,1>4,2>4")
523524
#[arg(long)]
524525
pub precedence_pairs: Option<String>,
525526
/// Resource bounds for ResourceConstrainedScheduling (comma-separated, e.g., "20,15")
@@ -534,7 +535,7 @@ pub struct CreateArgs {
534535
/// Deadline for FlowShopScheduling, MultiprocessorScheduling, or ResourceConstrainedScheduling
535536
#[arg(long)]
536537
pub deadline: Option<u64>,
537-
/// Number of processors/machines for FlowShopScheduling, MultiprocessorScheduling, or ResourceConstrainedScheduling
538+
/// Number of processors/machines for FlowShopScheduling, MultiprocessorScheduling, ResourceConstrainedScheduling, or SchedulingWithIndividualDeadlines
538539
#[arg(long)]
539540
pub num_processors: Option<usize>,
540541
/// Binary schedule patterns for StaffScheduling (semicolon-separated rows, e.g., "1,1,0;0,1,1")
@@ -708,7 +709,38 @@ pub fn print_subcommand_help_hint(error_msg: &str) {
708709

709710
#[cfg(test)]
710711
mod tests {
712+
use super::Cli;
711713
use super::*;
714+
use clap::CommandFactory;
715+
716+
#[test]
717+
fn test_create_help_mentions_scheduling_with_individual_deadlines_shared_flags() {
718+
let mut cmd = Cli::command();
719+
let create = cmd
720+
.find_subcommand_mut("create")
721+
.expect("create subcommand");
722+
let mut help = Vec::new();
723+
create
724+
.write_long_help(&mut help)
725+
.expect("render create help");
726+
let help = String::from_utf8(help).expect("utf8 help");
727+
728+
assert!(help.contains(
729+
"Deadlines for MinimumTardinessSequencing or SchedulingWithIndividualDeadlines"
730+
));
731+
assert!(help.contains(
732+
"Precedence pairs for MinimumTardinessSequencing or SchedulingWithIndividualDeadlines"
733+
));
734+
assert!(
735+
help.contains(
736+
"Number of processors/machines for FlowShopScheduling, MultiprocessorScheduling, ResourceConstrainedScheduling, or SchedulingWithIndividualDeadlines"
737+
),
738+
"create help should describe --num-processors for both scheduling models"
739+
);
740+
assert!(help.contains(
741+
"SchedulingWithIndividualDeadlines --n, --num-processors/--m, --deadlines [--precedence-pairs]"
742+
));
743+
}
712744

713745
#[test]
714746
fn test_create_parses_biconnectivity_augmentation_flags() {

0 commit comments

Comments
 (0)