Best Practices for converting a Mathematica expression to Julia

Thanks. Never heard of this Github Copilot.
Do you have an example of how you use it? It seems like a powerful thing.

Though it would be nice to find a julia solution…

Here is something you can copy and paste to see if it works for you:

using SymPy
const sympy_parsing_mathematica = SymPy.PyCall.pyimport("sympy.parsing.mathematica")

s = "(Sqrt[Pi])/(2*EllipticE[m])"
ex = sympy_parsing_mathematica.mathematica(s, Dict("EllipticE[x]"=>"elliptic_e(x)"))

SymPy.walk_expression(ex) #:((1 / 2) * pi ^ (1 / 2) * elliptic_e(m) ^ -1)

# or

SymPy.convert_expr(ex, use_julia_code=true) # :(sqrt(pi) ./ (2 * elliptic_e(m)))
2 Likes

That is phenomenal!

Some things still do not work as expected, though.

The walk_expression gives me something different, and I do not know why:

s = "(Sqrt[Pi])/(2*EllipticE[m])"
ex = sympy_parsing_mathematica.mathematica(s, Dict("EllipticE[x]"=>"elliptic_e(x)"))

SymPy.walk_expression(ex) # what I should get :((1 / 2) * pi ^ (1 / 2) * elliptic_e(m) ^ -1)
# what I get is
# :((1 / 2) * SymPy.__POW__(pi, 1 / 2) * SymPy.__POW__(elliptic_e(m), -1))

I can fix it using

SymPy.walk_expression(ex, fns=Dict("Pow"=>:^))
# :((1 / 2) * pi ^ (1 / 2) * elliptic_e(m) ^ -1)

The convert_expr does not work well.
First, I do not like that it returns all operations with the vectorized dot notion like so

:(sqrt(pi) ./ (2 * elliptic_e(m)))

The more problematic issue is that it does not deal with this expression for some reason

s = "Sqrt[2]/q2 EllipticF[x, 1 - q1^2/q2^2]"
ex = sympy_parsing_mathematica.mathematica(s, Dict("EllipticF[x, y]"=>"elliptic_e(x, y)"))

SymPy.convert_expr(ex, use_julia_code=true)

gives

Base.Meta.ParseError("use \"x^y\" instead of \"x**y\" for exponentiation, and \"x...\" instead of \"**x\" for splatting.")

Stacktrace:
 [1] #parse#3
   @ ./meta.jl:236 [inlined]
 [2] parse
   @ ./meta.jl:232 [inlined]
 [3] parse(str::String; raise::Bool, depwarn::Bool)
   @ Base.Meta ./meta.jl:267
 [4] parse(str::String)
   @ Base.Meta ./meta.jl:266
 [5] convert_expr(ex::Sym; fns::Dict{Any, Any}, values::Dict{Any, Any}, use_julia_code::Bool)
   @ SymPy ~/.julia/packages/SymPy/mpN0u/src/lambdify.jl:260
 [6] top-level scope
   @ In[37]:5

Sorry, convert_expr has some issues like this, hence the need for walk_expression. However, I realize I had some unpushed changes that are causing the differences you see with my example with walk_expression. I just made a PR, which I hope to tag once all the tests pass.

1 Like

Thanks again!

I see that the walk_expression does not know how to handle sqrt and it gives a power of 1/2 instead as can be seen here

Is there a way to get the sqrt from the walk_expression?
(The convert_expr gives sqrt, but as you said it has some issues).

Let me also mention for completeness that in the first time I run these functions I get the following warning:

sys:1: SymPyDeprecationWarning: 

The ``mathematica`` function for the Mathematica parser is now
deprecated. Use ``parse_mathematica`` instead.
The parameter ``additional_translation`` can be replaced by SymPy's
.replace( ) or .subs( ) methods on the output expression instead.

See https://docs.sympy.org/latest/explanation/active-deprecations.html#mathematica-parser-new
for details.

This has been deprecated since SymPy version 1.11. It
will be removed in a future version of SymPy.

Thanks. Looks like a need to update my sympy library!

Anyways, I just registered a fix to lambdify that should also give back sqrt, as you wanted.

2 Likes

Great. I saw that the sqrt was fixed!

Just mentioning that the issue with convert_expr is still there:

And also, this issue is still thereL

I extended @gangchern’s example to a package (GitHub - musoke/WolframExpr.jl). It’s bare bones, but does the basics of converting algebraic expressions from Mathematica to Julia. You can define mappings for special functions.

Example:

julia> using WolframExpr

julia> f = string_to_function("A[x,y]+y", [:A, :x, :y]);

julia> A(x, y) = x^2 + y^2;

julia> f(A, 1, 2)
7
2 Likes

Running now on Julia 1.9.3, with SymPy v2.0.1, it does not have convert_expr and walk_expression.
Does anyone know what is the alternative to these functions?

They are buried behind another module now. For ex coming from sympy_parsing_mathematica.mathematica try:

SymPy.SymPyCore.walk_expression(Sym(ex))
1 Like

Wonderful! that indeed works! thanks so much!

I have an issue with the // operation. The expression I get from sympy does not have parenthesis around the numbers, which causes then an error when I copy the expression into a function.

Here is an example:

using SymPy
const sympy_parsing_mathematica = SymPy.PyCall.pyimport("sympy.parsing.mathematica")

s = raw"1/Sqrt[(x+y)^3]"
ex = sympy_parsing_mathematica.mathematica(s)
SymPy.SymPyCore.walk_expression(Sym(ex))
# :(((x + y) ^ 3) ^ -1//2)

# my goal is to take the above output and make a function out of it.
# I want to do it often, so I want to automate it as much as possible.
# defining
ftest(x, y) = (((x + y) ^ 3) ^ -1//2)
# then results in an error
ftest(1, 1)

Here is the error:

MethodError: no method matching //(::Float64, ::Int64)

Closest candidates are:
  //(::Integer, ::Integer)
   @ Base rational.jl:62
  //(::Rational, ::Integer)
   @ Base rational.jl:64
  //(::Complex, ::Real)
   @ Base rational.jl:78
  ...


Stacktrace:
 [1] ftest(x::Int64, y::Int64)
   @ Main ./In[58]:1
 [2] top-level scope
   @ In[59]:1

Putting parenthesis around the // operation will solve it, but I want to do it automatically in the parsing. Is there a way to do that?

I have found some resolution using @generated macro:

s = raw"1/Sqrt[(x+y)^3]"
ex = sympy_parsing_mathematica.mathematica(s)
f_expr = SymPy.SymPyCore.walk_expression(Sym(ex))
# :(((x + y) ^ 3) ^ -1//2)
@generated ftest(x,y) = f_expr
ftest(1,0)
# 1.