Skip to content
Merged
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
99 changes: 99 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
"RectilinearPictureCompression": [Rectilinear Picture Compression],
"ResourceConstrainedScheduling": [Resource Constrained Scheduling],
"RootedTreeStorageAssignment": [Rooted Tree Storage Assignment],
"SchedulingToMinimizeWeightedCompletionTime": [Scheduling to Minimize Weighted Completion Time],
"SchedulingWithIndividualDeadlines": [Scheduling With Individual Deadlines],
"SequencingToMinimizeMaximumCumulativeCost": [Sequencing to Minimize Maximum Cumulative Cost],
"SequencingToMinimizeWeightedCompletionTime": [Sequencing to Minimize Weighted Completion Time],
Expand Down Expand Up @@ -5743,6 +5744,104 @@ A classical NP-complete problem from Garey and Johnson @garey1979[Ch.~3, p.~76],
]
]
}
#{
let x = load-model-example("SchedulingToMinimizeWeightedCompletionTime")
let ntasks = x.instance.lengths.len()
let m = x.instance.num_processors
let lengths = x.instance.lengths
let weights = x.instance.weights
let sigma = x.optimal_config
// Group tasks by processor
let tasks-by-proc = range(m).map(p =>
range(ntasks).filter(i => sigma.at(i) == p)
)
[
#problem-def("SchedulingToMinimizeWeightedCompletionTime")[
Given a finite set $T$ of tasks with processing lengths $ell: T -> ZZ^+$ and weights $w: T -> ZZ^+$, and a number $m in ZZ^+$ of identical processors, find an assignment $p: T -> {1, dots, m}$ that minimizes the total weighted completion time $sum_(t in T) w(t) dot C(t)$, where on each processor tasks are ordered by Smith's rule (non-decreasing $ell(t) "/" w(t)$ ratio) and $C(t)$ is the completion time of task $t$ (i.e., the cumulative processing time up to and including $t$ on its assigned processor).
][
Scheduling to Minimize Weighted Completion Time is problem A5 SS13 in Garey & Johnson @garey1979. NP-complete for $m = 2$ by reduction from Partition @lenstra1977, and NP-complete in the strong sense for arbitrary $m$. For a fixed assignment of tasks to processors, Smith's rule gives the optimal ordering on each processor, reducing the search space to $m^n$ processor assignments @smith1956. The problem is solvable in polynomial time when all lengths are equal or when all weights are equal @conway1967 @horn1973.

*Example.* Let $T = {t_1, dots, t_#ntasks}$ with lengths $(#lengths.map(str).join(", "))$, weights $(#weights.map(str).join(", "))$, and $m = #m$ processors. The optimal assignment $(#sigma.map(v => str(v + 1)).join(", "))$ achieves total weighted completion time #x.optimal_value:
#for p in range(m) [
- Processor #(p + 1): ${#tasks-by-proc.at(p).map(i => $t_#(i + 1)$).join(", ")}$#if tasks-by-proc.at(p).len() > 0 {
let proc-tasks = tasks-by-proc.at(p)
let elapsed = 0
let contributions = ()
for t in proc-tasks {
elapsed = elapsed + lengths.at(t)
contributions.push($#elapsed times #(weights.at(t)) = #(elapsed * weights.at(t))$)
}
[ -- contributions: #contributions.join(", ")]
}
]

#pred-commands(
"pred create --example " + problem-spec(x) + " -o scheduling-wct.json",
"pred solve scheduling-wct.json --solver brute-force",
"pred evaluate scheduling-wct.json --config " + x.optimal_config.map(str).join(","),
)

#figure({
canvas(length: 1cm, {
import draw: *
let scale = 0.2
let width = 1.2
let gap = 0.8
let colors = (
rgb("#4e79a7"),
rgb("#e15759"),
rgb("#76b7b2"),
rgb("#f28e2b"),
rgb("#59a14f"),
)

for p in range(m) {
let x0 = p * (width + gap)
let max-time = tasks-by-proc.at(p).fold(0, (acc, t) => acc + lengths.at(t))
rect((x0, 0), (x0 + width, max-time * scale), stroke: 0.8pt + black)
let y = 0
for task in tasks-by-proc.at(p) {
let len = lengths.at(task)
let col = colors.at(task)
rect(
(x0, y),
(x0 + width, y + len * scale),
fill: col.transparentize(25%),
stroke: 0.4pt + col,
)
content(
(x0 + width / 2, y + len * scale / 2),
text(7pt, fill: white)[$t_#(task + 1)$],
)
y += len * scale
}
content((x0 + width / 2, -0.3), text(8pt)[$P_#(p + 1)$])
}
})
},
caption: [Canonical Scheduling to Minimize Weighted Completion Time instance with #ntasks tasks on #m processors. Tasks are ordered on each processor by Smith's rule.],
) <fig:scheduling-wct>
]
]
}

// Reduction: SchedulingToMinimizeWeightedCompletionTime -> ILP
#reduction-rule("SchedulingToMinimizeWeightedCompletionTime", "ILP",
example: false,
)[
This $O(n^2 m)$ reduction constructs an ILP with binary assignment variables $x_(t,p)$, integer completion-time variables $C_t$, and binary ordering variables $y_(i,j)$ for task pairs. Big-M disjunctive constraints enforce non-overlapping execution on shared processors.
][
_Construction._ Let $n = |T|$ and $m$ be the number of processors. Create $n m$ binary assignment variables $x_(t,p) in {0, 1}$ (task $t$ on processor $p$), $n$ integer completion-time variables $C_t$, and $n(n-1)/2$ binary ordering variables $y_(i,j)$ for $i < j$. The constraints are:
(1) Assignment: $sum_p x_(t,p) = 1$ for each $t$.
(2) Completion bounds: $C_t >= ell(t)$ for each $t$.
(3) Disjunctive: for each pair $(i,j)$ with $i < j$ and each processor $p$, big-M constraints ensure that if both tasks are on processor $p$, one must complete before the other starts.
The objective minimizes $sum_t w(t) dot C_t$.

_Correctness._ ($arrow.r.double$) Any valid schedule gives a feasible ILP solution with the same objective. ($arrow.l.double$) Any ILP solution encodes a valid assignment and non-overlapping schedule.

_Solution extraction._ For each task $t$, find the processor $p$ with $x_(t,p) = 1$.
]

#{
let x = load-model-example("SequencingWithinIntervals")
let ntasks = x.instance.lengths.len()
Expand Down
27 changes: 27 additions & 0 deletions docs/paper/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,33 @@ @article{edmondsjohnson1973
year = {1973}
}

@article{lenstra1977,
author = {J. K. Lenstra and A. H. G. Rinnooy Kan and P. Brucker},
title = {Complexity of Machine Scheduling Problems},
journal = {Annals of Discrete Mathematics},
volume = {1},
pages = {343--362},
year = {1977},
doi = {10.1016/S0167-5060(08)70743-X}
}

@book{conway1967,
author = {Richard W. Conway and William L. Maxwell and Louis W. Miller},
title = {Theory of Scheduling},
publisher = {Addison-Wesley},
year = {1967}
}

@article{horn1973,
author = {W. A. Horn},
title = {Minimizing Average Flow Time with Parallel Machines},
journal = {Operations Research},
volume = {21},
number = {3},
pages = {846--847},
year = {1973}
}

@techreport{plaisted1976,
author = {David A. Plaisted},
title = {Some Polynomial and Integer Divisibility Problems Are {NP}-Hard},
Expand Down
6 changes: 4 additions & 2 deletions problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ Flags by problem type:
AcyclicPartition --arcs [--weights] [--arc-costs] --weight-bound --cost-bound [--num-vertices]
CVP --basis, --target-vec [--bounds]
MultiprocessorScheduling --lengths, --num-processors, --deadline
SchedulingToMinimizeWeightedCompletionTime --lengths, --weights, --num-processors
SequencingWithinIntervals --release-times, --deadlines, --lengths
OptimalLinearArrangement --graph
RootedTreeArrangement --graph, --bound
Expand Down Expand Up @@ -345,6 +346,7 @@ Examples:
pred create MIS/UnitDiskGraph --positions \"0,0;1,0;0.5,0.8\" --radius 1.5
pred create MIS --random --num-vertices 10 --edge-prob 0.3
pred create MultiprocessorScheduling --lengths 4,5,3,2,6 --num-processors 2 --deadline 10
pred create SchedulingToMinimizeWeightedCompletionTime --lengths 1,2,3,4,5 --weights 6,4,3,2,1 --num-processors 2
pred create UndirectedFlowLowerBounds --graph 0-1,0-2,1-3,2-3,1-4,3-5,4-5 --capacities 2,2,2,2,1,3,2 --lower-bounds 1,1,0,0,1,0,1 --source 0 --sink 5 --requirement 3
pred create ConsistencyOfDatabaseFrequencyTables --num-objects 6 --attribute-domains \"2,3,2\" --frequency-tables \"0,1:1,1,1|1,1,1;1,2:1,1|0,2|1,1\" --known-values \"0,0,0;3,0,1;1,2,1\"
pred create BiconnectivityAugmentation --graph 0-1,1-2,2-3 --potential-edges 0-2:3,0-3:4,1-3:2 --budget 5
Expand Down Expand Up @@ -668,7 +670,7 @@ pub struct CreateArgs {
/// Deadline for FlowShopScheduling, MultiprocessorScheduling, or ResourceConstrainedScheduling
#[arg(long)]
pub deadline: Option<u64>,
/// Number of processors/machines for FlowShopScheduling, JobShopScheduling, MultiprocessorScheduling, ResourceConstrainedScheduling, or SchedulingWithIndividualDeadlines
/// Number of processors/machines for FlowShopScheduling, JobShopScheduling, MultiprocessorScheduling, ResourceConstrainedScheduling, SchedulingToMinimizeWeightedCompletionTime, or SchedulingWithIndividualDeadlines
#[arg(long)]
pub num_processors: Option<usize>,
/// Binary schedule patterns for StaffScheduling (semicolon-separated rows, e.g., "1,1,0;0,1,1")
Expand Down Expand Up @@ -919,7 +921,7 @@ mod tests {
));
assert!(
help.contains(
"Number of processors/machines for FlowShopScheduling, JobShopScheduling, MultiprocessorScheduling, ResourceConstrainedScheduling, or SchedulingWithIndividualDeadlines"
"Number of processors/machines for FlowShopScheduling, JobShopScheduling, MultiprocessorScheduling, ResourceConstrainedScheduling, SchedulingToMinimizeWeightedCompletionTime, or SchedulingWithIndividualDeadlines"
),
"create help should describe --num-processors for both scheduling models"
);
Expand Down
43 changes: 39 additions & 4 deletions problemreductions-cli/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ use problemreductions::models::misc::{
JobShopScheduling, KnownValue, KthLargestMTuple, LongestCommonSubsequence,
MinimumTardinessSequencing, MultiprocessorScheduling, PaintShop, PartiallyOrderedKnapsack,
ProductionPlanning, QueryArg, RectilinearPictureCompression, ResourceConstrainedScheduling,
SchedulingWithIndividualDeadlines, SequencingToMinimizeMaximumCumulativeCost,
SequencingToMinimizeWeightedCompletionTime, SequencingToMinimizeWeightedTardiness,
SequencingWithReleaseTimesAndDeadlines, SequencingWithinIntervals, ShortestCommonSupersequence,
StringToStringCorrection, SubsetSum, SumOfSquaresPartition, ThreePartition, TimetableDesign,
SchedulingToMinimizeWeightedCompletionTime, SchedulingWithIndividualDeadlines,
SequencingToMinimizeMaximumCumulativeCost, SequencingToMinimizeWeightedCompletionTime,
SequencingToMinimizeWeightedTardiness, SequencingWithReleaseTimesAndDeadlines,
SequencingWithinIntervals, ShortestCommonSupersequence, StringToStringCorrection, SubsetSum,
SumOfSquaresPartition, ThreePartition, TimetableDesign,
};
use problemreductions::models::BiconnectivityAugmentation;
use problemreductions::prelude::*;
Expand Down Expand Up @@ -677,6 +678,9 @@ fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str {
"--num-periods 6 --demands 5,3,7,2,8,5 --capacities 12,12,12,12,12,12 --setup-costs 10,10,10,10,10,10 --production-costs 1,1,1,1,1,1 --inventory-costs 1,1,1,1,1,1 --cost-bound 80"
}
"MultiprocessorScheduling" => "--lengths 4,5,3,2,6 --num-processors 2 --deadline 10",
"SchedulingToMinimizeWeightedCompletionTime" => {
"--lengths 1,2,3,4,5 --weights 6,4,3,2,1 --num-processors 2"
}
"JobShopScheduling" => {
"--job-tasks \"0:3,1:4;1:2,0:3,1:2;0:4,1:3;1:5,0:2;0:2,1:3,0:1\" --num-processors 2"
}
Expand Down Expand Up @@ -3283,6 +3287,37 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
)
}

// SchedulingToMinimizeWeightedCompletionTime
"SchedulingToMinimizeWeightedCompletionTime" => {
let usage = "Usage: pred create SchedulingToMinimizeWeightedCompletionTime --lengths 1,2,3,4,5 --weights 6,4,3,2,1 --num-processors 2";
let lengths_str = args.lengths.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"SchedulingToMinimizeWeightedCompletionTime requires --lengths, --weights, and --num-processors\n\n{usage}"
)
})?;
let weights_str = args.weights.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"SchedulingToMinimizeWeightedCompletionTime requires --weights\n\n{usage}"
)
})?;
let num_processors = args.num_processors.ok_or_else(|| {
anyhow::anyhow!("SchedulingToMinimizeWeightedCompletionTime requires --num-processors\n\n{usage}")
})?;
if num_processors == 0 {
bail!("SchedulingToMinimizeWeightedCompletionTime requires --num-processors > 0\n\n{usage}");
}
let lengths: Vec<u64> = util::parse_comma_list(lengths_str)?;
let weights: Vec<u64> = util::parse_comma_list(weights_str)?;
(
ser(SchedulingToMinimizeWeightedCompletionTime::new(
lengths,
weights,
num_processors,
))?,
resolved_variant.clone(),
)
}

"CapacityAssignment" => {
let usage = "Usage: pred create CapacityAssignment --capacities 1,2,3 --cost-matrix \"1,3,6;2,4,7;1,2,5\" --delay-matrix \"8,4,1;7,3,1;6,3,1\" --delay-budget 12";
let capacities_str = args.capacities.as_deref().ok_or_else(|| {
Expand Down
3 changes: 3 additions & 0 deletions src/models/misc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ mod precedence_constrained_scheduling;
mod production_planning;
mod rectilinear_picture_compression;
pub(crate) mod resource_constrained_scheduling;
mod scheduling_to_minimize_weighted_completion_time;
mod scheduling_with_individual_deadlines;
mod sequencing_to_minimize_maximum_cumulative_cost;
mod sequencing_to_minimize_weighted_completion_time;
Expand Down Expand Up @@ -131,6 +132,7 @@ pub use precedence_constrained_scheduling::PrecedenceConstrainedScheduling;
pub use production_planning::ProductionPlanning;
pub use rectilinear_picture_compression::RectilinearPictureCompression;
pub use resource_constrained_scheduling::ResourceConstrainedScheduling;
pub use scheduling_to_minimize_weighted_completion_time::SchedulingToMinimizeWeightedCompletionTime;
pub use scheduling_with_individual_deadlines::SchedulingWithIndividualDeadlines;
pub use sequencing_to_minimize_maximum_cumulative_cost::SequencingToMinimizeMaximumCumulativeCost;
pub use sequencing_to_minimize_weighted_completion_time::SequencingToMinimizeWeightedCompletionTime;
Expand Down Expand Up @@ -164,6 +166,7 @@ pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::M
specs.extend(partition::canonical_model_example_specs());
specs.extend(production_planning::canonical_model_example_specs());
specs.extend(rectilinear_picture_compression::canonical_model_example_specs());
specs.extend(scheduling_to_minimize_weighted_completion_time::canonical_model_example_specs());
specs.extend(scheduling_with_individual_deadlines::canonical_model_example_specs());
specs.extend(sequencing_within_intervals::canonical_model_example_specs());
specs.extend(staff_scheduling::canonical_model_example_specs());
Expand Down
Loading
Loading