Skip to content

Commit 3076eb5

Browse files
committed
update docs
1 parent 8d2ccae commit 3076eb5

9 files changed

Lines changed: 933 additions & 933 deletions

File tree

.claude/CLAUDE.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ make clean # Clean build artifacts
2626
make diagrams # Generate SVG diagrams from Typst (light + dark)
2727
make examples # Generate example JSON for paper
2828
make compare # Generate and compare Rust mapping exports
29+
make jl-testdata # Regenerate Julia parity test data (requires julia)
2930
make run-plan # Execute a plan with Claude autorun
3031
make release V=x.y.z # Tag and push a new release (CI publishes to crates.io)
3132
```
@@ -44,7 +45,7 @@ make test clippy # Must pass before PR
4445
- `src/models/` - Problem implementations (SAT, Graph, Set, Optimization)
4546
- `src/rules/` - Reduction rules + inventory registration
4647
- `src/solvers/` - BruteForce solver, ILP solver (feature-gated)
47-
- `src/traits.rs` - `Problem`, `OptimizationProblem` traits
48+
- `src/traits.rs` - `Problem`, `OptimizationProblem`, `SatisfactionProblem` traits
4849
- `src/rules/traits.rs` - `ReduceTo<T>`, `ReductionResult` traits
4950
- `src/registry/` - Compile-time reduction metadata collection
5051
- `src/unit_tests/` - Unit test files (mirroring `src/` structure, referenced via `#[path]`)
@@ -63,15 +64,19 @@ Problem (core trait — all problems must implement)
6364
├── fn dims(&self) -> Vec<usize> // config space: [2, 2, 2] for 3 binary variables
6465
├── fn evaluate(&self, config) -> Metric
6566
├── fn variant() -> Vec<(&str, &str)> // e.g., [("graph","SimpleGraph"), ("weight","i32")]
66-
└── fn num_variables(&self) -> usize // default: dims().len()
67+
├── fn num_variables(&self) -> usize // default: dims().len()
68+
├── fn problem_size_names() -> &[&str] // static field names for size metrics
69+
└── fn problem_size_values(&self) -> Vec<usize> // instance-level size values
6770
6871
OptimizationProblem : Problem<Metric = SolutionSize<Self::Value>> (extension for optimization)
6972
7073
├── type Value: PartialOrd + Clone // inner objective type (i32, f64, etc.)
7174
└── fn direction(&self) -> Direction // Maximize or Minimize
75+
76+
SatisfactionProblem : Problem<Metric = bool> (marker trait for decision problems)
7277
```
7378

74-
**Satisfaction problems** (e.g., `Satisfiability`) use `Metric = bool` and do not implement `OptimizationProblem`.
79+
**Satisfaction problems** (e.g., `Satisfiability`) use `Metric = bool` and implement `SatisfactionProblem`.
7580

7681
**Optimization problems** (e.g., `MaximumIndependentSet`) use `Metric = SolutionSize<W>` where:
7782
```rust
@@ -84,7 +89,7 @@ enum Direction { Maximize, Minimize }
8489
- `ReductionResult` provides `target_problem()` and `extract_solution()`
8590
- `Solver::find_best()``Option<Vec<usize>>` for optimization problems; `Solver::find_satisfying()``Option<Vec<usize>>` for `Metric = bool`
8691
- `BruteForce::find_all_best()` / `find_all_satisfying()` return `Vec<Vec<usize>>` for all optimal/satisfying solutions
87-
- Graph types: SimpleGraph, GridGraph, UnitDiskGraph, Triangular, HyperGraph
92+
- Graph types: HyperGraph, SimpleGraph, PlanarGraph, BipartiteGraph, UnitDiskGraph, KingsSubgraph, TriangularSubgraph
8893
- Weight types: `One` (unit weight marker), `i32`, `f64` — all implement `WeightElement` trait
8994
- `WeightElement` trait: `type Sum: NumericSize` + `fn to_sum(&self)` — converts weight to a summable numeric type
9095
- Weight management via inherent methods (`weights()`, `set_weights()`, `is_weighted()`), not traits
@@ -94,7 +99,7 @@ enum Direction { Maximize, Minimize }
9499
Problem types use explicit optimization prefixes:
95100
- `MaximumIndependentSet`, `MaximumClique`, `MaximumMatching`, `MaximumSetPacking`
96101
- `MinimumVertexCover`, `MinimumDominatingSet`, `MinimumSetCovering`
97-
- No prefix: `MaxCut`, `SpinGlass`, `QUBO`, `ILP`, `Satisfiability`, `KSatisfiability`, `CircuitSAT`, `Factoring`, `MaximalIS`, `PaintShop`, `BicliqueCover`, `BMF`
102+
- No prefix: `MaxCut`, `SpinGlass`, `QUBO`, `ILP`, `Satisfiability`, `KSatisfiability`, `CircuitSAT`, `Factoring`, `MaximalIS`, `PaintShop`, `BicliqueCover`, `BMF`, `KColoring`, `TravelingSalesman`
98103

99104
### Problem Variant IDs
100105
Reduction graph nodes use variant key-value pairs from `Problem::variant()`:

README.md

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
[![codecov](https://codecov.io/github/CodingThrust/problem-reductions/graph/badge.svg?token=0CdEC8GHN0)](https://codecov.io/github/CodingThrust/problem-reductions)
66
[![Docs](https://img.shields.io/badge/docs-API-blue)](https://codingthrust.github.io/problem-reductions/)
77

8-
A Rust library for NP-hard problem definitions and reductions. We aim to implement >100 NP-hard problems and reductions rule between them, under the assistance of AI.
8+
A Rust library for NP-hard problem definitions and reductions. We aim to implement [100+ problems and reduction rules](https://codingthrust.github.io/problem-reductions/) between them, with automatic reduction path search. Built with AI assistance.
99

1010
This infrastructure aims to solve two problems:
11-
- Given a hard problem $A$, reduce it to the most vaible problem $B$, to be solved efficiently with an external solver.
12-
- Given a solver $S$ for problem $B$, explore how efficient it can be used for solving other problems.
11+
- Given a hard problem $A$, reduce it to the most viable problem $B$, to be solved efficiently with an external solver.
12+
- Given a solver $S$ for problem $B$, explore how efficiently it can be used for solving other problems.
1313

1414
Download [PDF manual](https://codingthrust.github.io/problem-reductions/reductions.pdf) for humans.
1515

@@ -19,30 +19,10 @@ Add to your `Cargo.toml`:
1919

2020
```toml
2121
[dependencies]
22-
problemreductions = "0.1"
22+
problemreductions = "0.2"
2323
```
2424

25-
## Quick Start
26-
27-
```rust
28-
use problemreductions::prelude::*;
29-
use problemreductions::models::optimization::ILP;
30-
31-
// Create an Independent Set problem on a path graph
32-
let problem = IndependentSet::<i32>::new(4, vec![(0, 1), (1, 2), (2, 3)]);
33-
34-
// Reduce to Integer Linear Programming
35-
let reduction = ReduceTo::<ILP>::reduce_to(&problem);
36-
let ilp = reduction.target_problem();
37-
38-
// Solve with ILP solver (efficient for larger instances)
39-
let solver = ILPSolver::new();
40-
let ilp_solution = solver.solve(ilp).unwrap();
41-
42-
// Extract solution back to original problem
43-
let solution = reduction.extract_solution(&ilp_solution);
44-
assert_eq!(solution.iter().sum::<usize>(), 2); // Max IS size is 2
45-
```
25+
See the [Getting Started](https://codingthrust.github.io/problem-reductions/getting-started.html) guide for usage examples and the reduction workflow.
4626

4727
## Contributing
4828

docs/src/design.md

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,39 @@ This guide covers the library internals for contributors.
1919
|--------|---------|
2020
| [`src/models/`](#problem-model) | Problem type implementations (SAT, Graph, Set, Optimization) |
2121
| [`src/rules/`](#reduction-rules) | Reduction rules with `ReduceTo` implementations |
22-
| [`src/registry/`](#reduction-graph) | Compile-time reduction graph metadata |
22+
| [`src/registry/`](#reduction-graph) | Reduction graph metadata (collected via `inventory`) |
2323
| [`src/solvers/`](#solvers) | BruteForce and ILP solvers |
24-
| `src/traits.rs` | Core `Problem` and `OptimizationProblem` traits (see [Problem Model](#problem-model)) |
24+
| `src/traits.rs` | Core `Problem`, `OptimizationProblem`, `SatisfactionProblem` traits (see [Problem Model](#problem-model)) |
2525
| `src/types.rs` | Shared types: `SolutionSize`, `Direction`, `ProblemSize` (see [Problem Model](#problem-model)) |
2626
| `src/variant.rs` | Variant parameter system (see [Variant System](#variant-system)) |
2727

2828
## Problem Model
2929

3030
Every problem implements `Problem`. Optimization problems additionally implement `OptimizationProblem`; satisfaction problems implement `SatisfactionProblem`.
3131

32-
- **`Problem`** — the base trait. Every problem declares a `NAME` (e.g., `"MaximumIndependentSet"`). The solver explores the configuration space defined by `dims()` and scores each configuration with `evaluate()`. For example, a 4-vertex MIS has `dims() = [2, 2, 2, 2]` (each vertex is selected or not); `evaluate(&[1, 0, 1, 0])` returns `Valid(2)` if vertices 0 and 2 form an independent set, or `Invalid` if they share an edge.
33-
- **`OptimizationProblem`** — extends `Problem` with a comparable `Value` type and a `direction()` (`Maximize` or `Minimize`).
34-
- **`SatisfactionProblem`** — constrains `Metric = bool`: `true` if all constraints are satisfied, `false` otherwise.
35-
36-
<div class="theme-light-only">
37-
38-
![Trait Hierarchy](static/trait-hierarchy.svg)
32+
```rust,ignore
33+
trait Problem: Clone {
34+
const NAME: &'static str; // e.g., "MaximumIndependentSet"
35+
type Metric: Clone; // SolutionSize<W> or bool
36+
fn dims(&self) -> Vec<usize>; // config space per variable
37+
fn evaluate(&self, config: &[usize]) -> Self::Metric;
38+
fn variant() -> Vec<(&'static str, &'static str)>; // e.g., [("graph", "SimpleGraph"), ("weight", "i32")]
39+
fn num_variables(&self) -> usize; // default: dims().len()
40+
fn problem_size_names() -> &'static [&'static str]; // e.g., ["num_vertices", "num_edges"]
41+
fn problem_size_values(&self) -> Vec<usize>; // e.g., [10, 15] for a specific instance
42+
}
3943
40-
</div>
41-
<div class="theme-dark-only">
44+
trait OptimizationProblem: Problem<Metric = SolutionSize<Self::Value>> {
45+
type Value: PartialOrd + Clone; // e.g., i32, f64
46+
fn direction(&self) -> Direction; // Maximize or Minimize
47+
}
4248
43-
![Trait Hierarchy](static/trait-hierarchy-dark.svg)
49+
trait SatisfactionProblem: Problem<Metric = bool> {} // marker trait
50+
```
4451

45-
</div>
52+
- **`Problem`** — the base trait. Every problem declares a `NAME` (e.g., `"MaximumIndependentSet"`). The solver explores the configuration space defined by `dims()` and scores each configuration with `evaluate()`. For example, a 4-vertex MIS has `dims() = [2, 2, 2, 2]` (each vertex is selected or not); `evaluate(&[1, 0, 1, 0])` returns `Valid(2)` if vertices 0 and 2 form an independent set, or `Invalid` if they share an edge. `problem_size_names()` and `problem_size_values()` expose the instance's structural dimensions (e.g., `num_vertices`, `num_edges`) as a `ProblemSize` — used by the reduction graph to evaluate overhead polynomials along a path.
53+
- **`OptimizationProblem`** — extends `Problem` with a comparable `Value` type and a `direction()` (`Maximize` or `Minimize`).
54+
- **`SatisfactionProblem`** — constrains `Metric = bool`: `true` if all constraints are satisfied, `false` otherwise.
4655

4756
## Variant System
4857

@@ -83,7 +92,7 @@ Variant types fall into three categories:
8392

8493
Each variant parameter type implements `VariantParam`, which declares its category, value, and optional parent:
8594

86-
```rust
95+
```rust,ignore
8796
pub trait VariantParam: 'static {
8897
const CATEGORY: &'static str; // e.g., "graph", "weight", "k"
8998
const VALUE: &'static str; // e.g., "SimpleGraph", "i32"
@@ -93,7 +102,7 @@ pub trait VariantParam: 'static {
93102

94103
Types with a parent also implement `CastToParent`, providing the runtime conversion for variant casts:
95104

96-
```rust
105+
```rust,ignore
97106
pub trait CastToParent: VariantParam {
98107
type Parent: VariantParam;
99108
fn cast_to_parent(&self) -> Self::Parent;
@@ -104,7 +113,7 @@ pub trait CastToParent: VariantParam {
104113

105114
The `impl_variant_param!` macro implements `VariantParam` (and optionally `CastToParent` / `KValue`) for a type:
106115

107-
```rust
116+
```rust,ignore
108117
// Root type (no parent):
109118
impl_variant_param!(HyperGraph, "graph");
110119
@@ -126,21 +135,21 @@ impl_variant_param!(K3, "k", parent: KN, cast: |_| KN, k: Some(3));
126135

127136
When a more specific variant needs to be treated as a less specific one, an explicit variant cast reduction is declared:
128137

129-
```rust
138+
```rust,ignore
130139
impl_variant_reduction!(
131140
MaximumIndependentSet,
132141
<KingsSubgraph, i32> => <UnitDiskGraph, i32>,
133142
fields: [num_vertices, num_edges],
134-
|src| MaximumIndependentSet::from_graph(
135-
src.graph().cast_to_parent(), src.weights())
143+
|src| MaximumIndependentSet::new(
144+
src.graph().cast_to_parent(), src.weights().to_vec())
136145
);
137146
```
138147

139148
### Composing `Problem::variant()`
140149

141150
The `variant_params!` macro composes the `Problem::variant()` body from type parameter names:
142151

143-
```rust
152+
```rust,ignore
144153
// MaximumIndependentSet<G: VariantParam, W: VariantParam>
145154
fn variant() -> Vec<(&'static str, &'static str)> {
146155
crate::variant_params![G, W]
@@ -157,7 +166,7 @@ A reduction requires two pieces: a **result struct** and a **`ReduceTo<T>` impl*
157166

158167
The result struct holds the target problem and the logic to map solutions back:
159168

160-
```rust
169+
```rust,ignore
161170
#[derive(Debug, Clone)]
162171
pub struct ReductionISToVC<W> {
163172
target: MinimumVertexCover<SimpleGraph, W>,
@@ -174,9 +183,9 @@ impl<W: WeightElement + VariantParam> ReductionResult for ReductionISToVC<W> {
174183
}
175184
```
176185

177-
The `#[reduction]` macro on the `ReduceTo<T>` impl registers the reduction in the compile-time graph:
186+
The `#[reduction]` attribute on the `ReduceTo<T>` impl registers the reduction in the global registry (via `inventory`):
178187

179-
```rust
188+
```rust,ignore
180189
#[reduction(
181190
overhead = {
182191
ReductionOverhead::new(vec![
@@ -198,7 +207,7 @@ impl ReduceTo<MinimumVertexCover<SimpleGraph, i32>>
198207

199208
The `#[reduction]` attribute expands to the original `impl` block plus an `inventory::submit!` call:
200209

201-
```rust
210+
```rust,ignore
202211
inventory::submit! {
203212
ReductionEntry {
204213
source_name: "MaximumIndependentSet",
@@ -218,17 +227,24 @@ inventory::submit! {
218227
}
219228
```
220229

221-
This `ReductionEntry` is collected at compile time by `inventory`, making the reduction discoverable by the `ReductionGraph` without any manual registration. The `reduce_fn` field provides a type-erased executor that enables runtime-discovered paths to chain reductions automatically.
230+
Each `ReductionEntry` is collected by `inventory` at link time and iterated at runtime, making every reduction discoverable by `ReductionGraph` without manual registration. The `reduce_fn` field provides a type-erased executor that enables dynamically discovered paths to chain reductions automatically.
222231

223232
</details>
224233

225234
## Reduction Graph
226235

227-
`ReductionGraph::new()` scans `inventory::iter::<ReductionEntry>` and builds a variant-level directed graph:
236+
`ReductionGraph::new()` iterates all registered `ReductionEntry` items (via `inventory`) and builds a variant-level directed graph:
228237

229238
- **Nodes** are unique `(problem_name, variant)` pairs — e.g., `("MaximumIndependentSet", {graph: "KingsSubgraph", weight: "i32"})`.
230239
- **Edges** come exclusively from `#[reduction]` registrations — both cross-problem reductions and variant casts. There are no auto-generated edges.
231240

241+
Exported files:
242+
243+
- [reduction_graph.json](reductions/reduction_graph.json) — all problem variants and reduction edges
244+
- [problem_schemas.json](reductions/problem_schemas.json) — field definitions for each problem type
245+
246+
Regenerate with `cargo run --example export_graph` and `cargo run --example export_schemas`.
247+
232248
### Path finding
233249

234250
All path-finding operates on **exact variant nodes**. Use `ReductionGraph::variant_to_map(&T::variant())` to convert a `Problem::variant()` into the required `BTreeMap<String, String>`.
@@ -245,8 +261,10 @@ The `PathCostFn` trait (used by `find_cheapest_path`) computes edge cost from ov
245261
| Cost function | Strategy |
246262
|--------------|----------|
247263
| `MinimizeSteps` | Minimize number of hops (unit edge cost) |
248-
| `Minimize("field")` | Minimize a single output field |
249-
| `CustomCost(closure)` | User-defined cost function |
264+
| `Minimize("field")` | Minimize a single output field (e.g., `Minimize("num_variables")`) |
265+
| `CustomCost(closure)` | User-defined: `\|overhead: &ReductionOverhead, size: &ProblemSize\| -> f64` |
266+
267+
`CustomCost` wraps a closure that receives the edge's `ReductionOverhead` (polynomial mapping from input to output size fields) and the current `ProblemSize` (accumulated field values at that point in the path), and returns an `f64` edge cost. Dijkstra minimizes the total cost along the path.
250268

251269
**Example:** Finding a path from `MIS{KingsSubgraph, i32}` to `VC{SimpleGraph, i32}`:
252270

@@ -259,17 +277,21 @@ MIS{KingsSubgraph,i32} -> MIS{UnitDiskGraph,i32} -> MIS{SimpleGraph,i32} -> VC{S
259277

260278
Convert a `ReductionPath` into a typed `ExecutablePath<S, T>` via `make_executable()`, then call `reduce()`:
261279

262-
```rust
280+
```rust,ignore
281+
// find_cheapest_path returns a ReductionPath (list of variant node IDs)
263282
let rpath = graph.find_cheapest_path("Factoring", &src_var,
264283
"SpinGlass", &dst_var, &ProblemSize::new(vec![]), &MinimizeSteps).unwrap();
284+
285+
// make_executable converts it into a typed, callable chain
265286
let path = graph.make_executable::<Factoring, SpinGlass<SimpleGraph, f64>>(&rpath).unwrap();
266287
288+
// reduce() applies each step, returning a ChainedReduction
267289
let reduction = path.reduce(&factoring_instance);
268-
let target = reduction.target_problem();
269-
let solution = reduction.extract_solution(&target_solution);
290+
let target: &SpinGlass<SimpleGraph, f64> = reduction.target_problem();
291+
let solution: Vec<usize> = reduction.extract_solution(&target_solution);
270292
```
271293

272-
Internally, `ExecutablePath` holds a type-erased executor per edge. `ChainedReduction` stores intermediate results and extracts solutions back through the chain in reverse.
294+
`ExecutablePath` holds a type-erased `ReduceFn` per edge. `reduce()` applies them sequentially, producing a `ChainedReduction` that stores each intermediate result. `extract_solution` maps the final solution back through the chain in reverse order.
273295

274296
For full type control, you can also chain `ReduceTo::reduce_to()` calls manually at each step.
275297

@@ -278,7 +300,7 @@ For full type control, you can also chain `ReduceTo::reduce_to()` calls manually
278300

279301
Each reduction declares how the output problem size relates to the input, expressed as polynomials. The `poly!` macro provides concise syntax:
280302

281-
```rust
303+
```rust,ignore
282304
poly!(num_vertices) // p(x) = num_vertices
283305
poly!(num_vertices ^ 2) // p(x) = num_vertices²
284306
poly!(3 * num_edges) // p(x) = 3 × num_edges
@@ -287,7 +309,7 @@ poly!(num_vertices * num_edges) // p(x) = num_vertices × num_edges
287309

288310
A `ReductionOverhead` pairs output field names with their polynomials:
289311

290-
```rust
312+
```rust,ignore
291313
ReductionOverhead::new(vec![
292314
("num_vars", poly!(num_vertices) + poly!(num_edges)),
293315
("num_clauses", poly!(3 * num_edges)),
@@ -309,7 +331,7 @@ For multi-step paths, overhead composes: the output of step N becomes the input
309331

310332
Solvers implement the `Solver` trait:
311333

312-
```rust
334+
```rust,ignore
313335
pub trait Solver {
314336
fn find_best<P: OptimizationProblem>(&self, problem: &P) -> Option<Vec<usize>>;
315337
fn find_satisfying<P: Problem<Metric = bool>>(&self, problem: &P) -> Option<Vec<usize>>;
@@ -319,26 +341,19 @@ pub trait Solver {
319341
| Solver | Description |
320342
|--------|-------------|
321343
| **BruteForce** | Enumerates all configurations. Also provides `find_all_best()` and `find_all_satisfying()`. Used for testing and verification. |
322-
| **ILPSolver** | Feature-gated (`ilp`). Uses HiGHS via `good_lp`. Also provides `solve_reduced()` for problems that implement `ReduceTo<ILP>`. |
344+
| **ILPSolver** | Enabled by default (`ilp` feature). Uses HiGHS via `good_lp`. Also provides `solve_reduced()` for problems that implement `ReduceTo<ILP>`. |
323345

324346
## JSON Serialization
325347

326348
All problem types support JSON serialization via serde:
327349

328-
```rust
350+
```rust,ignore
329351
use problemreductions::io::{to_json, from_json};
330352
331-
let json = to_json(&problem)?;
353+
let json: String = to_json(&problem)?;
332354
let restored: MaximumIndependentSet<SimpleGraph, i32> = from_json(&json)?;
333355
```
334356

335-
Exported files:
336-
337-
- [reduction_graph.json](reductions/reduction_graph.json) — all problem variants and reduction edges
338-
- [problem_schemas.json](reductions/problem_schemas.json) — field definitions for each problem type
339-
340-
Regenerate with `cargo run --example export_graph` and `cargo run --example export_schemas`.
341-
342357
## Contributing
343358

344359
See [Call for Contributions](./introduction.md#call-for-contributions) for the recommended issue-based workflow (no coding required).

0 commit comments

Comments
 (0)