[ANN] Symbolics.jl: A Modern Computer Algebra System for a Modern Language

The JuliaSymbolics Organization Roadmap

We need new Computer Algebra Systems (CAS) for this new era of computing. We need a CAS that dispatches in the multiple ways we think. We need a CAS that scales exponentially like our problems. We need a CAS that integrates with our package ecosystem, letting people extend parts and
contribute back to the core library all in one language. We need a modern CAS in a modern language.

Symbolics.jl is the answer. Symbolics.jl is a pure Julia CAS which uses the Julia core library to its fullest. It is built from the ground up with performance in mind. We use specialized structures
for automatic simplification to match the performance of the most fully optimized C++ libraries. It exploits parallelism at every level; our symbolic simplification takes advantage of Julia’s task-based
multithreading to transform symbolic equations into parallelized Julia code.

This reconstruction of the idea of CAS in Julia’s type system is entirely extensible. New term types enable fast symbolic arithmetic on standard and non-standard algebras; add-on libraries like
ModelingToolkit build a bridge from symbolics to numerics. Symbolics.jl and its ecosystem will be the common foundation on which the next generation of Domain-Specific Languages (DSLs) will be constructed, automatically updated and accelerated through with the growth of this system.

The Features of Symbolics.jl

Symbolics.jl at its launch in 2021 is already expansive. It includes:

  • Symbolic arithmetic with type information and multiple dispatch
  • Symbolic polynomials and trigonometric functions
  • Pattern matching, simplification and substitution
  • Differentiation
  • Symbolic linear algebra (factorizations, inversion, determinants, eigencomputations, etc.)
  • Discrete math (representations of summations, products, binomial coefficients, etc.)
  • Logical and Boolean expressions
  • Symbolic equation solving and conversion to arbitrary precision
  • Support for non-standard algebras (non-commutative symbols and customizable rulesets)
  • Special functions (list provided by SpecialFunctions.jl)
  • Automatic conversion of Julia code to symbolic code
  • Generation of (high performance and parallel) functions from symbolic expressions
  • Fast automated sparsity detection and generation of sparse Jacobian and Hessians

and much more. A lot of these features are for free given its deep integration with multiple dispatch and Julia’s type system.

Connection to the Package Ecosystem

The Connection to the ModelingToolkit.jl

Here in the Julia world, we like differential equations, maybe a little too much.

Symbolics.jl grew out of ModelingToolkit.jl, an equation-based modeling system for the Julia programming language. Its vision is that the best system for modeling requires having the ability to symbolically specify models and build a library of transformations for generating more stable
and performant code. While software in a similar space like Simulink and Modelica are disconnected from traditional programming languages and symbolic algebra systems, ModelingToolkit.jl weaves them together, allowing all aspects of the Julia programming language and symbolic computing to contribute to the richness of its design.

The ModelingToolkit.jl project has been almost too much of a success in that respect, reaching a feature-base that included an entire CAS as a submodule within itself. It was time for that CAS to be set free. Symbolics.jl is that CAS, now set in its own organization, JuliaSymbolics, with its ability to transform new domains.

ModelingToolkit.jl will continue to provide the symbolic representations of common numeric systems and the SciML organization, such as causal and acausal modeling (Simulink/Modelica) in the domains of:

  • Ordinary differential equations
  • Stochastic differential equations
  • Partial differential equations
  • Nonlinear systems
  • Optimization problems
  • Optimal Control

It will continue to power the connection the next generation of symbolic-numeric computation, blurring the boundaries by mixing analytical solutions with optimized and parallelized generated code. All symbolic functionality related to those domains will continue to thrive in that package, leaving Symbolics.jl the room to focus on the core of symbolic algebras: polynomials, Grobner bases, and more.

The Connection to SymbolicUtils.jl

Symbolics.jl is an opinionated CAS. It types its variables so that generic Julia functions which require Real numbers can automatically be converted into symbolic expressions. It uses the Leibniz rules for
defining derivatives. It does symbolic algebra as “the normal person would expect”.

However, there are some use cases in computational algebra which require non-standard rulesets. How would you define symbolic Octonian numbers or define differentiation on non-smooth manifolds that do not satisfy the Leibniz rule? For these questions, mathematicians have traditionally been on their own having to develop new tools from scratch. However, the JuliaSymbolics has refactored its core so that new algebras can easily be implemented and created, and automatically get the optimized high performance of Symbolics.jl. This is SymbolicUtils.jl.

SymbolicUtils.jl is a fast and parallel rule-based rewrite system. By specifying a list of rules, such as trigonometric identities, you can specify new mathematical domains and create the symbolic arithmetic that you need. Symbolics.jl is built on this foundation, adding the common simplification rules for real numbers, derivative definitions and rules for Newton differentiation, and more. However, if you’re so inclined, SymbolicUtils.jl is open for you to define BraKet algebras and more.

Goals for Symbolics.jl

Symbolics.jl will be a never ending project as we wish to provide a high performance implementation of every symbolic algorithm that can and does exist. But, there are specific goals we have in mind.

High-Performance Symbolic Arithmetic

Anywhere that we are beat in performance is a bug. Please file an issue immediately. This library should use every core, and it should use every core efficiently, allowing the exponential cost of symbolic arithmetic to tackled by the exponential gains in computational power and efficiency.

Pervasiveness Throughout the Julia DSLs

We want to provide a symbolic foundation which all DSLs can rely on. We do not believe that a pharmacometrics library should define how to symbolically calculate a Hessian, and then the mathematical programming library JuMP, etc. We believe that by pooling together on Symbolics.jl, we can accelerate the growth of DSLs throughout the language, offering a way to collaborate towards a single battletested implementation.

Bridge the Gap from Computer Scientists to Scientists and Mathematicans

While “code” is what package developers like to see, math is what practioners are trained on. We are committed to bridging that gap, making it easy to see the LaTeX-ification of the symbolic variables,
creating informative displays in notebooks. Symbolics.jl should look and feel like doing math.

Feature Completeness

We want Symbolics.jl to be the place where you check the documentation immediately for symbolic methods, knowing that if such a method exists then it’s implemented there. Symbolics.jl should not just cover the domain but also be an archive of its research and science, making it easy to explore algorithms and compare between them.

Next Steps for Symbolics.jl

We have not met all of our goals yet. While much of this roadmap has been accomplished, there is much in our way forward. Some major goals on our sights are:

How You Can Join The Process

If you want to be a part of JuliaSymbolics, that’s great, you’re in! Here are some things you can start doing:

  • Star our libraries like Symbolics.jl and our extensions like ModelingToolkit.jl. Such recognition drives our growth to sustain the project.
  • Join our chatroom to discuss with us. Our main chatroom is #symbolic programming on the Julia Zulip
  • If you’re a student, find a summer project that interests you and apply for funding through Google Summer of Code or other processes (contact us if you are interested)
  • Start contributing! We recommend opening up an issue to discuss first, and we can help you get started.
  • Help update our websites, tutorials, benchmarks, and documentation
  • Help answer questions on Stack Overflow, the Julia Discourse, and
    other sites!
  • Hold workshops to train others on our tools.

There are many ways to get involved, so if you’d like some help figuring out how, please get in touch with us.

This is in large part due to the amazing work of @shashi and @YingboMa, along with many others like @HarrisonGrodin, @dpsanders, and @Mason who have been pushing on various aspects of this project in its infancy. Hopefully now it will be collected to a place where it can thrive.

200 Likes

This is really exciting.
One challenge I’ve faced w/ CAS (like Mathematica) is to solve problems w/ N-variables.

Consider a simple example of utility maximization with N-good Cobb-Douglas utility:
\max \text{ } \frac{1}{2} \log \left( x_{1} \right) + \frac{1}{2} \log \left( x_{2} \right) + \cdots + \frac{1}{2} \log \left( x_{N} \right)
\text{ s.t. } x_{1} + \cdots + x_{N} =I

Analytic solution: x_{i}^{*} = \frac{I}{N}

Mathematica cannot solve this type of problem for general N-variables.
Instead you need to set eg N=6.

An even simpler example: f(x) = \frac{1}{2} \log \left( x_{1} \right) + \cdots +\frac{1}{2} \log \left( x_{N} \right)
f_{x_{i}} = \frac{1}{2x_{i}} for i \in \{1,\dots, N \}
Mathematica can’t compute this analytic derivative for general N-variables.

(I emailed them about this in 2009 & Steve said he was interested in this functionality. Doesn’t look like anything came of it…)

Can the new generation of CAS solve these types of problems (w/ N-variables)?
-my guess it might be related to theorem proving software :man_shrugging:

8 Likes

You mean you want symbolic dynamically-sized arrays?

3 Likes

I want to be able to define a function of N-variables such as
f(x) = \frac{1}{2} \log \left( x_{1} \right) + \cdots +\frac{1}{2} \log \left( x_{N} \right)

And then compute its analytic derivative, which in this case we know is:
f_{x_{i}} = \frac{1}{2x_{i}} for i \in \{1,\dots, N \}, N\geq 1

Maybe that’s what you mean by “symbolic dynamically-sized arrays”…

2 Likes

You are basically asking for symbolic “matrix calculus”, I think:

http://www.matrixcalculus.org/

8 Likes

Yes, it’s one of the next things @shashi is working on.

8 Likes

As a physicist, I’ve spent a little too much time in my life pasting (and then reformatting) expressions between Julia and Mathematica. Very excited about these developments!

23 Likes

Love it, I was waiting for something like this :slight_smile: :slight_smile:

Do not underestimate however how important precise, complete but also accessible documentstion is. SymPy doc is so lovely!

7 Likes

Bill Helton (emeritus UCSD) has been developing a Mathematica package called NCAlgebra for noncommutative algebra. Maybe some inspiration can be found there (at least for those with an access to Mathematica, but perhaps there is also free some viewer for Mathematica notebooks).

3 Likes

Not the most productive comment, so somebody please let me know if it is not good discourse manners, but: wow. This is mind-blowing.

30 Likes

Why would this be bad manners? There’s always space for people to praise others’ work and encourage them.

29 Likes

In SymPy you can use indexed objects.

from sympy import *
x = IndexedBase('x')
n = symbols('n', Integer=True)
i = Idx('i')
j = Idx('j')

u = Sum(S(1)/2 * log(x[i]), (i, 1, n))
du = u.diff(x[j]).doit()

You can get rid of the piecewise result by imposing bounds on the indices, or by simplifying:

refine(du, Q.is_true(1 <= j) & Q.is_true(j <= n))

which gives \frac{du}{dx_j} = \frac{1}{2 x_j} for any 1 \le j \le n.

6 Likes

Is the intention for Symbolics.jl to eventually compete w/ CASs (SymPy, Mathematica, Maple etc)?
SymPy already has >260k lines of code…
Or is it more to supplement existing stuff?

While I’m sure CAS can work better in Julia, this feels like an insanely ambitious project for an already insanely busy organization.
I really hope you have funding/labor/craziness to make this work

5 Likes

It’s already been one of SciML’s biggest projects for the last year, enough to be a full organization itself. It’s the foundation of ModelingToolkit.jl. There’s also a lot of buy-in from industry partners. More on that at JuliaCon.

34 Likes

How hard would it be to modify Julia by removing all Irrational code and replacing it with Symbolics.jl? Irrational is in a weird place, where it seems to kind of want to be part of a symbolic number system, but that obviously hasn’t happened.

2 Likes

I think removing an π from base would be a rather bad idea. The rest are kinda out of place and not being used in base itself from what I’m aware.

The problem with having an π in Base is that it makes defining operations like sin(π) into type piracy. Base currently has 6 extra math functions (sinpi and friends) that could just be methods of sin if we had better symbolics.

3 Likes

I really think that removing them would be wildly unpopular, whether or not it’s a good idea and they’re standing in the way of more convenient CAS functionality in an external package. But I could definitely be wrong.

1 Like

It sounds like a breaking change to remove irrational and π, but perhaps there are other Unicode symbols that could denote the symbolic versions and simplify things like sin(4*symbolicpi). People could choose to go with the one they prefer. I guess an issue is the symbols should look sufficiently distinct to avoid confusion, while also sufficiently like and π to make sense to humans.

1 Like

This is amazing! There is a feature that has been the bane of many CAS implementations and has caused an enormous amount of productive “bikeshedding” on the mailing lists of all open source CAS projects: assumptions

How will “assumptions” be dealt with in Symbolics.jl? I am asking as I remember how early on in Sympy there was much growing pain related to the difficulty of adding a comprehensive assumption system after the library had already grown. Sympy ended up having “new” and “old” assumption systems. And this has been a pain in many other older open source symbolic systems. It seems incredibly hard to add such a system after the fact. And it does not seem to be listed in the parity with sympy issue.

What would be needed to do this with Symbolics.jl:
image
image

relevant links:

Personally, refine is one of the most important features for productive interactive work (after substitution).

23 Likes