tpt-symbolic

Rust

Pure-Rust, no_std, exact symbolic math engine: canonical ASTs, rewrite-based simplification, differentiation, integration, and equation solving.

0 stars0 forks0 watchersApache License 2.0

Languages

Rust100.0%
README

tpt-symbolic

Exact, pure-Rust symbolic mathematics for the TPT ecosystem. It builds expressions as canonical trees, simplifies them, differentiates and integrates them, solves equations, and evaluates them exactly at rational points.

  • No floating point anywhere. Constants are exact rationals of any size.
  • no_std + alloc. Builds for wasm32-unknown-unknown and riscv32imac-unknown-none-elf.
  • Canonical on construction: a + b and b + a are the same tree.
  • Zero dependencies and zero C-FFI.

Quickstart

Add it to Cargo.toml (it is not on crates.io yet, so use a path or git dependency):

[dependencies]
tpt-symbolic = { path = "../tpt-symbolic" }
use tpt_symbolic::calculus::diff;
use tpt_symbolic::parse::parse;
use tpt_symbolic::simplify::simplify;

let e = parse("sin(x)^2 + cos(x)^2").unwrap();
assert_eq!(simplify(&e).to_string(), "1");

let f = parse("x^3 + 2*x").unwrap();
assert_eq!(diff(&f, "x").unwrap().to_string(), "2 + 3*x^2");

Run the full tour with cargo run --example quickstart.

Command line

A small tpt-sym binary is included:

cargo run -- simplify "x*x + x*x"       # 2*x^2
cargo run -- diff "x^3" x               # 3*x^2
cargo run -- integrate "2*x" x          # x^2 + C
cargo run -- solve "x^2 - 2 = 0" x      # -2^(1/2), 2^(1/2)
cargo run -- eval "x^2 + 1" x=1/2       # 5/4
cargo run -- decimal "1/3" 6            # 0.333333
cargo run -- latex "x/2"                # \frac{x}{2}

Syntax

you writemeaning
2*x, 2x, x yproduct (juxtaposition multiplies)
x^2, x**2power, right-associative: 2^3^2 is 2^(3^2)
x/2, 0.25exact rational: 0.25 is 1/4, never a float
a + b, a - b, -asum, difference, negation
a = bequation (used by solve)
and, or, notlogical connectives
true, falselogical constants
sin(x), log(x), ...built-in functions: sin cos tan asin acos atan sinh cosh tanh exp log abs
sqrt(x)same as x^(1/2)

A name followed directly by ( must be a built-in function. Anything else is an error, so foo(x) is rejected rather than read as foo*x. Names are ASCII identifiers, and a multi-letter name is one variable: xy is not x*y.

Exact and approximate

operationresult
parsing, printing, simplifyexact
diff, integrateexact; each antiderivative is checked by differentiating it
eval at rational pointsexact rational, or Unsupported when irrational
to_decimal(n, digits)decimal digits, truncated (not rounded)
solve (degree 1, 2, rational roots, x^n = c)exact radicals such as 2^(1/3)
solve (anything else)SolveResult::Numeric, a hand-off for numeric root-finding

Nothing rounds on its own. An irrational value stays symbolic until you ask for digits, and then only the digits you ask for are produced.

Limits and assumptions

  • Real numbers only. Discriminants and radicands are assumed non-negative when they are symbolic. x*x^-1 simplifies to 1 without checking x != 0.
  • Products of sums expand. (x + 1)/(x - 1) is canonicalised to x*(x - 1)^(-1) + (x - 1)^(-1). Factored forms are not kept, and there is no factor.
  • Solver scope. Exact answers exist for degree 1 and 2, for polynomials with rational roots, and for x^n = c. A polynomial that has some rational roots and a leftover irreducible factor is reported as numeric as a whole.
  • Integration scope. Power rule, linear arguments, basic trig and exponential forms, 1/x, asin, acos, atan, and simple u-substitution. Integration by parts, partial fractions and definite integrals are not implemented.
  • Nesting limit. The parser accepts up to 128 levels of nesting and reports deeper input as an error.

Features

featuredefaultpurpose
stub-mathryeslocal exact-number shim, standing in for tpt-mathr
stdnostd::error::Error impl for Error
uirnotext IR codec (UirCodec), standing in for tpt-uir
testkitnodeterministic random-expression generator for property tests

Examples

exampleshows
quickstartparse, simplify, expand, differentiate, solve
calculusderivatives, gradients, verified integrals
solve_equationsexact roots, no-solution cases, numeric hand-off
linear_systemsunique, parametric and null-space results
balance_chemistrya balanced reaction from an integer null space
exact_numbersbig integers, evaluation, truncated decimals
parse_and_printsyntax, round trips, LaTeX, error messages
benchtiming on large expressions

Run any of them with cargo run --example <name>.

Pipeline example

use tpt_symbolic::calculus::{integrate, Integral};
use tpt_symbolic::parse::parse;
use tpt_symbolic::simplify::simplify;
use tpt_symbolic::solve::{solve, SolveResult};
use tpt_symbolic::Expr;

// Simplify with trigonometric identities.
let e = parse("sin(x)^2 + cos(x)^2").unwrap();
assert_eq!(simplify(&e), Expr::one());

// Solve a circle constraint for y: y = ±sqrt(25 - x^2).
let circle = parse("x^2 + y^2 = 25").unwrap();
if let SolveResult::Exact(roots) = solve(&circle, "y").unwrap() {
    assert_eq!(roots.len(), 2);
}

// Integrate with verification. Unsupported forms return Unevaluated.
match integrate(&parse("x*cos(x^2)").unwrap(), "x") {
    Integral::Exact(f) => assert_eq!(f, parse("1/2*sin(x^2)").unwrap()),
    _ => unreachable!("the README example integral is supported"),
}

Status

Implemented: canonical AST, parser and printer, prioritised rewrite engine, expand, diff (including higher derivatives and gradients), verified integrate, exact evaluation and truncated decimals, LaTeX output, exact linear systems and nullspace, and the solvers described above.

Not yet implemented: cubic and quartic radicals beyond the cases above, factoring, matrices, series and limits, complex numbers, assumptions on variables, and the integrations with the other TPT crates. See TODO.md.

Testing

cargo test --all-features

The design is in spec.txt and the work plan in TODO.md. Contributors should read CONTRIBUTING.md first.

Licence

Licensed under either of Apache License, Version 2.0 or MIT licence, at your option.