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
5 changes: 3 additions & 2 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ Reduction graph nodes use variant IDs: `ProblemName[/GraphType][/Weighted]`
- Test naming: `test_<source>_to_<target>_closed_loop`

### Paper (docs/paper/reductions.typ)
- `problem-def(name, title, body)` — defines a problem with auto-generated schema, reductions list, and label `<def:ProblemName>`
- `reduction-rule(source, target, ...)` — generates a theorem with label `<thm:Source-to-Target>` and registers in `covered-rules` state
- `problem-def(name)[body]` — defines a problem with auto-generated schema, reductions list, and label `<def:ProblemName>`. Title comes from `display-name` dict.
- `reduction-rule(source, target, example: bool, ...)[rule][proof]` — generates a theorem with label `<thm:Source-to-Target>` and registers in `covered-rules` state. Overhead auto-derived from JSON edge data.
- Every directed reduction needs its own `reduction-rule` entry
- Completeness warnings auto-check that all JSON graph nodes/edges are covered in the paper
- `display-name` dict maps `ProblemName` to display text

Expand Down
68 changes: 18 additions & 50 deletions .claude/rules/adding-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,23 @@ paths:

# Adding a Model (Problem Type)

## 1. Define the Model
Create `src/models/<category>/<name>.rs`:

```rust
use serde::{Deserialize, Serialize};
use crate::traits::{Problem, ProblemSize};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MyProblem<W> {
// Problem data fields
pub size: usize,
pub weights: Vec<W>,
// ...
}

impl<W: Clone> Problem for MyProblem<W> {
fn num_variables(&self) -> usize { ... }
fn problem_size(&self) -> ProblemSize { ... }
fn is_valid_solution(&self, solution: &[usize]) -> bool { ... }
}
```

## 2. Register in Module
Add to `src/models/<category>/mod.rs`:
```rust
mod my_problem;
pub use my_problem::MyProblem;
```

## 3. Categories
Place models in appropriate category:
- `src/models/satisfiability/` - Satisfiability, KSatisfiability, CircuitSAT
- `src/models/graph/` - MaximumIndependentSet, MinimumVertexCover, KColoring, etc.
- `src/models/set/` - MinimumSetCovering, MaximumSetPacking
- `src/models/optimization/` - SpinGlass, QUBO, ILP

## 4. Required Traits
- `Serialize`, `Deserialize` - JSON I/O support
- `Clone`, `Debug` - Standard Rust traits
- `Problem` - Core trait with `num_variables()`, `problem_size()`, `is_valid_solution()`
- Consider `ConstraintSatisfactionProblem` if applicable

## 5. Naming
Use explicit optimization prefixes: `Maximum` for maximization, `Minimum` for minimization (e.g., `MaximumIndependentSet`, `MinimumVertexCover`).
**Reference implementation:** `src/models/graph/kcoloring.rs`

## Steps

1. **Create** `src/models/<category>/<name>.rs` — follow the reference for struct definition, `Problem` impl, and optionally `ConstraintSatisfactionProblem` impl.
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...]`.

## 6. Documentation
Document in `docs/paper/reductions.typ` using `#problem-def("ProblemName", "Display Title")[...]`
## Categories

## Anti-patterns
- Don't create models without JSON serialization support
- Don't forget to implement `is_valid_solution()` correctly
- Don't use concrete types when generic `W` is appropriate
- `src/models/satisfiability/` — Satisfiability, KSatisfiability, CircuitSAT
- `src/models/graph/` — MaximumIndependentSet, MinimumVertexCover, KColoring, etc.
- `src/models/set/` — MinimumSetCovering, MaximumSetPacking
- `src/models/optimization/` — SpinGlass, QUBO, ILP
- `src/models/specialized/` — Factoring

## Naming

Use explicit optimization prefixes: `Maximum` for maximization, `Minimum` for minimization (e.g., `MaximumIndependentSet`, `MinimumVertexCover`).
109 changes: 25 additions & 84 deletions .claude/rules/adding-reductions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,104 +3,45 @@ paths:
- "src/rules/**/*.rs"
---

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

## 0. Brainstorm & Generate Test Data First
**Reference implementation:** `src/rules/minimumvertexcover_maximumindependentset.rs`
**Reference test:** `src/unit_tests/rules/minimumvertexcover_maximumindependentset.rs`
**Reference example:** `examples/reduction_minimumvertexcover_to_maximumindependentset.rs`
**Reference paper entry:** `docs/paper/reductions.typ` (search for `MinimumVertexCover` `MaximumIndependentSet`)

Before writing any Rust code, follow this workflow:
## 0. Before Writing Code

1. **Brainstorm the reduction** — use `superpowers:brainstorming` to discuss with the user:
- Research the mathematical formulation (paper, textbook, or derive it)
- Understand the variable mapping and constraint encoding
- Discuss implementation approach: penalty values, matrix construction, solution extraction
- Read reference implementations in the codebase (e.g., `src/rules/spinglass_qubo.rs`) to understand conventions
- Agree on scope (weighted vs unweighted, specific graph types, const generics)
2. **Generate ground truth test data** — use an existing library (e.g., Python with qubogen, qubovert, or networkx) to create small instances, reduce them, brute-force solve both sides, and export as JSON to `tests/data/<target>/`. It is recommended to download the relevant package and check the existing tests to understand how to construct tests. To generate the test data, you can use the following command:
```bash
# Example: generate QUBO test data
cd scripts && uv run python generate_qubo_tests.py
```
3. **Create a practical example** — design a small, explainable instance for `examples/` (e.g., "wireless tower placement" for MaximumIndependentSet, "map coloring" for KColoring). This example will also appear in the `docs/paper/reductions.typ`.
4. **Write the implementation plan** — save to `docs/plans/` using `superpowers:writing-plans`. The plan must include implementation details from the brainstorming session (formulas, penalty terms, matrix construction, variable indexing).
1. **Brainstorm** — use `superpowers:brainstorming` to discuss with the user:
- The math (variable mapping, constraint encoding, penalty terms)
- Which example instance to use in `examples/` (must be small, human-explainable, and agreed with the user)
2. **Generate ground truth** — use Python scripts in `scripts/` (run with `uv`) to create test data in `tests/data/<target>/`.
3. **Write plan** — save to `docs/plans/` using `superpowers:writing-plans`.

## 1. Implementation
## 1. Implement

Create `src/rules/<source>_<target>.rs` following the pattern in `src/rules/spinglass_qubo.rs`:
Create `src/rules/<source>_<target>.rs` following the reference. Key pieces:
- `ReductionResult` struct + impl (`target_problem`, `extract_solution`, `source_size`, `target_size`)
- `#[reduction(...)]` macro on `ReduceTo<Target> for Source` impl (auto-generates `inventory::submit!`)
- `#[cfg(test)] #[path = ...]` linking to unit tests

```rust
use crate::reduction;
Register in `src/rules/mod.rs`.

#[derive(Debug, Clone)]
pub struct ReductionSourceToTarget {
target: TargetProblem<...>,
source_size: ProblemSize,
// + any metadata needed for extract_solution
}
## 2. Test

impl ReductionResult for ReductionSourceToTarget {
type Source = SourceProblem<...>;
type Target = TargetProblem<...>;

fn target_problem(&self) -> &Self::Target { &self.target }
fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> { ... }
fn source_size(&self) -> ProblemSize { self.source_size.clone() }
fn target_size(&self) -> ProblemSize { self.target.problem_size() }
}

#[reduction(
overhead = { ReductionOverhead::new(vec![...]) }
)]
impl ReduceTo<TargetProblem<...>> for SourceProblem<...> {
type Result = ReductionSourceToTarget;
fn reduce_to(&self) -> Self::Result { ... }
}

#[cfg(test)]
#[path = "../unit_tests/rules/<source>_<target>.rs"]
mod tests;
```

The `#[reduction]` macro auto-generates the `inventory::submit!` call. Optional attributes: `source_graph`, `target_graph`, `source_weighted`, `target_weighted`.

Register module in `src/rules/mod.rs`:
```rust
mod source_target;
pub use source_target::ReductionSourceToTarget;
```

## 2. Tests (Required)

- **Unit tests** in `src/unit_tests/rules/<source>_<target>.rs` — closed-loop + edge cases. See `rules/testing.md`.
- **Integration tests** in `tests/suites/reductions.rs` — compare against JSON ground truth from step 0.
- Test name: `test_<source>_to_<target>_closed_loop`
- **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 a round-trip demo to `examples/` showing a practical, explainable instance:
1. Create source problem with a real-world story
2. Reduce to target, solve, extract solution
3. Print human-readable explanation
Add `examples/reduction_<source>_to_<target>.rs` — create, reduce, solve, extract, verify, export JSON (see reference example).

## 4. Documentation
## 4. Document

Update `docs/paper/reductions.typ` (see `rules/documentation.md` for the pattern):
- Add `reduction-rule("Source", "Target", ...)` theorem with proof sketch
- Add Rust code example from the example program
- Add `display-name` entry if the problem is new
Update `docs/paper/reductions.typ` — add `reduction-rule("Source", "Target", ...)` with proof sketch (see `rules/documentation.md`).

The goal is to 1. prove the correctness of the reduction to human beings. 2. provide a minimal working example to the readers.
## 5. Regenerate Graph

Citations must be verifiable. Use `[Folklore]` or `—` for trivial reductions.

## 5. Regenerate Reduction Graph
```bash
make export-graph
cargo run --example export_graph
```

## Anti-patterns
- Don't write Rust code before understanding the math and having test data
- Don't create reductions without closed-loop tests
- Don't forget `inventory::submit!` registration (reduction graph won't update)
- Don't hardcode weights - use generic `W` parameter
- Don't skip overhead polynomial specification
- Don't skip the example program — every reduction needs an explainable demo
38 changes: 10 additions & 28 deletions .claude/rules/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,32 @@ paths:

# Documentation Requirements

The technical paper (`docs/paper/reductions.typ`) must include:

1. **Problem Definitions** — using `problem-def` wrapper
2. **Reduction Theorems** — using `reduction-rule` function
3. **Reduction Examples** — minimal working example showing reduce → solve → extract
**Reference:** search `docs/paper/reductions.typ` for `MinimumVertexCover` `MaximumIndependentSet` to see a complete problem-def + reduction-rule example.

## Adding a Problem Definition

```typst
#problem-def("MaximumIndependentSet", "Maximum Independent Set (MIS)")[
#problem-def("ProblemName")[
Mathematical definition...
]
```

This auto-generates:
- A label `<def:MaximumIndependentSet>` for cross-references
- The problem's schema (fields from Rust struct)
- The list of available reductions

Also add an entry to the `display-name` dictionary:
Also add to the `display-name` dictionary:
```typst
"MaximumIndependentSet": "MIS",
"ProblemName": [Problem Name],
```

## Adding a Reduction Theorem

```typst
#reduction-rule(
"MaximumIndependentSet", "QUBO",
example: "maximumindependentset_to_qubo",
overhead: (n: 0, m: 1),
#reduction-rule("Source", "Target",
example: true,
example-caption: [caption text],
)[
Rule statement...
][
Proof sketch...
]
```

This auto-generates:
- A theorem label `<thm:MaximumIndependentSet-to-QUBO>`
- References to source/target problem definitions (if they exist)
- Registration in `covered-rules` state for completeness checking
- The example code block from `examples/reduction_<example>.rs`

## Completeness Warnings

The paper auto-checks completeness:
- After Problem Definitions: warns if JSON graph nodes are missing from `display-name`
- After Reductions section: warns if JSON graph edges are missing from `covered-rules`
Every directed reduction in the graph needs its own `reduction-rule` entry. The paper auto-checks completeness against `reduction_graph.json`.
60 changes: 11 additions & 49 deletions .claude/rules/testing.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,18 @@
# Testing Requirements

## Coverage Requirement
New code must have >95% test coverage.
**Reference test:** `src/unit_tests/rules/minimumvertexcover_maximumindependentset.rs`

```bash
# Check coverage for specific module
cargo tarpaulin --features ilp --skip-clean --ignore-tests -- <module_name>
## Coverage

# Generate full HTML report
make coverage
```
New code must have >95% test coverage. Run `make coverage` to check.

## Naming

## Test Naming Conventions
- Reduction tests: `test_<source>_to_<target>_closed_loop`
- Model tests: `test_<model>_basic`, `test_<model>_serialization`
- Solver tests: `test_<solver>_<problem>`

## Closed-Loop Test Pattern
Every reduction MUST have a closed-loop test:

```rust
#[test]
fn test_source_to_target_closed_loop() {
// 1. Create small instance
let problem = SourceProblem::new(...);

// 2. Reduce
let reduction = problem.reduce_to::<TargetProblem>();
let target = reduction.target_problem();

// 3. Solve target
let solver = BruteForce::new();
let solutions = solver.find_best(target);

// 4. Extract and verify
for sol in solutions {
let extracted = reduction.extract_solution(&sol);
assert!(problem.is_valid_solution(&extracted));
}
}
```

## Before Submitting PR
```bash
make test # All tests pass
make clippy # No warnings
make coverage # >95% for new code
```

## Test File Organization
## File Organization

Unit tests live in `src/unit_tests/`, mirroring `src/` structure. Source files reference them via `#[path]`:

Expand All @@ -59,12 +23,10 @@ Unit tests live in `src/unit_tests/`, mirroring `src/` structure. Source files r
mod tests;
```

The `#[path]` is relative to the source file's directory. `use super::*` in the test file resolves to the parent module (same as inline tests).
Integration tests are in `tests/suites/`, consolidated through `tests/main.rs`.

Integration tests are consolidated into a single binary at `tests/main.rs`, with test modules in `tests/suites/`.
## Before PR

## Anti-patterns
- Don't skip closed-loop tests for reductions
- Don't test only happy paths - include edge cases
- Don't ignore clippy warnings
- Don't add inline `mod tests` blocks in `src/` — use `src/unit_tests/` with `#[path]`
```bash
make test clippy
```
Loading