Skip to content

Commit 65bb77c

Browse files
GiggleLiuclaude
andauthored
Remove weight type parameter from CircuitSAT and KColoring (#56)
* docs: add graph visualization library and tutorial-style KColoring→QUBO example Create docs/paper/lib.typ with reusable graph drawing functions (draw-graph, petersen-graph, house-graph, octahedral-graph, draw-grid-graph, draw-triangular-graph) and import it from reductions.typ. Add a featured KColoring→QUBO example with a colored house graph visualization and a 4-step tutorial walkthrough (encode, one-hot penalty, edge conflict, verify). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove weight type parameter from CircuitSAT and KColoring CircuitSAT and KColoring are satisfaction problems (Metric = bool), not optimization problems, so they should not have a weight type parameter W. - CircuitSAT: remove W, simplify from CircuitSAT<W> to CircuitSAT - KColoring: remove W, simplify from KColoring<K, G, W> to KColoring<K, G> - Fix #[reduction] macro: check second type param is actually a weight type before treating it as one (fixes KColoring<K, SimpleGraph> being misinterpreted as having weight "SimpleGraph") - Update all reduction rules, tests, examples, benchmarks - Regenerate reduction_graph.json Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add reduction macro redesign design doc Dynamic variant extraction using fn pointers instead of static inference from type parameters. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: make CLAUDE.md declarative, move action pipelines to issue-to-pr skill Move "Adding a Reduction Rule" and "Adding a Model" procedural sections from CLAUDE.md into the issue-to-pr skill's plan-writing step. CLAUDE.md now contains only global rules and reference; the skill contains actionable pipelines keyed by issue type ([Rule] and [Model]). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: natural variant reductions, improved tests, and paper updates - Add natural variant reduction design and implementation plans - Expand unit tests with variant checking and edge cases - Update paper with streamlined reduction content - Update examples with improved structure - Update issue template and README Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: auto-derive reduction variants via Problem::variant() Replace hardcoded ("graph", ...), ("weight", ...) variant fields in the #[reduction] macro with fn pointers that call Problem::variant() at runtime. This makes Problem::variant() the single source of truth for variant fields, fixing disconnected KColoring and KSatisfiability nodes in the reduction graph. Key changes: - ReductionEntry/ConcreteVariantEntry use fn pointers for variants - Macro substitutes usize::MAX for const generics (maps to "N") - Added "k" field support in is_variant_reducible() for natural edges - KColoring[k=3] → KColoring[k=N] → ILP/QUBO now properly connected - KSatisfiability[k=2,3] → KSatisfiability[k=N] now properly connected Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 715ae78 commit 65bb77c

83 files changed

Lines changed: 2172 additions & 860 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CLAUDE.md

Lines changed: 3 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Problem (core trait — all problems must implement)
5656
├── type Metric: Clone // SolutionSize<W> for optimization, bool for satisfaction
5757
├── fn dims(&self) -> Vec<usize> // config space: [2, 2, 2] for 3 binary variables
5858
├── fn evaluate(&self, config) -> Metric
59-
├── fn variant() -> Vec<(&str, &str)> // [("graph","SimpleGraph"), ("weight","i32")]
59+
├── fn variant() -> Vec<(&str, &str)> // e.g., [("graph","SimpleGraph"), ("weight","i32")]
6060
└── fn num_variables(&self) -> usize // default: dims().len()
6161
6262
OptimizationProblem : Problem<Metric = SolutionSize<Self::Value>> (extension for optimization)
@@ -74,7 +74,7 @@ enum Direction { Maximize, Minimize }
7474
```
7575

7676
### Key Patterns
77-
- Problems parameterized by weight type `W` and graph type `G`
77+
- Problems parameterized by graph type `G` and optionally weight type `W` (problem-dependent)
7878
- `ReductionResult` provides `target_problem()` and `extract_solution()`
7979
- `Solver::find_best()``Option<Vec<usize>>` for optimization problems; `Solver::find_satisfying()``Option<Vec<usize>>` for `Metric = bool`
8080
- `BruteForce::find_all_best()` / `find_all_satisfying()` return `Vec<Vec<usize>>` for all optimal/satisfying solutions
@@ -111,90 +111,6 @@ Reduction graph nodes use variant IDs: `ProblemName[/GraphType][/Weighted]`
111111
- Completeness warnings auto-check that all JSON graph nodes/edges are covered in the paper
112112
- `display-name` dict maps `ProblemName` to display text
113113

114-
## Adding a Reduction Rule (A -> B)
115-
116-
**Reference implementations — read these first:**
117-
- **Reduction rule:** `src/rules/minimumvertexcover_maximumindependentset.rs``ReductionResult` + `ReduceTo` + `#[reduction]` macro
118-
- **Unit test:** `src/unit_tests/rules/minimumvertexcover_maximumindependentset.rs` — closed-loop + edge cases
119-
- **Example program:** `examples/reduction_minimumvertexcover_to_maximumindependentset.rs` — create, reduce, solve, extract, verify, export
120-
- **Paper entry:** `docs/paper/reductions.typ` (search for `MinimumVertexCover` `MaximumIndependentSet`)
121-
- **Traits:** `src/rules/traits.rs``ReductionResult` and `ReduceTo` trait definitions
122-
123-
### 0. Before Writing Code
124-
125-
1. **Ensure you have enough information**
126-
- The reduction algorithm, from reliable source, e.g. a paper or a famous website.
127-
- Which example instance to use in `examples/`, example is expected for human reading.
128-
- The method to generate test data in `tests/data/<target>/` as json files.
129-
130-
otherwise use `superpowers:brainstorming` to discuss with the user.
131-
2. **Write plan** — save to `docs/plans/` using `superpowers:writing-plans`.
132-
133-
### 1. Implement
134-
135-
Create `src/rules/<source>_<target>.rs` following the reference. Key pieces:
136-
137-
- **`ReductionResult` struct + impl**`target_problem()` + `extract_solution()` (see reference)
138-
- **`ReduceTo` impl with `#[reduction(...)]` macro** — auto-generates `inventory::submit!`; only `overhead` attribute needed (graph/weight types are inferred, defaulting to `SimpleGraph`/`Unweighted`)
139-
- **`#[cfg(test)] #[path = ...]`** linking to unit tests
140-
141-
Register in `src/rules/mod.rs`.
142-
143-
### 2. Test
144-
145-
- **Unit tests** in `src/unit_tests/rules/<source>_<target>.rs` — closed-loop + edge cases (see reference test).
146-
- **Integration tests** in `tests/suites/reductions.rs` — compare against JSON ground truth.
147-
148-
### 3. Example Program
149-
150-
Add `examples/reduction_<source>_to_<target>.rs` — create, reduce, solve, extract, verify, export JSON (see reference example).
151-
152-
Examples must expose `pub fn run()` with `fn main() { run() }` so they can be tested directly via `include!` (no subprocess). Use regular comments (`//`) not inner doc comments (`//!`), and hardcode the example name instead of using `env!("CARGO_BIN_NAME")`.
153-
154-
Register the example in `tests/suites/examples.rs` by adding:
155-
```rust
156-
example_test!(reduction_<source>_to_<target>);
157-
example_fn!(test_<source>_to_<target>, reduction_<source>_to_<target>);
158-
```
159-
160-
### 4. Document
161-
162-
Update `docs/paper/reductions.typ` — add `reduction-rule("Source", "Target", ...)` with proof sketch (see Documentation Requirements section below).
163-
164-
### 5. Regenerate Graph
165-
166-
```bash
167-
cargo run --example export_graph
168-
```
169-
170-
## Adding a Model (Problem Type)
171-
172-
**Reference implementations — read these first:**
173-
- **Optimization problem:** `src/models/graph/maximum_independent_set.rs``Problem` + `OptimizationProblem` with `Metric = SolutionSize<W>`
174-
- **Satisfaction problem:** `src/models/satisfiability/sat.rs``Problem` with `Metric = bool`
175-
- **Reference test:** `src/unit_tests/models/graph/maximum_independent_set.rs`
176-
177-
### Steps
178-
179-
1. **Create** `src/models/<category>/<name>.rs` — follow the reference for struct definition, `Problem` impl, and `OptimizationProblem` impl (if applicable).
180-
2. **Register** in `src/models/<category>/mod.rs`.
181-
3. **Add tests** in `src/unit_tests/models/<category>/<name>.rs` (linked via `#[path]`).
182-
4. **Document** in `docs/paper/reductions.typ`: add `display-name` entry and `#problem-def("Name")[definition...]`.
183-
184-
### Trait Implementations
185-
186-
See Trait Hierarchy above for `Problem` and `OptimizationProblem` members. Weight management (`weights()`, `set_weights()`, `is_weighted()`) goes on inherent `impl` blocks, not traits. See the reference implementation for the pattern.
187-
188-
### Categories
189-
190-
- `src/models/satisfiability/` — Satisfiability, KSatisfiability
191-
- `src/models/graph/` — MaximumIndependentSet, MinimumVertexCover, KColoring, etc.
192-
- `src/models/set/` — MinimumSetCovering, MaximumSetPacking
193-
- `src/models/optimization/` — SpinGlass, QUBO, ILP
194-
- `src/models/specialized/` — CircuitSAT, Factoring, PaintShop, BicliqueCover, BMF
195-
196-
Naming convention: see Problem Names above.
197-
198114
## Testing Requirements
199115

200116
**Reference implementations — read these first:**
@@ -219,7 +135,7 @@ See Key Patterns above for solver API signatures. Follow the reference files for
219135

220136
### File Organization
221137

222-
Unit tests in `src/unit_tests/` linked via `#[path]` (see Core Modules above). Integration tests in `tests/suites/`, consolidated through `tests/main.rs`. Example tests in `tests/suites/examples.rs` (see Example Program in Adding a Reduction above).
138+
Unit tests in `src/unit_tests/` linked via `#[path]` (see Core Modules above). Integration tests in `tests/suites/`, consolidated through `tests/main.rs`. Example tests in `tests/suites/examples.rs` using `include!` for direct invocation.
223139

224140
## Documentation Requirements
225141

.claude/skills/issue-to-pr.md

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,74 @@ If the reference is a paper or textbook, search for accessible summaries, lectur
7272

7373
### 5. Write Plan
7474

75-
Write plan to `docs/plans/YYYY-MM-DD-<slug>.md` using `superpowers:writing-plans`:
75+
Write plan to `docs/plans/YYYY-MM-DD-<slug>.md` using `superpowers:writing-plans`.
76+
77+
The plan MUST include an **action pipeline** section with concrete steps based on issue type.
78+
79+
#### For `[Rule]` issues (A -> B reduction)
80+
81+
**Reference implementations — read these first:**
82+
- Reduction rule: `src/rules/minimumvertexcover_maximumindependentset.rs`
83+
- Unit test: `src/unit_tests/rules/minimumvertexcover_maximumindependentset.rs`
84+
- Example program: `examples/reduction_minimumvertexcover_to_maximumindependentset.rs`
85+
- Paper entry: search `docs/paper/reductions.typ` for `MinimumVertexCover` `MaximumIndependentSet`
86+
- Traits: `src/rules/traits.rs`
87+
88+
**Action pipeline:**
89+
90+
1. **Implement reduction** — Create `src/rules/<source>_<target>.rs`:
91+
- `ReductionResult` struct + impl (`target_problem()` + `extract_solution()`)
92+
- `ReduceTo` impl with `#[reduction(...)]` macro (only `overhead` attribute needed)
93+
- `#[cfg(test)] #[path = ...]` linking to unit tests
94+
- Register in `src/rules/mod.rs`
95+
96+
2. **Write unit tests** — Create `src/unit_tests/rules/<source>_<target>.rs`:
97+
- Closed-loop test: create source → reduce → solve target → extract → verify
98+
- Edge cases
99+
100+
3. **Write example program** — Create `examples/reduction_<source>_to_<target>.rs`:
101+
- Must have `pub fn run()` + `fn main() { run() }`
102+
- Use regular comments (`//`), hardcode example name
103+
- Create, reduce, solve, extract, verify, export JSON
104+
- Register in `tests/suites/examples.rs`
105+
106+
4. **Document in paper** — Update `docs/paper/reductions.typ`:
107+
- Add `reduction-rule("Source", "Target", ...)` with proof sketch
108+
- Present example in tutorial style (see KColoring→QUBO section for reference)
109+
110+
5. **Regenerate graph**`cargo run --example export_graph`
111+
112+
**Rules for solver implementation:**
113+
- Make sure at least one solver is provided in the issue template. Check if the solving strategy is valid. If not, reply under issue to ask for clarification.
114+
- If the solver uses integer programming, implement the model and ILP reduction rule together.
115+
- Otherwise, ensure the information provided is enough to implement a solver.
116+
117+
**Rules for example writing:**
118+
- Implement the user-provided example instance as an example program in `examples/`.
119+
- Run the example; verify JSON output against user-provided information.
120+
- Present in `docs/paper/reductions.typ` in tutorial style with clear intuition (see KColoring→QUBO section for reference).
121+
122+
#### For `[Model]` issues
123+
124+
**Reference implementations — read these first:**
125+
- Optimization problem: `src/models/graph/maximum_independent_set.rs`
126+
- Satisfaction problem: `src/models/satisfiability/sat.rs`
127+
- Reference test: `src/unit_tests/models/graph/maximum_independent_set.rs`
128+
129+
**Action pipeline:**
130+
131+
1. **Implement model** — Create `src/models/<category>/<name>.rs`:
132+
- Struct definition, `Problem` impl, `OptimizationProblem` impl (if applicable)
133+
- Weight management via inherent methods (`weights()`, `set_weights()`, `is_weighted()`), not traits
134+
- Register in `src/models/<category>/mod.rs`
135+
136+
2. **Write tests** — Create `src/unit_tests/models/<category>/<name>.rs`:
137+
- Basic evaluation tests, serialization tests
138+
- Link via `#[path]`
139+
140+
3. **Document** — Update `docs/paper/reductions.typ`:
141+
- Add `display-name` entry
142+
- Add `#problem-def("Name")[definition...]`
76143

77144
### 6. Create PR
78145

.github/ISSUE_TEMPLATE/problem.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ Connect fields to the symbols defined above.
5555
## How to solve
5656
<!--
5757
Solver is required for reduction rule verification purpose.
58-
- Can it be solved by (existing) bruteforce?
59-
- Can it be solved by reducing the integer programming? If so, how to reduce?
60-
- If none apply
6158
-->
59+
- [ ] It can be solved by (existing) bruteforce.
60+
- [ ] It can be solved by reducing the integer programming, through #issue-number (please file a new issue it is not exist).
61+
- [ ] Other, refer to ...
6262

6363
## Example Instance
6464

.github/ISSUE_TEMPLATE/rule.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ E.g.
4343
- Use external solver to cross-check
4444
-->
4545

46-
## Example Source Instance
46+
## Example
4747

4848
<!-- A small but non-trivial source instance for the paper illustration.
4949
Must be small enough for brute-force solving, but large enough to exercise the reduction meaningfully. E.g. "petersen graph: |V|=10, |E|=15, 3-regular" should be perfect.

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,10 @@ assert_eq!(solution.iter().sum::<usize>(), 2); // Max IS size is 2
5454

5555
1. **Open an issue** using the [Problem](https://github.com/CodingThrust/problem-reductions/issues/new?template=problem.md) or [Rule](https://github.com/CodingThrust/problem-reductions/issues/new?template=rule.md) template. Fill in all sections — the templates guide you through the required information (definition, algorithm, size overhead, example instance, etc.).
5656

57-
2. Optionally, if you prefer to make a **concrete plan** or **implement yourself**, I will recommend you to use the [superpowers:brainstorming](https://github.com/obra/superpowers) skill to help you write a detailed plan. After making implementation plan, you can either implement the plan yourself or create a PR with prompt:
58-
```
59-
Create a pull request starting with "[action]" in the description.
60-
```
61-
to trigger automated implementation.
57+
2. Our AI agents will pick-up the issue and generate a plan to implement the reduction rule.
58+
3. You will be mentioned in the pull request, provide feedback to the AI agents. If you are satisfied with the plan, you can merge the PR.
59+
60+
Optionally, if you prefer to **implement yourself**, I will recommend you to use the [superpowers:brainstorming](https://github.com/obra/superpowers) skill to help you write a detailed plan. Create a PR and let maintainers help review and merge the PR.
6261

6362
### Developer Commands
6463

benches/solver_benchmarks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ fn bench_coloring(c: &mut Criterion) {
138138

139139
for n in [3, 4, 5, 6].iter() {
140140
let edges: Vec<(usize, usize)> = (0..*n - 1).map(|i| (i, i + 1)).collect();
141-
let problem = KColoring::<3, SimpleGraph, i32>::new(*n, edges);
141+
let problem = KColoring::<3, SimpleGraph>::new(*n, edges);
142142
let solver = BruteForce::new();
143143

144144
group.bench_with_input(BenchmarkId::new("path_3colors", n), n, |b, _| {

docs/paper/lib.typ

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Graph visualization library for the problem-reductions paper
2+
#import "@preview/cetz:0.4.2": canvas, draw
3+
4+
// ── Style defaults ─────────────────────────────────────────────
5+
6+
// Color palette for k-coloring visualizations
7+
#let graph-colors = (rgb("#4e79a7"), rgb("#e15759"), rgb("#76b7b2"))
8+
9+
// Weight-based fill colors for grid graph nodes
10+
#let weight-color(w) = if w == 1 { blue } else if w == 2 { red } else { green }
11+
12+
// ── Primitives: g-node, g-edge ─────────────────────────────────
13+
// All graph drawing goes through these two functions.
14+
// They define the standard style; callers can override any parameter.
15+
16+
// Draw a single graph node.
17+
// pos: (x, y) position
18+
// name: CetZ element name (for edge references)
19+
// label: none or content to place inside the node
20+
#let g-node(
21+
pos,
22+
name: none,
23+
radius: 0.2,
24+
fill: white,
25+
stroke: 0.5pt,
26+
label: none,
27+
label-size: 8pt,
28+
) = {
29+
draw.circle(pos, radius: radius, fill: fill, stroke: stroke, name: name)
30+
if label != none {
31+
draw.content(name, text(label-size, label))
32+
}
33+
}
34+
35+
// Draw a single graph edge between two named nodes or positions.
36+
#let g-edge(
37+
from,
38+
to,
39+
stroke: 1pt + black,
40+
) = {
41+
draw.line(from, to, stroke: stroke)
42+
}
43+
44+
// ── Pre-defined graph layouts ──────────────────────────────────
45+
// Each returns (vertices: [...], edges: [...])
46+
47+
// Petersen graph: outer pentagon (0-4) + inner star (5-9)
48+
#let petersen-graph() = {
49+
let r-outer = 1.2
50+
let r-inner = 0.6
51+
let vertices = ()
52+
for i in range(5) {
53+
let angle = 90deg - i * 72deg
54+
vertices.push((calc.cos(angle) * r-outer, calc.sin(angle) * r-outer))
55+
}
56+
for i in range(5) {
57+
let angle = 90deg - i * 72deg
58+
vertices.push((calc.cos(angle) * r-inner, calc.sin(angle) * r-inner))
59+
}
60+
let edges = (
61+
(0,1),(0,4),(0,5),(1,2),(1,6),(2,3),(2,7),(3,4),(3,8),(4,9),
62+
(5,7),(5,8),(6,8),(6,9),(7,9),
63+
)
64+
(vertices: vertices, edges: edges)
65+
}
66+
67+
// House graph: square base (0-1-3-2) + triangle roof (2-3-4)
68+
#let house-graph() = {
69+
let vertices = ((0, 0), (1, 0), (0, 1), (1, 1), (0.5, 1.7))
70+
let edges = ((0,1),(0,2),(1,3),(2,3),(2,4),(3,4))
71+
(vertices: vertices, edges: edges)
72+
}
73+
74+
// Octahedral graph (K_{2,2,2}): 6 vertices, 12 edges
75+
// Layout: top/bottom poles with 4 equatorial vertices
76+
#let octahedral-graph() = {
77+
let vertices = (
78+
(0, -1.2), // 0: bottom pole
79+
(-1.0, 0), // 1: left
80+
(0, 0.5), // 2: upper-center
81+
(0, -0.5), // 3: lower-center
82+
(1.0, 0), // 4: right
83+
(0, 1.2), // 5: top pole
84+
)
85+
let edges = (
86+
(0,1),(0,2),(0,3),(0,4),(1,2),(1,3),(1,5),(2,4),(2,5),(3,4),(3,5),(4,5),
87+
)
88+
(vertices: vertices, edges: edges)
89+
}
90+
91+
// ── Grid graph functions (JSON-driven) ─────────────────────────
92+
// Extract positions from JSON, draw with dense styling via g-node/g-edge.
93+
94+
// King's subgraph from JSON with weight-based coloring
95+
#let draw-grid-graph(data, cell-size: 0.2) = canvas(length: 1cm, {
96+
let grid-data = data.grid_graph
97+
let positions = grid-data.nodes.map(n => (n.col * cell-size, -n.row * cell-size))
98+
let fills = grid-data.nodes.map(n => weight-color(n.weight))
99+
let edges = grid-data.edges.map(e => (e.at(0), e.at(1)))
100+
for (u, v) in edges { g-edge(positions.at(u), positions.at(v), stroke: 0.4pt + gray) }
101+
for (k, pos) in positions.enumerate() {
102+
g-node(pos, radius: 0.04, stroke: none, fill: fills.at(k))
103+
}
104+
})
105+
106+
// Triangular lattice from JSON with weight-based coloring
107+
// Matches Rust GridGraph::physical_position_static for Triangular (offset_even_cols=true)
108+
#let draw-triangular-graph(data, cell-size: 0.2) = canvas(length: 1cm, {
109+
let grid-data = data.grid_graph
110+
let sqrt3_2 = calc.sqrt(3) / 2
111+
let positions = grid-data.nodes.map(n => {
112+
let offset = if calc.rem(n.col, 2) == 0 { 0.5 } else { 0.0 }
113+
((n.row + offset) * cell-size, -n.col * sqrt3_2 * cell-size)
114+
})
115+
let fills = grid-data.nodes.map(n => weight-color(n.weight))
116+
let edges = grid-data.edges.map(e => (e.at(0), e.at(1)))
117+
for (u, v) in edges { g-edge(positions.at(u), positions.at(v), stroke: 0.3pt + gray) }
118+
for (k, pos) in positions.enumerate() {
119+
g-node(pos, radius: 0.025, stroke: none, fill: fills.at(k))
120+
}
121+
})

0 commit comments

Comments
 (0)