CAS Best Practices

I think there may be a bit of a disconnect in this discussion between traditional “CAS systems” like Mathematica and Macsyma, which, being entire self-contained systems, have to answer all the questions that anyone might have about how to do any symbolic or numeric computation that can be expressed. Since they are entire programming systems, they have to be huge and complete. At the same time, they have to be convenient and intuitive enough that mathematicians will want to use them. Both of those are massive burdens and really hard to satisfy.

Symbolics.jl isn’t trying to do any of this — it’s not that kind of CAS. It’s not a self-contained system or even a language. It’s a toolbox for doing symbolic reasoning about Julia ASTs in the context of Julia’s type system. It doesn’t need to create any of the language level stuff — that already exists. It doesn’t need to invent a type system — that also already exists. It doesn’t need to provide definitive answers to all the possible ways values can be represented or computations can be done — the language already lets people do that. In fact, trying to do this would be too limited because one of the strengths of the language is letting people build their own new ways to represent things and express how to do computations with those representations. If Symbolics tried to provide all the answers, it would be too limited to be useful for its intended purpose. Instead, it aims to give people who need to do symbolic reasoning about Julia code the tools to do it in a flexible way. When there are questions about which way to do things, it shouldn’t pick an answer, it should provide a way to let the user choose.

So, for example, Symbolics.jl doesn’t need to decide for the user which representation of polar intervals is better. They will already have to have done that in their code that works with polar intervals. There’s more than one way to do those computations, and the language’s job is not to pick the best one for you, it’s to let you easily express the approach you want and to switch easily between different approaches. I realize that’s a philosophical stance that’s pretty different from traditional CAS systems, which generally try to automagically figure out a good way to do your computation, but Julia really avoids magic of any kind — everything should instead be explicit but easy to express.

Similarly I don’t really understand the issue with a being equivalent to 0. If you’ve provided a set of symbolic rules that allows Symbolics to simplify that expression to zero, then it will get simplified; if you haven’t, then it won’t. There’s no one true answer — it depends on the set of rules you’re operating under.

I think this difference in philosophy and scope is what makes the project interesting and worth pursuing. If this were just an attempt to build another end-user CAS system, I agree, why bother? We already have several good ones. But that’s not the endeavor here at all. The scope is limited to symbolic reasoning about Julia ASTs within the context of Julia’s type system. Specifically because that turns out to be really incredibly useful when doing numerical computations. Philosophically, rather than trying to be complete and automatic, this is limited and completely explicit (but with an easy way to get a reasonable usable set of defaults), because it’s not even meant to be used by end-users, it’s meant to be used by library authors. For comparison, the project I’m aware of that seems most similar in spirit is GiNaC, a C++ library for embedding efficient symbolic computation in C++ programs, which is explicitly not a CAS (GiNaC = “GiNaC is Not a CAS”).

This doesn’t mean there aren’t big challenges here. I just got off a (Julia Computing) compiler team meeting where we spent a bunch of time discussing how to meet the needs of Symbolics, which are not simple. The folks on the Symbolics side (Chris & Shashi) came in with an ask about changing Julia’s inheritance system in a pretty fundamental way, which all the compiler side folks pushed back on, but we counter-proposed something similar to Cassette, which can take existing code and recursively rewrite it, replacing method calls with other method calls. In the end, we’ll figure out something that works for Symbolics without being too specific to it, possibly a mechanism that can be used by both AD systems and for symbolic computations.

23 Likes