363 lines
9.4 KiB
Markdown
363 lines
9.4 KiB
Markdown
# SymClaw API Reference
|
||
|
||
## Rust API (`symclaw-core`)
|
||
|
||
### `symclaw_core::parser::parse`
|
||
|
||
Parse a mathematical expression string into an AST.
|
||
|
||
```rust
|
||
pub fn parse(input: &str) -> Result<Arc<Expr>, ParseError>
|
||
```
|
||
|
||
**Supported syntax:**
|
||
- Arithmetic: `+`, `-`, `*`, `/`, `^` (with standard precedence)
|
||
- Implicit multiplication: `2x`, `3(x+1)`, `xy`
|
||
- Functions: `sin`, `cos`, `tan`, `exp`, `ln`, `log`, `sqrt`, `abs`, `sinh`, `cosh`, `tanh`, `asin`, `acos`, `atan`
|
||
- Constants: `pi`, `e`
|
||
- Parentheses: `(`, `)`
|
||
|
||
```rust
|
||
use symclaw_core::parser::parse;
|
||
|
||
let expr = parse("2*x^2 + 3*x - 5").unwrap();
|
||
let expr = parse("sin(x^2) * exp(-x)").unwrap();
|
||
let expr = parse("(x + 1)/(x - 1)").unwrap();
|
||
```
|
||
|
||
**Errors:** Returns `ParseError` with byte offset and description for malformed input.
|
||
|
||
---
|
||
|
||
### `symclaw_core::simplify::simplify`
|
||
|
||
Simplify an expression to canonical form using the algebraic pipeline.
|
||
|
||
```rust
|
||
pub fn simplify(expr: &Expr) -> Arc<Expr>
|
||
```
|
||
|
||
Applies: constant folding, identity elimination, like-term collection, canonical ordering. Does *not* invoke e-graph saturation (use `simplify_egraph` for that).
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, simplify::simplify};
|
||
|
||
let expr = parse("x + 0 + 2*x + 3").unwrap();
|
||
let result = simplify(&expr);
|
||
// → 3*x + 3
|
||
```
|
||
|
||
---
|
||
|
||
### `symclaw_core::egraph::simplify_egraph`
|
||
|
||
Deep simplification via equality saturation.
|
||
|
||
```rust
|
||
pub fn simplify_egraph(expr: &Expr) -> Arc<Expr>
|
||
```
|
||
|
||
Inserts the expression into an e-graph, applies 30+ rewrite rules until saturation, then extracts the smallest equivalent expression. More powerful than `simplify()` but slower (~10-100× depending on expression complexity).
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, egraph::simplify_egraph};
|
||
|
||
let expr = parse("sin(x)^2 + cos(x)^2").unwrap();
|
||
let result = simplify_egraph(&expr);
|
||
// → 1
|
||
```
|
||
|
||
---
|
||
|
||
### `symclaw_core::egraph::explain_equivalence`
|
||
|
||
Prove that two expressions are equivalent by returning the chain of rewrite rules.
|
||
|
||
```rust
|
||
pub fn explain_equivalence(a: &Expr, b: &Expr) -> Option<Vec<String>>
|
||
```
|
||
|
||
Returns `None` if the expressions are not equivalent within the saturation budget.
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, egraph::explain_equivalence};
|
||
|
||
let a = parse("(x+1)^2").unwrap();
|
||
let b = parse("x^2 + 2*x + 1").unwrap();
|
||
let steps = explain_equivalence(&a, &b).unwrap();
|
||
for step in &steps {
|
||
println!("{step}");
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### `symclaw_core::differentiate::differentiate`
|
||
|
||
Compute the symbolic derivative of an expression.
|
||
|
||
```rust
|
||
pub fn differentiate(expr: &Expr, var: Symbol) -> Arc<Expr>
|
||
```
|
||
|
||
Supports all elementary functions with chain rule. Result is automatically simplified.
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, differentiate::differentiate};
|
||
|
||
let expr = parse("sin(x^2)").unwrap();
|
||
let deriv = differentiate(&expr, "x".into());
|
||
// → 2*x*cos(x^2)
|
||
|
||
// Higher-order
|
||
let second = differentiate(&deriv, "x".into());
|
||
|
||
// Partial derivatives
|
||
let expr = parse("x^2 * y + y^3").unwrap();
|
||
let dx = differentiate(&expr, "x".into()); // → 2*x*y
|
||
let dy = differentiate(&expr, "y".into()); // → x^2 + 3*y^2
|
||
```
|
||
|
||
---
|
||
|
||
### `symclaw_core::integrate::integrate`
|
||
|
||
Compute the symbolic indefinite integral.
|
||
|
||
```rust
|
||
pub fn integrate(expr: &Expr, var: Symbol) -> Option<Arc<Expr>>
|
||
```
|
||
|
||
Returns `None` when no closed-form antiderivative is found. Does not include the constant of integration.
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, integrate::integrate};
|
||
|
||
let expr = parse("x^2").unwrap();
|
||
let result = integrate(&expr, "x".into());
|
||
// → Some(x^3/3)
|
||
|
||
let expr = parse("sin(x) * cos(x)").unwrap();
|
||
let result = integrate(&expr, "x".into());
|
||
// → Some(sin(x)^2/2)
|
||
```
|
||
|
||
**Strategies applied:** linearity, power rule, known antiderivatives, u-substitution, integration by parts.
|
||
|
||
---
|
||
|
||
### `symclaw_core::solve::solve`
|
||
|
||
Solve the equation `expr = 0` for the given variable.
|
||
|
||
```rust
|
||
pub fn solve(expr: &Expr, var: Symbol) -> Vec<Arc<Expr>>
|
||
```
|
||
|
||
Returns all found solutions as simplified expressions. Empty vector if no solutions are found.
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, solve::solve};
|
||
|
||
// Quadratic
|
||
let expr = parse("x^2 - 5*x + 6").unwrap();
|
||
let solutions = solve(&expr, "x".into());
|
||
// → [2, 3]
|
||
|
||
// Linear
|
||
let expr = parse("3*x + 7").unwrap();
|
||
let solutions = solve(&expr, "x".into());
|
||
// → [-7/3]
|
||
|
||
// Transcendental
|
||
let expr = parse("exp(x) - 1").unwrap();
|
||
let solutions = solve(&expr, "x".into());
|
||
// → [0]
|
||
```
|
||
|
||
---
|
||
|
||
### `symclaw_core::series::taylor`
|
||
|
||
Compute the Taylor series expansion of an expression.
|
||
|
||
```rust
|
||
pub fn taylor(expr: &Expr, var: Symbol, point: &Expr, order: u32) -> Arc<Expr>
|
||
```
|
||
|
||
Expands `expr` around `point` to the given `order`. Uses fast-path for known functions, falls back to repeated differentiation.
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, series::taylor};
|
||
|
||
let expr = parse("sin(x)").unwrap();
|
||
let zero = parse("0").unwrap();
|
||
let series = taylor(&expr, "x".into(), &zero, 5);
|
||
// → x - x^3/6 + x^5/120
|
||
|
||
let expr = parse("exp(x)").unwrap();
|
||
let series = taylor(&expr, "x".into(), &zero, 4);
|
||
// → 1 + x + x^2/2 + x^3/6 + x^4/24
|
||
```
|
||
|
||
---
|
||
|
||
### `symclaw_core::eval::eval`
|
||
|
||
Numerically evaluate an expression with variable substitution.
|
||
|
||
```rust
|
||
pub fn eval(expr: &Expr, vars: &HashMap<Symbol, f64>) -> Result<f64, EvalError>
|
||
```
|
||
|
||
```rust
|
||
use std::collections::HashMap;
|
||
use symclaw_core::{parser::parse, eval::eval};
|
||
|
||
let expr = parse("x^2 + y").unwrap();
|
||
let mut vars = HashMap::new();
|
||
vars.insert("x".into(), 3.0);
|
||
vars.insert("y".into(), 1.0);
|
||
let result = eval(&expr, &vars).unwrap();
|
||
// → 10.0
|
||
```
|
||
|
||
**Errors:** `EvalError::UndefinedVariable` if a variable has no binding, `EvalError::DomainError` for operations like `ln(-1)`.
|
||
|
||
---
|
||
|
||
### `symclaw_core::latex::to_latex`
|
||
|
||
Render an expression as a LaTeX string.
|
||
|
||
```rust
|
||
pub fn to_latex(expr: &Expr) -> String
|
||
```
|
||
|
||
```rust
|
||
use symclaw_core::{parser::parse, latex::to_latex};
|
||
|
||
let expr = parse("x^2/(2*y) + sqrt(z)").unwrap();
|
||
let latex = to_latex(&expr);
|
||
// → "\\frac{x^{2}}{2 y} + \\sqrt{z}"
|
||
```
|
||
|
||
Handles fractions (`\frac`), square roots (`\sqrt`), Greek letters, subscripts, and proper spacing.
|
||
|
||
---
|
||
|
||
## WASM API (`symclaw-wasm`)
|
||
|
||
All WASM functions accept and return strings. Import via:
|
||
|
||
```javascript
|
||
import init, * as symclaw from 'symclaw-wasm';
|
||
await init();
|
||
```
|
||
|
||
| Function | Signature | Description |
|
||
|---|---|---|
|
||
| `parse(input)` | `string → string` | Parse and echo canonical form |
|
||
| `simplify(input)` | `string → string` | Simplify expression |
|
||
| `differentiate(expr, var)` | `(string, string) → string` | Symbolic derivative |
|
||
| `integrate(expr, var)` | `(string, string) → string \| null` | Symbolic integral |
|
||
| `solve(expr, var)` | `(string, string) → string` | JSON array of solutions |
|
||
| `taylor(expr, var, point, order)` | `(string, string, string, number) → string` | Taylor expansion |
|
||
| `eval_expr(expr, vars_json)` | `(string, string) → number` | Numeric evaluation |
|
||
| `to_latex(expr)` | `string → string` | LaTeX rendering |
|
||
| `simplify_egraph(expr)` | `string → string` | E-graph simplification |
|
||
| `explain(a, b)` | `(string, string) → string \| null` | Equivalence proof (JSON) |
|
||
| `plot_data(expr, var, start, end, steps)` | `(...) → string` | JSON array of [x, y] points |
|
||
| `version()` | `() → string` | Engine version |
|
||
|
||
---
|
||
|
||
## Skill Protocol (JSON-RPC)
|
||
|
||
The `symclaw-skill` binary reads JSON objects from stdin (one per line) and writes responses to stdout.
|
||
|
||
### `simplify`
|
||
|
||
```json
|
||
// Request
|
||
{"action": "simplify", "expr": "x^2 + 2*x + 1"}
|
||
|
||
// Response
|
||
{"success": true, "result": "(x + 1)^2", "latex": "(x + 1)^{2}"}
|
||
```
|
||
|
||
### `differentiate`
|
||
|
||
```json
|
||
// Request
|
||
{"action": "differentiate", "expr": "sin(x^2)", "var": "x"}
|
||
// "var" defaults to "x" if omitted
|
||
|
||
// Response
|
||
{"success": true, "result": "2*x*cos(x^2)", "latex": "2 x \\cos(x^{2})"}
|
||
```
|
||
|
||
### `integrate`
|
||
|
||
```json
|
||
// Indefinite
|
||
{"action": "integrate", "expr": "x^2", "var": "x"}
|
||
→ {"success": true, "result": "x^3/3", "latex": "\\frac{x^{3}}{3}"}
|
||
|
||
// Definite
|
||
{"action": "integrate", "expr": "x^2", "var": "x", "lower": "0", "upper": "1"}
|
||
→ {"success": true, "result": "1/3", "latex": "\\frac{1}{3}"}
|
||
```
|
||
|
||
### `solve`
|
||
|
||
```json
|
||
{"action": "solve", "expr": "x^2 - 5*x + 6", "var": "x"}
|
||
→ {"success": true, "solutions": ["2", "3"], "latex_solutions": ["2", "3"]}
|
||
```
|
||
|
||
### `taylor`
|
||
|
||
```json
|
||
{"action": "taylor", "expr": "sin(x)", "var": "x", "point": "0", "order": 5}
|
||
→ {"success": true, "result": "x - x^3/6 + x^5/120", "latex": "x - \\frac{x^{3}}{6} + \\frac{x^{5}}{120}"}
|
||
```
|
||
|
||
### `eval`
|
||
|
||
```json
|
||
{"action": "eval", "expr": "x^2 + y", "vars": {"x": 3.0, "y": 1.0}}
|
||
→ {"success": true, "value": 10.0}
|
||
```
|
||
|
||
### `latex`
|
||
|
||
```json
|
||
{"action": "latex", "expr": "x^2/(2*y) + sqrt(z)"}
|
||
→ {"success": true, "latex": "\\frac{x^{2}}{2 y} + \\sqrt{z}"}
|
||
```
|
||
|
||
### `plot_data`
|
||
|
||
```json
|
||
{"action": "plot_data", "expr": "sin(x)", "var": "x", "start": -6.283, "end": 6.283, "steps": 200}
|
||
→ {"success": true, "points": [[-6.283, 0.0016], ..., [6.283, -0.0016]]}
|
||
```
|
||
|
||
### Error Response
|
||
|
||
All actions return this format on failure:
|
||
|
||
```json
|
||
{"success": false, "error": "Parse error at byte 5: unexpected token ')'"}
|
||
```
|
||
|
||
### Canvas Actions
|
||
|
||
The skill also supports canvas rendering via the `skill.json` manifest:
|
||
|
||
- **`canvas/manipulate.html`** — Interactive plot with parameter sliders. The gateway loads this in an iframe and passes expression + parameter ranges. WASM evaluates client-side.
|
||
- **`canvas/result.html`** — Formatted result display with LaTeX rendering via KaTeX.
|
||
|
||
These are triggered by the OpenClaw gateway when the skill returns canvas-compatible data, not by direct JSON-RPC calls.
|