Trying to switch from sympy


#1

Hi,

I’m pretty new to Julia, coming from Python. In the transition process I’m looking at the different symbolic math options in Julia, both SymEngine.jl, and SymPy.jl

I was wondering if there’s a way to lambdify a symbolic matrix using either of the two (or some other) packages. Something like (I tried doing this in SymPy.sj too):

using SymEngine

@vars x1 x2 x3 x4

x = [x1, x2, x3, x4]

A = [[x1 x2];[x3 x4]]

f1 = lambdify(A[1,1]) #works, but not really what I'm looking for

f2 = lambdify(x,A) #does not work
f3 = lambdify(A) #does not work

In python - sympy I can do (with equivalent setup):

f = lambdify(x,A)

I was hoping someone here may have an idea of what I’m doing wrong . . .

Thanks.


#2

lambdify.(A) if you want to apply it elementwise?


#3

What is lambdify(x,A) supposed to do?


#4

That will give an array of functions though, instead of a function that produces an array. There’s an open issue on this topic (I’m guessing the same author).

It probably needs a function defined that internally lambdifys the array and then builds the return function. If there’s a more standard SymEngine answer, @isuruf would be the one who would know.


#5

Why do you even want to use sympy for this? can’t you just do:

f(x) = [[a b];[c d]]

Why not just do that if that’s the function you want. With my symbolic package Reduce.jl, there is no such thing as lambdify because it works directly on Julia abstract syntax trees. No need to overcomplicate things.

From my perspective, having separate types of symbolic variables and implementing a lambdify function are all superfluous, since one can just work with abstract syntax trees to manipulate and construct the expression one wants to get. So the way I see it the other symbolic packages way overcomplicate things that can be accomplished in a much simpler way using Julia’s language itself.

Since you do not appear to do any symbolic operations, you can just do this:

julia> f(a,b,c,d) = [a b;c d]
f (generic function with 1 method)

julia> f(1,2,3,4)
2×2 Array{Int64,2}:
 1  2
 3  4

Although, you could also manipulate an expression like that using Reduce. For example, you could differentiate with respect to variable a like so:

julia> Expr(:function,:(f(a,b,c,d)),:([a b;c d]))
:(function f(a, b, c, d)
        [a b; c d]
    end)

julia> df(ans, :a)
:(function f(a, b, c, d)
        [1 0; 0 0]
    end)

Then to use the function you just created, you can eval it and use it like any other Julia function. With a Reduce.jl style symbolic package in Julia, you don’t even need lambdify or declare @vars.

Here is another example:

julia> using Reduce;
julia> Expr(:function,:(f(a,b,c,d)),:([a^2 b*x;c*a d]))
:(function f(a, b, c, d)
        [a ^ 2 b * x; c * a d]
    end)

julia> df(ans,:a)
:(function f(a, b, c, d)
        [2a 0; c 0]
    end)

julia> eval(ans)
f (generic function with 1 method)

julia> f(1,2,3,4)
2×2 Array{Int64,2}:
 2  0
 3  0

Just work with julia’s abstract syntax tree, is what I would say.


#6

One way to do this in Symata.jl is

symata > A = [[x1,x2], [x3,x4]]
Out(1) = [[x1,x2],[x3,x4]]

symata 2> f = Compile(Evaluate(A))
Out(2) = (::#3) (generic function with 1 method)

symata 3> f(1,2,3,4)
Out(3) = [[1,2],[3,4]]

symata 4> g = Compile(sin(x)/x)
Out(4) = (::#5) (generic function with 1 method)

symata 5> g(0.0001)
Out(5) = 0.9999999983333334

I should mention that this is done with Julia v0.5. But these work with v0.6.2 as well.


#7

Thank you all for the replies.

@mzaffalon lambdify(x,A) generates a function which will return a matrix A, when given a vector of parameters the same length and order as x. So it’s a way to take a symbolic matrix with symbols in x, and then get a numeric matrix out.

@stevengj and @ChrisRackauckas. You’re right, that’s my issue as well. I wasn’t sure if I was just missing something, so I thought I’d ask here as well.

@chakravala thanks for pointing out your repo I’ll take a closer look at it. The example I gave was the smallest working bit of code to show what I was trying to do. The matrices I’m trying to lambdify come from robot kinematics/dynamics. So I’m symbolically calculating things like jacobian, inertia, and coriolis matrices and I want to be able to get numerical versions of those as a function of the robot state.

Thanks all.


#8

If that’s your use case, you might be interested in https://github.com/JuliaRobotics/RigidBodyDynamics.jl/blob/master/notebooks/Symbolic%20double%20pendulum.ipynb. I don’t know of a good solution to your particular problem (maybe try https://github.com/rdeits/CommonSubexpressions.jl ?), but please feel free to contact me about robot kinematics and dynamics in Julia, and please share your findings!


#9

Following works in SymEngine.jl master version.

f2 = lambdify(A, x)
f3 = lambdify(A)

I recommend the first method as the order of the variables in the second one is undefined if there is more than one variable.


#10

@isuruf Thank you! Cloning the package (as opposed to just Pkg.add()) did it. Everything seems to work in the master branch.

@tkoolen Thanks for the notebook link. I actually started looking at Julia because of RigidBodyDynamics.jl. I’ll be sure to post anything interesting I end up doing with Julia.


#11

Awesome, thank you!


#12

I noticed that in the example above the output of the compiled Symata code is still a Symata List. But, the result is meant to be used in julia-only code. So, the Lists should be converted to Julia Arrays. I changed this and committed to both branches jv0.5only, and v0.6-a. Below is the corrected example plus another way to get the same result from Symata

symata 1> A = [[x1,x2], [x3,x4]]
Out(1) = [[x1,x2],[x3,x4]]

symata 2> f = Compile(Evaluate(A))
Out(2) = (::#3) (generic function with 1 method)

symata 3> f(1,2,3,4)  # result is now a Julia Array
Out(3) = 2-element Array{Array{Int64,1},1}:
 [1, 2]
 [3, 4]
symata 4> ExportJ(f) # export the function from Symata to Julia

julia> f(5,6,7,8)  # now call from Julia (after changing repl mode with `=`)
2-element Array{Array{Int64,1},1}:
 [5, 6]
 [7, 8]

# @symExpr interprets its argument as a Symata expression,
# evaluates it, and translates the result to a Julia expression
julia> g(x1,x2,x3,x4) = @symExpr A
g (generic function with 1 method)

julia> g(5,6,7,8)
2-element Array{Array{Int64,1},1}:
 [5, 6]
 [7, 8]

A more interesting example:

symata 26> Table(LegendreP(i,x), [i,1,3])
Out(26) = [x,-1/2 + (3/2)*x^2,(-3/2)*x + (5/2)*x^3]

julia> j(x) = @symExpr Table(LegendreP(i,x), [i,1,3])
j (generic function with 1 method)

julia> j(.5)
3-element Array{Float64,1}:
  0.5   
 -0.125 
 -0.4375

julia> j(1//2)
3-element Array{Rational{Int64},1}:
  1//2 
 -1//8 
 -7//16

#13

Instead of cloning, you can also use Pkg.add("Pack") and then Pkg.checkout("Pack") to get the master branch. Then Pkg.free("Pack") to return to version management to Pkg.