Files
symclaw/docs/ARCHITECTURE.md
T

296 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SymClaw Architecture
This document describes the internal architecture of SymClaw for contributors and anyone curious about how a modern symbolic math engine works.
## System Overview
```
User Input
┌────────────┼────────────┐
▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌──────────┐
│ CLI │ │ WASM │ │ OpenClaw │
│ REPL │ │ Browser │ │ Skill │
└───┬────┘ └────┬────┘ └────┬─────┘
│ │ │
└────────────┼────────────┘
┌─────────────────┐
│ Action Router │
│ (symclaw-skill)│
└────────┬────────┘
┌────────────────────────────────┐
│ symclaw-core │
│ │
│ ┌────────┐ ┌───────────┐ │
│ │ Parser │───▶│ AST │ │
│ │ (nom) │ │ Arc<Expr> │ │
│ └────────┘ └─────┬─────┘ │
│ │ │
│ ┌────────────┼───────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌────────┐ ┌──┴──────┐
│ │ Simplify │ │E-graph │ │ Diff │
│ │(pipeline)│ │ (egg) │ │ Integ │
│ └──────────┘ └────────┘ │ Solve │
│ │ Series │
│ │ Eval │
│ └─────────┘
│ │
│ ┌──────────────────────┘
│ ▼
│ ┌──────────┐
│ │ LaTeX │
│ │ Renderer │
│ └──────────┘
└────────────────────────────────┘
```
## Expression AST
All symbolic expressions are represented as `Arc<Expr>`, defined in `ast.rs`:
```rust
pub enum Expr {
Num(Rational64), // Exact rational: 3/4, -7, 0
Float(f64), // IEEE 754 when exactness isn't needed
Sym(Symbol), // Interned symbol (fast equality)
Add(Vec<Arc<Expr>>), // Flattened sum: a + b + c
Mul(Vec<Arc<Expr>>), // Flattened product: a * b * c
Pow(Arc<Expr>, Arc<Expr>), // Exponentiation: base^exp
Func(Symbol, Vec<Arc<Expr>>), // Named function: sin(x), ln(x+1)
Neg(Arc<Expr>), // Unary negation
Inv(Arc<Expr>), // Multiplicative inverse: 1/x
}
```
### Design Principles
- **`Arc`-based sharing**: Subtrees are reference-counted, so `x^2 + x^2` shares the same `x^2` node. Cloning is O(1).
- **Canonical ordering**: `Add` and `Mul` children are sorted by a deterministic ordering (symbols alphabetically, numbers first). This makes structural equality reliable.
- **Interned symbols**: Variable names are stored as integer IDs via `string-interner`. Comparing `x == x` is a single integer comparison, not a string comparison.
- **Flattened n-ary operators**: `a + b + c` is `Add([a, b, c])`, not `Add(Add(a, b), c)`. This simplifies pattern matching and avoids arbitrary associativity choices.
- **Exact rational arithmetic**: Core operations use `Rational64` (from `num-rational`) to avoid floating-point error. `1/3 + 1/3 + 1/3` is exactly `1`, not `0.9999...`.
## Simplification Pipeline
Simplification runs in `simplify.rs` as a multi-pass pipeline. Each pass is a pure function `Arc<Expr> → Arc<Expr>`:
```
Input Expression
┌──────────────┐
│ Constant Fold│ 2 + 3 → 5, sin(0) → 0
└──────┬───────┘
┌──────────────┐
│ Identity │ x + 0 → x, x * 1 → x, x^1 → x
│ Elimination │
└──────┬───────┘
┌──────────────┐
│ Like-Term │ 2x + 3x → 5x, x·x → x²
│ Collection │
└──────┬───────┘
┌──────────────┐
│ Canonical │ Sort children, flatten nested Add/Mul
│ Form │
└──────┬───────┘
┌──────────────┐
│ E-graph │ Optional: equality saturation for
│ Saturation │ deep algebraic identities
└──────┬───────┘
Simplified Expression
```
The pipeline iterates until a fixed point (expression stops changing) or a maximum iteration count is reached. In practice, 2-3 passes suffice for most expressions.
## E-graph Integration
SymClaw uses the [egg](https://egraphs-good.github.io/) crate for equality saturation — a technique that explores all equivalent forms of an expression simultaneously, then extracts the "best" one.
### How It Works
1. **Insert** the expression into an e-graph
2. **Apply rewrite rules** (30+ rules covering algebra, trig, logarithms, exponents)
3. **Saturate** until no new equalities are discovered (or a node/time limit is hit)
4. **Extract** the smallest equivalent expression by AST cost
### Rewrite Rules (Sample)
```
// Algebra
a + 0 ⟶ a
a * 1 ⟶ a
a * 0 ⟶ 0
a - a ⟶ 0
a / a ⟶ 1 (a ≠ 0)
a^0 ⟶ 1
a^1 ⟶ a
(a^m)^n ⟶ a^(m*n)
// Trig
sin(x)^2 + cos(x)^2 ⟶ 1
sin(0) ⟶ 0
cos(0) ⟶ 1
// Logarithms
ln(e^x) ⟶ x
e^(ln(x)) ⟶ x (x > 0)
ln(a*b) ⟶ ln(a) + ln(b) (a,b > 0)
ln(a/b) ⟶ ln(a) - ln(b) (a,b > 0)
```
### Explain Mode
`explain_equivalence(a, b)` returns a step-by-step proof of why two expressions are equal, listing which rewrite rules were applied at each step. Used for educational output and debugging.
## Differentiation
`differentiate.rs` implements symbolic differentiation via recursive structural rules:
| Expression | Derivative (w.r.t. x) |
|---|---|
| `c` (constant) | `0` |
| `x` | `1` |
| `f + g` | `f' + g'` |
| `f · g` | `f'·g + f·g'` (product rule) |
| `f^n` (n const) | `n·f^(n-1)·f'` (chain rule) |
| `f^g` (general) | `f^g·(g'·ln(f) + g·f'/f)` |
| `sin(f)` | `cos(f)·f'` |
| `cos(f)` | `-sin(f)·f'` |
| `exp(f)` | `exp(f)·f'` |
| `ln(f)` | `f'/f` |
**Higher-order**: `differentiate_n(expr, var, n)` applies differentiation `n` times.
**Partial derivatives**: Differentiate with respect to any variable; other variables are treated as constants.
The result is always passed through `simplify()` to reduce to canonical form.
## Integration
`integrate.rs` implements symbolic integration via pattern matching with fallback strategies:
1. **Linearity**: `∫(a·f + b·g) dx = a·∫f dx + b·∫g dx`
2. **Power rule**: `∫x^n dx = x^(n+1)/(n+1)` for n ≠ -1
3. **Known antiderivatives**: sin, cos, exp, ln, tan, sec², etc.
4. **U-substitution**: Detect `∫f(g(x))·g'(x) dx` patterns
5. **Integration by parts**: `∫u dv = u·v - ∫v du` with LIATE heuristic for choosing u
6. **Definite integrals**: Evaluate antiderivative at bounds (Fundamental Theorem)
Returns `Option<Arc<Expr>>``None` when no closed-form antiderivative is found.
## Equation Solver
`solve.rs` solves equations of the form `expr = 0`:
1. **Polynomial detection**: Extract coefficients of `x^0, x^1, x^2, ...`
2. **Linear**: `ax + b = 0``x = -b/a`
3. **Quadratic**: `ax² + bx + c = 0` → Exact quadratic formula with rational simplification
4. **Higher polynomial**: Rational root theorem to find integer/rational roots, then synthetic division
5. **Transcendental**: Pattern matching for forms like `e^x = k`, `sin(x) = k`
6. **Systems (2×2)**: Gaussian elimination for pairs of linear equations
All solutions are returned as `Vec<Arc<Expr>>` in simplified form.
## Taylor Series
`series.rs` computes Taylor expansions `f(x) ≈ Σ f^(n)(a)/n! · (x-a)^n`:
### Fast Path
For known functions (sin, cos, exp, ln, sinh, cosh), series coefficients are computed directly from known formulas without repeated differentiation. This is O(n) instead of O(n²).
### General Path
For arbitrary expressions, the engine differentiates `n` times at the expansion point, evaluating each derivative numerically or symbolically. The coefficients are assembled into a polynomial.
### Maclaurin
When `point = 0`, this is a Maclaurin series. The fast path handles the most common cases.
## WASM Compilation
`symclaw-wasm` compiles `symclaw-core` to WebAssembly via `wasm-bindgen`:
- **No std dependencies**: Core engine uses only `alloc`, making WASM compilation clean
- **12 exported functions**: `parse`, `simplify`, `differentiate`, `integrate`, `solve`, `taylor`, `eval_expr`, `to_latex`, `simplify_egraph`, `explain`, `plot_data`, `version`
- **String-based API**: WASM boundary passes strings (JSON for complex types)
- **Size**: ~800 KB gzipped
The WASM module runs entirely client-side — no server roundtrips for computation.
## OpenClaw Skill Protocol
`symclaw-skill` implements the OpenClaw skill protocol:
- **Transport**: JSON-RPC over stdin/stdout (one JSON object per line)
- **Actions**: 8 tool endpoints (`simplify`, `differentiate`, `integrate`, `solve`, `taylor`, `eval`, `latex`, `plot_data`)
- **Canvas**: Two HTML canvases — `manipulate.html` (interactive plotting with sliders) and `result.html` (formatted result display)
### Request/Response Flow
```
OpenClaw Gateway
│ {"action": "differentiate", "expr": "sin(x^2)", "var": "x"}
symclaw-skill (stdin)
│ parse → differentiate → simplify → latex
symclaw-skill (stdout)
│ {"success": true, "result": "2*x*cos(x^2)", "latex": "2 x \\cos(x^{2})"}
OpenClaw Gateway → User's channel
```
### Canvas Manipulate
The `manipulate.html` canvas provides Mathematica-style interactive exploration:
1. User specifies an expression and parameter ranges (e.g., `a*sin(b*x)` with `a ∈ [0,5]`, `b ∈ [1,10]`)
2. Canvas renders sliders for each parameter
3. The WASM module evaluates the expression at 200+ points per slider change
4. A canvas-based plot updates in real time (~60 fps)
All computation happens client-side via the WASM module — no server calls during interaction.
## Performance Characteristics
| Component | Complexity | Notes |
|---|---|---|
| Parse | O(n) | n = input length, nom zero-copy |
| Simplify (algebraic) | O(n log n) | Sorting + single-pass rewrites |
| E-graph saturation | O(rules × nodes) | Bounded by iteration/node limits |
| Differentiate | O(n) | n = AST nodes, single structural pass |
| Integrate | O(n²) worst | Pattern matching + recursive attempts |
| Solve (quadratic) | O(1) | Direct formula |
| Taylor (fast path) | O(order) | Known function coefficients |
| Taylor (general) | O(order²) | Repeated differentiation |
Memory: All expressions are `Arc`-shared. A deeply nested expression with shared subterms uses far less memory than its unfolded size.
## Dependencies
| Crate | Purpose | Why |
|---|---|---|
| `egg` | E-graph equality saturation | Best-in-class Rust e-graph library |
| `nom` | Parser combinators | Zero-copy, composable, fast |
| `num-rational` | Exact rational arithmetic | Avoid floating-point errors |
| `num-bigint` | Big integer support | Overflow-safe coefficients |
| `string-interner` | Symbol interning | O(1) symbol comparison |
| `ordered-float` | Hashable f64 | For numeric expression keys |
| `serde` / `serde_json` | Serialization | Skill protocol + WASM boundary |
| `ahash` | Fast hashing | Expression caching |
| `lru` | LRU cache | Memoize expensive operations |