Symbolics.jl

repository·master·Indexed 23 days ago

https://github.com/juliasymbolics/symbolics.jl

A high-performance, modern Computer Algebra System (CAS) for Julia designed for symbolic-numerics. Built on SymbolicUtils.jl, it provides tools for symbolic differentiation, Jacobian and Hessian computation, algebraic simplification, and substitution. It features a powerful `build_function` for generating JIT-compiled, parallelized, and non-allocating Julia functions, with deep integration into the SciML ecosystem and support for sparse matrices.

Tokens
15K
Snippets
37
Records
105
Agent score
81%

What's inside Symbolics.jl

  1. Overview of Symbolics.jl features

    master

    Symbolics.jl is a high-performance Computer Algebra System (CAS) built for Julia. Because it leverages Julia's multiple dispatch, many generic functions in Base Julia work directly with symbolic expressions (e.g., performing LU-factorization on matrices of symbolic expressions).

    Key features include:

    • Symbolic Arithmetic: Supports type information and multiple dispatch.
    • Algebraic Operations: Polynomials, trigonometric functions, and non-standard algebras (non-commutative symbols).
    • Calculus & Math: Differentiation, pattern matching, simplification, substitution, and discrete math (summations, products, binomial coefficients).
    • Linear Algebra: Symbolic factorizations, inversion, determinants, and eigencomputations.
    • Equation Solving: Symbolic equation solving and conversion to arbitrary precision.
    • Code Generation: Automatic conversion of Julia code to symbolic code and generation of high-performance, parallelized functions from symbolic expressions.
    • Sparsity: Fast automated sparsity detection and generation of sparse Jacobians and Hessians.
    • Special Functions: Support for functions provided by SpecialFunctions.jl.
  2. How the symbolic solver works internally

    master

    The symbolic_solve function operates through a hierarchy of specialized solvers:

    1. solve_univar: The core building block. It uses analytic solutions for polynomials up to degree 4 and factoring for higher-degree univariate polynomials.
    2. solve_multipoly: Uses GCD on input polynomials to reduce them before passing them to solve_univar.
    3. solve_multivar: Uses Groebner basis and a separating form to transform multivariate systems into linear equations and a single high-degree equation in a separating variable. Each resulting equation is then passed to solve_univar.
    4. ia_solve: If the input is not a valid polynomial and cannot be solved by the methods above, ia_solve attempts solving by attraction and isolation. This is used for single expressions where the user wants the answer in terms of a single variable (e.g., solving log(x) - a == 0 for x).
  3. Understand the design goals of Symbolics.jl vs SymPy

    master

    Symbolics.jl is designed specifically for symbolic-numerics: the combination of symbolic computing with numerical methods for high-performance computing (HPC). While SymPy is a general-purpose symbolic math library in Python, Symbolics.jl is optimized for performance and integration into high-speed simulation workflows.

    Key differentiators include:

    • Performance: Built natively in Julia to provide much higher performance bars than Python-based alternatives.
    • build_function: Unlike SymPy's lambdify, Symbolics.jl's build_function generates fast, JIT-compiled Julia functions. It supports HPC-specific features like static arrays, non-allocating functions via mutation, and optimized operations on sparse matrices.
    • Parallelism: Features pervasive parallelism, including built-in parallelism in symbolic simplification (via SymbolicUtils.jl), thread-parallelized functions, and compatibility with GPU libraries like CUDA.jl.
    • Extensibility: Because it is written in pure Julia, users can extend the library by adding new simplification rules or derivatives directly in Julia code.
    • Ecosystem Integration: Deeply integrated with the SciML ecosystem. It leverages automatic differentiation, Julia Base linear algebra, and tools like DataDrivenDiffEq.jl (for reconstructing expressions from data) and NeuralPDE.jl (for solving PDEs via physics-informed neural networks).
  4. Representing partial derivatives with `Differential`

    master

    A Differential(op) represents a partial derivative with respect to the operand op. You can apply a differential to a symbolic expression by calling it as a function. For example, if D = Differential(t), then D(x + y) represents $\frac{d(x+y)}{dt}$.

    By default, derivatives are left unexpanded to preserve the symbolic representation of differential equations. To expand all differentials into basic one-variable expressions, use the expand_derivatives function.

  5. Relationship between Symbolics.jl, SymbolicUtils.jl, and ModelingToolkit.jl

    master

    Understanding the ecosystem hierarchy:

    • SymbolicUtils.jl: The core rule-rewriting system. It is the foundation for Symbolics.jl. Use this if you want to build a custom CAS for specific algebras from scratch.
    • Symbolics.jl: Built on top of SymbolicUtils.jl. It extends the rewriting system into a full symbolic algebra system with support for differentiation, solving equations, and more.
    • ModelingToolkit.jl: A symbolic-numeric modeling system for the SciML ecosystem. It uses Symbolics.jl for symbolic equation representation and adds support for modeling systems like ODEs and SDEs.
  6. Symbolic Arrays vs Arrays of Symbolic Expressions

    master

    Symbolics.jl provides two distinct ways to handle arrays containing symbolic elements. Choosing the right one depends on whether you prioritize ease of use or computational efficiency.

    1. Arrays of Symbolic Expressions: These are standard Julia arrays (e.g., Vector or Matrix) containing Symbolics.jl objects.

      • Behavior: Indexing returns a scalar symbolic value. Operations use Julia's standard array functionality and perform symbolic operations on the individual scalar values.
      • Pros: High accessibility; works with almost all existing symbolic operations.
      • Cons: Can lead to very large, expanded expressions during operations.
    2. Symbolic Arrays: These are $O(1)$ symbolic representations of an entire array.

      • Behavior: Indexing (e.g., A[1,1]) does not return a variable, but a symbolic expression representing the indexing operation itself. It holds linear algebra expressions in a non-expanded form.
      • Pros: Much more efficient for large-scale linear algebra; keeps expressions compact.
      • Cons: Requires operations to use registered symbolic array functions (lower coverage than scalar symbolics).

    Recommendation: Default to Arrays of Symbolic Expressions unless you specifically require the expression simplification benefits of the Symbolic Array approach.

  7. Generate symbolic expressions via Direct Tracing

    master

    Because Symbolics.jl expressions respect Julia semantics, you can generate symbolic expressions by passing Symbolics variables (@variables) into existing imperative Julia code. This process, called tracing, transforms the imperative code into a declarative symbolic graph composed of pre-registered primitive functions (like * or -).

    Limitations:

    • Tracing only works if the code is composed of already registered functions. Calls to unregistered functions (e.g., C code) will cause an error.
    • Tracing does not guarantee the exact numerical result, as the symbolic system may re-distribute arithmetic or simplify expressions. If a function is highly sensitive to numerical details, you should register it as a primitive instead of tracing it.
    • To maximize the power of symbolic manipulations (like simplification), minimize the number of registered functions; only register when necessary, as registered functions are treated as black-box imperative code.
    using Symbolics
    function lorenz(du,u,p,t)
     du[1] = 10.0(u[2]-u[1])
     du[2] = u[1]*(28.0-u[3]) - u[2]
     du[3] = u[1]*u[2] - (8/3)*u[3]
    end
    @variables t p[1:3] u(t)[1:3]
    du = Array{Any}(undef, 3)
    lorenz(du,u,p,t)
    du
  8. Understand Symbolics IR types: Sym, Term, and Num

    master

    Symbolics uses an Intermediate Representation (IR) that mirrors the Julia AST but follows mathematical semantics.

    • Sym: Defines a symbolic variable.
    • Term: Represents a symbolic expression (an iscall object).
    • Equation: Defined using the ~ operator (e.g., op1 ~ op2), representing symbolic equality.
    • Num: A wrapper type that is a subtype of Real. It wraps Sym or Term objects and forwards mathematical operations to them. This allows symbolic expressions to be used in functions that require Number or Real types.

    To access the underlying symbolic object from a Num wrapper, use Symbolics.value(x).