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
24 changes: 24 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@

// Problem display names for theorem headers
#let display-name = (
"AdditionalKey": [Additional Key],
"MaximumIndependentSet": [Maximum Independent Set],
"MinimumVertexCover": [Minimum Vertex Cover],
"MaxCut": [Max-Cut],
Expand Down Expand Up @@ -3638,6 +3639,29 @@ A classical NP-complete problem from Garey and Johnson @garey1979[Ch.~3, p.~76],
) <fig:d2cif>
]

#problem-def("AdditionalKey")[
Given a set $A$ of attribute names, a collection $F$ of functional dependencies on $A$,
a subset $R subset.eq A$, and a set $K$ of candidate keys for the relational scheme $chevron.l R, F chevron.r$,
determine whether there exists a subset $R' subset.eq R$ such that $R' in.not K$,
the closure $R'^+$ under $F$ equals $R$, and no proper subset of $R'$ also has this property.
][
A classical NP-complete problem from relational database theory @beeri1979.
Enumerating all candidate keys is necessary to verify Boyce-Codd Normal Form (BCNF),
and the NP-completeness of Additional Key implies that BCNF testing is intractable in general.
The best known exact algorithm is brute-force enumeration of all $2^(|R|)$ subsets,
checking each for the key property via closure computation under Armstrong's axioms.
#footnote[No algorithm improving on brute-force is known for the Additional Key problem.]

*Example.* Consider attribute set $A = {0, 1, 2, 3, 4, 5}$ with functional dependencies
$F = {{0,1} -> {2,3}, {2,3} -> {4,5}, {4,5} -> {0,1}, {0,2} -> {3}, {3,5} -> {1}}$,
relation $R = A$, and known keys $K = {{0,1}, {2,3}, {4,5}}$.
The subset ${0,2}$ is an additional key: starting from ${0,2}$, we apply ${0,2} -> {3}$
to get ${0,2,3}$, then ${2,3} -> {4,5}$ to get ${0,2,3,4,5}$, then ${4,5} -> {0,1}$
to reach $R^+ = A$. The set ${0,2}$ is minimal (neither ${0}$ nor ${2}$ alone determines $A$)
and ${0,2} in.not K$, so the answer is YES.
]


#{
let x = load-model-example("ConjunctiveBooleanQuery")
let d = x.instance.domain_size
Expand Down
11 changes: 11 additions & 0 deletions docs/paper/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@ @article{lucas2014
year = {2014}
}

@article{beeri1979,
author = {Catriel Beeri and Philip A. Bernstein},
title = {Computational Problems Related to the Design of Normal Form Relational Schemas},
journal = {ACM Transactions on Database Systems},
volume = {4},
number = {1},
pages = {30--59},
year = {1979},
doi = {10.1145/320064.320066}
}

@article{barahona1982,
author = {Francisco Barahona},
title = {On the computational complexity of Ising spin glass models},
Expand Down
19 changes: 13 additions & 6 deletions problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ Flags by problem type:
OptimalLinearArrangement --graph, --bound
RuralPostman (RPP) --graph, --edge-weights, --required-edges, --bound
MultipleChoiceBranching --arcs [--weights] --partition --bound [--num-vertices]
AdditionalKey --num-attributes, --dependencies, --relation-attrs [--known-keys]
SubgraphIsomorphism --graph (host), --pattern (pattern)
LCS --strings, --bound [--alphabet-size]
FAS --arcs [--weights] [--num-vertices]
Expand Down Expand Up @@ -540,6 +541,18 @@ pub struct CreateArgs {
/// Alphabet size for LCS, SCS, or StringToStringCorrection (optional; inferred from the input strings if omitted)
#[arg(long)]
pub alphabet_size: Option<usize>,
/// Number of attributes for AdditionalKey or MinimumCardinalityKey
#[arg(long)]
pub num_attributes: Option<usize>,
/// Functional dependencies for AdditionalKey (e.g., "0,1:2,3;2,3:4,5") or MinimumCardinalityKey (semicolon-separated "lhs>rhs" pairs, e.g., "0,1>2;0,2>3")
#[arg(long)]
pub dependencies: Option<String>,
/// Relation scheme attributes for AdditionalKey (comma-separated, e.g., "0,1,2,3,4,5")
#[arg(long)]
pub relation_attrs: Option<String>,
/// Known candidate keys for AdditionalKey (e.g., "0,1;2,3")
#[arg(long)]
pub known_keys: Option<String>,
/// Domain size for ConjunctiveBooleanQuery
#[arg(long)]
pub domain_size: Option<usize>,
Expand All @@ -558,12 +571,6 @@ pub struct CreateArgs {
/// Number of groups for SumOfSquaresPartition
#[arg(long)]
pub num_groups: Option<usize>,
/// Functional dependencies for MinimumCardinalityKey (semicolon-separated "lhs>rhs" pairs, e.g., "0,1>2;0,2>3")
#[arg(long)]
pub dependencies: Option<String>,
/// Number of attributes for MinimumCardinalityKey
#[arg(long)]
pub num_attributes: Option<usize>,
/// Source string for StringToStringCorrection (comma-separated symbol indices, e.g., "0,1,2,3")
#[arg(long)]
pub source_string: Option<String>,
Expand Down
54 changes: 53 additions & 1 deletion problemreductions-cli/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use problemreductions::models::graph::{
MultipleChoiceBranching, SteinerTree, StrongConnectivityAugmentation,
};
use problemreductions::models::misc::{
BinPacking, BoyceCoddNormalFormViolation, CbqRelation, ConjunctiveBooleanQuery,
AdditionalKey, BinPacking, BoyceCoddNormalFormViolation, CbqRelation, ConjunctiveBooleanQuery,
FlowShopScheduling, LongestCommonSubsequence, MinimumTardinessSequencing,
MultiprocessorScheduling, PaintShop, PartiallyOrderedKnapsack, QueryArg,
RectilinearPictureCompression, ResourceConstrainedScheduling,
Expand Down Expand Up @@ -121,6 +121,10 @@ fn all_data_flags_empty(args: &CreateArgs) -> bool {
&& args.sink_2.is_none()
&& args.requirement_1.is_none()
&& args.requirement_2.is_none()
&& args.num_attributes.is_none()
&& args.dependencies.is_none()
&& args.relation_attrs.is_none()
&& args.known_keys.is_none()
&& args.domain_size.is_none()
&& args.relations.is_none()
&& args.conjuncts_spec.is_none()
Expand Down Expand Up @@ -412,6 +416,7 @@ fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str {
"MultipleChoiceBranching" => {
"--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"
}
"AdditionalKey" => "--num-attributes 6 --dependencies \"0,1:2,3;2,3:4,5;4,5:0,1\" --relation-attrs 0,1,2,3,4,5 --known-keys \"0,1;2,3;4,5\"",
"SubgraphIsomorphism" => "--graph 0-1,1-2,2-0 --pattern 0-1",
"RectilinearPictureCompression" => {
"--matrix \"1,1,0,0;1,1,0,0;0,0,1,1;0,0,1,1\" --k 2"
Expand Down Expand Up @@ -1402,6 +1407,51 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
}
}

// AdditionalKey
"AdditionalKey" => {
let usage = "Usage: pred create AdditionalKey --num-attributes 6 --dependencies \"0,1:2,3;2,3:4,5\" --relation-attrs \"0,1,2,3,4,5\" --known-keys \"0,1;2,3\"";
let num_attributes = args.num_attributes.ok_or_else(|| {
anyhow::anyhow!("AdditionalKey requires --num-attributes\n\n{usage}")
})?;
let deps_str = args.dependencies.as_deref().ok_or_else(|| {
anyhow::anyhow!("AdditionalKey requires --dependencies\n\n{usage}")
})?;
let ra_str = args.relation_attrs.as_deref().ok_or_else(|| {
anyhow::anyhow!("AdditionalKey requires --relation-attrs\n\n{usage}")
})?;
let dependencies: Vec<(Vec<usize>, Vec<usize>)> = deps_str
.split(';')
.map(|dep| {
let parts: Vec<&str> = dep.trim().split(':').collect();
anyhow::ensure!(
parts.len() == 2,
"Invalid dependency format '{}', expected 'lhs:rhs' (e.g., '0,1:2,3')",
dep.trim()
);
let lhs: Vec<usize> = util::parse_comma_list(parts[0].trim())?;
let rhs: Vec<usize> = util::parse_comma_list(parts[1].trim())?;
Ok((lhs, rhs))
})
.collect::<Result<Vec<_>>>()?;
let relation_attrs: Vec<usize> = util::parse_comma_list(ra_str)?;
let known_keys: Vec<Vec<usize>> = match args.known_keys.as_deref() {
Some(s) if !s.is_empty() => s
.split(';')
.map(|k| util::parse_comma_list(k.trim()))
.collect::<Result<Vec<_>>>()?,
_ => vec![],
};
(
ser(AdditionalKey::new(
num_attributes,
dependencies,
relation_attrs,
known_keys,
))?,
resolved_variant.clone(),
)
}

// SubsetSum
"SubsetSum" => {
let sizes_str = args.sizes.as_deref().ok_or_else(|| {
Expand Down Expand Up @@ -4439,6 +4489,8 @@ mod tests {
domain_size: None,
relations: None,
conjuncts_spec: None,
relation_attrs: None,
known_keys: None,
costs: None,
cut_bound: None,
size_bound: None,
Expand Down
15 changes: 8 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ pub mod prelude {
UndirectedTwoCommodityIntegralFlow,
};
pub use crate::models::misc::{
BinPacking, BoyceCoddNormalFormViolation, CbqRelation, ConjunctiveBooleanQuery,
ConjunctiveQueryFoldability, Factoring, FlowShopScheduling, Knapsack,
LongestCommonSubsequence, MinimumTardinessSequencing, MultiprocessorScheduling, PaintShop,
Partition, QueryArg, RectilinearPictureCompression, ResourceConstrainedScheduling,
SequencingToMinimizeMaximumCumulativeCost, SequencingWithReleaseTimesAndDeadlines,
SequencingWithinIntervals, ShortestCommonSupersequence, StaffScheduling,
StringToStringCorrection, SubsetSum, SumOfSquaresPartition, Term,
AdditionalKey, BinPacking, BoyceCoddNormalFormViolation, CbqRelation,
ConjunctiveBooleanQuery, ConjunctiveQueryFoldability, Factoring, FlowShopScheduling,
Knapsack, LongestCommonSubsequence, MinimumTardinessSequencing, MultiprocessorScheduling,
PaintShop, Partition, QueryArg, RectilinearPictureCompression,
ResourceConstrainedScheduling, SequencingToMinimizeMaximumCumulativeCost,
SequencingWithReleaseTimesAndDeadlines, SequencingWithinIntervals,
ShortestCommonSupersequence, StaffScheduling, StringToStringCorrection, SubsetSum,
SumOfSquaresPartition, Term,
};
pub use crate::models::set::{
ComparativeContainment, ConsecutiveSets, ExactCoverBy3Sets, MaximumSetPacking,
Expand Down
Loading
Loading