From 168b9d414a1928b6709435143a135f203895077d Mon Sep 17 00:00:00 2001 From: zazabap Date: Thu, 12 Mar 2026 13:18:48 +0000 Subject: [PATCH 1/6] Add plan for #402: SubsetSum model --- docs/plans/2026-03-12-subset-sum-model.md | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 docs/plans/2026-03-12-subset-sum-model.md diff --git a/docs/plans/2026-03-12-subset-sum-model.md b/docs/plans/2026-03-12-subset-sum-model.md new file mode 100644 index 000000000..64559e15a --- /dev/null +++ b/docs/plans/2026-03-12-subset-sum-model.md @@ -0,0 +1,45 @@ +# Plan: Add SubsetSum Model (#402) + +## Overview +Add SubsetSum as a satisfaction problem in `src/models/misc/`. SubsetSum asks whether any subset of positive integers sums to a target value B. + +## Steps + +### 1. Model file: `src/models/misc/subset_sum.rs` +- Struct: `SubsetSum { sizes: Vec, target: u64 }` +- Getters: `sizes()`, `target()`, `num_items()` +- `Problem` impl: NAME = "SubsetSum", Metric = bool, dims = [2; n], evaluate checks if selected subset sums to target +- `SatisfactionProblem` impl (marker) +- `declare_variants!` with complexity `2^(num_items / 2)` (Horowitz-Sahni meet-in-the-middle) +- `inventory::submit!` for ProblemSchemaEntry +- Unit test path link + +### 2. Register in `src/models/misc/mod.rs` +- Add `mod subset_sum;` and `pub use subset_sum::SubsetSum;` + +### 3. Export from `src/lib.rs` prelude +- Add `SubsetSum` to the misc imports in prelude + +### 4. Unit tests: `src/unit_tests/models/misc/subset_sum.rs` +- test_subset_sum_basic: field accessors, dims, NAME, variant +- test_subset_sum_evaluate_feasible: config that sums to target -> true +- test_subset_sum_evaluate_infeasible: config that doesn't sum -> false +- test_subset_sum_empty: empty set +- test_subset_sum_brute_force: find_satisfying +- test_subset_sum_serialization: round-trip JSON +- test_subset_sum_no_solution: impossible target +- test_subset_sum_all_selected: all items sum to target + +### 5. CLI registration +- `problemreductions-cli/src/problem_name.rs`: add "subsetsum" alias +- `problemreductions-cli/src/dispatch.rs`: add deser_sat/try_ser for SubsetSum +- `problemreductions-cli/src/cli.rs`: add SubsetSum to help table +- `problemreductions-cli/src/commands/create.rs`: add create handler using --sizes and --target flags + +### 6. CLI args +- Reuse `--sizes` flag from BinPacking +- Add `--target` usage note (already exists for Factoring, but Factoring uses u64 target) +- SubsetSum uses: `pred create SubsetSum --sizes 3,7,1,8,2,4 --target 11` + +### 7. Regenerate exports +- `make export-schemas` From de865a46a7cb7b3e2cef68705ed26b42b7737b0b Mon Sep 17 00:00:00 2001 From: zazabap Date: Thu, 12 Mar 2026 13:24:19 +0000 Subject: [PATCH 2/6] Implement #402: Add SubsetSum model - Add SubsetSum satisfaction problem to src/models/misc/ - Binary variables: x_i = 1 if element i is in the subset - Evaluates true iff selected subset sums to target B - Complexity: 2^(n/2) via Horowitz-Sahni meet-in-the-middle - CLI: pred create SubsetSum --sizes 3,7,1,8,2,4 --target 11 - 15 unit tests covering basic, edge cases, brute force, serialization - Regenerated problem schemas JSON Co-Authored-By: Claude Opus 4.6 --- docs/src/reductions/problem_schemas.json | 16 +++ problemreductions-cli/src/cli.rs | 1 + problemreductions-cli/src/commands/create.rs | 23 +++- problemreductions-cli/src/dispatch.rs | 4 +- problemreductions-cli/src/problem_name.rs | 1 + src/lib.rs | 2 +- src/models/misc/mod.rs | 3 + src/models/misc/subset_sum.rs | 117 +++++++++++++++++ src/unit_tests/models/misc/subset_sum.rs | 128 +++++++++++++++++++ 9 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 src/models/misc/subset_sum.rs create mode 100644 src/unit_tests/models/misc/subset_sum.rs diff --git a/docs/src/reductions/problem_schemas.json b/docs/src/reductions/problem_schemas.json index 50d327e9b..0e4d404b5 100644 --- a/docs/src/reductions/problem_schemas.json +++ b/docs/src/reductions/problem_schemas.json @@ -397,6 +397,22 @@ } ] }, + { + "name": "SubsetSum", + "description": "Decide if a subset of positive integers sums to a target value", + "fields": [ + { + "name": "sizes", + "type_name": "Vec", + "description": "Positive integer size s(a) for each element a in A" + }, + { + "name": "target", + "type_name": "u64", + "description": "Target sum B" + } + ] + }, { "name": "TravelingSalesman", "description": "Find minimum weight Hamiltonian cycle in a graph (Traveling Salesman Problem)", diff --git a/problemreductions-cli/src/cli.rs b/problemreductions-cli/src/cli.rs index 91e9bd252..fad4b497b 100644 --- a/problemreductions-cli/src/cli.rs +++ b/problemreductions-cli/src/cli.rs @@ -210,6 +210,7 @@ Flags by problem type: KColoring --graph, --k Factoring --target, --m, --n BinPacking --sizes, --capacity + SubsetSum --sizes, --target PaintShop --sequence MaximumSetPacking --sets [--weights] MinimumSetCovering --universe, --sets [--weights] diff --git a/problemreductions-cli/src/commands/create.rs b/problemreductions-cli/src/commands/create.rs index 3594a24a0..03ff8cf8c 100644 --- a/problemreductions-cli/src/commands/create.rs +++ b/problemreductions-cli/src/commands/create.rs @@ -5,7 +5,7 @@ use crate::problem_name::{parse_problem_spec, resolve_variant}; use crate::util; use anyhow::{bail, Context, Result}; use problemreductions::models::algebraic::{ClosestVectorProblem, BMF}; -use problemreductions::models::misc::{BinPacking, PaintShop}; +use problemreductions::models::misc::{BinPacking, PaintShop, SubsetSum}; use problemreductions::prelude::*; use problemreductions::registry::collect_schemas; use problemreductions::topology::{ @@ -333,6 +333,27 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> { } } + // SubsetSum + "SubsetSum" => { + let sizes_str = args.sizes.as_deref().ok_or_else(|| { + anyhow::anyhow!( + "SubsetSum requires --sizes and --target\n\n\ + Usage: pred create SubsetSum --sizes 3,7,1,8,2,4 --target 11" + ) + })?; + let target = args.target.ok_or_else(|| { + anyhow::anyhow!( + "SubsetSum requires --target\n\n\ + Usage: pred create SubsetSum --sizes 3,7,1,8,2,4 --target 11" + ) + })?; + let sizes: Vec = util::parse_comma_list(sizes_str)?; + ( + ser(SubsetSum::new(sizes, target))?, + resolved_variant.clone(), + ) + } + // PaintShop "PaintShop" => { let seq_str = args.sequence.as_deref().ok_or_else(|| { diff --git a/problemreductions-cli/src/dispatch.rs b/problemreductions-cli/src/dispatch.rs index 7a8498421..72bc23997 100644 --- a/problemreductions-cli/src/dispatch.rs +++ b/problemreductions-cli/src/dispatch.rs @@ -1,6 +1,6 @@ use anyhow::{bail, Context, Result}; use problemreductions::models::algebraic::{ClosestVectorProblem, ILP}; -use problemreductions::models::misc::{BinPacking, Knapsack}; +use problemreductions::models::misc::{BinPacking, Knapsack, SubsetSum}; use problemreductions::prelude::*; use problemreductions::rules::{MinimizeSteps, ReductionGraph}; use problemreductions::solvers::{BruteForce, ILPSolver, Solver}; @@ -245,6 +245,7 @@ pub fn load_problem( _ => deser_opt::>(data), }, "Knapsack" => deser_opt::(data), + "SubsetSum" => deser_sat::(data), _ => bail!("{}", crate::problem_name::unknown_problem_error(&canonical)), } } @@ -305,6 +306,7 @@ pub fn serialize_any_problem( _ => try_ser::>(any), }, "Knapsack" => try_ser::(any), + "SubsetSum" => try_ser::(any), _ => bail!("{}", crate::problem_name::unknown_problem_error(&canonical)), } } diff --git a/problemreductions-cli/src/problem_name.rs b/problemreductions-cli/src/problem_name.rs index acd9b4b59..cbee6c98d 100644 --- a/problemreductions-cli/src/problem_name.rs +++ b/problemreductions-cli/src/problem_name.rs @@ -52,6 +52,7 @@ pub fn resolve_alias(input: &str) -> String { "binpacking" => "BinPacking".to_string(), "cvp" | "closestvectorproblem" => "ClosestVectorProblem".to_string(), "knapsack" => "Knapsack".to_string(), + "subsetsum" => "SubsetSum".to_string(), _ => input.to_string(), // pass-through for exact names } } diff --git a/src/lib.rs b/src/lib.rs index b0d99699a..34e61c557 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,7 @@ pub mod prelude { KColoring, MaxCut, MaximalIS, MaximumClique, MaximumIndependentSet, MaximumMatching, MinimumDominatingSet, MinimumVertexCover, TravelingSalesman, }; - pub use crate::models::misc::{BinPacking, Factoring, Knapsack, PaintShop}; + pub use crate::models::misc::{BinPacking, Factoring, Knapsack, PaintShop, SubsetSum}; pub use crate::models::set::{MaximumSetPacking, MinimumSetCovering}; // Core traits diff --git a/src/models/misc/mod.rs b/src/models/misc/mod.rs index cdb66e969..9cdb972a8 100644 --- a/src/models/misc/mod.rs +++ b/src/models/misc/mod.rs @@ -5,13 +5,16 @@ //! - [`Factoring`]: Integer factorization //! - [`Knapsack`]: 0-1 Knapsack (maximize value subject to weight capacity) //! - [`PaintShop`]: Minimize color switches in paint shop scheduling +//! - [`SubsetSum`]: Subset Sum (decide if a subset sums to a target) mod bin_packing; pub(crate) mod factoring; mod knapsack; pub(crate) mod paintshop; +mod subset_sum; pub use bin_packing::BinPacking; pub use factoring::Factoring; pub use knapsack::Knapsack; pub use paintshop::PaintShop; +pub use subset_sum::SubsetSum; diff --git a/src/models/misc/subset_sum.rs b/src/models/misc/subset_sum.rs new file mode 100644 index 000000000..f117074b5 --- /dev/null +++ b/src/models/misc/subset_sum.rs @@ -0,0 +1,117 @@ +//! Subset Sum problem implementation. +//! +//! The Subset Sum problem asks whether there exists a subset of a given +//! set of positive integers that sums to exactly a target value B. + +use crate::registry::{FieldInfo, ProblemSchemaEntry}; +use crate::traits::{Problem, SatisfactionProblem}; +use serde::{Deserialize, Serialize}; + +inventory::submit! { + ProblemSchemaEntry { + name: "SubsetSum", + module_path: module_path!(), + description: "Decide if a subset of positive integers sums to a target value", + fields: &[ + FieldInfo { name: "sizes", type_name: "Vec", description: "Positive integer size s(a) for each element a in A" }, + FieldInfo { name: "target", type_name: "u64", description: "Target sum B" }, + ], + } +} + +/// The Subset Sum problem. +/// +/// Given a finite set A with sizes `s(a) ∈ Z⁺` for each element and a +/// positive integer B, determine whether there exists a subset `A' ⊆ A` +/// such that `∑_{a ∈ A'} s(a) = B`. +/// +/// # Representation +/// +/// Each element has a binary variable: `x_i = 1` if element `a_i` is in +/// the selected subset, `0` otherwise. +/// +/// # Example +/// +/// ``` +/// use problemreductions::models::misc::SubsetSum; +/// use problemreductions::{Problem, Solver, BruteForce}; +/// +/// // {3, 7, 1, 8, 2, 4} with target 11 +/// let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); +/// let solver = BruteForce::new(); +/// let solution = solver.find_satisfying(&problem); +/// assert!(solution.is_some()); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SubsetSum { + /// Positive integer sizes for each element. + sizes: Vec, + /// Target sum B. + target: u64, +} + +impl SubsetSum { + /// Create a new Subset Sum instance. + pub fn new(sizes: Vec, target: u64) -> Self { + Self { sizes, target } + } + + /// Get the element sizes. + pub fn sizes(&self) -> &[u64] { + &self.sizes + } + + /// Get the target sum. + pub fn target(&self) -> u64 { + self.target + } + + /// Get the number of elements (items). + pub fn num_items(&self) -> usize { + self.sizes.len() + } + + /// Check if a configuration is a valid solution (subset sums to target). + pub fn is_valid_solution(&self, config: &[usize]) -> bool { + self.evaluate(config) + } +} + +impl Problem for SubsetSum { + const NAME: &'static str = "SubsetSum"; + type Metric = bool; + + fn variant() -> Vec<(&'static str, &'static str)> { + crate::variant_params![] + } + + fn dims(&self) -> Vec { + vec![2; self.num_items()] + } + + fn evaluate(&self, config: &[usize]) -> bool { + if config.len() != self.num_items() { + return false; + } + if config.iter().any(|&v| v >= 2) { + return false; + } + let total: u64 = config + .iter() + .enumerate() + .filter(|(_, &x)| x == 1) + .map(|(i, _)| self.sizes[i]) + .sum(); + total == self.target + } +} + +impl SatisfactionProblem for SubsetSum {} + +crate::declare_variants! { + SubsetSum => "2^(num_items / 2)", +} + +#[cfg(test)] +#[path = "../../unit_tests/models/misc/subset_sum.rs"] +mod tests; diff --git a/src/unit_tests/models/misc/subset_sum.rs b/src/unit_tests/models/misc/subset_sum.rs new file mode 100644 index 000000000..6e1e4a0eb --- /dev/null +++ b/src/unit_tests/models/misc/subset_sum.rs @@ -0,0 +1,128 @@ +use super::*; +use crate::solvers::{BruteForce, Solver}; +use crate::traits::Problem; + +#[test] +fn test_subset_sum_basic() { + let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); + assert_eq!(problem.num_items(), 6); + assert_eq!(problem.sizes(), &[3, 7, 1, 8, 2, 4]); + assert_eq!(problem.target(), 11); + assert_eq!(problem.dims(), vec![2; 6]); + assert_eq!(::NAME, "SubsetSum"); + assert_eq!(::variant(), vec![]); +} + +#[test] +fn test_subset_sum_evaluate_feasible() { + // {3, 7, 1, 8, 2, 4}, target = 11 + // Subset {3, 8} = indices 0, 3 -> sum = 11 + let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); + assert!(problem.evaluate(&[1, 0, 0, 1, 0, 0])); // 3 + 8 = 11 + assert!(problem.evaluate(&[0, 1, 0, 0, 0, 1])); // 7 + 4 = 11 +} + +#[test] +fn test_subset_sum_evaluate_infeasible() { + let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); + assert!(!problem.evaluate(&[1, 1, 0, 0, 0, 0])); // 3 + 7 = 10 != 11 + assert!(!problem.evaluate(&[0, 0, 0, 0, 0, 0])); // 0 != 11 + assert!(!problem.evaluate(&[1, 1, 1, 1, 1, 1])); // 25 != 11 +} + +#[test] +fn test_subset_sum_empty_set() { + // Empty set, target 0 -> empty subset sums to 0 + let problem = SubsetSum::new(vec![], 0); + assert_eq!(problem.num_items(), 0); + assert_eq!(problem.dims(), Vec::::new()); + assert!(problem.evaluate(&[])); // empty sum = 0 = target +} + +#[test] +fn test_subset_sum_empty_set_nonzero_target() { + // Empty set, target 5 -> impossible + let problem = SubsetSum::new(vec![], 5); + assert!(!problem.evaluate(&[])); +} + +#[test] +fn test_subset_sum_brute_force() { + let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); + let solver = BruteForce::new(); + let solution = solver.find_satisfying(&problem); + assert!(solution.is_some()); + let sol = solution.unwrap(); + assert!(problem.evaluate(&sol)); +} + +#[test] +fn test_subset_sum_brute_force_all() { + let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); + let solver = BruteForce::new(); + let solutions = solver.find_all_satisfying(&problem); + assert!(!solutions.is_empty()); + for sol in &solutions { + assert!(problem.evaluate(sol)); + } +} + +#[test] +fn test_subset_sum_no_solution() { + // All sizes are even, target is odd -> no solution + let problem = SubsetSum::new(vec![2, 4, 6, 8], 3); + let solver = BruteForce::new(); + let solution = solver.find_satisfying(&problem); + assert!(solution.is_none()); +} + +#[test] +fn test_subset_sum_all_selected() { + // Target equals sum of all elements + let problem = SubsetSum::new(vec![1, 2, 3, 4], 10); + assert!(problem.evaluate(&[1, 1, 1, 1])); // 1+2+3+4 = 10 +} + +#[test] +fn test_subset_sum_single_element() { + let problem = SubsetSum::new(vec![5], 5); + assert!(problem.evaluate(&[1])); + assert!(!problem.evaluate(&[0])); +} + +#[test] +fn test_subset_sum_wrong_config_length() { + let problem = SubsetSum::new(vec![3, 7, 1], 11); + assert!(!problem.evaluate(&[1, 0])); // too short + assert!(!problem.evaluate(&[1, 0, 0, 1])); // too long +} + +#[test] +fn test_subset_sum_invalid_variable_value() { + let problem = SubsetSum::new(vec![3, 7], 3); + assert!(!problem.evaluate(&[2, 0])); // invalid: value >= 2 +} + +#[test] +fn test_subset_sum_serialization() { + let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); + let json = serde_json::to_value(&problem).unwrap(); + let restored: SubsetSum = serde_json::from_value(json).unwrap(); + assert_eq!(restored.sizes(), problem.sizes()); + assert_eq!(restored.target(), problem.target()); +} + +#[test] +fn test_subset_sum_is_valid_solution() { + let problem = SubsetSum::new(vec![3, 7, 1, 8, 2, 4], 11); + assert!(problem.is_valid_solution(&[1, 0, 0, 1, 0, 0])); + assert!(!problem.is_valid_solution(&[0, 0, 0, 0, 0, 0])); +} + +#[test] +fn test_subset_sum_target_zero() { + // Target 0 with non-empty set: only empty subset works + let problem = SubsetSum::new(vec![1, 2, 3], 0); + assert!(problem.evaluate(&[0, 0, 0])); // empty subset sums to 0 + assert!(!problem.evaluate(&[1, 0, 0])); // 1 != 0 +} From 1083dffd049eca418479d34c2e44978dce28b5b2 Mon Sep 17 00:00:00 2001 From: zazabap Date: Thu, 12 Mar 2026 13:24:23 +0000 Subject: [PATCH 3/6] chore: remove plan file after implementation --- docs/plans/2026-03-12-subset-sum-model.md | 45 ----------------------- 1 file changed, 45 deletions(-) delete mode 100644 docs/plans/2026-03-12-subset-sum-model.md diff --git a/docs/plans/2026-03-12-subset-sum-model.md b/docs/plans/2026-03-12-subset-sum-model.md deleted file mode 100644 index 64559e15a..000000000 --- a/docs/plans/2026-03-12-subset-sum-model.md +++ /dev/null @@ -1,45 +0,0 @@ -# Plan: Add SubsetSum Model (#402) - -## Overview -Add SubsetSum as a satisfaction problem in `src/models/misc/`. SubsetSum asks whether any subset of positive integers sums to a target value B. - -## Steps - -### 1. Model file: `src/models/misc/subset_sum.rs` -- Struct: `SubsetSum { sizes: Vec, target: u64 }` -- Getters: `sizes()`, `target()`, `num_items()` -- `Problem` impl: NAME = "SubsetSum", Metric = bool, dims = [2; n], evaluate checks if selected subset sums to target -- `SatisfactionProblem` impl (marker) -- `declare_variants!` with complexity `2^(num_items / 2)` (Horowitz-Sahni meet-in-the-middle) -- `inventory::submit!` for ProblemSchemaEntry -- Unit test path link - -### 2. Register in `src/models/misc/mod.rs` -- Add `mod subset_sum;` and `pub use subset_sum::SubsetSum;` - -### 3. Export from `src/lib.rs` prelude -- Add `SubsetSum` to the misc imports in prelude - -### 4. Unit tests: `src/unit_tests/models/misc/subset_sum.rs` -- test_subset_sum_basic: field accessors, dims, NAME, variant -- test_subset_sum_evaluate_feasible: config that sums to target -> true -- test_subset_sum_evaluate_infeasible: config that doesn't sum -> false -- test_subset_sum_empty: empty set -- test_subset_sum_brute_force: find_satisfying -- test_subset_sum_serialization: round-trip JSON -- test_subset_sum_no_solution: impossible target -- test_subset_sum_all_selected: all items sum to target - -### 5. CLI registration -- `problemreductions-cli/src/problem_name.rs`: add "subsetsum" alias -- `problemreductions-cli/src/dispatch.rs`: add deser_sat/try_ser for SubsetSum -- `problemreductions-cli/src/cli.rs`: add SubsetSum to help table -- `problemreductions-cli/src/commands/create.rs`: add create handler using --sizes and --target flags - -### 6. CLI args -- Reuse `--sizes` flag from BinPacking -- Add `--target` usage note (already exists for Factoring, but Factoring uses u64 target) -- SubsetSum uses: `pred create SubsetSum --sizes 3,7,1,8,2,4 --target 11` - -### 7. Regenerate exports -- `make export-schemas` From ddfc8cbe314e2e2301296e94fe02561f9fbf2248 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Fri, 13 Mar 2026 11:46:45 +0800 Subject: [PATCH 4/6] Regenerate reduction_graph.json and problem_schemas.json Address Copilot review comment: ensure SubsetSum appears in both generated JSON docs files. Co-Authored-By: Claude Opus 4.6 --- docs/src/reductions/reduction_graph.json | 26 +++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/src/reductions/reduction_graph.json b/docs/src/reductions/reduction_graph.json index 0b3bf3b6f..9dfc9c34d 100644 --- a/docs/src/reductions/reduction_graph.json +++ b/docs/src/reductions/reduction_graph.json @@ -1,3 +1,12 @@ +{ + "nodes": [ + { + "name": "BMF", + "variant": {}, + +Exported to: docs/src/reductions/reduction_graph.json + +JSON content: { "nodes": [ { @@ -412,6 +421,21 @@ } ], "edges": [ + { + "source": 3, + "target": 9, + "overhead": [ + { + "field": "num_vars", + "formula": "num_items * num_items + num_items" + }, + { + "field": "num_constraints", + "formula": "2 * num_items" + } + ], + "doc_path": "rules/binpacking_ilp/index.html" + }, { "source": 4, "target": 9, @@ -1195,4 +1219,4 @@ "doc_path": "rules/travelingsalesman_ilp/index.html" } ] -} \ No newline at end of file +} From 925796abdb76a738d5cb6a3bd7576997e52ccd10 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Fri, 13 Mar 2026 11:57:30 +0800 Subject: [PATCH 5/6] Add CLI format hints and example for SubsetSum Fix type_format_hint() to handle Vec and i64 types, and add SubsetSum entry to example_for() so `pred create SubsetSum` shows a helpful usage example. Co-Authored-By: Claude Opus 4.6 --- problemreductions-cli/src/commands/create.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/problemreductions-cli/src/commands/create.rs b/problemreductions-cli/src/commands/create.rs index 16cdea476..9d0996f3e 100644 --- a/problemreductions-cli/src/commands/create.rs +++ b/problemreductions-cli/src/commands/create.rs @@ -62,6 +62,8 @@ fn type_format_hint(type_name: &str, graph_type: Option<&str>) -> &'static str { "Vec>" => "semicolon-separated rows: \"1,0.5;0.5,2\"", "usize" => "integer", "u64" => "integer", + "i64" => "integer", + "Vec" => "comma-separated integers: 3,7,1,8", _ => "value", } } @@ -87,6 +89,7 @@ fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str { "SpinGlass" => "--graph 0-1,1-2 --couplings 1,1", "KColoring" => "--graph 0-1,1-2,2-0 --k 3", "Factoring" => "--target 15 --m 4 --n 4", + "SubsetSum" => "--sizes 3,7,1,8,2,4 --target 11", _ => "", } } From cb80f3aa12686325103e7e543ab4b707ca2561b2 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Fri, 13 Mar 2026 19:24:51 +0800 Subject: [PATCH 6/6] fix: regenerate corrupted reduction_graph.json and apply rustfmt - Fix corrupted reduction_graph.json that had stdout text mixed into the file - Apply rustfmt formatting to two example files Co-Authored-By: Claude Opus 4.6 --- .../maximumindependentset_to_maximumclique.json | 4 ++-- docs/src/reductions/reduction_graph.json | 11 +---------- examples/detect_unreachable_from_3sat.rs | 13 +++---------- examples/reduction_binpacking_to_ilp.rs | 14 ++++++-------- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/docs/paper/examples/maximumindependentset_to_maximumclique.json b/docs/paper/examples/maximumindependentset_to_maximumclique.json index 21a8853eb..352ddeb68 100644 --- a/docs/paper/examples/maximumindependentset_to_maximumclique.json +++ b/docs/paper/examples/maximumindependentset_to_maximumclique.json @@ -31,8 +31,8 @@ "target": { "problem": "MaximumClique", "variant": { - "weight": "i32", - "graph": "SimpleGraph" + "graph": "SimpleGraph", + "weight": "i32" }, "instance": { "edges": [ diff --git a/docs/src/reductions/reduction_graph.json b/docs/src/reductions/reduction_graph.json index 9dfc9c34d..92a965b9a 100644 --- a/docs/src/reductions/reduction_graph.json +++ b/docs/src/reductions/reduction_graph.json @@ -1,12 +1,3 @@ -{ - "nodes": [ - { - "name": "BMF", - "variant": {}, - -Exported to: docs/src/reductions/reduction_graph.json - -JSON content: { "nodes": [ { @@ -1219,4 +1210,4 @@ JSON content: "doc_path": "rules/travelingsalesman_ilp/index.html" } ] -} +} \ No newline at end of file diff --git a/examples/detect_unreachable_from_3sat.rs b/examples/detect_unreachable_from_3sat.rs index c48672276..b770ae6e9 100644 --- a/examples/detect_unreachable_from_3sat.rs +++ b/examples/detect_unreachable_from_3sat.rs @@ -109,8 +109,7 @@ fn main() { // Check if ALL variants of this problem are P-time // (conservative: if any variant could be hard, don't classify as P) let variants = graph.variants_for(name); - variants.len() == 1 - && variants[0].get(*key).map(|s| s.as_str()) == Some(*val) + variants.len() == 1 && variants[0].get(*key).map(|s| s.as_str()) == Some(*val) } } }); @@ -143,10 +142,7 @@ fn main() { } if !p_time.is_empty() { - println!( - "In P — correctly unreachable ({}):", - p_time.len() - ); + println!("In P — correctly unreachable ({}):", p_time.len()); for name in &p_time { println!(" {name}"); } @@ -165,10 +161,7 @@ fn main() { } if !orphans.is_empty() { - println!( - "Orphans — no reductions at all ({}):", - orphans.len() - ); + println!("Orphans — no reductions at all ({}):", orphans.len()); for name in &orphans { println!(" {name}"); } diff --git a/examples/reduction_binpacking_to_ilp.rs b/examples/reduction_binpacking_to_ilp.rs index 104d707a4..74b87e1bd 100644 --- a/examples/reduction_binpacking_to_ilp.rs +++ b/examples/reduction_binpacking_to_ilp.rs @@ -53,7 +53,10 @@ pub fn run() { // 5. Extract source solution let bp_solution = reduction.extract_solution(&ilp_solution); - println!("Source BinPacking solution (bin assignments): {:?}", bp_solution); + println!( + "Source BinPacking solution (bin assignments): {:?}", + bp_solution + ); // 6. Verify let size = bp.evaluate(&bp_solution); @@ -76,13 +79,8 @@ pub fn run() { let source_variant = variant_to_map(BinPacking::::variant()); let target_variant = variant_to_map(ILP::::variant()); - let overhead = lookup_overhead( - "BinPacking", - &source_variant, - "ILP", - &target_variant, - ) - .unwrap_or_default(); + let overhead = + lookup_overhead("BinPacking", &source_variant, "ILP", &target_variant).unwrap_or_default(); let data = ReductionData { source: ProblemSide {