Files
symclaw/docs/ARCHITECTURE.md
T

13 KiB
Raw Blame History

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:

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 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 = 0x = -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