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
52 changes: 52 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"MultipleCopyFileAllocation": [Multiple Copy File Allocation],
"ExpectedRetrievalCost": [Expected Retrieval Cost],
"MultiprocessorScheduling": [Multiprocessor Scheduling],
"ProductionPlanning": [Production Planning],
"PartitionIntoPathsOfLength2": [Partition into Paths of Length 2],
"PartitionIntoTriangles": [Partition Into Triangles],
"PrecedenceConstrainedScheduling": [Precedence Constrained Scheduling],
Expand Down Expand Up @@ -5282,6 +5283,57 @@ A classical NP-complete problem from Garey and Johnson @garey1979[Ch.~3, p.~76],
]
}

#{
let x = load-model-example("ProductionPlanning")
let n = x.instance.num_periods
let demands = x.instance.demands
let capacities = x.instance.capacities
let setup-costs = x.instance.setup_costs
let production-costs = x.instance.production_costs
let inventory-costs = x.instance.inventory_costs
let bound = x.instance.cost_bound
let plan = x.optimal_config
let prefix-production = range(n).map(i => plan.slice(0, i + 1).sum())
let prefix-demand = range(n).map(i => demands.slice(0, i + 1).sum())
let inventory = range(n).map(i => prefix-production.at(i) - prefix-demand.at(i))
let production-total = range(n).map(i => production-costs.at(i) * plan.at(i)).sum()
let inventory-total = range(n).map(i => inventory-costs.at(i) * inventory.at(i)).sum()
let setup-total = range(n).filter(i => plan.at(i) > 0).map(i => setup-costs.at(i)).sum()
[
#problem-def("ProductionPlanning")[
Given a positive integer $n$, period demands $r_1, dots, r_n in ZZ_(>= 0)$, production capacities $c_1, dots, c_n in ZZ_(>= 0)$, setup costs $b_1, dots, b_n in ZZ_(>= 0)$, per-unit production costs $p_1, dots, p_n in ZZ_(>= 0)$, per-unit inventory costs $h_1, dots, h_n in ZZ_(>= 0)$, and a bound $B in ZZ_(>= 0)$, determine whether there exist production quantities $x_1, dots, x_n$ such that $0 <= x_i <= c_i$ for every period $i$, the inventory prefix $I_i = sum_(j=1)^i (x_j - r_j)$ satisfies $I_i >= 0$ for every $i$, and $sum_(i=1)^n (p_i x_i + h_i I_i) + sum_(i: x_i > 0) b_i <= B$.
][
Production Planning is the lot-sizing feasibility problem SS21 in Garey & Johnson @garey1979. Florian, Lenstra, and Rinnooy Kan show that the general problem is NP-complete even under strong restrictions, while also giving pseudo-polynomial dynamic-programming algorithms for capacitated variants @florianLenstraRinnooyKan1980. The implementation in this repository uses one bounded integer variable per period, so the registered exact baseline explores the direct witness space $product_i (c_i + 1)$; under the uniform-capacity bound $C = max_i c_i$, this becomes $O^*((C + 1)^n)$#footnote[This is the search bound induced by the configuration space exposed by the implementation, not a literature-best exact algorithm claim.].

*Example.* Consider the canonical instance with #n periods, demands $(#demands.map(str).join(", "))$, capacities $(#capacities.map(str).join(", "))$, setup costs $(#setup-costs.map(str).join(", "))$, production costs $(#production-costs.map(str).join(", "))$, inventory costs $(#inventory-costs.map(str).join(", "))$, and budget $B = #bound$. The satisfying production plan $x = (#plan.map(str).join(", "))$ yields prefix inventories $(#inventory.map(str).join(", "))$. The verifier therefore accepts, and its cost breakdown is $#production-total + #inventory-total + #setup-total = #(production-total + inventory-total + setup-total) <= #bound$.

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

#figure({
table(
columns: n + 1,
align: center,
inset: 4pt,
table.header([*Period*], ..range(n).map(i => [#(i + 1)])),
[$r_i$], ..range(n).map(i => [#demands.at(i)]),
[$c_i$], ..range(n).map(i => [#capacities.at(i)]),
[$b_i$], ..range(n).map(i => [#setup-costs.at(i)]),
[$p_i$], ..range(n).map(i => [#production-costs.at(i)]),
[$h_i$], ..range(n).map(i => [#inventory-costs.at(i)]),
[$x_i$], ..range(n).map(i => [#plan.at(i)]),
[$I_i$], ..range(n).map(i => [#inventory.at(i)]),
)
},
caption: [Canonical Production Planning instance from the example DB. The documented plan meets every prefix-demand constraint and stays within the budget $B = #bound$.],
) <fig:production-planning>
]
]
}

#{
let x = load-model-example("CapacityAssignment")
[
Expand Down
10 changes: 10 additions & 0 deletions docs/paper/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,16 @@ @book{garey1979
year = {1979}
}

@article{florianLenstraRinnooyKan1980,
author = {M. Florian and J. K. Lenstra and A. H. G. Rinnooy Kan},
title = {Deterministic Production Planning: Algorithms and Complexity},
journal = {Management Science},
volume = {26},
number = {7},
pages = {669--679},
year = {1980}
}

@article{busingstiller2011,
author = {Christina Büsing and Sebastian Stiller},
title = {Line planning, path constrained network flow and inapproximability},
Expand Down
14 changes: 14 additions & 0 deletions problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ Flags by problem type:
Factoring --target, --m, --n
BinPacking --sizes, --capacity
CapacityAssignment --capacities, --cost-matrix, --delay-matrix, --cost-budget, --delay-budget
ProductionPlanning --num-periods, --demands, --capacities, --setup-costs, --production-costs, --inventory-costs, --cost-budget
SubsetSum --sizes, --target
SumOfSquaresPartition --sizes, --num-groups, --bound
ExpectedRetrievalCost --probabilities, --num-sectors, --latency-bound
Expand Down Expand Up @@ -329,6 +330,7 @@ Examples:
pred create SAT --num-vars 3 --clauses \"1,2;-1,3\"
pred create QUBO --matrix \"1,0.5;0.5,2\"
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\" --cost-budget 10 --delay-budget 12
pred create ProductionPlanning --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-budget 80
pred create GeneralizedHex --graph 0-1,0-2,0-3,1-4,2-4,3-4,4-5 --source 0 --sink 5
pred create IntegralFlowWithMultipliers --arcs \"0>1,0>2,1>3,2>3\" --capacities 1,1,2,2 --source 0 --sink 3 --multipliers 1,2,3,1 --requirement 2
pred create MultipleChoiceBranching/i32 --arcs \"0>1,0>2,1>3,2>3,1>4,3>5,4>5,2>4\" --weights 3,2,4,1,2,3,1,3 --partition \"0,1;2,3;4,7;5,6\" --bound 10
Expand Down Expand Up @@ -378,6 +380,18 @@ pub struct CreateArgs {
/// Capacities (edge capacities for flow problems, capacity levels for CapacityAssignment)
#[arg(long)]
pub capacities: Option<String>,
/// Demands for ProductionPlanning (comma-separated, e.g., "5,3,7,2,8,5")
#[arg(long)]
pub demands: Option<String>,
/// Setup costs for ProductionPlanning (comma-separated, e.g., "10,10,10,10,10,10")
#[arg(long)]
pub setup_costs: Option<String>,
/// Per-unit production costs for ProductionPlanning (comma-separated, e.g., "1,1,1,1,1,1")
#[arg(long)]
pub production_costs: Option<String>,
/// Per-unit inventory costs for ProductionPlanning (comma-separated, e.g., "1,1,1,1,1,1")
#[arg(long)]
pub inventory_costs: Option<String>,
/// Bundle capacities for IntegralFlowBundles (e.g., 1,1,1)
#[arg(long)]
pub bundle_capacities: Option<String>,
Expand Down
Loading
Loading