Getting Started with Symmetrica
Learn how to install and use Symmetrica in your Rust projects.
Installation
Symmetrica requires Rust 1.70 or later. If you don't have Rust installed:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Adding to Your Project
Add the crates you need to your Cargo.toml
:
[dependencies]
expr_core = { git = "https://github.com/Sir-Teo/Symmetrica" }
simplify = { git = "https://github.com/Sir-Teo/Symmetrica" }
calculus = { git = "https://github.com/Sir-Teo/Symmetrica" }
solver = { git = "https://github.com/Sir-Teo/Symmetrica" }
polys = { git = "https://github.com/Sir-Teo/Symmetrica" }
Quick Start
Here's a simple example to get you started:
use expr_core::Store;
use simplify::simplify;
use calculus::diff;
fn main() {
let mut st = Store::new();
let x = st.sym("x");
// Build expression: x² + 3x + 1
let expr = st.add(vec![
st.pow(x, st.int(2)),
st.mul(vec![st.int(3), x]),
st.int(1),
]);
// Simplify
let simplified = simplify(&mut st, expr);
println!("Expression: {}", st.to_string(simplified));
// Differentiate
let derivative = diff(&mut st, expr, "x");
let result = simplify(&mut st, derivative);
println!("Derivative: {}", st.to_string(result));
}
// Output:
// Expression: 1 + 3 * x + x^2
// Derivative: 3 + 2 * x
Basic Concepts
The Store
The Store
is the core data structure that manages all expressions:
let mut st = Store::new();
All expressions are created through the store, which ensures hash-consing and structural sharing.
Expression IDs
Expressions are represented as ExprId
, which is a lightweight handle:
let x: ExprId = st.sym("x");
let two: ExprId = st.int(2);
let expr: ExprId = st.pow(x, two);
Immutability
All expressions are immutable. Operations create new expressions:
let a = st.add(vec![x, y]); // Creates new expression
let b = st.mul(vec![a, z]); // 'a' is unchanged
Canonical Forms
Expressions are automatically normalized:
let a = st.add(vec![x, y]);
let b = st.add(vec![y, x]);
assert_eq!(a, b); // Order doesn't matter for Add/Mul
Module Overview
expr_core
The foundation of Symmetrica. Provides the expression kernel with hash-consing.
use expr_core::Store;
let mut st = Store::new();
let x = st.sym("x"); // Symbol
let n = st.int(42); // Integer
let r = st.rat(1, 3); // Rational
let sum = st.add(vec![x, n]); // Addition
let prod = st.mul(vec![x, r]); // Multiplication
let pow = st.pow(x, st.int(2)); // Power
simplify
Algebraic simplification with assumption-aware transformations.
use simplify::simplify;
use assumptions::{Context, Prop};
let mut st = Store::new();
let x = st.sym("x");
// Basic simplification
let expr = st.add(vec![x, x]);
let result = simplify(&mut st, expr);
// → 2 * x
// With assumptions
let mut ctx = Context::new();
ctx.assume("x", Prop::Positive);
let expr = st.func("ln", vec![st.func("exp", vec![x])]);
let result = simplify_with(&mut st, expr, &ctx);
// → x (when x > 0)
calculus
Differentiation, integration, series expansion, and limits.
use calculus::{diff, integrate, maclaurin};
// Differentiation
let expr = st.pow(x, st.int(3));
let derivative = diff(&mut st, expr, "x");
// → 3 * x^2
// Integration
let expr = st.pow(x, st.int(2));
let integral = integrate(&mut st, expr, "x").unwrap();
// → (1/3) * x^3
// Series expansion
let expr = st.func("exp", vec![x]);
let series = maclaurin(&st, expr, "x", 5).unwrap();
// → 1 + x + x²/2 + x³/6 + x⁴/24 + x⁵/120
polys
Polynomial operations: GCD, factorization, partial fractions.
use polys::{expr_to_unipoly, factor, partial_fractions_simple};
// Convert expression to polynomial
let poly = expr_to_unipoly(&st, expr, "x").unwrap();
// Factor polynomial
let factors = factor(&st, expr, "x").unwrap();
// Partial fractions
let num = st.add(vec![st.mul(vec![st.int(2), x]), st.int(3)]);
let den = st.add(vec![
st.pow(x, st.int(2)),
st.mul(vec![st.int(3), x]),
st.int(2)
]);
let result = partial_fractions_simple(&mut st, num, den, "x");
// → A/(x+1) + B/(x+2)
solver
Equation solving for polynomials up to degree 4.
use solver::solve_univariate;
// Solve x² + 3x + 2 = 0
let eq = st.add(vec![
st.pow(x, st.int(2)),
st.mul(vec![st.int(3), x]),
st.int(2)
]);
let roots = solve_univariate(&mut st, eq, "x").unwrap();
for root in roots {
println!("x = {}", st.to_string(root));
}
// Output: x = -1, x = -2
pattern
Pattern matching and rewriting with AC-aware matching.
use pattern::{subst_symbol, ac::match_expr};
// Substitution
let expr = st.pow(x, st.int(2));
let y = st.sym("y");
let result = subst_symbol(&mut st, expr, "x", y);
// → y^2
// Pattern matching
use pattern::ac::Pat;
let pattern = Pat::Add(vec![
Pat::Any("a".into()),
Pat::Integer(0)
]);
// Matches: x + 0, 0 + x, etc.
assumptions
Tri-valued logic for domain-aware transformations.
use assumptions::{Context, Prop, Truth};
let mut ctx = Context::new();
ctx.assume("x", Prop::Positive);
ctx.assume("x", Prop::Real);
// Query assumptions
match ctx.has("x", Prop::Positive) {
Truth::True => println!("x is positive"),
Truth::False => println!("x is not positive"),
Truth::Unknown => println!("unknown"),
}
// Scoped assumptions
ctx.push(); // New scope
ctx.assume("y", Prop::Integer);
ctx.pop(); // y assumption removed
matrix
Exact linear algebra over rationals.
use matrix::{Matrix, determinant, solve_system};
let mut st = Store::new();
// Create matrix
let mat = Matrix::from_rows(vec![
vec![st.int(1), st.int(2)],
vec![st.int(3), st.int(4)]
]);
// Determinant
let det = determinant(&mut st, &mat);
// → -2
// Solve linear system
let b = vec![st.int(5), st.int(6)];
let solution = solve_system(&mut st, &mat, &b).unwrap();