[ANN] SymbolicUtils.jl -- Groundwork for a symbolic ecosystem in Julia

Thanks Yuri! I fixed this now. I’m still getting to know Franklin :slight_smile:

4 Likes

I assure you that we did not ignore them. Shashi and I both put serious effort towards trying to make Rewrite.jl work for this before Shashi started SymbolicUtils.jl. ModelingToolkit.jl was revamped to rely on SymbolicUtils and I also plan to revive my old package, Symbolics.jl but ripping out most of its guts and replacing them with the infrastructure provided by SymbolicUtils.jl.

I think it could be interesting to eventually try and take over the role SymPy takes in providing simplification for other packages, i.e. things like Symata.jl and if @jlapeyre wants to collaborate on such a project I’d certainly be interested.

The whole idea here is to bring all these packages closer together, not push them away!

13 Likes

This is a off-topic question. Is there any Julia package that offers full functionality of Matlab Symbolic Math Toolbox?

1 Like

SymPy.jl is probably the closest

4 Likes

I find it strange that the symbol is not a subtype of Number.
Is the Idea for another package, using SymbolicUtils, to provide that interface?

julia> using SymbolicUtils

julia> @syms x::Real
(x,)

julia> f(x::Number) = 2*x
f (generic function with 1 method)

julia> f(x)
ERROR: MethodError: no method matching f(::SymbolicUtils.Sym{Real})
Closest candidates are:
  f(::Number) at REPL[7]:1
Stacktrace:
 [1] top-level scope at REPL[8]:1
1 Like

This is something we agonized over quite a bit. I think Sym <: Number is just plain not good enough. We want to be able to support symbolic Float64s, Matrixs, Strings or really any datatype. One approach would be to just create a billion SymbolicX types which subtype X, but that has many downsides.

What we really want is Sym{T} <: T, but that’s not supported by Julia’s semantics, however it can actually be done inside an IRTools.jl pass like I show in this proof of concept. I plan to investigate this approach further.

One thing I feel somewhat strongly about is that this package can’t just be for math on numbers. It should have wider applicability than that. Mathematica has many flaws, but I think it demonstrates the value of symbolic programming in all sorts of domains outside simple math.

23 Likes

Yes it is! I think we should mention this in the docs, thank you!

We are looking into tracing tools like Cassette or IRTools to make this kinda thing work for all types.

The basic problem is we need something that behaves like Sym{T} <: T for Sym to take on the role of any type but be symbolic.

Edit: I just added it to the docs!

9 Likes

Is this similar to the wrapper array problem so it would be helpful if T were a trait ?

It’s similar, but it wouldn’t help much if T is a trait since existing code does not dispatch on the trait.

1 Like

Right. Hopefully at some point traits will get first class language support and become more pervasive :slight_smile:

I think this is a type system limitation and it would be great if we didn’t have to rely on compiler transforms to work around it.

However, there are some packages that use traits for dispatch/abstract type. Can this interop with those as well?

This looks great!

One glaring omission from most symbolic systems is a nice way to reason about array manipulations. Any chance there’s anything in the works specific to this? An interface to one of the tensor manipulation libraries would be especially exciting.

7 Likes

Yes, this is indeed one major reason I got involved with this whole project, and why we are pushing so hard for parameterized types, despite the headaches they induce.

Symbolically manipulating tensor expressions instead of arrays of symbolic numbers is an important distinction and something I’d really like to explore.

I think there’s still a bit more ‘plumbing’ that I’d like to work on before getting into a high level application like this, but it’s certainly on my radar and if anyone would be interested in collaborating on it, I’m all ears!

14 Likes

I just started using your package, pretty nice. Thanks!

In version v0.4.3, @syms a; 0* a gives zero, while in the new version v0.5.0, it gives 0 * a. How can I force it to be zero?

Also I observed the following behavior:

@syms a
@syms b::Real

then 0a gives 0 * a, but 0b gives the following error:

ERROR: syntax: invalid numeric constant "0b"
Stacktrace:
 [1] top-level scope at none:1
1 Like

This is because 0b1011 is syntax for creating an unsigned integer from binary. You need to explicitely write 0*b in this case.

4 Likes

Thanks.

figured out this can be done using r = @rule (0 * ~a) => 0; r(0*a) gives zero. But would it be better to give zero automatically in this case?

On v0.5.0 we stopped simplifying expressions to print them.

@syms a; 0* a gives zero

It wasn’t actually returning zero but printing zero rather confusingly.

You can call simplify(0*a) to get 0.

2 Likes

got it. many thanks. but would it be better to automatically simplify such a case?

1 Like

You could define that yes, but it would be a kludge. We would have to define many methods and check that one of the inputs is a number and is zero.

Now we do automatically simplify this and a number of other common cases. simplify itself is rarely needed now :}

7 Likes

That’s great! Thanks.

1 Like