694 lines
27 KiB
Markdown
694 lines
27 KiB
Markdown
# SymClaw — Implementation Plan & Critical Analysis
|
||
|
||
## Project: Open-Source Symbolic Computing for the Agentic Age
|
||
|
||
**Codename:** SymClaw
|
||
**Stack:** Rust Symbolic Engine + OpenClaw Agentic Bridge
|
||
**Author:** Omar (HPC-AI Platform)
|
||
**Date:** 2026-02-12
|
||
**Status:** Architecture Review
|
||
|
||
---
|
||
|
||
## 1. Strategic Assessment
|
||
|
||
### 1.1 What Makes This a Paradigm Shift
|
||
|
||
The roadmap you've drafted is strong, but to truly be paradigm-shifting, there are dimensions the original plan underweights. Here's what elevates this from "another CAS" to a genuine platform shift:
|
||
|
||
**The Killer Insight: Deterministic Compute + Agentic Intelligence**
|
||
|
||
Every existing CAS is either (a) deterministic but dumb (Mathematica, SymPy — you type exact commands) or (b) intelligent but unreliable (ChatGPT doing math — hallucinates). SymClaw is the first system where:
|
||
|
||
- The **LLM handles intent** ("what's the derivative of this messy physics equation?")
|
||
- The **symbolic engine guarantees correctness** (deterministic Rust computation)
|
||
- The **e-graph finds the optimal form** (not just *an* answer, but the *best* answer)
|
||
- The **agentic layer delivers it anywhere** (WhatsApp, Telegram, Canvas, voice)
|
||
|
||
This is the "compiler + IDE" moment for scientific computing. No one else is building this.
|
||
|
||
### 1.2 Critical Gaps in the Original Roadmap
|
||
|
||
| Gap | Why It Matters | Resolution |
|
||
|-----|---------------|------------|
|
||
| **No numeric fallback strategy** | Many real-world problems can't be solved symbolically | Add hybrid symbolic-numeric pipeline (Phase 2.5) |
|
||
| **No error recovery / graceful degradation** | Users will hit unsolvable expressions constantly | Implement "best effort" mode with confidence scoring |
|
||
| **Parser underspecified** | Input parsing is 50% of the user experience | Dedicate Sprint 1 to parser with extensive edge-case handling |
|
||
| **No caching layer** | Same expressions re-evaluated across sessions | Add expression-level memoization with LRU cache |
|
||
| **Security model missing** | Arbitrary expression evaluation is a DoS vector | Expression complexity limits, timeout enforcement, sandboxing |
|
||
| **No migration path from existing tools** | Scientists won't switch without import capability | SymPy/LaTeX import in V0.5, Mathematica notebook import in V1.0 |
|
||
| **Collaboration undefined** | Research is collaborative | Multi-user Canvas sessions via OpenClaw multi-agent routing (V1.0) |
|
||
| **No offline mode** | Scientists work on planes, in labs without internet | WASM module works fully offline; LLM intent parsing degrades to direct command mode |
|
||
|
||
### 1.3 Build vs. Integrate Decisions
|
||
|
||
Before writing a single line, these decisions save months:
|
||
|
||
| Component | Build | Integrate | Recommendation |
|
||
|-----------|-------|-----------|----------------|
|
||
| Expression AST | ✅ | | Build — core IP, must be Arc-based for your cluster |
|
||
| Parser | ✅ | | Build with nom — need custom notation support |
|
||
| Simplifier | Hybrid | `egg` crate | Integrate egg, build rule sets on top |
|
||
| Differentiation | ✅ | | Build — well-defined algorithms, your rules |
|
||
| Integration | ✅ | | Build core; consider SymPy FFI for edge cases |
|
||
| Linear Algebra | | `nalgebra` | Integrate nalgebra for numeric, build symbolic layer |
|
||
| Plotting | | Plotly.js | Integrate in Canvas; don't build a plotting library |
|
||
| LaTeX rendering | | KaTeX | Integrate — battle-tested, fast, WASM-compatible |
|
||
| Arbitrary precision | | `num` crate | Integrate |
|
||
| WASM compilation | | `wasm-bindgen` | Integrate |
|
||
|
||
---
|
||
|
||
## 2. Architecture Deep Dive
|
||
|
||
### 2.1 Expression AST — The Foundation
|
||
|
||
This is the most critical design decision. Get it wrong and everything downstream suffers.
|
||
|
||
```rust
|
||
// Core expression type — every operation in the system flows through this
|
||
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
|
||
pub enum Expr {
|
||
// Atoms
|
||
Num(Rational), // Exact: 3/7, -42, 0
|
||
Float(OrderedFloat<f64>), // IEEE 754 when exact isn't needed
|
||
Symbol(Symbol), // Interned string: x, y, theta
|
||
Complex(Arc<Expr>, Arc<Expr>), // a + bi
|
||
|
||
// N-ary operations (flattened, canonically sorted)
|
||
Add(Vec<Arc<Expr>>), // a + b + c (not nested)
|
||
Mul(Vec<Arc<Expr>>), // a * b * c (not nested)
|
||
|
||
// Binary operations
|
||
Pow(Arc<Expr>, Arc<Expr>), // base^exponent
|
||
|
||
// Functions
|
||
Func(FuncId, Vec<Arc<Expr>>), // sin(x), log(x, base), custom(a, b, c)
|
||
|
||
// Calculus
|
||
Derivative(Arc<Expr>, Symbol, u32), // d^n f / dx^n
|
||
Integral(Arc<Expr>, Symbol, Option<Bounds>), // ∫f dx or ∫_a^b f dx
|
||
Limit(Arc<Expr>, Symbol, Arc<Expr>, Direction),
|
||
Sum(Arc<Expr>, Symbol, Arc<Expr>, Arc<Expr>), // Σ
|
||
Product(Arc<Expr>, Symbol, Arc<Expr>, Arc<Expr>), // Π
|
||
|
||
// Structural
|
||
Eq(Arc<Expr>, Arc<Expr>), // equation
|
||
Matrix(MatrixData), // symbolic matrix
|
||
Piecewise(Vec<(Arc<Expr>, Condition)>),
|
||
|
||
// Meta
|
||
Undefined, // 0/0, etc.
|
||
Infinity(Sign),
|
||
}
|
||
```
|
||
|
||
**Critical Design Notes:**
|
||
|
||
1. **Arc, not Box** — You need Send + Sync for rayon parallelism across your Pi cluster nodes. The ~8 bytes overhead per Arc is negligible vs. the threading flexibility.
|
||
|
||
2. **Interned Symbols** — Use the `string_interner` crate. In a typical session, the same variable names (x, y, t, theta) appear thousands of times. Interning reduces memory and makes comparison O(1).
|
||
|
||
3. **Canonical Ordering** — `Add(vec![x, 3])` and `Add(vec![3, x])` must be the same expression. Sort commutative operands by a canonical ordering (constants first, then symbols alphabetically, then complex expressions by depth). This is essential for e-graph efficiency.
|
||
|
||
4. **Flattened N-ary** — `(a + b) + c` becomes `Add(vec![a, b, c])`. This eliminates association ambiguity and makes pattern matching simpler.
|
||
|
||
### 2.2 The Egg Integration — This Is Your Moat
|
||
|
||
The equality saturation engine via `egg` is what differentiates SymClaw from SymPy. Here's why and how:
|
||
|
||
**Why it matters:**
|
||
|
||
Traditional CAS simplifiers apply rules greedily: pick a rule, apply it, repeat. This gets stuck in local optima. Example:
|
||
|
||
```
|
||
Input: (x^2 - 1) / (x - 1)
|
||
Greedy: tries to simplify numerator and denominator separately → stuck
|
||
Egg: discovers x^2 - 1 = (x+1)(x-1), cancels → (x+1) ✓
|
||
```
|
||
|
||
Egg explores ALL possible rewrites simultaneously using e-graphs, then extracts the simplest result. This is provably optimal (given enough rules and iterations).
|
||
|
||
**Implementation strategy:**
|
||
|
||
```rust
|
||
use egg::{*, rewrite as rw};
|
||
|
||
define_language! {
|
||
pub enum MathLang {
|
||
Num(i64),
|
||
"+" = Add([Id; 2]),
|
||
"*" = Mul([Id; 2]),
|
||
"/" = Div([Id; 2]),
|
||
"^" = Pow([Id; 2]),
|
||
"neg" = Neg([Id; 1]),
|
||
"sin" = Sin([Id; 1]),
|
||
"cos" = Cos([Id; 1]),
|
||
"ln" = Ln([Id; 1]),
|
||
"exp" = Exp([Id; 1]),
|
||
"d" = Deriv([Id; 2]), // d(expr, var)
|
||
Symbol(Symbol),
|
||
}
|
||
}
|
||
|
||
fn math_rules() -> Vec<Rewrite<MathLang, ConstantFold>> {
|
||
vec![
|
||
// Arithmetic identities
|
||
rw!("add-0"; "(+ ?a 0)" => "?a"),
|
||
rw!("mul-1"; "(* ?a 1)" => "?a"),
|
||
rw!("mul-0"; "(* ?a 0)" => "0"),
|
||
rw!("pow-0"; "(^ ?a 0)" => "1"),
|
||
rw!("pow-1"; "(^ ?a 1)" => "?a"),
|
||
|
||
// Commutativity
|
||
rw!("add-comm"; "(+ ?a ?b)" => "(+ ?b ?a)"),
|
||
rw!("mul-comm"; "(* ?a ?b)" => "(* ?b ?a)"),
|
||
|
||
// Associativity
|
||
rw!("add-assoc"; "(+ ?a (+ ?b ?c))" => "(+ (+ ?a ?b) ?c)"),
|
||
rw!("mul-assoc"; "(* ?a (* ?b ?c))" => "(* (* ?a ?b) ?c)"),
|
||
|
||
// Distribution
|
||
rw!("distribute"; "(* ?a (+ ?b ?c))" => "(+ (* ?a ?b) (* ?a ?c))"),
|
||
rw!("factor"; "(+ (* ?a ?b) (* ?a ?c))" => "(* ?a (+ ?b ?c))"),
|
||
|
||
// Differentiation rules (the magic — egg discovers derivatives)
|
||
rw!("d-const"; "(d ?c ?x)" => "0" if is_const("?c", "?x")),
|
||
rw!("d-var"; "(d ?x ?x)" => "1"),
|
||
rw!("d-add"; "(d (+ ?a ?b) ?x)" => "(+ (d ?a ?x) (d ?b ?x))"),
|
||
rw!("d-mul"; "(d (* ?a ?b) ?x)" =>
|
||
"(+ (* (d ?a ?x) ?b) (* ?a (d ?b ?x)))"),
|
||
rw!("d-pow"; "(d (^ ?a ?n) ?x)" =>
|
||
"(* (* ?n (^ ?a (+ ?n -1))) (d ?a ?x))"
|
||
if is_const("?n", "?x")),
|
||
rw!("d-sin"; "(d (sin ?a) ?x)" => "(* (cos ?a) (d ?a ?x))"),
|
||
rw!("d-cos"; "(d (cos ?a) ?x)" => "(* (neg (sin ?a)) (d ?a ?x))"),
|
||
rw!("d-exp"; "(d (exp ?a) ?x)" => "(* (exp ?a) (d ?a ?x))"),
|
||
rw!("d-ln"; "(d (ln ?a) ?x)" => "(* (/ 1 ?a) (d ?a ?x))"),
|
||
|
||
// Trig identities
|
||
rw!("sin2+cos2"; "(+ (^ (sin ?a) 2) (^ (cos ?a) 2))" => "1"),
|
||
rw!("exp-ln"; "(exp (ln ?a))" => "?a"),
|
||
rw!("ln-exp"; "(ln (exp ?a))" => "?a"),
|
||
]
|
||
}
|
||
```
|
||
|
||
**Explain Mode — The "Show Your Work" Feature:**
|
||
|
||
```rust
|
||
// Using egg's explain API to show step-by-step derivation
|
||
let runner = Runner::default()
|
||
.with_explanations_enabled()
|
||
.with_expr(&start)
|
||
.run(&rules);
|
||
|
||
let explanation = runner.explain_equivalence(&start, &goal);
|
||
// Returns: [(rule_name, before_expr, after_expr), ...]
|
||
// User sees: "Applied product rule → Applied power rule → Simplified"
|
||
```
|
||
|
||
This is huge for education. No other CAS can show WHY it simplified an expression.
|
||
|
||
### 2.3 OpenClaw Skill Architecture
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ User's Phone/Desktop │
|
||
│ Telegram │ WhatsApp │ Canvas │ WebChat │
|
||
└─────────────┬───────────────────────────┘
|
||
│ message: "derive sin(x^2)"
|
||
▼
|
||
┌─────────────────────────────────────────┐
|
||
│ OpenClaw Gateway (Node.js) │
|
||
│ ┌─────────────────────────────────┐ │
|
||
│ │ LLM (Claude/GPT) — Intent Parse │ │
|
||
│ │ "derive sin(x^2)" │ │
|
||
│ │ → tool: math_eval │ │
|
||
│ │ → expr: "d/dx(sin(x^2))" │ │
|
||
│ └──────────────┬──────────────────┘ │
|
||
│ │ │
|
||
│ ┌──────────────▼──────────────────┐ │
|
||
│ │ symclaw Skill (JS wrapper) │ │
|
||
│ │ ┌────────────────────────┐ │ │
|
||
│ │ │ Option A: WASM Module │ │ │
|
||
│ │ │ (in-process, fast) │ │ │
|
||
│ │ └────────────────────────┘ │ │
|
||
│ │ ┌────────────────────────┐ │ │
|
||
│ │ │ Option B: Subprocess │ │ │
|
||
│ │ │ (native, full perf) │ │ │
|
||
│ │ └────────────────────────┘ │ │
|
||
│ └──────────────┬──────────────────┘ │
|
||
│ │ result + LaTeX │
|
||
│ ┌──────────────▼──────────────────┐ │
|
||
│ │ Response Formatter │ │
|
||
│ │ Telegram: ASCII + LaTeX image │ │
|
||
│ │ Canvas: Interactive HTML │ │
|
||
│ │ WhatsApp: ASCII + rendered PNG │ │
|
||
│ └─────────────────────────────────┘ │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.4 Canvas Manipulate — Real-Time Math Exploration
|
||
|
||
This is the feature that makes scientists' eyes light up. Mathematica's `Manipulate[]` is one of its most beloved features. Here's how we replicate it:
|
||
|
||
```javascript
|
||
// A2UI JSONL payload pushed to Canvas
|
||
{"surfaceUpdate": {
|
||
"surfaceId": "manipulate-1",
|
||
"components": [
|
||
{"id": "title", "component": {"Text": {
|
||
"text": {"literalString": "f(x) = a·sin(b·x + c)"},
|
||
"usageHint": "h2"
|
||
}}},
|
||
{"id": "slider-a", "component": {"Slider": {
|
||
"min": 0.1, "max": 5.0, "step": 0.1, "value": 1.0,
|
||
"label": "a (amplitude)"
|
||
}}},
|
||
{"id": "slider-b", "component": {"Slider": {
|
||
"min": 0.1, "max": 10.0, "step": 0.1, "value": 1.0,
|
||
"label": "b (frequency)"
|
||
}}},
|
||
{"id": "slider-c", "component": {"Slider": {
|
||
"min": -3.14, "max": 3.14, "step": 0.01, "value": 0.0,
|
||
"label": "c (phase)"
|
||
}}},
|
||
{"id": "plot", "component": {"WebView": {
|
||
"html": "<canvas id='plot'></canvas><script>/* Plotly + WASM eval */</script>"
|
||
}}}
|
||
]
|
||
}}
|
||
```
|
||
|
||
The magic: slider changes trigger WASM evaluation directly in the Canvas JavaScript, no round-trip to the server needed. 30fps updates on a phone.
|
||
|
||
---
|
||
|
||
## 3. Testing Strategy — Correctness Is Non-Negotiable
|
||
|
||
### 3.1 The Testing Pyramid
|
||
|
||
```
|
||
╱╲
|
||
╱ ╲ Manual Testing
|
||
╱ 5% ╲ (demo to researchers, get feedback)
|
||
╱──────╲
|
||
╱ ╲ Stress/Load Tests
|
||
╱ 10% ╲ (Pi cluster, WASM limits, DoS)
|
||
╱────────────╲
|
||
╱ ╲ Integration Tests
|
||
╱ 15% ╲ (OpenClaw → Engine → Canvas pipeline)
|
||
╱──────────────────╲
|
||
╱ ╲ Property-Based Tests
|
||
╱ 25% ╲ (proptest: numerical invariants)
|
||
╱────────────────────────╲
|
||
╱ ╲ Unit Tests
|
||
╱ 45% ╲ (every rule, every function)
|
||
╱──────────────────────────────╲
|
||
```
|
||
|
||
### 3.2 Property-Based Testing (The Secret Weapon)
|
||
|
||
```rust
|
||
use proptest::prelude::*;
|
||
|
||
// Strategy: generate random mathematical expressions
|
||
fn arb_expr(depth: u32) -> impl Strategy<Value = Expr> {
|
||
let leaf = prop_oneof![
|
||
(1..100i64).prop_map(|n| Expr::Num(Rational::from(n))),
|
||
Just(Expr::Symbol("x".into())),
|
||
Just(Expr::Symbol("y".into())),
|
||
];
|
||
|
||
leaf.prop_recursive(depth, 256, 10, |inner| {
|
||
prop_oneof![
|
||
(inner.clone(), inner.clone()).prop_map(|(a, b)|
|
||
Expr::Add(vec![Arc::new(a), Arc::new(b)])),
|
||
(inner.clone(), inner.clone()).prop_map(|(a, b)|
|
||
Expr::Mul(vec![Arc::new(a), Arc::new(b)])),
|
||
inner.clone().prop_map(|a|
|
||
Expr::Func(FuncId::Sin, vec![Arc::new(a)])),
|
||
]
|
||
})
|
||
}
|
||
|
||
proptest! {
|
||
// INVARIANT 1: Simplification preserves numerical value
|
||
#[test]
|
||
fn simplify_preserves_value(
|
||
expr in arb_expr(4),
|
||
x in -10.0f64..10.0,
|
||
) {
|
||
let original = expr.eval(&[("x", x)]);
|
||
let simplified = simplify(&expr);
|
||
let result = simplified.eval(&[("x", x)]);
|
||
|
||
if original.is_finite() && result.is_finite() {
|
||
prop_assert!((original - result).abs() < 1e-10,
|
||
"Simplification changed value: {} → {}", original, result);
|
||
}
|
||
}
|
||
|
||
// INVARIANT 2: Simplification is idempotent
|
||
#[test]
|
||
fn simplify_idempotent(expr in arb_expr(3)) {
|
||
let once = simplify(&expr);
|
||
let twice = simplify(&once);
|
||
prop_assert_eq!(once, twice, "Not idempotent");
|
||
}
|
||
|
||
// INVARIANT 3: Parse round-trip
|
||
#[test]
|
||
fn parse_roundtrip(expr in arb_expr(3)) {
|
||
let printed = format!("{}", expr);
|
||
let reparsed = parse(&printed).unwrap();
|
||
prop_assert_eq!(expr, reparsed, "Round-trip failed");
|
||
}
|
||
|
||
// INVARIANT 4: Derivative + Integral approximate inverse
|
||
#[test]
|
||
fn derivative_integral_roundtrip(
|
||
expr in arb_polynomial(3), // restrict to polynomials for tractability
|
||
x in -5.0f64..5.0,
|
||
) {
|
||
let d = differentiate(&expr, "x");
|
||
let i = integrate(&d, "x");
|
||
let diff = (expr.eval(&[("x", x)]) - i.eval(&[("x", x)])).abs();
|
||
// Should differ by at most a constant
|
||
let diff2 = (expr.eval(&[("x", x + 0.1)]) - i.eval(&[("x", x + 0.1)])).abs();
|
||
prop_assert!((diff - diff2).abs() < 1e-8,
|
||
"Integral of derivative should differ by constant");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 Integration Test: Full Pipeline
|
||
|
||
```typescript
|
||
// test/integration/telegram-to-canvas.test.ts
|
||
describe('Telegram → Engine → Canvas pipeline', () => {
|
||
it('should differentiate and render on Canvas', async () => {
|
||
// 1. Simulate Telegram message
|
||
const response = await gateway.handleMessage({
|
||
channel: 'telegram',
|
||
sender: '+15555551234',
|
||
text: 'What is the derivative of x^3 * sin(x)?'
|
||
});
|
||
|
||
// 2. Verify text response
|
||
expect(response.text).toContain('3x²·sin(x) + x³·cos(x)');
|
||
|
||
// 3. Verify LaTeX image was generated
|
||
expect(response.attachments).toHaveLength(1);
|
||
expect(response.attachments[0].mimeType).toBe('image/png');
|
||
|
||
// 4. Verify Canvas push (if mobile node connected)
|
||
const canvasPush = await gateway.getLastCanvasPush();
|
||
expect(canvasPush.surfaceId).toBe('math-result');
|
||
expect(canvasPush.components).toContainEqual(
|
||
expect.objectContaining({ id: 'latex-display' })
|
||
);
|
||
});
|
||
});
|
||
```
|
||
|
||
### 3.4 Stress Test: Pi Cluster
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# stress_test.sh — Run on Pi cluster control node
|
||
# Generate 1000 random expressions and fire them concurrently
|
||
|
||
for i in $(seq 1 1000); do
|
||
EXPR=$(symclaw-cli random-expr --depth 5 --seed $i)
|
||
curl -s -X POST http://gateway:18789/api/eval \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"expr\": \"$EXPR\"}" &
|
||
|
||
# Rate limit: 50 concurrent
|
||
[ $(jobs -r | wc -l) -ge 50 ] && wait -n
|
||
done
|
||
wait
|
||
|
||
echo "All 1000 evaluations complete"
|
||
# Verify: no crashes, no OOM, all returned valid results
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Deployment Architecture
|
||
|
||
### 4.1 Single-Node (90% of users)
|
||
|
||
```yaml
|
||
# docker-compose.yml — one command: docker compose up
|
||
version: '3.8'
|
||
services:
|
||
symclaw:
|
||
image: ghcr.io/symclaw/symclaw:latest
|
||
ports:
|
||
- "18789:18789" # OpenClaw Gateway
|
||
- "3000:3000" # WebChat
|
||
volumes:
|
||
- ~/.openclaw:/root/.openclaw
|
||
- ~/.symclaw:/root/.symclaw
|
||
environment:
|
||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||
- SYMCLAW_ENGINE=wasm # or 'native' for full performance
|
||
restart: unless-stopped
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 2G
|
||
```
|
||
|
||
### 4.2 Pi Cluster (Power Users)
|
||
|
||
```
|
||
┌──────────────────────────────────────────┐
|
||
│ Pi 5 (Control Node) │
|
||
│ OpenClaw Gateway + Tailscale │
|
||
│ Load Balancer (round-robin) │
|
||
│ ┌────────┐ ┌────────┐ ┌────────┐ │
|
||
│ │ Pi 4 #1│ │ Pi 4 #2│ │ Pi 4 #3│ │
|
||
│ │ Engine │ │ Engine │ │ Engine │ │
|
||
│ │ Worker │ │ Worker │ │ Worker │ │
|
||
│ └────────┘ └────────┘ └────────┘ │
|
||
│ ↕ Tailscale mesh ↕ │
|
||
│ ┌──────────────────────────────┐ │
|
||
│ │ x86 Workstation (optional) │ │
|
||
│ │ RTX 5090 — heavy compute │ │
|
||
│ └──────────────────────────────┘ │
|
||
└──────────────────────────────────────────┘
|
||
```
|
||
|
||
### 4.3 Nix Deployment
|
||
|
||
```nix
|
||
# flake.nix
|
||
{
|
||
inputs = {
|
||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||
rust-overlay.url = "github:oxalica/rust-overlay";
|
||
crane.url = "github:ipetkov/crane";
|
||
};
|
||
|
||
outputs = { self, nixpkgs, rust-overlay, crane, ... }:
|
||
let
|
||
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||
in {
|
||
packages = builtins.listToAttrs (map (system: {
|
||
name = system;
|
||
value = {
|
||
symclaw-core = crane.lib.${system}.buildPackage {
|
||
src = ./core;
|
||
# Cross-compile for Pi from x86
|
||
};
|
||
symclaw-wasm = crane.lib.${system}.buildPackage {
|
||
src = ./wasm;
|
||
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
|
||
};
|
||
};
|
||
}) systems);
|
||
|
||
nixosModules.symclaw = { config, ... }: {
|
||
services.symclaw = {
|
||
enable = true;
|
||
port = 18789;
|
||
engineMode = "native"; # or "wasm"
|
||
anthropicApiKey = config.sops.secrets.anthropic.path;
|
||
};
|
||
};
|
||
};
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Open Source & Community Strategy
|
||
|
||
### 5.1 Launch Sequence
|
||
|
||
| Week | Action | Channel |
|
||
|------|--------|---------|
|
||
| T-2 | Soft launch: share with 10 trusted alpha testers | Direct invite |
|
||
| T-1 | Blog post: "Why We Built an Open-Source Mathematica in Rust" | Personal blog + dev.to |
|
||
| T+0 | GitHub public + crates.io publish | GitHub |
|
||
| T+0 | Hacker News "Show HN" post | Hacker News |
|
||
| T+0 | r/rust, r/math, r/opensource posts | Reddit |
|
||
| T+1 | OpenClaw Discord announcement | OpenClaw community |
|
||
| T+2 | Video demo: "From Telegram to LaTeX in 2 Seconds" | YouTube + X |
|
||
| T+4 | Reach out to university math departments | Email + academic networks |
|
||
|
||
### 5.2 Good First Issues (Pre-Labeled)
|
||
|
||
- "Add hyperbolic trig functions (sinh, cosh, tanh)"
|
||
- "Implement pretty-print for matrix expressions"
|
||
- "Add LaTeX input parser (basic subset)"
|
||
- "Write property test for trig identities"
|
||
- "Add Fibonacci sequence to the series module"
|
||
- "Improve error messages for parse failures"
|
||
- "Add --json output flag to CLI"
|
||
|
||
### 5.3 Governance
|
||
|
||
- **License:** MIT + Apache 2.0 (dual license, standard Rust ecosystem)
|
||
- **Contributions:** All PRs require 1 review + CI green + property tests pass
|
||
- **Rule contributions:** New rewrite rules require (a) correctness proof sketch, (b) proptest coverage, (c) benchmark showing no performance regression
|
||
- **Breaking changes:** Semantic versioning, deprecation warnings in minor versions
|
||
|
||
---
|
||
|
||
## 6. What the Roadmap Was Missing — Additions
|
||
|
||
### 6.1 Caching Layer (Add to Phase 1)
|
||
|
||
```rust
|
||
use lru::LruCache;
|
||
use std::sync::Mutex;
|
||
|
||
// Expression-level memoization
|
||
lazy_static! {
|
||
static ref SIMPLIFY_CACHE: Mutex<LruCache<u64, Arc<Expr>>> =
|
||
Mutex::new(LruCache::new(NonZeroUsize::new(10_000).unwrap()));
|
||
}
|
||
|
||
pub fn simplify_cached(expr: &Expr) -> Arc<Expr> {
|
||
let hash = expr.stable_hash();
|
||
if let Some(cached) = SIMPLIFY_CACHE.lock().unwrap().get(&hash) {
|
||
return cached.clone();
|
||
}
|
||
let result = simplify(expr);
|
||
SIMPLIFY_CACHE.lock().unwrap().put(hash, result.clone());
|
||
result
|
||
}
|
||
```
|
||
|
||
### 6.2 Confidence Scoring (Add to Phase 2)
|
||
|
||
Not all simplifications are equally trustworthy. Add a confidence score:
|
||
|
||
```rust
|
||
pub struct EvalResult {
|
||
pub expr: Expr,
|
||
pub latex: String,
|
||
pub confidence: Confidence,
|
||
pub steps: Vec<Step>,
|
||
}
|
||
|
||
pub enum Confidence {
|
||
Exact, // Algebraic simplification, fully verified
|
||
HighConfidence, // Integration by standard method
|
||
Heuristic, // Pattern-matched, not formally verified
|
||
NumericOnly, // Could not solve symbolically, numeric result
|
||
Failed(String), // Could not compute, here's why
|
||
}
|
||
```
|
||
|
||
### 6.3 Offline Mode (Add to Phase 3)
|
||
|
||
When no LLM is available (no API key, no internet), SymClaw should still work:
|
||
|
||
```
|
||
User types: /math d/dx(x^2 * sin(x))
|
||
→ Direct command parsing (no LLM needed)
|
||
→ Engine computes result
|
||
→ Returns ASCII: 2x*sin(x) + x^2*cos(x)
|
||
|
||
User types: "what's the derivative of x squared times sine x"
|
||
→ No LLM available
|
||
→ Respond: "Natural language mode requires an AI model connection.
|
||
Use command syntax: /derive x^2 * sin(x)"
|
||
```
|
||
|
||
### 6.4 Benchmark Suite (Add to Phase 5)
|
||
|
||
Compare against SymPy on standardized problems:
|
||
|
||
```rust
|
||
// benchmarks/sympy_parity.rs
|
||
const BENCHMARK_PROBLEMS: &[(&str, &str)] = &[
|
||
("simplify", "(x^2 - 1)/(x - 1)", "x + 1"),
|
||
("derive", "d/dx(x^3 * sin(x))", "3*x^2*sin(x) + x^3*cos(x)"),
|
||
("integrate", "∫ x*exp(x) dx", "x*exp(x) - exp(x)"),
|
||
("solve", "x^2 - 5*x + 6 = 0", "{2, 3}"),
|
||
("taylor", "sin(x) about 0 order 5", "x - x^3/6 + x^5/120"),
|
||
("limit", "lim(x→0) sin(x)/x", "1"),
|
||
// ... 100+ problems from MIT OCW
|
||
];
|
||
```
|
||
|
||
### 6.5 Telemetry & Observability (Add to Phase 4)
|
||
|
||
```rust
|
||
// Prometheus metrics exposed at /metrics
|
||
pub struct EngineMetrics {
|
||
pub eval_latency: Histogram, // How long evaluations take
|
||
pub egraph_size: Gauge, // Current e-graph node count
|
||
pub cache_hit_rate: Counter, // Memoization effectiveness
|
||
pub parse_errors: Counter, // Input quality signal
|
||
pub timeout_count: Counter, // Expressions that hit limits
|
||
pub active_sessions: Gauge, // Connected OpenClaw sessions
|
||
}
|
||
```
|
||
|
||
Surface these in Omar's OpenClaw Mission Control Dashboard.
|
||
|
||
---
|
||
|
||
## 7. Timeline Summary
|
||
|
||
```
|
||
Week 1-2: Phase 0 — Foundation (CI/CD, Nix, project scaffolding)
|
||
Week 3-6: Phase 1 — Engine Core: Arithmetic, Algebra, Egg, CLI
|
||
Week 7-10: Phase 2 — Engine Core: Calculus, Solving, Series, WASM
|
||
Week 11-13: Phase 3 — OpenClaw Skill, Canvas, Manipulate
|
||
Week 14-15: Phase 4 — Docker, Nix, Pi Cluster, Tailscale
|
||
Week 16-18: Phase 5 — Testing, Docs, Community, V0.1 Alpha Release
|
||
─────────────────────────────────────────────────────────────────
|
||
Week 19-30: Phase 6 — V0.5 Beta (LinAlg, ODEs, Jupyter)
|
||
Week 31-44: Phase 7 — V1.0 Launch (GPU, Clawbernetes, Collaboration)
|
||
```
|
||
|
||
**Total to Alpha: ~18 weeks**
|
||
**Total to Production Launch: ~44 weeks**
|
||
|
||
---
|
||
|
||
## 8. Final Recommendation
|
||
|
||
This project is viable and genuinely differentiated. The combination of:
|
||
|
||
1. **Rust performance** (10-100x over Python CAS)
|
||
2. **Equality saturation** (provably optimal simplification)
|
||
3. **Agentic AI delivery** (message your math assistant on WhatsApp)
|
||
4. **Edge deployment** (Pi cluster, WASM, offline-capable)
|
||
5. **Open source** (MIT, community-driven)
|
||
|
||
...creates a product that doesn't exist today. The closest comparison is if Wolfram built Mathematica on top of ChatGPT and gave it away for free — except you own the infrastructure.
|
||
|
||
**Key risk to manage:** Scope. Mathematica has 6,600 functions built over 35 years. SymClaw needs to ship V0.1 with ~50 functions that cover 80% of undergraduate math, then let the community build the rest. The architecture (extensible rule sets, community ClawHub skills) makes this feasible.
|
||
|
||
**The paradigm shift isn't the math engine — it's the delivery model.** Scientists don't want another desktop app. They want to text their math assistant at 2am from bed and get a correct, beautifully rendered answer on their phone. That's what SymClaw delivers.
|