Numeric value substitution into SymPy Symbolic array or matrix

johannesnauta
Mon, 22 May 2023 08:34:51 +0000
Numerics

I am having trouble substituting numerical values into symbolic arrays and matrices.
I have read about lambdify, but I still fail to provide numerical values for the arguments in a consistent way.

As an example, consider the following Julia code

julia> using Sympy
julia> n = 4;
julia> η = [symbols("η$i", positive=true) for i in 1:n];
julia> α = [(i<3) ? 1 : symbols("α", positive=true) for i in 1:n];
julia> η .*= α
4-element Vector{Sym}:
   η₁
   η₂
 α⋅η₃
 α⋅η₄

julia> syms = Symbol.(free_symbols(tensors[:η]))
5-element Vector{Symbol}:
 :α
 :η1
 :η2
 :η3
 :η4

julia> f = lambdify(η, syms)
#122 (generic function with 1 method)

julia> f.(0.5,1.0,1.0,1.0,1.0)
4-element Vector{Float64}:
 1.0
 1.0
 0.5
 0.5

As expected, the outcome is indeed the substitution for α=0.5 and all η=1.0. However, my problem arises from the fact that I would like to feed the expressions from arrays or tuples, i.e. what I would like is:

julia> values = (0.5, 1.0, 1.0, 1.0, 1.0);
julia> f.(values)
ERROR: MethodError: no method matching var"##339"(::Float64)

Closest candidates are:
  var"##339"(::Any, ::Any, ::Any, ::Any, ::Any)
   @ SymPy none:0

Stacktrace:
  [1] invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Base ./essentials.jl:816
  [2] invokelatest(::Any, ::Any, ::Vararg{Any})
    @ Base ./essentials.jl:813
  [3] (::SymPy.var"#118#119"{SymPy.var"###339"})(::Float64, ::Vararg{Float64})
    @ SymPy ~/.julia/packages/SymPy/gkr7m/src/lambdify.jl:223
  [4] map(::Function, ::Float64)
    @ Base ./number.jl:284
  [5] _broadcast_getindex_evalf
    @ ./broadcast.jl:683 [inlined]
  [6] _broadcast_getindex
    @ ./broadcast.jl:656 [inlined]
  [7] getindex
    @ ./broadcast.jl:610 [inlined]
  [8] copy
    @ ./broadcast.jl:912 [inlined]
  [9] materialize
    @ ./broadcast.jl:873 [inlined]
 [10] (::SymPy.var"#122#124"{Vector{SymPy.var"#118#119"}})(args::Float64)
    @ SymPy ~/.julia/packages/SymPy/gkr7m/src/lambdify.jl:253
 [11] _broadcast_getindex_evalf
    @ ./broadcast.jl:683 [inlined]
 [12] _broadcast_getindex
    @ ./broadcast.jl:656 [inlined]
 [13] #31
    @ ./broadcast.jl:1088 [inlined]
 [14] macro expansion
    @ ./ntuple.jl:72 [inlined]
 [15] ntuple
    @ ./ntuple.jl:69 [inlined]
 [16] copy
    @ ./broadcast.jl:1088 [inlined]
 [17] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Nothing, SymPy.var"#122#124"{Vector{SymPy.var"#118#119"}}, Tuple{NTuple{5, Float64}}})
    @ Base.Broadcast ./broadcast.jl:873
 [18] top-level scope
    @ REPL[199]:1

The reason that I want this is so that I can make all of my symbolic arrays into “lambdified” expressions and evaluate them, for example at multiple points in parallel. Additionally, when the array is instead a matrix, I also do not wish to manually input all the matrix elements – especially when the sizes of the matrices grow.
Consider for example,

julia> M = [symbols("M$i$j") for i in 1:3 for j in 1:3].reshape(3,3)
3×3 Matrix{Sym}:
 M₁₁  M₁₂  M₁₃
 M₂₁  M₂₂  M₂₃
 M₃₁  M₃₂  M₃₃
julia> syms_M = free_symbols.(M)
3×3 Matrix{Vector{Sym}}:
 [M11]  [M12]  [M13]
 [M21]  [M22]  [M23]
 [M31]  [M32]  [M33]
julia> fM = lambdify(M, syms_M)
#122 (generic function with 1 method)

How do I use fM to return a numeric valued matrix, e.g. when Mvals = rand(3,3)? Any help would be greatly appreciated!

6 posts - 2 participants