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
38 changes: 38 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"MaximumClique": [Maximum Clique],
"MaximumSetPacking": [Maximum Set Packing],
"MinimumSetCovering": [Minimum Set Covering],
"SetBasis": [Set Basis],
"SpinGlass": [Spin Glass],
"QUBO": [QUBO],
"ILP": [Integer Linear Programming],
Expand Down Expand Up @@ -1107,6 +1108,43 @@ NP-completeness was established by Garey, Johnson, and Stockmeyer @gareyJohnsonS
]
}

#{
let x = load-model-example("SetBasis")
let coll = x.instance.collection
let m = coll.len()
let U-size = x.instance.universe_size
let k = x.instance.k
let sample = x.samples.at(0)
let sat-count = x.optimal.len()
let basis = range(k).map(i =>
range(U-size).filter(j => sample.config.at(i * U-size + j) == 1)
)
let fmt-set(s) = "${" + s.map(e => str(e + 1)).join(", ") + "}$"
[
#problem-def("SetBasis")[
Given finite set $S$, collection $cal(C)$ of subsets of $S$, and integer $k$, does there exist a family $cal(B) = {B_1, ..., B_k}$ with each $B_i subset.eq S$ such that for every $C in cal(C)$ there exists $cal(B)_C subset.eq cal(B)$ with $union.big_(B in cal(B)_C) B = C$?
][
The Set Basis problem was shown NP-complete by Stockmeyer @stockmeyer1975setbasis and appears as SP7 in Garey & Johnson @garey1979. It asks for an exact union-based description of a family of sets, unlike Set Cover which only requires covering the underlying universe. Applications include data compression, database schema design, and Boolean function minimization. The library's decision encoding uses $k |S|$ membership bits, so brute-force over those bits gives an $O^*(2^(k |S|))$ exact algorithm#footnote[This is the direct search bound induced by the encoding implemented here; we are not aware of a faster general exact worst-case algorithm for this representation.].

*Example.* Let $S = {1, 2, 3, 4}$, $k = #k$, and $cal(C) = {#range(m).map(i => $C_#(i + 1)$).join(", ")}$ with #coll.enumerate().map(((i, s)) => $C_#(i + 1) = #fmt-set(s)$).join(", "). The sample basis from the issue is $cal(B) = {#range(k).map(i => $B_#(i + 1)$).join(", ")}$ with #basis.enumerate().map(((i, s)) => $B_#(i + 1) = #fmt-set(s)$).join(", "). Then $C_1 = B_1 union B_2$, $C_2 = B_2 union B_3$, $C_3 = B_1 union B_3$, and $C_4 = B_1 union B_2 union B_3$. There are #sat-count satisfying encodings in total: the singleton basis can be permuted in $3! = 6$ ways, and the three pair sets $C_1, C_2, C_3$ also form a basis with another six row permutations.

#figure(
canvas(length: 1cm, {
let elems = ((-0.9, 0.2), (0.0, -0.5), (0.9, 0.2), (1.8, -0.5))
for i in range(k) {
let positions = basis.at(i).map(e => elems.at(e))
sregion(positions, pad: 0.28, label: [$B_#(i + 1)$], ..sregion-selected)
}
for (idx, pos) in elems.enumerate() {
selem(pos, label: [#(idx + 1)], fill: if idx < 3 { black } else { luma(160) })
}
}),
caption: [Set Basis example: the singleton basis $cal(B) = {#range(k).map(i => $B_#(i + 1)$).join(", ")}$ reconstructs every target set in $cal(C)$; element $4$ is unused by the target family.],
) <fig:set-basis>
]
]
}

== Optimization Problems

#{
Expand Down
9 changes: 9 additions & 0 deletions docs/paper/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,15 @@ @article{arora2009
doi = {10.1145/1502793.1502794}
}

@techreport{stockmeyer1975setbasis,
author = {Larry J. Stockmeyer},
title = {The Set Basis Problem Is NP-Complete},
institution = {IBM Thomas J. Watson Research Center},
number = {RC 5431},
address = {Yorktown Heights, New York},
year = {1975}
}

@article{cygan2014,
author = {Marek Cygan and Daniel Lokshtanov and Marcin Pilipczuk and Micha{\l} Pilipczuk and Saket Saurabh},
title = {Minimum Bisection Is Fixed Parameter Tractable},
Expand Down
21 changes: 21 additions & 0 deletions docs/src/reductions/problem_schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,27 @@
}
]
},
{
"name": "SetBasis",
"description": "Determine whether a collection of sets admits a basis of size k under union",
"fields": [
{
"name": "universe_size",
"type_name": "usize",
"description": "Size of the ground set S"
},
{
"name": "collection",
"type_name": "Vec<Vec<usize>>",
"description": "Collection C of target subsets of S"
},
{
"name": "k",
"type_name": "usize",
"description": "Required number of basis sets"
}
]
},
{
"name": "ShortestCommonSupersequence",
"description": "Find a common supersequence of bounded length for a set of strings",
Expand Down
27 changes: 17 additions & 10 deletions docs/src/reductions/reduction_graph.json
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,13 @@
"doc_path": "models/formula/struct.Satisfiability.html",
"complexity": "2^num_variables"
},
{
"name": "SetBasis",
"variant": {},
"category": "set",
"doc_path": "models/set/struct.SetBasis.html",
"complexity": "2^(basis_size * universe_size)"
},
{
"name": "ShortestCommonSupersequence",
"variant": {},
Expand Down Expand Up @@ -569,7 +576,7 @@
},
{
"source": 4,
"target": 54,
"target": 55,
"overhead": [
{
"field": "num_spins",
Expand Down Expand Up @@ -733,7 +740,7 @@
},
{
"source": 21,
"target": 58,
"target": 59,
"overhead": [
{
"field": "num_elements",
Expand Down Expand Up @@ -789,7 +796,7 @@
},
{
"source": 25,
"target": 54,
"target": 55,
"overhead": [
{
"field": "num_spins",
Expand Down Expand Up @@ -1235,7 +1242,7 @@
},
{
"source": 49,
"target": 53,
"target": 54,
"overhead": [
{
"field": "num_spins",
Expand Down Expand Up @@ -1320,7 +1327,7 @@
"doc_path": "rules/sat_minimumdominatingset/index.html"
},
{
"source": 53,
"source": 54,
"target": 49,
"overhead": [
{
Expand All @@ -1331,7 +1338,7 @@
"doc_path": "rules/spinglass_qubo/index.html"
},
{
"source": 54,
"source": 55,
"target": 25,
"overhead": [
{
Expand All @@ -1346,8 +1353,8 @@
"doc_path": "rules/spinglass_maxcut/index.html"
},
{
"source": 54,
"target": 53,
"source": 55,
"target": 54,
"overhead": [
{
"field": "num_spins",
Expand All @@ -1361,7 +1368,7 @@
"doc_path": "rules/spinglass_casts/index.html"
},
{
"source": 59,
"source": 60,
"target": 12,
"overhead": [
{
Expand All @@ -1376,7 +1383,7 @@
"doc_path": "rules/travelingsalesman_ilp/index.html"
},
{
"source": 59,
"source": 60,
"target": 49,
"overhead": [
{
Expand Down
4 changes: 3 additions & 1 deletion problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ Flags by problem type:
MaximumSetPacking --sets [--weights]
MinimumSetCovering --universe, --sets [--weights]
X3C (ExactCoverBy3Sets) --universe, --sets (3 elements each)
SetBasis --universe, --sets, --k
BicliqueCover --left, --right, --biedges, --k
BMF --matrix (0/1), --rank
SteinerTree --graph, --edge-weights, --terminals
Expand Down Expand Up @@ -264,7 +265,8 @@ 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 FVS --arcs \"0>1,1>2,2>0\" --weights 1,1,1
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\"")]
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\"
pred create SetBasis --universe 4 --sets \"0,1;1,2;0,2;0,1,2\" --k 3")]
pub struct CreateArgs {
/// Problem type (e.g., MIS, QUBO, SAT). Omit when using --example.
#[arg(value_parser = crate::problem_name::ProblemNameParser)]
Expand Down
53 changes: 52 additions & 1 deletion problemreductions-cli/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,21 @@ fn type_format_hint(type_name: &str, graph_type: Option<&str>) -> &'static str {
}
}

fn cli_flag_name(field_name: &str) -> String {
match field_name {
"universe_size" => "universe".to_string(),
"collection" | "subsets" => "sets".to_string(),
"left_size" => "left".to_string(),
"right_size" => "right".to_string(),
"edges" => "biedges".to_string(),
"vertex_weights" => "weights".to_string(),
"edge_lengths" => "edge-weights".to_string(),
"num_tasks" => "n".to_string(),
"precedences" => "precedence-pairs".to_string(),
_ => field_name.replace('_', "-"),
}
}

fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str {
match canonical {
"MaximumIndependentSet"
Expand Down Expand Up @@ -248,6 +263,7 @@ fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str {
}
"SubgraphIsomorphism" => "--graph 0-1,1-2,2-0 --pattern 0-1",
"SubsetSum" => "--sizes 3,7,1,8,2,4 --target 11",
"SetBasis" => "--universe 4 --sets \"0,1;1,2;0,2;0,1,2\" --k 3",
"ShortestCommonSupersequence" => "--strings \"0,1,2;1,2,0\" --bound 4",
_ => "",
}
Expand Down Expand Up @@ -280,7 +296,7 @@ fn print_problem_help(canonical: &str, graph_type: Option<&str>) -> Result<()> {
let hint = type_format_hint(&field.type_name, graph_type);
eprintln!(
" --{:<16} {} ({})",
field.name.replace('_', "-"),
cli_flag_name(&field.name),
field.description,
hint
);
Expand Down Expand Up @@ -718,6 +734,41 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
)
}

// SetBasis
"SetBasis" => {
let universe = args.universe.ok_or_else(|| {
anyhow::anyhow!(
"SetBasis requires --universe, --sets, and --k\n\n\
Usage: pred create SetBasis --universe 4 --sets \"0,1;1,2;0,2;0,1,2\" --k 3"
)
})?;
let k = args.k.ok_or_else(|| {
anyhow::anyhow!(
"SetBasis requires --k\n\n\
Usage: pred create SetBasis --universe 4 --sets \"0,1;1,2;0,2;0,1,2\" --k 3"
)
})?;
let sets = parse_sets(args)?;
for (i, set) in sets.iter().enumerate() {
for &element in set {
if element >= universe {
bail!(
"Set {} contains element {} which is outside universe of size {}",
i,
element,
universe
);
}
}
}
(
ser(problemreductions::models::set::SetBasis::new(
universe, sets, k,
))?,
resolved_variant.clone(),
)
}

// BicliqueCover
"BicliqueCover" => {
let left = args.left.ok_or_else(|| {
Expand Down
Loading
Loading