Automatic Conversion of Julia Code to C Functions
Since Symbolics.jl can trace Julia code into Symbolics IR that can be built and compiled via build_function
to C, this gives us a nifty way to automatically generate C functions from Julia code! To see this in action, let's start with the Lotka-Volterra equations:
using Symbolics
function lotka_volterra!(du, u, p, t)
x, y = u
α, β, δ, γ = p
du[1] = dx = α*x - β*x*y
du[2] = dy = -δ*y + γ*x*y
end
lotka_volterra! (generic function with 1 method)
Now we trace this into Symbolics:
@variables t du[1:2] u[1:2] p[1:4]
du = collect(du)
lotka_volterra!(du, u, p, t)
du
\[ \begin{equation} \left[ \begin{array}{c} p_{1} u_{1} - p_{2} u_{1} u_{2} \\ - p_{3} u_{2} + p_{4} u_{1} u_{2} \\ \end{array} \right] \end{equation} \]
and then we build the function:
build_function(du, u, p, t, target=Symbolics.CTarget())
"#include <math.h>\nvoid diffeqf(double* du, const double* RHS1, const double* RHS2, const double RHS3) {\n du[0] = RHS2[0] * RHS1[0] + -1 * RHS2[1] * RHS1[0] * RHS1[1];\n du[1] = -1 * RHS2[2] * RHS1[1] + RHS2[3] * RHS1[0] * RHS1[1];\n}\n"
If we want to compile this, we do expression=Val{false}
:
f = build_function(du, u, p, t, target=Symbolics.CTarget(), expression=Val{false})
RuntimeGeneratedFunction(#=in Symbolics=#, #=using Symbolics=#, :((du, u, p, t)->begin
#= /home/runner/work/Symbolics.jl/Symbolics.jl/src/build_function.jl:809 =#
ccall(("diffeqf", "/tmp/jl_GFoLSRMvsp"), Cvoid, (Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Float64), du, u, p, t)
end))
now we check it computes the same thing:
du = rand(2); du2 = rand(2)
u = rand(2)
p = rand(4)
t = rand()
f(du, u, p, t)
lotka_volterra!(du2, u, p, t)
du == du2 # true!
true