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. For a local setup, see Installation.

Run Symbolica in your browser

Python examples with a Run button can be edited and run here. The first run loads Python, Symbolica, hover documentation, and code completion.

Create expressions

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

Tip

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

from symbolica import *

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

expr
use symbolica::prelude::*;

fn main() {
    let (x, y, f) = symbol!("x", "y", "f");
    let expr = (x + 1).pow(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)
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)')
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()
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)

Integrate with steps

Integrate expressions symbolically, and optionally get a step-by-step overview of the integration process.

This example requires a local Symbolica installation with symbolic integration; the current browser build does not include that backend.

from symbolica import *

x = S('x')
expr = (1/(x**2+1)).integrate(x) # atan(x)

result, overview, steps = (x/(x+1)).integrate_with_steps(x)
print(overview)
result

Add the symbolica-integrate crate to your Cargo.toml to use the integration features. Include the steps feature to enable step-by-step integration.

use symbolica::prelude::*;
use symbolica_integrate::Integrate;

fn main() {
    let x = symbol!("x");
    let expr = parse!("1/(x^2+1)").integrate(x).unwrap(); // atan(x)

    let (result, overview, steps) = parse!("x/(1+x)").integrate_with_steps(x);
    println!("{overview}");
}

Output

∫ x/(1+x) dx = ∫ 1-1/(1+x) dx
  ∫ 1-1/(1+x) dx = ∫ 1 dx+∫ 1/(-1-x) dx
    ∫ 1 dx = x
    ∫ -1/(1+x) dx = -log(1+x)
= x-log(1+x)

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. In Python, apart() chooses variables automatically; apart([x, y]) performs multivariate decomposition.

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))
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 and distinguishes known coefficients from the unknown remainder. Symbolica solves supported linear and nonlinear equations exactly. The example below selects one solution branch; for general problems, inspect every branch and its conditions and check solution coverage.

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([f(c)*x + y + c, y + c**2], [x, y])[0]
sol
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 solutions = Atom::solve(&equations).wrt(&variables).unwrap();

    for (var, value) in &solutions[0] {
        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])

ev.evaluate_with_prec([Float(1), Float(2)], 16)[0]

The browser example uses arbitrary-precision evaluation. For NumPy arrays on a local installation, use ev.evaluate([[1.0, 2.0]])[0, 0]; native JIT compilation is unavailable in the browser.

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

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. See Algebraic numbers to put radicals and exact roots into the coefficient field.

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))
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.