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
90 changes: 3 additions & 87 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Problem (core trait — all problems must implement)
├── type Metric: Clone // SolutionSize<W> for optimization, bool for satisfaction
├── fn dims(&self) -> Vec<usize> // config space: [2, 2, 2] for 3 binary variables
├── fn evaluate(&self, config) -> Metric
├── fn variant() -> Vec<(&str, &str)> // [("graph","SimpleGraph"), ("weight","i32")]
├── fn variant() -> Vec<(&str, &str)> // e.g., [("graph","SimpleGraph"), ("weight","i32")]
└── fn num_variables(&self) -> usize // default: dims().len()

OptimizationProblem : Problem<Metric = SolutionSize<Self::Value>> (extension for optimization)
Expand All @@ -74,7 +74,7 @@ enum Direction { Maximize, Minimize }
```

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

## Adding a Reduction Rule (A -> B)

**Reference implementations — read these first:**
- **Reduction rule:** `src/rules/minimumvertexcover_maximumindependentset.rs` — `ReductionResult` + `ReduceTo` + `#[reduction]` macro
- **Unit test:** `src/unit_tests/rules/minimumvertexcover_maximumindependentset.rs` — closed-loop + edge cases
- **Example program:** `examples/reduction_minimumvertexcover_to_maximumindependentset.rs` — create, reduce, solve, extract, verify, export
- **Paper entry:** `docs/paper/reductions.typ` (search for `MinimumVertexCover` `MaximumIndependentSet`)
- **Traits:** `src/rules/traits.rs` — `ReductionResult` and `ReduceTo` trait definitions

### 0. Before Writing Code

1. **Ensure you have enough information**
- The reduction algorithm, from reliable source, e.g. a paper or a famous website.
- Which example instance to use in `examples/`, example is expected for human reading.
- The method to generate test data in `tests/data/<target>/` as json files.

otherwise use `superpowers:brainstorming` to discuss with the user.
2. **Write plan** — save to `docs/plans/` using `superpowers:writing-plans`.

### 1. Implement

Create `src/rules/<source>_<target>.rs` following the reference. Key pieces:

- **`ReductionResult` struct + impl** — `target_problem()` + `extract_solution()` (see reference)
- **`ReduceTo` impl with `#[reduction(...)]` macro** — auto-generates `inventory::submit!`; only `overhead` attribute needed (graph/weight types are inferred, defaulting to `SimpleGraph`/`Unweighted`)
- **`#[cfg(test)] #[path = ...]`** linking to unit tests

Register in `src/rules/mod.rs`.

### 2. Test

- **Unit tests** in `src/unit_tests/rules/<source>_<target>.rs` — closed-loop + edge cases (see reference test).
- **Integration tests** in `tests/suites/reductions.rs` — compare against JSON ground truth.

### 3. Example Program

Add `examples/reduction_<source>_to_<target>.rs` — create, reduce, solve, extract, verify, export JSON (see reference example).

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")`.

Register the example in `tests/suites/examples.rs` by adding:
```rust
example_test!(reduction_<source>_to_<target>);
example_fn!(test_<source>_to_<target>, reduction_<source>_to_<target>);
```

### 4. Document

Update `docs/paper/reductions.typ` — add `reduction-rule("Source", "Target", ...)` with proof sketch (see Documentation Requirements section below).

### 5. Regenerate Graph

```bash
cargo run --example export_graph
```

## Adding a Model (Problem Type)

**Reference implementations — read these first:**
- **Optimization problem:** `src/models/graph/maximum_independent_set.rs` — `Problem` + `OptimizationProblem` with `Metric = SolutionSize<W>`
- **Satisfaction problem:** `src/models/satisfiability/sat.rs` — `Problem` with `Metric = bool`
- **Reference test:** `src/unit_tests/models/graph/maximum_independent_set.rs`

### Steps

1. **Create** `src/models/<category>/<name>.rs` — follow the reference for struct definition, `Problem` impl, and `OptimizationProblem` impl (if applicable).
2. **Register** in `src/models/<category>/mod.rs`.
3. **Add tests** in `src/unit_tests/models/<category>/<name>.rs` (linked via `#[path]`).
4. **Document** in `docs/paper/reductions.typ`: add `display-name` entry and `#problem-def("Name")[definition...]`.

### Trait Implementations

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.

### Categories

- `src/models/satisfiability/` — Satisfiability, KSatisfiability
- `src/models/graph/` — MaximumIndependentSet, MinimumVertexCover, KColoring, etc.
- `src/models/set/` — MinimumSetCovering, MaximumSetPacking
- `src/models/optimization/` — SpinGlass, QUBO, ILP
- `src/models/specialized/` — CircuitSAT, Factoring, PaintShop, BicliqueCover, BMF

Naming convention: see Problem Names above.

## Testing Requirements

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

### File Organization

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).
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.

## Documentation Requirements

Expand Down
69 changes: 68 additions & 1 deletion .claude/skills/issue-to-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,74 @@ If the reference is a paper or textbook, search for accessible summaries, lectur

### 5. Write Plan

Write plan to `docs/plans/YYYY-MM-DD-<slug>.md` using `superpowers:writing-plans`:
Write plan to `docs/plans/YYYY-MM-DD-<slug>.md` using `superpowers:writing-plans`.

The plan MUST include an **action pipeline** section with concrete steps based on issue type.

#### For `[Rule]` issues (A -> B reduction)

**Reference implementations — read these first:**
- Reduction rule: `src/rules/minimumvertexcover_maximumindependentset.rs`
- Unit test: `src/unit_tests/rules/minimumvertexcover_maximumindependentset.rs`
- Example program: `examples/reduction_minimumvertexcover_to_maximumindependentset.rs`
- Paper entry: search `docs/paper/reductions.typ` for `MinimumVertexCover` `MaximumIndependentSet`
- Traits: `src/rules/traits.rs`

**Action pipeline:**

1. **Implement reduction** — Create `src/rules/<source>_<target>.rs`:
- `ReductionResult` struct + impl (`target_problem()` + `extract_solution()`)
- `ReduceTo` impl with `#[reduction(...)]` macro (only `overhead` attribute needed)
- `#[cfg(test)] #[path = ...]` linking to unit tests
- Register in `src/rules/mod.rs`

2. **Write unit tests** — Create `src/unit_tests/rules/<source>_<target>.rs`:
- Closed-loop test: create source → reduce → solve target → extract → verify
- Edge cases

3. **Write example program** — Create `examples/reduction_<source>_to_<target>.rs`:
- Must have `pub fn run()` + `fn main() { run() }`
- Use regular comments (`//`), hardcode example name
- Create, reduce, solve, extract, verify, export JSON
- Register in `tests/suites/examples.rs`

4. **Document in paper** — Update `docs/paper/reductions.typ`:
- Add `reduction-rule("Source", "Target", ...)` with proof sketch
- Present example in tutorial style (see KColoring→QUBO section for reference)

5. **Regenerate graph** — `cargo run --example export_graph`

**Rules for solver implementation:**
- 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.
- If the solver uses integer programming, implement the model and ILP reduction rule together.
- Otherwise, ensure the information provided is enough to implement a solver.

**Rules for example writing:**
- Implement the user-provided example instance as an example program in `examples/`.
- Run the example; verify JSON output against user-provided information.
- Present in `docs/paper/reductions.typ` in tutorial style with clear intuition (see KColoring→QUBO section for reference).

#### For `[Model]` issues

**Reference implementations — read these first:**
- Optimization problem: `src/models/graph/maximum_independent_set.rs`
- Satisfaction problem: `src/models/satisfiability/sat.rs`
- Reference test: `src/unit_tests/models/graph/maximum_independent_set.rs`

**Action pipeline:**

1. **Implement model** — Create `src/models/<category>/<name>.rs`:
- Struct definition, `Problem` impl, `OptimizationProblem` impl (if applicable)
- Weight management via inherent methods (`weights()`, `set_weights()`, `is_weighted()`), not traits
- Register in `src/models/<category>/mod.rs`

2. **Write tests** — Create `src/unit_tests/models/<category>/<name>.rs`:
- Basic evaluation tests, serialization tests
- Link via `#[path]`

3. **Document** — Update `docs/paper/reductions.typ`:
- Add `display-name` entry
- Add `#problem-def("Name")[definition...]`

### 6. Create PR

Expand Down
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/problem.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ Connect fields to the symbols defined above.
## How to solve
<!--
Solver is required for reduction rule verification purpose.
- Can it be solved by (existing) bruteforce?
- Can it be solved by reducing the integer programming? If so, how to reduce?
- If none apply
-->
- [ ] It can be solved by (existing) bruteforce.
- [ ] It can be solved by reducing the integer programming, through #issue-number (please file a new issue it is not exist).
- [ ] Other, refer to ...

## Example Instance

Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ E.g.
- Use external solver to cross-check
-->

## Example Source Instance
## Example

<!-- A small but non-trivial source instance for the paper illustration.
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.
Expand Down
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,10 @@ assert_eq!(solution.iter().sum::<usize>(), 2); // Max IS size is 2

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.).

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:
```
Create a pull request starting with "[action]" in the description.
```
to trigger automated implementation.
2. Our AI agents will pick-up the issue and generate a plan to implement the reduction rule.
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.

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.

### Developer Commands

Expand Down
2 changes: 1 addition & 1 deletion benches/solver_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ fn bench_coloring(c: &mut Criterion) {

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

group.bench_with_input(BenchmarkId::new("path_3colors", n), n, |b, _| {
Expand Down
121 changes: 121 additions & 0 deletions docs/paper/lib.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Graph visualization library for the problem-reductions paper
#import "@preview/cetz:0.4.2": canvas, draw

// ── Style defaults ─────────────────────────────────────────────

// Color palette for k-coloring visualizations
#let graph-colors = (rgb("#4e79a7"), rgb("#e15759"), rgb("#76b7b2"))

// Weight-based fill colors for grid graph nodes
#let weight-color(w) = if w == 1 { blue } else if w == 2 { red } else { green }

// ── Primitives: g-node, g-edge ─────────────────────────────────
// All graph drawing goes through these two functions.
// They define the standard style; callers can override any parameter.

// Draw a single graph node.
// pos: (x, y) position
// name: CetZ element name (for edge references)
// label: none or content to place inside the node
#let g-node(
pos,
name: none,
radius: 0.2,
fill: white,
stroke: 0.5pt,
label: none,
label-size: 8pt,
) = {
draw.circle(pos, radius: radius, fill: fill, stroke: stroke, name: name)
if label != none {
draw.content(name, text(label-size, label))
}
}

// Draw a single graph edge between two named nodes or positions.
#let g-edge(
from,
to,
stroke: 1pt + black,
) = {
draw.line(from, to, stroke: stroke)
}

// ── Pre-defined graph layouts ──────────────────────────────────
// Each returns (vertices: [...], edges: [...])

// Petersen graph: outer pentagon (0-4) + inner star (5-9)
#let petersen-graph() = {
let r-outer = 1.2
let r-inner = 0.6
let vertices = ()
for i in range(5) {
let angle = 90deg - i * 72deg
vertices.push((calc.cos(angle) * r-outer, calc.sin(angle) * r-outer))
}
for i in range(5) {
let angle = 90deg - i * 72deg
vertices.push((calc.cos(angle) * r-inner, calc.sin(angle) * r-inner))
}
let edges = (
(0,1),(0,4),(0,5),(1,2),(1,6),(2,3),(2,7),(3,4),(3,8),(4,9),
(5,7),(5,8),(6,8),(6,9),(7,9),
)
(vertices: vertices, edges: edges)
}

// House graph: square base (0-1-3-2) + triangle roof (2-3-4)
#let house-graph() = {
let vertices = ((0, 0), (1, 0), (0, 1), (1, 1), (0.5, 1.7))
let edges = ((0,1),(0,2),(1,3),(2,3),(2,4),(3,4))
(vertices: vertices, edges: edges)
}

// Octahedral graph (K_{2,2,2}): 6 vertices, 12 edges
// Layout: top/bottom poles with 4 equatorial vertices
#let octahedral-graph() = {
let vertices = (
(0, -1.2), // 0: bottom pole
(-1.0, 0), // 1: left
(0, 0.5), // 2: upper-center
(0, -0.5), // 3: lower-center
(1.0, 0), // 4: right
(0, 1.2), // 5: top pole
)
let edges = (
(0,1),(0,2),(0,3),(0,4),(1,2),(1,3),(1,5),(2,4),(2,5),(3,4),(3,5),(4,5),
)
(vertices: vertices, edges: edges)
}

// ── Grid graph functions (JSON-driven) ─────────────────────────
// Extract positions from JSON, draw with dense styling via g-node/g-edge.

// King's subgraph from JSON with weight-based coloring
#let draw-grid-graph(data, cell-size: 0.2) = canvas(length: 1cm, {
let grid-data = data.grid_graph
let positions = grid-data.nodes.map(n => (n.col * cell-size, -n.row * cell-size))
let fills = grid-data.nodes.map(n => weight-color(n.weight))
let edges = grid-data.edges.map(e => (e.at(0), e.at(1)))
for (u, v) in edges { g-edge(positions.at(u), positions.at(v), stroke: 0.4pt + gray) }
for (k, pos) in positions.enumerate() {
g-node(pos, radius: 0.04, stroke: none, fill: fills.at(k))
}
})

// Triangular lattice from JSON with weight-based coloring
// Matches Rust GridGraph::physical_position_static for Triangular (offset_even_cols=true)
#let draw-triangular-graph(data, cell-size: 0.2) = canvas(length: 1cm, {
let grid-data = data.grid_graph
let sqrt3_2 = calc.sqrt(3) / 2
let positions = grid-data.nodes.map(n => {
let offset = if calc.rem(n.col, 2) == 0 { 0.5 } else { 0.0 }
((n.row + offset) * cell-size, -n.col * sqrt3_2 * cell-size)
})
let fills = grid-data.nodes.map(n => weight-color(n.weight))
let edges = grid-data.edges.map(e => (e.at(0), e.at(1)))
for (u, v) in edges { g-edge(positions.at(u), positions.at(v), stroke: 0.3pt + gray) }
for (k, pos) in positions.enumerate() {
g-node(pos, radius: 0.025, stroke: none, fill: fills.at(k))
}
})
Loading