Generate code from Symbolics.jl derivative

Following this example here, I am trying to generate Julia or MATLAB code from a derivative.

using Symbolics
@variables x a b c
D    = Differential(x)
F    = a*x^3 + b*x^2 + c*x
dFdx = expand_derivatives(D(F))

Targeting Julia:

build_function(dFdx, x, a, b, c, target=Symbolics.JuliaTarget())

How do I convert the following expression to Julia syntax?

:(function (x, a, b, c)
      #= /home/user/.julia/packages/SymbolicUtils/jf8aQ/src/code.jl:385 =#
      #= /home/user/.julia/packages/SymbolicUtils/jf8aQ/src/code.jl:386 =#
      #= /home/user/.julia/packages/SymbolicUtils/jf8aQ/src/code.jl:387 =#
      (+)((+)(c, (*)((*)(2, b), x)), (*)((*)(3, a), (^)(x, 2)))

I think MacroTools.jl might have something but have not found the appropriate function.

Targeting MATLAB:

build_function(dFdx, x, a, b, c, target=Symbolics.MATLABTarget())

For any symbolic expression, I get this type of error:

BoundsError: attempt to access 3-element Vector{Symbol} at index [4]

throw_boundserror(A::Vector{Symbol}, I::Tuple{Int64}) at essentials.jl:14
getindex at essentials.jl:916 [inlined]
numbered_expr(::SymbolicUtils.BasicSymbolic{Real}, ::Dict{Num, Tuple{Int64, Int64}}, ::Num, ::Vararg{Num}; varordering::Num, offset::Int64, states::SymbolicUtils.Code.LazyState, lhsname::Symbol, rhsnames::Vector{Symbol}) at build_function.jl:590
kwcall(::@NamedTuple{offset::Int64, lhsname::Symbol, rhsnames::Vector{Symbol}, varordering::Num}, ::typeof(Symbolics.numbered_expr), ::SymbolicUtils.BasicSymbolic{Real}, ::Dict{Num, Tuple{Int64, Int64}}, ::Num, ::Vararg{Num}) at build_function.jl:583
#462 at none:0 [inlined]
iterate(::Base.Generator{Vector{Any}, Symbolics.var"#462#465"{Num, Int64, Symbol, Vector{Symbol}, Dict{Num, Tuple{Int64, Int64}}}}) at generator.jl:48
numbered_expr(::SymbolicUtils.BasicSymbolic{Real}, ::Dict{Num, Tuple{Int64, Int64}}, ::Num, ::Vararg{Num}; varordering::Num, offset::Int64, states::SymbolicUtils.Code.LazyState, lhsname::Symbol, rhsnames::Vector{Symbol}) at build_function.jl:598
kwcall(::@NamedTuple{lhsname::Symbol, rhsnames::Vector{Symbol}, offset::Int64}, ::typeof(Symbolics.numbered_expr), ::SymbolicUtils.BasicSymbolic{Real}, ::Dict{Num, Tuple{Int64, Int64}}, ::Num, ::Vararg{Num}) at build_function.jl:583
_build_function(::Symbolics.MATLABTarget, ::Vector{Num}, ::Num, ::Vararg{Num}; columnmajor::Bool, conv::Function, expression::Type, fname::Symbol, lhsname::Symbol, rhsnames::Vector{Symbol}) at build_function.jl:995
_build_function(::Symbolics.MATLABTarget, ::Vector{Num}, ::Num, ::Num, ::Num, ::Num) at build_function.jl:969
_build_function(::Symbolics.MATLABTarget, ::Num, ::Num, ::Vararg{Num}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}) at build_function.jl:1011
_build_function(::Symbolics.MATLABTarget, ::Num, ::Num, ::Num, ::Num, ::Num) at build_function.jl:1011
build_function(::Num, ::Vararg{Num}; target::Symbolics.MATLABTarget, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}) at build_function.jl:87
kwcall(::@NamedTuple{target::Symbolics.MATLABTarget}, ::typeof(build_function), ::Num, ::Vararg{Num}) at build_function.jl:86

Any suggestions on how to proceed?

Hello!
I have the exact same problem. Have tried a variety of things. Me too facing the similar issues.

Best Regards,
Christin

1 Like

build_function(..., expression=false) causes it to return a function instead of an expression.

1 Like

As @baggepinnen said, you can easily generate a Julia function (object) from the derivative’s symbolic expression. In most cases, that should be sufficient, unless you really want to generate Julia code (text)?

Yes, actually I want to paste the code into MATLAB to work with some legacy code. If it doesn’t work with MATLABTarget I was content to manually edit the Julia code to make it MATLAB compatible.

The MATLAB target needs to be generalized to handle more general functions. It handles a lot of the ODE cases that come from MTK but not all sets of input arguments but it hasn’t been high on the priority list. It wouldn’t be so hard for someone who cares to edit it though.

It seems to return something close to anonymous function syntax mixed with AST expressions.

RuntimeGeneratedFunction(#=in Symbolics=#, #=using Symbolics=#, :((x, a, b, c)->begin          
          #= /home/user/.julia/packages/SymbolicUtils/jf8aQ/src/code.jl:385 =#
          #= /home/user/.julia/packages/SymbolicUtils/jf8aQ/src/code.jl:386 =#
          #= /home/user/.julia/packages/SymbolicUtils/jf8aQ/src/code.jl:387 =#
          (+)((+)(c, (*)((*)(2, b), x)), (*)((*)(3, a), (^)(x, 2)))
      end))

To state my use case, I’m actually interested in generating Julia code so that I can copy-paste it into MATLAB (with some modifications).

I feel like the expression above should be convertible with a function like ast.unparse in Python but I cannot find the equivalent function in Julia.

I see - thanks. I am content to manually edit (with string replace) Julia code to MATLAB for the moment.