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
12 changes: 12 additions & 0 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"Satisfiability": [SAT],
"KSatisfiability": [$k$-SAT],
"CircuitSAT": [CircuitSAT],
"CosineProductIntegration": [Cosine Product Integration],
"Factoring": [Factoring],
"KingsSubgraph": [King's Subgraph MIS],
"TriangularSubgraph": [Triangular Subgraph MIS],
Expand Down Expand Up @@ -1911,6 +1912,17 @@ NP-completeness was established by Garey, Johnson, and Stockmeyer @gareyJohnsonS
*Example.* Let $A = {3, 7, 1, 8, 2, 4}$ ($n = 6$) and target $B = 11$. Selecting $A' = {3, 8}$ gives sum $3 + 8 = 11 = B$. Another solution: $A' = {7, 4}$ with sum $7 + 4 = 11 = B$.
]

#problem-def("CosineProductIntegration")[
Given integers $a_1, dots, a_n$, determine whether
$
integral_0^(2 pi) product_(i=1)^n cos(a_i theta) dif theta != 0.
$
][
Cosine Product Integration appears as problem A7 AN14 in Garey and Johnson @garey1979. Expanding each cosine as $(e^(i a_i theta) + e^(-i a_i theta)) / 2$ shows that the integral equals $(2 pi / 2^n)$ times the number of sign vectors $epsilon in {-1, 1}^n$ with $sum_i epsilon_i a_i = 0$. Therefore the integral is nonzero exactly when there exists a balanced signed sum. Equivalently, after replacing each coefficient by its absolute value, the YES instances are precisely those whose multiset can be partitioned into two equal-sum parts. Garey and Johnson note the resulting decision problem remains solvable in pseudo-polynomial time @garey1979, and the same equivalence inherits the $O^*(2^(n slash 2))$ meet-in-the-middle exact algorithm used for Partition and Subset Sum @horowitz1974.

*Example.* For coefficients $(2, 3, 5)$ we have the balanced sign assignment $+2 + 3 - 5 = 0$, so the integral equals $(2 pi / 2^3) times 2 = pi / 2$ and the answer is YES. For coefficients $(1, 2, 6)$ the total absolute sum is $9$, which is odd, so no balanced sign assignment exists and the integral is zero; hence the answer is NO.
]

#{
let x = load-model-example("ShortestCommonSupersequence")
let alpha-size = x.instance.alphabet_size
Expand Down
13 changes: 12 additions & 1 deletion docs/src/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ pred create UndirectedTwoCommodityIntegralFlow --graph 0-2,1-2,2-3 --capacities
pred create LengthBoundedDisjointPaths --graph 0-1,1-6,0-2,2-3,3-6,0-4,4-5,5-6 --source 0 --sink 6 --num-paths-required 2 --bound 3 -o lbdp.json
pred create Factoring --target 15 --bits-m 4 --bits-n 4 -o factoring.json
pred create Factoring --target 21 --bits-m 3 --bits-n 3 -o factoring2.json
pred create CosineProductIntegration --coefficients 2,3,5 -o cpi.json
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" -o x3c.json
pred create MinimumTardinessSequencing --n 5 --deadlines 5,5,5,3,3 --precedence-pairs "0>3,1>3,1>4,2>4" -o mts.json
```
Expand Down Expand Up @@ -447,6 +448,14 @@ Solution: [1, 0, 0, 1]
Evaluation: Valid(2)
```

Some models do not yet have an ILP reduction path. For example, `CosineProductIntegration`
currently needs brute force:

```bash
pred create CosineProductIntegration --coefficients 2,3,5 -o cpi.json
pred solve cpi.json --solver brute-force
```

Solve a reduction bundle (from `pred reduce`):

```bash
Expand Down Expand Up @@ -483,7 +492,7 @@ If the shell argument is omitted, `pred completions` auto-detects your current s

## JSON Output

All commands support `-o` to write JSON to a file and `--json` to print JSON to stdout:
Successful data-producing commands support `-o` to write JSON to a file and `--json` to print JSON to stdout:

```bash
pred list -o problems.json # save to file
Expand All @@ -493,6 +502,8 @@ pred path MIS QUBO --json
pred solve problem.json --json
```

Errors are still reported as plain text on stderr, even when `--json` is set.

This is useful for scripting and piping:

```bash
Expand Down
11 changes: 9 additions & 2 deletions problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ Piping (use - to read from stdin):
pred create MIS --graph 0-1,1-2 | pred evaluate - --config 1,0,1
pred create MIS --graph 0-1,1-2 | pred reduce - --to QUBO

JSON output (any command):
JSON output (successful commands):
pred list --json # JSON to stdout
pred show MIS --json | jq '.' # pipe to jq

Errors are reported on stderr as text, even when `--json` is set.

Use `pred <command> --help` for detailed usage of each command.
Use `pred list` to see all available problem types.

Expand Down Expand Up @@ -229,6 +231,7 @@ Flags by problem type:
LengthBoundedDisjointPaths --graph, --source, --sink, --num-paths-required, --bound
Factoring --target, --m, --n
BinPacking --sizes, --capacity
CosineProductIntegration --coefficients
SubsetSum --sizes, --target
PaintShop --sequence
MaximumSetPacking --sets [--weights]
Expand Down Expand Up @@ -274,7 +277,8 @@ Examples:
pred create FVS --arcs \"0>1,1>2,2>0\" --weights 1,1,1
pred create UndirectedTwoCommodityIntegralFlow --graph 0-2,1-2,2-3 --capacities 1,1,2 --source-1 0 --sink-1 3 --source-2 1 --sink-2 3 --requirement-1 1 --requirement-2 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 SetBasis --universe 4 --sets \"0,1;1,2;0,2;0,1,2\" --k 3")]
pred create SetBasis --universe 4 --sets \"0,1;1,2;0,2;0,1,2\" --k 3
pred create CosineProductIntegration --coefficients 2,3,5")]
pub struct CreateArgs {
/// Problem type (e.g., MIS, QUBO, SAT). Omit when using --example.
#[arg(value_parser = crate::problem_name::ProblemNameParser)]
Expand Down Expand Up @@ -375,6 +379,9 @@ pub struct CreateArgs {
/// Item sizes for BinPacking (comma-separated, e.g., "3,3,2,2")
#[arg(long)]
pub sizes: Option<String>,
/// Integer cosine frequencies for CosineProductIntegration (comma-separated, e.g., "2,3,5")
#[arg(long)]
pub coefficients: Option<String>,
/// Bin capacity for BinPacking
#[arg(long)]
pub capacity: Option<String>,
Expand Down
23 changes: 20 additions & 3 deletions problemreductions-cli/src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use problemreductions::models::graph::{
SteinerTree,
};
use problemreductions::models::misc::{
BinPacking, FlowShopScheduling, LongestCommonSubsequence, MinimumTardinessSequencing,
PaintShop, SequencingWithinIntervals, ShortestCommonSupersequence, SubsetSum,
BinPacking, CosineProductIntegration, FlowShopScheduling, LongestCommonSubsequence,
MinimumTardinessSequencing, PaintShop, SequencingWithinIntervals, ShortestCommonSupersequence,
SubsetSum,
};
use problemreductions::prelude::*;
use problemreductions::registry::collect_schemas;
Expand Down Expand Up @@ -55,6 +56,7 @@ fn all_data_flags_empty(args: &CreateArgs) -> bool {
&& args.requirement_1.is_none()
&& args.requirement_2.is_none()
&& args.sizes.is_none()
&& args.coefficients.is_none()
&& args.capacity.is_none()
&& args.sequence.is_none()
&& args.sets.is_none()
Expand Down Expand Up @@ -233,7 +235,6 @@ fn type_format_hint(type_name: &str, graph_type: Option<&str>) -> &'static str {
"Vec<Vec<usize>>" => "semicolon-separated groups: \"0,1;2,3\"",
"usize" => "integer",
"u64" => "integer",
"Vec<u64>" => "comma-separated integers: 0,0,5",
"i64" => "integer",
"BigUint" => "nonnegative decimal integer",
"Vec<BigUint>" => "comma-separated nonnegative decimal integers: 3,7,1,8",
Expand Down Expand Up @@ -280,6 +281,7 @@ fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str {
"PartitionIntoTriangles" => "--graph 0-1,1-2,0-2",
"Factoring" => "--target 15 --m 4 --n 4",
"SequencingWithinIntervals" => "--release-times 0,0,5 --deadlines 11,11,6 --lengths 3,1,1",
"CosineProductIntegration" => "--coefficients 2,3,5",
"SteinerTree" => "--graph 0-1,1-2,1-3,3-4 --edge-weights 2,2,1,1 --terminals 0,2,4",
"OptimalLinearArrangement" => "--graph 0-1,1-2,2-3 --bound 5",
"DirectedTwoCommodityIntegralFlow" => {
Expand Down Expand Up @@ -954,6 +956,21 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
)
}

// CosineProductIntegration
"CosineProductIntegration" => {
let coefficients_str = args.coefficients.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"CosineProductIntegration requires --coefficients\n\n\
Usage: pred create CosineProductIntegration --coefficients 2,3,5"
)
})?;
let coefficients: Vec<i64> = util::parse_comma_list(coefficients_str)?;
(
ser(CosineProductIntegration::new(coefficients))?,
resolved_variant.clone(),
)
}

// PaintShop
"PaintShop" => {
let seq_str = args.sequence.as_deref().ok_or_else(|| {
Expand Down
80 changes: 80 additions & 0 deletions problemreductions-cli/tests/cli_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4068,6 +4068,86 @@ fn test_create_factoring_missing_bits() {
);
}

// ---- CosineProductIntegration create tests ----

#[test]
fn test_create_cosine_product_integration() {
let output = pred()
.args([
"create",
"CosineProductIntegration",
"--coefficients",
"2,3,5",
])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).unwrap();
let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
assert_eq!(json["type"], "CosineProductIntegration");
assert_eq!(json["data"]["coefficients"], serde_json::json!([2, 3, 5]));
}

#[test]
fn test_create_cosine_product_integration_no_flags_shows_help() {
let output = pred()
.args(["create", "CosineProductIntegration"])
.output()
.unwrap();
assert!(
!output.status.success(),
"should exit non-zero when showing help without data flags"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("--coefficients"),
"expected '--coefficients' in help output, got: {stderr}"
);
assert!(
stderr.contains("pred create CosineProductIntegration --coefficients 2,3,5"),
"expected cosine-product example in help output, got: {stderr}"
);
}

#[test]
fn test_create_cosine_product_integration_missing_coefficients() {
let output = pred()
.args(["create", "CosineProductIntegration", "--target", "15"])
.output()
.unwrap();
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("CosineProductIntegration requires --coefficients"),
"expected missing-coefficients error, got: {stderr}"
);
assert!(
stderr.contains("pred create CosineProductIntegration --coefficients 2,3,5"),
"expected usage example in error output, got: {stderr}"
);
}

#[test]
fn test_create_model_example_cosine_product_integration() {
let output = pred()
.args(["create", "--example", "CosineProductIntegration"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).unwrap();
let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
assert_eq!(json["type"], "CosineProductIntegration");
assert_eq!(json["data"]["coefficients"], serde_json::json!([2, 3, 5]));
}

// ---- Timeout tests (H3) ----

#[test]
Expand Down
Loading
Loading