Skip to content

Commit c758eae

Browse files
GiggleLiuclaude
andauthored
feat: Implement Factoring → ILP reduction (issue #21) (#22)
* docs: Add design document for Factoring to ILP reduction Design for issue #21 - integer programming based Factoring solver. Uses McCormick linearization for binary products with carry propagation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: Add code example for Factoring → ILP reduction Shows closed-loop test pattern: create problem, reduce to ILP, solve, extract solution, verify correctness. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add step-by-step comments to factor_15 test Documents the closed-loop test pattern matching the typst example. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Integrate Factoring → ILP into reduction graph - Register reduction in mod.rs - Add edge in graph.rs type registry - Update reduction graph documentation - Regenerate reduction_graph.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Address PR review comments - Fix design doc reference to use existing vertexcovering_ilp.rs - Add #[cfg(feature = "ilp")] gate to Factoring→ILP edge in graph.rs - Add shift overflow guard for k >= 64 in bit extraction - Add feature requirement note to typst example - Clarify overhead formula comments for infeasible cases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Make reduction graph JSON export deterministic Sort nodes by id and edges by (source, target) to ensure stable output across runs. Regenerate reduction_graph.json with sorted data. Addresses PR review comment #6. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Remove redundant add_edge for Factoring→ILP inventory::submit! already adds edges automatically. Manual add_edge! is only needed for legacy reductions without inventory registration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Remove all redundant manual add_edge! calls All reductions now use inventory::submit! for auto-discovery. The register_reductions function is kept as a template for future manual registrations if needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Remove empty register_reductions function All reductions use inventory::submit! for auto-discovery. No need for manual registration function. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f81fef3 commit c758eae

7 files changed

Lines changed: 888 additions & 115 deletions

File tree

docs/paper/reduction_graph.json

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
{
22
"nodes": [
33
{
4-
"id": "MaxCut",
5-
"label": "MaxCut",
4+
"id": "CircuitSAT",
5+
"label": "CircuitSAT",
6+
"category": "satisfiability"
7+
},
8+
{
9+
"id": "Coloring",
10+
"label": "Coloring",
611
"category": "graph"
712
},
813
{
@@ -11,13 +16,13 @@
1116
"category": "graph"
1217
},
1318
{
14-
"id": "SpinGlass",
15-
"label": "SpinGlass",
16-
"category": "optimization"
19+
"id": "Factoring",
20+
"label": "Factoring",
21+
"category": "specialized"
1722
},
1823
{
19-
"id": "QUBO",
20-
"label": "QUBO",
24+
"id": "ILP",
25+
"label": "ILP",
2126
"category": "optimization"
2227
},
2328
{
@@ -26,49 +31,49 @@
2631
"category": "graph"
2732
},
2833
{
29-
"id": "SetPacking",
30-
"label": "SetPacking",
31-
"category": "set"
32-
},
33-
{
34-
"id": "Satisfiability",
35-
"label": "Satisfiability",
34+
"id": "KSatisfiability",
35+
"label": "KSatisfiability",
3636
"category": "satisfiability"
3737
},
3838
{
39-
"id": "Coloring",
40-
"label": "Coloring",
39+
"id": "Matching",
40+
"label": "Matching",
4141
"category": "graph"
4242
},
4343
{
44-
"id": "VertexCovering",
45-
"label": "VertexCovering",
44+
"id": "MaxCut",
45+
"label": "MaxCut",
4646
"category": "graph"
4747
},
4848
{
49-
"id": "KSatisfiability",
50-
"label": "KSatisfiability",
51-
"category": "satisfiability"
49+
"id": "QUBO",
50+
"label": "QUBO",
51+
"category": "optimization"
5252
},
5353
{
54-
"id": "CircuitSAT",
55-
"label": "CircuitSAT",
54+
"id": "Satisfiability",
55+
"label": "Satisfiability",
5656
"category": "satisfiability"
5757
},
5858
{
59-
"id": "Factoring",
60-
"label": "Factoring",
61-
"category": "specialized"
59+
"id": "SetCovering",
60+
"label": "SetCovering",
61+
"category": "set"
6262
},
6363
{
64-
"id": "Matching",
65-
"label": "Matching",
66-
"category": "graph"
64+
"id": "SetPacking",
65+
"label": "SetPacking",
66+
"category": "set"
6767
},
6868
{
69-
"id": "SetCovering",
70-
"label": "SetCovering",
71-
"category": "set"
69+
"id": "SpinGlass",
70+
"label": "SpinGlass",
71+
"category": "optimization"
72+
},
73+
{
74+
"id": "VertexCovering",
75+
"label": "VertexCovering",
76+
"category": "graph"
7277
}
7378
],
7479
"edges": [
@@ -78,38 +83,33 @@
7883
"bidirectional": false
7984
},
8085
{
81-
"source": "Matching",
82-
"target": "SetPacking",
86+
"source": "Factoring",
87+
"target": "CircuitSAT",
8388
"bidirectional": false
8489
},
85-
{
86-
"source": "Satisfiability",
87-
"target": "KSatisfiability",
88-
"bidirectional": true
89-
},
9090
{
9191
"source": "Factoring",
92-
"target": "CircuitSAT",
92+
"target": "ILP",
9393
"bidirectional": false
9494
},
9595
{
96-
"source": "IndependentSet",
97-
"target": "SetPacking",
96+
"source": "KSatisfiability",
97+
"target": "Satisfiability",
9898
"bidirectional": true
9999
},
100100
{
101-
"source": "SpinGlass",
102-
"target": "QUBO",
103-
"bidirectional": true
101+
"source": "Matching",
102+
"target": "SetPacking",
103+
"bidirectional": false
104104
},
105105
{
106-
"source": "MaxCut",
107-
"target": "SpinGlass",
108-
"bidirectional": true
106+
"source": "Satisfiability",
107+
"target": "Coloring",
108+
"bidirectional": false
109109
},
110110
{
111-
"source": "VertexCovering",
112-
"target": "SetCovering",
111+
"source": "Satisfiability",
112+
"target": "DominatingSet",
113113
"bidirectional": false
114114
},
115115
{
@@ -118,18 +118,28 @@
118118
"bidirectional": false
119119
},
120120
{
121-
"source": "Satisfiability",
122-
"target": "Coloring",
123-
"bidirectional": false
121+
"source": "SetPacking",
122+
"target": "IndependentSet",
123+
"bidirectional": true
124+
},
125+
{
126+
"source": "SpinGlass",
127+
"target": "MaxCut",
128+
"bidirectional": true
124129
},
125130
{
126-
"source": "IndependentSet",
127-
"target": "VertexCovering",
131+
"source": "SpinGlass",
132+
"target": "QUBO",
128133
"bidirectional": true
129134
},
130135
{
131-
"source": "Satisfiability",
132-
"target": "DominatingSet",
136+
"source": "VertexCovering",
137+
"target": "IndependentSet",
138+
"bidirectional": true
139+
},
140+
{
141+
"source": "VertexCovering",
142+
"target": "SetCovering",
133143
"bidirectional": false
134144
}
135145
]

docs/paper/reductions.typ

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"VertexCovering": (-0.5, 2),
6565
"Matching": (-2, 2),
6666
"SpinGlass": (2.5, 2),
67+
"ILP": (3.5, 1),
6768
// Row 3: Leaf nodes
6869
"SetPacking": (-1.5, 3),
6970
"SetCovering": (0.5, 3),
@@ -330,6 +331,53 @@ In all graph problems below, $G = (V, E)$ denotes an undirected graph with $|V|
330331
_Solution extraction._ Without ancilla: identity. With ancilla: if $sigma_a = 1$, flip all spins before removing ancilla.
331332
]
332333

334+
#theorem[
335+
*(Factoring $arrow.r$ ILP)* Integer factorization reduces to binary ILP using McCormick linearization with $O(m n)$ variables and constraints.
336+
]
337+
338+
#proof[
339+
_Construction._ For target $N$ with $m$-bit factor $p$ and $n$-bit factor $q$:
340+
341+
_Variables:_ Binary $p_i, q_j in {0,1}$ for factor bits; binary $z_(i j) in {0,1}$ for products $p_i dot q_j$; integer $c_k >= 0$ for carries at each bit position.
342+
343+
_Product linearization (McCormick):_ For each $z_(i j) = p_i dot q_j$:
344+
$ z_(i j) <= p_i, quad z_(i j) <= q_j, quad z_(i j) >= p_i + q_j - 1 $
345+
346+
_Bit-position equations:_ For each bit position $k$:
347+
$ sum_(i+j=k) z_(i j) + c_(k-1) = N_k + 2 c_k $
348+
where $N_k$ is the $k$-th bit of $N$ and $c_(-1) = 0$.
349+
350+
_No overflow:_ $c_(m+n-1) = 0$.
351+
352+
_Correctness._ The McCormick constraints enforce $z_(i j) = p_i dot q_j$ for binary variables. The bit equations encode $p times q = N$ via carry propagation, matching array multiplier semantics.
353+
354+
_Solution extraction._ Read $p = sum_i p_i 2^i$ and $q = sum_j q_j 2^j$ from the binary variables.
355+
]
356+
357+
_Example: Factoring 15._ The following Rust code demonstrates the closed-loop reduction (requires `ilp` feature: `cargo add problemreductions --features ilp`):
358+
359+
```rust
360+
use problemreductions::prelude::*;
361+
362+
// 1. Create factoring instance: find p (4-bit) × q (4-bit) = 15
363+
let problem = Factoring::new(4, 4, 15);
364+
365+
// 2. Reduce to ILP
366+
let reduction = ReduceTo::<ILP>::reduce_to(&problem);
367+
let ilp = reduction.target_problem();
368+
369+
// 3. Solve ILP
370+
let solver = ILPSolver::new();
371+
let ilp_solution = solver.solve(ilp).unwrap();
372+
373+
// 4. Extract factoring solution
374+
let extracted = reduction.extract_solution(&ilp_solution);
375+
376+
// 5. Verify: reads factors and confirms p × q = 15
377+
let (p, q) = problem.read_factors(&extracted);
378+
assert_eq!(p * q, 15); // e.g., (3, 5) or (5, 3)
379+
```
380+
333381
= Summary <sec:summary>
334382

335383
#let gray = rgb("#e8e8e8")
@@ -352,6 +400,7 @@ In all graph problems below, $G = (V, E)$ denotes an undirected graph with $|V|
352400
[CircuitSAT $arrow.r$ SpinGlass], [$O(|"gates"|)$], [@whitfield2012 @lucas2014],
353401
[Factoring $arrow.r$ CircuitSAT], [$O(m n)$], [Folklore],
354402
[SpinGlass $arrow.l.r$ MaxCut], [$O(n + |J|)$], [@barahona1982 @lucas2014],
403+
table.cell(fill: gray)[Factoring $arrow.r$ ILP], table.cell(fill: gray)[$O(m n)$], table.cell(fill: gray)[—],
355404
),
356405
caption: [Summary of reductions. Gray rows indicate trivial reductions.]
357406
) <tab:summary>

0 commit comments

Comments
 (0)