Skip to content

Commit d3d974d

Browse files
GiggleLiuclaude
andcommitted
docs: add design for redundant rule detection utility (#193)
Detect primitive reduction rules dominated by composite paths using asymptotic growth rate classification of overhead expressions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5fbde0e commit d3d974d

1 file changed

Lines changed: 119 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Redundant Rule Detection Utility
2+
3+
**Date:** 2026-03-09
4+
**Issue:** #193
5+
6+
## Goal
7+
8+
Detect primitive reduction rules whose overhead is dominated (equal or worse) by a composite path through other rules. Prevents adding redundant rules and documents existing redundancies.
9+
10+
## Location
11+
12+
`src/rules/analysis.rs` — new module alongside `graph.rs`, `registry.rs`, `cost.rs`.
13+
14+
## Types
15+
16+
```rust
17+
/// Asymptotic growth rate classification.
18+
/// Ordered: Constant < Logarithmic < Polynomial(k) < Exponential(c) < SuperExponential.
19+
#[derive(Debug, Clone, PartialEq, PartialOrd)]
20+
enum GrowthRate {
21+
Constant,
22+
Logarithmic,
23+
Polynomial(f64), // degree k
24+
Exponential(f64), // base c
25+
SuperExponential,
26+
}
27+
28+
/// A primitive rule dominated by a composite path.
29+
struct DominatedRule {
30+
source: String,
31+
target: String,
32+
primitive_overhead: ReductionOverhead,
33+
dominating_path: ReductionPath,
34+
composed_overhead: ReductionOverhead,
35+
comparison: Vec<(String, Ordering)>, // per-field ordering
36+
}
37+
```
38+
39+
## Key Functions
40+
41+
### `Expr::growth_rate(var: &str) -> Result<GrowthRate, String>`
42+
43+
Recursively classify an expression's growth w.r.t. a single variable.
44+
45+
Rules (recursive — classify children first, then combine):
46+
47+
| Expression | Child classifications | Result |
48+
|---|---|---|
49+
| `Const(_)` || `Constant` |
50+
| `Var(v)` where `v == var` || `Polynomial(1.0)` |
51+
| `Var(v)` where `v != var` || `Constant` |
52+
| `Add(a, b)` | any | `max(a, b)` |
53+
| `Mul(a, b)` | both `Poly(k1, k2)` | `Polynomial(k1 + k2)` |
54+
| `Mul(a, b)` | one `Exp`, one `Poly` | `Exponential(same base)` |
55+
| `Mul(a, b)` | otherwise | `Error` |
56+
| `Pow(base, exp)` | `Poly(k)`, `Const(c)` | `Polynomial(k * c)` |
57+
| `Pow(base, exp)` | `Const(c)`, `Poly(k)` | `Exponential(c)` |
58+
| `Pow(base, exp)` | `Const(c)`, `Logarithmic` | `Polynomial(c.ln())` |
59+
| `Pow(base, exp)` | `Poly`, `Poly` | `SuperExponential` |
60+
| `Pow(base, exp)` | otherwise | `Error` |
61+
| `Exp(Log(inner))` || `growth_rate(inner)` (cancellation) |
62+
| `Exp(inner)` | `Constant` | `Constant` |
63+
| `Exp(inner)` | `Poly(k)`, k >= 1 | `Exponential(e)` |
64+
| `Exp(inner)` | `Exponential` | `SuperExponential` |
65+
| `Exp(inner)` | otherwise | `Error` |
66+
| `Log(inner)` | `Constant` | `Constant` |
67+
| `Log(inner)` | `Polynomial(_)` | `Logarithmic` |
68+
| `Log(inner)` | `Exponential(c)` | `Polynomial(1.0)` |
69+
| `Log(inner)` | otherwise | `Error` |
70+
| `Sqrt(inner)` | `Polynomial(k)` | `Polynomial(k / 2.0)` |
71+
| `Sqrt(inner)` | otherwise | `Error` |
72+
73+
Returns `Err` for unclassifiable expressions rather than guessing.
74+
75+
### `compare_overhead(primitive: &ReductionOverhead, composite: &ReductionOverhead) -> Option<Ordering>`
76+
77+
For each common field:
78+
1. Collect all variables referenced by both expressions
79+
2. For each variable, compute `growth_rate` of both expressions
80+
3. Take the max growth rate across all variables for each expression
81+
4. Compare: composite <= primitive on all fields → `Some(Equal | Less)`; otherwise `None`
82+
83+
### `find_dominated_rules(graph: &ReductionGraph) -> Vec<DominatedRule>`
84+
85+
1. Iterate all edges in the variant-level graph
86+
2. For each edge (u, v), call `find_all_paths(u, v)`
87+
3. Filter to composite paths (len > 1)
88+
4. Compose overhead via `ReductionOverhead::compose`
89+
5. Compare with `compare_overhead`
90+
6. Collect dominated results
91+
92+
## Integration Test
93+
94+
In `src/unit_tests/rules/analysis.rs`:
95+
- Call `find_dominated_rules` on the global `ReductionGraph`
96+
- Maintain an allow-list of known dominated rules (currently 9: 6 genuine + 3 cast-composed)
97+
- **Fail if a new dominated rule appears** that is not in the allow-list
98+
- **Fail if an allow-listed rule is no longer dominated** (stale allow-list)
99+
100+
## Existing Infrastructure Used
101+
102+
- `ReductionGraph::find_all_paths()` — path enumeration
103+
- `ReductionOverhead::compose()` — chains overheads via `Expr::substitute`
104+
- `Expr::is_polynomial()` — partial classification (extended by `growth_rate`)
105+
- `Expr::substitute()` — variable substitution for composition
106+
107+
## Known Dominated Rules (Allow-List)
108+
109+
| Primitive | Best Composite | Category |
110+
|---|---|---|
111+
| KColoring → QUBO | KColoring → ILP → QUBO | genuine |
112+
| MIS → ILP | MIS → MVC → ILP | genuine |
113+
| MIS → QUBO | MIS → MVC → QUBO | genuine |
114+
| MaxSetPacking → ILP | SetPacking → MIS → ILP | genuine |
115+
| MVC → ILP | MVC → MinSetCovering → ILP | genuine |
116+
| MVC → QUBO | MVC → MIS → QUBO | genuine |
117+
| KSAT/K2 → SAT | K2 → KN → SAT | cast-composed |
118+
| KSAT/K3 → SAT | K3 → KN → SAT | cast-composed |
119+
| MIS/One → MIS/Kings/i32 | One→i32 → Kings/i32 | cast-composed |

0 commit comments

Comments
 (0)