Skip to content

Commit b0db295

Browse files
committed
fix skills
1 parent c63fce7 commit b0db295

File tree

5 files changed

+362
-95
lines changed

5 files changed

+362
-95
lines changed

.claude/CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
Rust library for NP-hard problem reductions. Implements computational problems with reduction rules for transforming between equivalent formulations.
55

66
## Skills
7-
When resolving an issue with pull request, please use [issue-to-pr](skills/issue-to-pr.md) skill to convert the issue into a pull request with a plan.
7+
- [issue-to-pr](skills/issue-to-pr.md) -- Convert a GitHub issue into a PR with an implementation plan. Validates the issue against the appropriate checklist, then dispatches to `add-model` or `add-rule`.
8+
- [add-model](skills/add-model.md) -- Add a new problem model. Can be used standalone (brainstorms with user) or called from `issue-to-pr`.
9+
- [add-rule](skills/add-rule.md) -- Add a new reduction rule. Can be used standalone (brainstorms with user) or called from `issue-to-pr`.
810

911
## Commands
1012
```bash

.claude/skills/add-model.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
name: add-model
3+
description: Use when adding a new problem model to the codebase, either from an issue or interactively
4+
---
5+
6+
# Add Model
7+
8+
Step-by-step guide for adding a new problem model to the codebase.
9+
10+
## Step 0: Gather Required Information
11+
12+
Before any implementation, collect all required information. If called from `issue-to-pr`, the issue should already provide these. If used standalone, brainstorm with the user to fill in every item below.
13+
14+
### Required Information Checklist
15+
16+
| # | Item | Description | Example |
17+
|---|------|-------------|---------|
18+
| 1 | **Problem name** | Struct name with optimization prefix | `MaximumClique`, `MinimumDominatingSet` |
19+
| 2 | **Mathematical definition** | Formal definition with objective/constraints | "Given graph G=(V,E), find max-weight subset S where all pairs in S are adjacent" |
20+
| 3 | **Problem type** | Optimization (maximize/minimize) or satisfaction | Optimization (Maximize) |
21+
| 4 | **Type parameters** | Graph type `G`, weight type `W`, or other | `G: Graph`, `W: WeightElement` |
22+
| 5 | **Struct fields** | What the struct holds | `graph: G`, `weights: Vec<W>` |
23+
| 6 | **Configuration space** | What `dims()` returns | `vec![2; num_vertices]` for binary vertex selection |
24+
| 7 | **Feasibility check** | How to validate a configuration | "All selected vertices must be pairwise adjacent" |
25+
| 8 | **Objective function** | How to compute the metric | "Sum of weights of selected vertices" |
26+
| 9 | **Complexity class** | NP-hard, NP-complete, etc. | NP-hard |
27+
| 10 | **Solving strategy** | How it can be solved | "BruteForce works; ILP reduction available" |
28+
| 11 | **Category** | Which sub-module under `src/models/` | `graph`, `optimization`, `satisfiability`, `set`, `specialized` |
29+
30+
If any item is missing, ask the user to provide it. Do NOT proceed until the checklist is complete.
31+
32+
## Reference Implementations
33+
34+
Read these first to understand the patterns:
35+
- **Optimization problem:** `src/models/graph/maximum_independent_set.rs`
36+
- **Satisfaction problem:** `src/models/satisfiability/sat.rs`
37+
- **Model tests:** `src/unit_tests/models/graph/maximum_independent_set.rs`
38+
- **Trait definitions:** `src/traits.rs` (`Problem`, `OptimizationProblem`, `SatisfactionProblem`)
39+
- **CLI dispatch:** `problemreductions-cli/src/dispatch.rs`
40+
- **CLI aliases:** `problemreductions-cli/src/problem_name.rs`
41+
42+
## Step 1: Determine the category
43+
44+
Choose the appropriate sub-module under `src/models/`:
45+
- `graph/` -- problems defined on graphs (vertex/edge selection)
46+
- `optimization/` -- generic optimization formulations (QUBO, ILP, SpinGlass)
47+
- `satisfiability/` -- boolean satisfaction problems (SAT, k-SAT)
48+
- `set/` -- set-based problems (set packing, set cover)
49+
- `specialized/` -- problems that don't fit other categories (factoring, circuit, paintshop)
50+
51+
## Step 2: Implement the model
52+
53+
Create `src/models/<category>/<name>.rs`:
54+
55+
```rust
56+
// Required structure:
57+
// 1. inventory::submit! for ProblemSchemaEntry
58+
// 2. Struct definition with #[derive(Debug, Clone, Serialize, Deserialize)]
59+
// 3. Constructor (new) + accessor methods
60+
// 4. Problem trait impl (NAME, Metric, dims, evaluate, variant, problem_size_names, problem_size_values)
61+
// 5. OptimizationProblem or SatisfactionProblem impl
62+
// 6. #[cfg(test)] #[path = "..."] mod tests;
63+
```
64+
65+
Key decisions:
66+
- **Optimization problems:** `type Metric = SolutionSize<W::Sum>`, implement `OptimizationProblem` with `direction()`
67+
- **Satisfaction problems:** `type Metric = bool`, implement `SatisfactionProblem` (marker trait)
68+
- **Weight management:** use inherent methods (`weights()`, `set_weights()`, `is_weighted()`), NOT traits
69+
- **`dims()`:** returns the configuration space dimensions (e.g., `vec![2; n]` for binary variables)
70+
- **`evaluate()`:** must check feasibility first, then compute objective
71+
72+
## Step 3: Register the model
73+
74+
Update these files to register the new problem type:
75+
76+
1. `src/models/<category>/mod.rs` -- add `pub(crate) mod <name>;` and `pub use <name>::<ProblemType>;`
77+
2. `src/models/mod.rs` -- add to the appropriate re-export line
78+
3. `src/lib.rs` or `prelude` -- if the type should be in `prelude::*`, add it there
79+
80+
## Step 4: Register in CLI
81+
82+
Update the CLI dispatch table so `pred` can load, solve, and serialize the new problem:
83+
84+
1. **`problemreductions-cli/src/dispatch.rs`:**
85+
- Add a match arm in `load_problem()` -- use `deser_opt::<T>` for optimization or `deser_sat::<T>` for satisfaction
86+
- Add a match arm in `serialize_any_problem()` -- use `try_ser::<T>`
87+
88+
2. **`problemreductions-cli/src/problem_name.rs`:**
89+
- Add a lowercase alias mapping in `resolve_alias()` (e.g., `"newproblem" => "NewProblem".to_string()`)
90+
- Optionally add short aliases to `ALIASES` array (e.g., `("NP", "NewProblem")`)
91+
92+
## Step 5: Write unit tests
93+
94+
Create `src/unit_tests/models/<category>/<name>.rs`:
95+
96+
Required tests:
97+
- `test_<name>_creation` -- construct an instance, verify dimensions
98+
- `test_<name>_evaluation` -- verify `evaluate()` on valid and invalid configs
99+
- `test_<name>_direction` -- verify optimization direction (if optimization problem)
100+
- `test_<name>_serialization` -- round-trip serde test (optional but recommended)
101+
- `test_<name>_solver` -- verify brute-force solver finds correct solutions
102+
103+
Link the test file via `#[cfg(test)] #[path = "..."] mod tests;` at the bottom of the model file.
104+
105+
## Step 6: Document in paper
106+
107+
Update `docs/paper/reductions.typ`:
108+
- Add to the `display-name` dictionary: `"ProblemName": [Display Name],`
109+
- Add a `#problem-def("ProblemName")[...]` block with the mathematical definition
110+
111+
## Step 7: Verify
112+
113+
```bash
114+
make test clippy # Must pass
115+
```
116+
117+
## Naming Conventions
118+
119+
- Struct names use explicit optimization prefixes: `MaximumX`, `MinimumX`
120+
- No prefix for problems without clear min/max direction: `QUBO`, `Satisfiability`, `KColoring`
121+
- File names use snake_case: `maximum_independent_set.rs`
122+
- See CLAUDE.md "Problem Names" section for the full list
123+
124+
## Common Mistakes
125+
126+
| Mistake | Fix |
127+
|---------|-----|
128+
| Implementing weight management as a trait | Use inherent methods: `weights()`, `set_weights()`, `is_weighted()` |
129+
| Forgetting `inventory::submit!` | Every problem needs a `ProblemSchemaEntry` registration |
130+
| Missing `#[path]` test link | Add `#[cfg(test)] #[path = "..."] mod tests;` at file bottom |
131+
| Wrong `dims()` | Must match the actual configuration space (e.g., `vec![2; n]` for binary) |
132+
| Not registering in `mod.rs` | Must update both `<category>/mod.rs` and `models/mod.rs` |
133+
| Forgetting CLI dispatch | Must add match arms in `dispatch.rs` (`load_problem` + `serialize_any_problem`) |
134+
| Forgetting CLI alias | Must add lowercase entry in `problem_name.rs` `resolve_alias()` |

.claude/skills/add-rule.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
---
2+
name: add-rule
3+
description: Use when adding a new reduction rule to the codebase, either from an issue or interactively
4+
---
5+
6+
# Add Rule
7+
8+
Step-by-step guide for adding a new reduction rule (A -> B) to the codebase.
9+
10+
## Step 0: Gather Required Information
11+
12+
Before any implementation, collect all required information. If called from `issue-to-pr`, the issue should already provide these. If used standalone, brainstorm with the user to fill in every item below.
13+
14+
### Required Information Checklist
15+
16+
| # | Item | Description | Example |
17+
|---|------|-------------|---------|
18+
| 1 | **Source problem** | The problem being reduced FROM (must already exist) | `MinimumVertexCover<SimpleGraph, i32>` |
19+
| 2 | **Target problem** | The problem being reduced TO (must already exist) | `MaximumIndependentSet<SimpleGraph, i32>` |
20+
| 3 | **Reduction algorithm** | How to transform source instance to target | "Copy graph and weights; IS on same graph as VC" |
21+
| 4 | **Solution extraction** | How to map target solution back to source | "Complement: `1 - x` for each variable" |
22+
| 5 | **Correctness argument** | Why the reduction preserves optimality | "S is independent set iff V\S is vertex cover" |
23+
| 6 | **Size overhead** | How target size relates to source size | `num_vertices: poly!(num_vertices), num_edges: poly!(num_edges)` |
24+
| 7 | **Concrete example** | A small worked-out instance (tutorial style, clear intuition) | "Triangle graph: VC={0,1} -> IS={2}" |
25+
| 8 | **Solving strategy** | How to solve the target problem | "BruteForce, or existing ILP reduction" |
26+
| 9 | **Reference** | Paper, textbook, or URL for the reduction | URL or citation |
27+
28+
If any item is missing, ask the user to provide it. Put a high standard on item 7 (concrete example): it must be in tutorial style with clear intuition and easy to understand. Do NOT proceed until the checklist is complete.
29+
30+
## Reference Implementations
31+
32+
Read these first to understand the patterns:
33+
- **Reduction rule:** `src/rules/minimumvertexcover_maximumindependentset.rs`
34+
- **Reduction tests:** `src/unit_tests/rules/minimumvertexcover_maximumindependentset.rs`
35+
- **Example program:** `examples/reduction_minimumvertexcover_to_maximumindependentset.rs`
36+
- **Example registration:** `tests/suites/examples.rs`
37+
- **Paper entry:** search `docs/paper/reductions.typ` for `MinimumVertexCover` `MaximumIndependentSet`
38+
- **Traits:** `src/rules/traits.rs` (`ReduceTo<T>`, `ReductionResult`)
39+
40+
## Step 1: Implement the reduction
41+
42+
Create `src/rules/<source>_<target>.rs` (all lowercase, no underscores between words within a problem name):
43+
44+
```rust
45+
// Required structure:
46+
// 1. ReductionResult struct (holds the target problem + mapping state)
47+
// 2. ReductionResult trait impl (target_problem + extract_solution)
48+
// 3. #[reduction(overhead = { ... })] on ReduceTo impl
49+
// 4. ReduceTo trait impl (reduce_to method)
50+
// 5. #[cfg(test)] #[path = "..."] mod tests;
51+
```
52+
53+
Key elements:
54+
55+
**ReductionResult struct:**
56+
```rust
57+
#[derive(Debug, Clone)]
58+
pub struct ReductionXToY {
59+
target: TargetType,
60+
// any additional mapping state needed for extract_solution
61+
}
62+
```
63+
64+
**ReductionResult trait impl:**
65+
```rust
66+
impl ReductionResult for ReductionXToY {
67+
type Source = SourceType;
68+
type Target = TargetType;
69+
fn target_problem(&self) -> &Self::Target { &self.target }
70+
fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
71+
// Map target solution back to source solution
72+
}
73+
}
74+
```
75+
76+
**ReduceTo with `#[reduction]` macro:**
77+
```rust
78+
#[reduction(
79+
overhead = {
80+
ReductionOverhead::new(vec![
81+
("field_name", poly!(source_field)),
82+
])
83+
}
84+
)]
85+
impl ReduceTo<TargetType> for SourceType {
86+
type Result = ReductionXToY;
87+
fn reduce_to(&self) -> Self::Result { ... }
88+
}
89+
```
90+
91+
## Step 2: Register in mod.rs
92+
93+
Add to `src/rules/mod.rs`:
94+
- `mod <source>_<target>;`
95+
- If feature-gated (e.g., ILP): wrap with `#[cfg(feature = "ilp-solver")]`
96+
97+
## Step 3: Write unit tests
98+
99+
Create `src/unit_tests/rules/<source>_<target>.rs`:
100+
101+
**Required: closed-loop test** (`test_<source>_to_<target>_closed_loop`):
102+
```rust
103+
// 1. Create source problem instance
104+
// 2. Reduce: let reduction = ReduceTo::<Target>::reduce_to(&source);
105+
// 3. Solve target: solver.find_all_best(reduction.target_problem())
106+
// 4. Extract: reduction.extract_solution(&target_sol)
107+
// 5. Verify: extracted solution is valid and optimal for source
108+
```
109+
110+
Additional recommended tests:
111+
- Verify target problem structure (correct size, edges, constraints)
112+
- Edge cases (empty graph, single vertex, etc.)
113+
- Weight preservation (if applicable)
114+
115+
Link via `#[cfg(test)] #[path = "..."] mod tests;` at the bottom of the rule file.
116+
117+
## Step 4: Write example program
118+
119+
Create `examples/reduction_<source>_to_<target>.rs`:
120+
121+
Required structure:
122+
- `pub fn run()` -- main logic (required for test harness)
123+
- `fn main() { run() }` -- entry point
124+
- Use regular comments (`//`), not doc comments
125+
- Create source instance, reduce, solve, extract, verify, export JSON
126+
127+
Register in `tests/suites/examples.rs`:
128+
```rust
129+
example_test!(reduction_<source>_to_<target>);
130+
// ...
131+
example_fn!(test_<source>_to_<target>, reduction_<source>_to_<target>);
132+
```
133+
134+
## Step 5: Document in paper
135+
136+
Update `docs/paper/reductions.typ`:
137+
138+
```typst
139+
#reduction-rule("Source", "Target",
140+
example: true,
141+
example-caption: [Caption text],
142+
)[
143+
Reduction rule statement...
144+
][
145+
Proof sketch...
146+
]
147+
```
148+
149+
Present the example in tutorial style with clear intuition. Reference the KColoring -> QUBO section for style guidance.
150+
151+
## Step 6: Regenerate graph and verify
152+
153+
```bash
154+
cargo run --example export_graph # Update reduction_graph.json
155+
make test clippy # Must pass
156+
```
157+
158+
## Solver Rules
159+
160+
- If the target problem already has a solver, use it directly.
161+
- If the solving strategy requires ILP, implement the ILP reduction rule alongside (feature-gated under `ilp-solver`).
162+
- If a custom solver is needed, implement in `src/solvers/` and document.
163+
164+
## CLI Impact
165+
166+
Adding a reduction rule does NOT require CLI changes -- the reduction graph is auto-generated from `#[reduction]` macros and the CLI discovers paths dynamically. However, both source and target models must already be registered in the CLI dispatch table (see `add-model` skill).
167+
168+
## File Naming
169+
170+
- Rule file: `src/rules/<sourcelower>_<targetlower>.rs` -- no underscores within a problem name
171+
- e.g., `maximumindependentset_qubo.rs`, `minimumvertexcover_maximumindependentset.rs`
172+
- Example file: `examples/reduction_<source>_to_<target>.rs`
173+
- e.g., `reduction_minimumvertexcover_to_maximumindependentset.rs`
174+
- Test file: `src/unit_tests/rules/<sourcelower>_<targetlower>.rs`
175+
176+
## Common Mistakes
177+
178+
| Mistake | Fix |
179+
|---------|-----|
180+
| Forgetting `#[reduction(...)]` macro | Required for compile-time registration in the reduction graph |
181+
| Wrong overhead polynomial | Must accurately reflect the size relationship |
182+
| Missing `extract_solution` mapping state | Store any index maps needed in the ReductionResult struct |
183+
| Example missing `pub fn run()` | Required for the test harness (`include!` pattern) |
184+
| Not registering example in `tests/suites/examples.rs` | Must add both `example_test!` and `example_fn!` |
185+
| Not regenerating reduction graph | Run `cargo run --example export_graph` after adding a rule |
186+
| Source/target model not in CLI dispatch | Both problems must be registered -- use `add-model` skill first |

0 commit comments

Comments
 (0)