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
9 changes: 9 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"QUBO": [QUBO],
"ILP": [Integer Linear Programming],
"Knapsack": [Knapsack],
"PartiallyOrderedKnapsack": [Partially Ordered Knapsack],
"Satisfiability": [SAT],
"KSatisfiability": [$k$-SAT],
"CircuitSAT": [CircuitSAT],
Expand Down Expand Up @@ -1164,6 +1165,14 @@ Biclique Cover is equivalent to factoring the biadjacency matrix $M$ of the bipa
*Example.* Let $n = 4$ items with weights $(2, 3, 4, 5)$, values $(3, 4, 5, 7)$, and capacity $C = 7$. Selecting $S = {1, 2}$ (items with weights 3 and 4) gives total weight $3 + 4 = 7 lt.eq C$ and total value $4 + 5 = 9$. Selecting $S = {0, 3}$ (weights 2 and 5) gives weight $2 + 5 = 7 lt.eq C$ and value $3 + 7 = 10$, which is optimal.
]

#problem-def("PartiallyOrderedKnapsack")[
Given a finite set $U$ with $|U| = n$ items, a partial order $<$ on $U$ (given by its cover relations), for each $u in U$ a size $s(u) in ZZ^+$ and a value $v(u) in ZZ^+$, and a capacity $B in ZZ^+$, find a downward-closed subset $U' subset.eq U$ (i.e., if $u in U'$ and $u' < u$ then $u' in U'$) maximizing $sum_(u in U') v(u)$ subject to $sum_(u in U') s(u) lt.eq B$.
][
Garey and Johnson's problem A6 MP12 @garey1979. Unlike standard Knapsack, the partial order constraint makes the problem _strongly_ NP-complete --- it remains NP-complete even when $s(u) = v(u)$ for all $u$, so no pseudo-polynomial algorithm exists unless $P = N P$. The problem arises in manufacturing scheduling, project selection, and mining operations. For tree partial orders, Johnson and Niemi @johnson1983 gave pseudo-polynomial $O(n dot B)$ tree DP and an FPTAS. Kolliopoulos and Steiner @kolliopoulos2007 extended the FPTAS to 2-dimensional partial orders with $O(n^4 slash epsilon)$ running time.

*Example.* Consider $n = 6$ items with partial order given by cover relations $a < c$, $a < d$, $b < e$, $d < f$, $e < f$. Sizes $(2, 3, 4, 1, 2, 3)$, values $(3, 2, 5, 4, 3, 8)$, and capacity $B = 11$. Selecting $U' = {a, b, d, e, f}$ is downward-closed (all predecessors included), has total size $2 + 3 + 1 + 2 + 3 = 11 lt.eq B$, and total value $3 + 2 + 4 + 3 + 8 = 20$. Adding $c$ would exceed capacity ($15 > 11$).
]

#problem-def("RuralPostman")[
Given an undirected graph $G = (V, E)$ with edge lengths $l: E -> ZZ_(gt.eq 0)$, a subset $E' subset.eq E$ of required edges, and a bound $B in ZZ^+$, determine whether there exists a circuit (closed walk) in $G$ that traverses every edge in $E'$ and has total length at most $B$.
][
Expand Down
22 changes: 22 additions & 0 deletions docs/paper/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,28 @@ @article{cygan2014
doi = {10.1137/140990255}
}

@article{johnson1983,
author = {David S. Johnson and Kenneth A. Niemi},
title = {On Knapsacks, Partitions, and a New Dynamic Programming Technique for Trees},
journal = {Mathematics of Operations Research},
volume = {8},
number = {1},
pages = {1--14},
year = {1983},
doi = {10.1287/moor.8.1.1}
}

@article{kolliopoulos2007,
author = {Stavros G. Kolliopoulos and George Steiner},
title = {Partially Ordered Knapsack and Applications to Scheduling},
journal = {Discrete Applied Mathematics},
volume = {155},
number = {8},
pages = {889--897},
year = {2007},
doi = {10.1016/j.dam.2006.09.003}
}

@article{raiha1981,
author = {Kari-Jouko R{\"a}ih{\"a} and Esko Ukkonen},
title = {The Shortest Common Supersequence Problem over Binary Alphabet is {NP}-Complete},
Expand Down
26 changes: 26 additions & 0 deletions docs/src/reductions/problem_schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,32 @@
}
]
},
{
"name": "PartiallyOrderedKnapsack",
"description": "Select items to maximize total value subject to precedence constraints and weight capacity",
"fields": [
{
"name": "sizes",
"type_name": "Vec<i64>",
"description": "Item sizes s(u) for each item"
},
{
"name": "values",
"type_name": "Vec<i64>",
"description": "Item values v(u) for each item"
},
{
"name": "precedences",
"type_name": "Vec<(usize, usize)>",
"description": "Precedence pairs (a, b) meaning a must be included before b"
},
{
"name": "capacity",
"type_name": "i64",
"description": "Knapsack capacity B"
}
]
},
{
"name": "PartitionIntoTriangles",
"description": "Partition vertices into triangles (K3 subgraphs)",
Expand Down
59 changes: 33 additions & 26 deletions docs/src/reductions/reduction_graph.json
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@
"doc_path": "models/misc/struct.PaintShop.html",
"complexity": "2^num_cars"
},
{
"name": "PartiallyOrderedKnapsack",
"variant": {},
"category": "misc",
"doc_path": "models/misc/struct.PartiallyOrderedKnapsack.html",
"complexity": "2^num_items"
},
{
"name": "PartitionIntoTriangles",
"variant": {
Expand Down Expand Up @@ -535,7 +542,7 @@
},
{
"source": 4,
"target": 52,
"target": 53,
"overhead": [
{
"field": "num_spins",
Expand Down Expand Up @@ -595,7 +602,7 @@
},
{
"source": 11,
"target": 47,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand Down Expand Up @@ -636,7 +643,7 @@
},
{
"source": 18,
"target": 47,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand All @@ -662,7 +669,7 @@
},
{
"source": 19,
"target": 47,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand All @@ -688,7 +695,7 @@
},
{
"source": 20,
"target": 47,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand All @@ -699,7 +706,7 @@
},
{
"source": 20,
"target": 54,
"target": 55,
"overhead": [
{
"field": "num_elements",
Expand All @@ -710,7 +717,7 @@
},
{
"source": 21,
"target": 49,
"target": 50,
"overhead": [
{
"field": "num_clauses",
Expand All @@ -729,7 +736,7 @@
},
{
"source": 22,
"target": 47,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand All @@ -755,7 +762,7 @@
},
{
"source": 24,
"target": 52,
"target": 53,
"overhead": [
{
"field": "num_spins",
Expand Down Expand Up @@ -1070,7 +1077,7 @@
},
{
"source": 36,
"target": 47,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand Down Expand Up @@ -1185,7 +1192,7 @@
"doc_path": "rules/minimumvertexcover_minimumsetcovering/index.html"
},
{
"source": 47,
"source": 48,
"target": 11,
"overhead": [
{
Expand All @@ -1200,8 +1207,8 @@
"doc_path": "rules/qubo_ilp/index.html"
},
{
"source": 47,
"target": 51,
"source": 48,
"target": 52,
"overhead": [
{
"field": "num_spins",
Expand All @@ -1211,7 +1218,7 @@
"doc_path": "rules/spinglass_qubo/index.html"
},
{
"source": 49,
"source": 50,
"target": 4,
"overhead": [
{
Expand All @@ -1226,7 +1233,7 @@
"doc_path": "rules/sat_circuitsat/index.html"
},
{
"source": 49,
"source": 50,
"target": 15,
"overhead": [
{
Expand All @@ -1241,7 +1248,7 @@
"doc_path": "rules/sat_coloring/index.html"
},
{
"source": 49,
"source": 50,
"target": 20,
"overhead": [
{
Expand All @@ -1256,7 +1263,7 @@
"doc_path": "rules/sat_ksat/index.html"
},
{
"source": 49,
"source": 50,
"target": 29,
"overhead": [
{
Expand All @@ -1271,7 +1278,7 @@
"doc_path": "rules/sat_maximumindependentset/index.html"
},
{
"source": 49,
"source": 50,
"target": 38,
"overhead": [
{
Expand All @@ -1286,8 +1293,8 @@
"doc_path": "rules/sat_minimumdominatingset/index.html"
},
{
"source": 51,
"target": 47,
"source": 52,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand All @@ -1297,7 +1304,7 @@
"doc_path": "rules/spinglass_qubo/index.html"
},
{
"source": 52,
"source": 53,
"target": 24,
"overhead": [
{
Expand All @@ -1312,8 +1319,8 @@
"doc_path": "rules/spinglass_maxcut/index.html"
},
{
"source": 52,
"target": 51,
"source": 53,
"target": 52,
"overhead": [
{
"field": "num_spins",
Expand All @@ -1327,7 +1334,7 @@
"doc_path": "rules/spinglass_casts/index.html"
},
{
"source": 55,
"source": 56,
"target": 11,
"overhead": [
{
Expand All @@ -1342,8 +1349,8 @@
"doc_path": "rules/travelingsalesman_ilp/index.html"
},
{
"source": 55,
"target": 47,
"source": 56,
"target": 48,
"overhead": [
{
"field": "num_vars",
Expand Down
7 changes: 7 additions & 0 deletions problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ Flags by problem type:
LCS --strings
FAS --arcs [--weights] [--num-vertices]
FVS --arcs [--weights] [--num-vertices]
PartiallyOrderedKnapsack --sizes, --values, --capacity, --precedences
FlowShopScheduling --task-lengths, --deadline [--num-processors]
SCS --strings, --bound [--alphabet-size]
ILP, CircuitSAT (via reduction only)
Expand Down Expand Up @@ -382,6 +383,12 @@ pub struct CreateArgs {
/// Directed arcs for directed graph problems (e.g., 0>1,1>2,2>0)
#[arg(long)]
pub arcs: Option<String>,
/// Item values (e.g., 3,4,5,7) for PartiallyOrderedKnapsack
#[arg(long)]
pub values: Option<String>,
/// Precedence pairs (e.g., "0>2,0>3,1>4") for PartiallyOrderedKnapsack
#[arg(long, alias = "item-precedences")]
pub precedences: Option<String>,
/// Task lengths for FlowShopScheduling (semicolon-separated rows: "3,4,2;2,3,5;4,1,3")
#[arg(long)]
pub task_lengths: Option<String>,
Expand Down
50 changes: 49 additions & 1 deletion problemreductions-cli/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use problemreductions::export::{ModelExample, ProblemRef, ProblemSide, RuleExamp
use problemreductions::models::algebraic::{ClosestVectorProblem, BMF};
use problemreductions::models::graph::{GraphPartitioning, HamiltonianPath};
use problemreductions::models::misc::{
BinPacking, FlowShopScheduling, LongestCommonSubsequence, PaintShop,
BinPacking, FlowShopScheduling, LongestCommonSubsequence, PaintShop, PartiallyOrderedKnapsack,
ShortestCommonSupersequence, SubsetSum,
};
use problemreductions::prelude::*;
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.values.is_none()
&& args.precedences.is_none()
&& args.task_lengths.is_none()
&& args.deadline.is_none()
&& args.num_processors.is_none()
Expand Down Expand Up @@ -982,6 +984,52 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
)
}

// PartiallyOrderedKnapsack
"PartiallyOrderedKnapsack" => {
let sizes_str = args.sizes.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"PartiallyOrderedKnapsack requires --sizes, --values, and --capacity (--precedences is optional)\n\n\
Usage: pred create PartiallyOrderedKnapsack --sizes 2,3,4,1,2,3 --values 3,2,5,4,3,8 --precedences \"0>2,0>3,1>4,3>5,4>5\" --capacity 11"
)
})?;
let values_str = args.values.as_deref().ok_or_else(|| {
anyhow::anyhow!("PartiallyOrderedKnapsack requires --values (e.g., 3,2,5,4,3,8)")
})?;
let cap_str = args.capacity.as_deref().ok_or_else(|| {
anyhow::anyhow!("PartiallyOrderedKnapsack requires --capacity (e.g., 11)")
})?;
let sizes: Vec<i64> = util::parse_comma_list(sizes_str)?;
let values: Vec<i64> = util::parse_comma_list(values_str)?;
let capacity: i64 = cap_str.parse()?;
let precedences = match args.precedences.as_deref() {
Some(s) if !s.trim().is_empty() => s
.split(',')
.map(|pair| {
let parts: Vec<&str> = pair.trim().split('>').collect();
anyhow::ensure!(
parts.len() == 2,
"Invalid precedence format '{}', expected 'a>b'",
pair.trim()
);
Ok((
parts[0].trim().parse::<usize>()?,
parts[1].trim().parse::<usize>()?,
))
})
.collect::<Result<Vec<_>>>()?,
_ => vec![],
};
(
ser(PartiallyOrderedKnapsack::new(
sizes,
values,
precedences,
capacity,
))?,
resolved_variant.clone(),
)
}

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

Expand Down
Loading
Loading