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" }
Note: Symmetrica is modular. Only include the crates you need!

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();

Next Steps