First steps

First Symbolica examples in Python and Rust, including expressions, rewrites, derivatives, evaluation, solving, and polynomials.

This page is a short tour of the most common Symbolica operations. It assumes you have already installed Symbolica from Installation.

Tip

In Python, S, E, N, and T are shorthands for Expression.symbol, Expression.parse, Expression.num, and Transformer.

Create expressions

Symbols can be used as variables or called as functions. Arithmetic with Symbolica expressions keeps exact integers and rational numbers exact.

from symbolica import *

x, y, f = S('x', 'y', 'f')
expr = (x + 1)**2 + f(y, x) / 3

print(expr)
use symbolica::prelude::*;

fn main() {
    let (x, y, f) = symbol!("x", "y", "f");
    let expr = (x + 1).npow(2) + function!(f, y, x) / 3;

    println!("{expr}");
}

Output

1/3*f(y,x)+(1+x)^2

Parse, expand, and collect

Use the parser for compact input, then apply algebraic operations directly to the expression.

from symbolica import *

x, y = S('x', 'y')
expr = E('(x+y)^3 + x*y')
expanded = expr.expand()

print(expanded)
print(expanded.collect(x))
use symbolica::prelude::*;

fn main() {
    let expr = parse!("(x+y)^3 + x*y");
    let expanded = expr.expand();

    println!("{expanded}");
    println!("{}", expanded.collect::<u8>(parse!("x")));
}

Output

x*y+3*x*y^2+3*x^2*y+x^3+y^3
x*(y+3*y^2)+3*x^2*y+x^3+y^3

Replace expressions

Literal replacements substitute a specific expression everywhere it appears. Wildcards, such as x_, match a class of expressions and are the basis of pattern matching.

from symbolica import *

x, y, f = S('x', 'y', 'f')
expr = f(x) + x
print(expr.replace(x, y + 1))

x_ = S('x_')
expr = E('f(1) + f(2) + f(3)')
print(expr.replace(f(x_), x_**2))
use symbolica::prelude::*;

fn main() {
    let x = symbol!("x");
    let expr = parse!("f(x) + x");
    let shifted = expr.replace(x).with(parse!("y + 1"));

    println!("{shifted}");

    let expr = parse!("f(1) + f(2) + f(3)");
    println!("{}", expr.replace(parse!("f(x_)")).with(parse!("x_^2")));
}

Output

1+y+f(1+y)
14

For more control over wildcards and restrictions, see Pattern matching.

Differentiate and factor

Symbolica can differentiate expressions and factor many expressions over the rationals.

from symbolica import *

x, y = S('x', 'y')
expr = E('(x+1)^3 + sin(x)')
print(expr.derivative(x))

poly_expr = E('(x+1)^2*(x+y)').expand()
print(poly_expr.factor())
use symbolica::prelude::*;

fn main() {
    let x = symbol!("x");
    let expr = parse!("(x+1)^3 + sin(x)");
    println!("{}", expr.derivative(x));

    let poly_expr = parse!("(x+1)^2*(x+y)").expand();
    println!("{}", poly_expr.factor());
}

Output

cos(x)+3*(1+x)^2
(1+x)^2*(x+y)

Simplify rational expressions

Use together to put terms over a common denominator, apart for partial fractions, and cancel to remove common factors without expanding everything else.

from symbolica import *

x, y, z = S('x', 'y', 'z')

print(E('1/x + (2*x+y+z)/y').together())
print(E('(y+2*x^2+x*y+x*z)/(x*y)').apart(x))
print(E('1+(y+1)^10*(x+1)/(x^2+2*x+1)').cancel())
use symbolica::prelude::*;

fn main() {
    let x = symbol!("x");

    println!("{}", parse!("1/x + (2*x+y+z)/y").together());
    println!("{}", parse!("(y+2*x^2+x*y+x*z)/(x*y)").apart(x));
    println!("{}", parse!("1+(y+1)^10*(x+1)/(x^2+2*x+1)").cancel());
}

Output

(x*y+x*z+y+2*x^2)/(x*y)
1/x+(2*x+y+z)/y
1+(1+y)^10/(1+x)

Expand series and solve systems

Series expansions return a series object that supports arithmetic. Symbolica can also solve linear systems exactly when the coefficients are symbolic.

from symbolica import *

x, y, c, f = S('x', 'y', 'c', 'f')

print(E('exp(5+x)/(1-x)').series(x, 0, 3))

sol = Expression.solve_linear_system([f(c)*x + y + c, y + c**2], [x, y])
for var, value in zip([x, y], sol):
    print(f'{var} = {value}')
use symbolica::prelude::*;

fn main() {
    let x = symbol!("x");
    let series = parse!("exp(5+x)/(1-x)").series(x, 0, 3).unwrap();
    println!("{}", series.to_atom());

    let equations = [parse!("f(c)*x + y + c"), parse!("y + c^2")];
    let variables = [parse!("x"), parse!("y")];
    let solution = Atom::solve_linear_system::<u8, _, _>(&equations, &variables).unwrap();

    for (var, value) in ["x", "y"].iter().zip(solution) {
        println!("{var} = {value}");
    }
}

Output

exp(5)+2*exp(5)*x+5/2*exp(5)*x^2+8/3*exp(5)*x^3+O(x^4)
x = (-c+c^2)/f(c)
y = -c^2

Evaluate numerically

For repeated numerical evaluation, first build an evaluator. Evaluators can optimize expressions and, in Python, can JIT compile on first use.

from symbolica import *

x, y = S('x', 'y')
ev = E('x*y + x^2').evaluator([x, y])

print(ev.evaluate([[1.0, 2.0]])[0, 0])
use symbolica::prelude::*;

fn main() {
    let params = vec![parse!("x"), parse!("y")];
    let mut ev = parse!("x*y + x^2")
        .evaluator(&params)
        .build()
        .unwrap()
        .map_coeff(&|c| c.re.to_f64());

    println!("{}", ev.evaluate_single(&[1.0, 2.0]));
}

Output

3.0

For nested functions, multiple outputs, JIT settings, and code generation, see Numerical evaluation.

Work with polynomials

Expressions can be converted into Symbolica’s polynomial data structures for faster polynomial arithmetic and factorization.

from symbolica import *

x, y = S('x', 'y')
poly = E('(x+1)^2*(x+y)').expand().to_polynomial(vars=[x, y])

print(poly)
print(poly.derivative(x))
print(poly.factor())
use symbolica::prelude::*;

fn main() {
    let expr = parse!("(x+1)^2*(x+y)").expand();
    let poly: MultivariatePolynomial<_, u8> = expr.to_polynomial(&Z, None);

    println!("{poly}");
    println!("{}", poly.derivative(0));
    let factors = poly.factor();
    let rendered = factors
        .iter()
        .map(|(factor, power)| format!("({factor}, {power})"))
        .collect::<Vec<_>>()
        .join(", ");
    println!("[{rendered}]");
}

Output

y+x+2*x*y+2*x^2+x^2*y+x^3
1+2*y+4*x+2*x*y+3*x^2
[(1+x, 2), (y+x, 1)]

Where to go next

  • Symbols: define symbols, namespaces, attributes, tags, aliases, and custom behavior.
  • Expressions: learn the expression tree, parsing, printing, and normalization.
  • Pattern matching: rewrite expressions with wildcards and restrictions.
  • Numerical evaluation: turn expressions into fast numerical evaluators.
  • Polynomials: use finite fields, rational functions, and polynomial algorithms.