Converting symbolic function to numeric with SymPy

I have

using SymPy
a = Sym("a")
g(a) = a^2 - a
g_prime(a) = diff(g(a))
g_prime(a)
> 2𝑎−1

I would like to get a function numeric g_prime(a) that I can plug numbers into and evaluate. How do I do that?

The Julia SymPy docs talk about conversion to numeric here but they don’t specifically address functions there. There’s some answers addressing other symbolic packages, but I’m using SymPy now so I’d hope to stick with that.

Thanks

You can use lambdify:

julia> g_prime_n = lambdify(g_prime(a));

julia> g_prime_n(2.4)
3.8
1 Like

Thanks that works well with one variable, but I’m having some issues when I try to generalize to multivariable.

using SymPy
o1, o2, p1, p2 = Sym("o1,o2,p1,p2")
B1(o1,o2,p1,p2) = (-2.0*o1 + o2)*(p1 - .3)

B1_n = lambdify(B1(o1,o2,p1,p2))
B1_n(0.0,0.0,0.0,0.0)

> MethodError: no method matching var"##325"(::Float64, ::Float64, ::Float64, ::Float64)

It does appear to work when I use all of the variables in the function. So if I modify B1 so that it does something with p2, it works:

B1(o1,o2,p1,p2) = (-2.0*o1 + o2)*(p1 + p2  - .3)

B1_n = lambdify(B1(o1,o2,p1,p2))
B1_n(0.0,0.0,0.0,0.0)
> -0.0

But I don’t want the function to do something with all the variables. Any thoughts on this?

You can define your arguments explicitly to avoid this issue. You can see an example in the lambdify documentation:

julia> @doc lambdify
  lambdify(ex, vars=free_symbols();
           fns=Dict(), values=Dict, use_julia_code=false,
           invoke_latest=true)

  Take a symbolic expression and return a Julia function or expression to build a function.

    •  ex::Sym a symbolic expression with 0, 1, or more free symbols

    •  vars a container of symbols to use for the function arguments. The default is free_symbols which has a specific
       ordering. Specifying vars allows this default ordering of arguments to be customized.

    •  fns::Dict, vals::Dict: Dictionaries that allow customization of the function that walks the expression ex and
       creates the corresponding AST for a Julia expression. See SymPy.fn_map and SymPy.val_map for the default
       mappings of sympy functions and values into Julia's AST.

    •  use_julia_code::Bool: use SymPy's conversion to an expression, the default is false

    •  invoke_latest=true: if true will call eval and Base.invokelatest to return a function that should not have any
       world age issue. If false will return a Julia expression that can be evaled to produce a function.

  Example:

  julia> using SymPy

  julia> @syms x y z
  (x, y, z)

  julia> ex = x^2 * sin(x)
   2
  x ⋅sin(x)

  julia> fn = lambdify(ex);

  julia> fn(pi)
  1.2086779438644711e-15

  julia> ex = x + 2y + 3z
  x + 2⋅y + 3⋅z

  julia> fn = lambdify(ex);

  julia> fn(1,2,3) # order is by free_symbols
  14

  julia> ex(x=>1, y=>2, z=>3)
  14

  julia> fn = lambdify(ex, (y,x,z));

  julia> fn(1,2,3)
  13
1 Like

Alao, Symbolics.jl’s build_function might be of interest, if you want to use a pure Julia equivalent.

1 Like