Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "SymPyPythonCall"
uuid = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c"
authors = ["jverzani <jverzani@gmail.com> and contributors"]
version = "0.1.2"
version = "0.1.3"

[deps]
CommonEq = "3709ef60-1bee-4518-9f2f-acd86f176c50"
Expand Down
3 changes: 3 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
SymPyPythonCall = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c"

[compat]
Documenter = "1"
27 changes: 2 additions & 25 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ using Documenter
makedocs(
sitename = "SymPyPythonCall",
format = Documenter.HTML(),
modules = [SymPyPythonCall]
modules = [SymPyPythonCall],
warnonly = [:missing_docs],
)

# Documenter can also automatically deploy documentation to gh-pages.
Expand All @@ -17,27 +18,3 @@ makedocs(
deploydocs(
repo = "github.com/jverzani/SymPyPythonCall.jl.git"
)


#DocMeta.setdocmeta!(SymPyPythonCall, :DocTestSetup, :(using SymPyPythonCall); recursive=true)

# makedocs(;
# modules=[SymPyPythonCall],
# authors="jverzani <jverzani@gmail.com> and contributors",
# repo="https://github.com/jverzani/SymPyPythonCall.jl/blob/{commit}{path}#{line}",
# sitename="SymPyPythonCall.jl",
# format=Documenter.HTML(;
# prettyurls=get(ENV, "CI", "false") == "true",
# canonical="https://jverzani.github.io/SymPyPythonCall.jl",
# assets=String[],
# ),
# pages=[
# "Home" => "index.md",
# "Examples" => "introduction.md"
# ],
# )

# # deploydocs(;
# repo="github.com/jverzani/SymPyPythonCall.jl",
# devbranch="main",
# )
5 changes: 2 additions & 3 deletions ext/SymPyPythonCallSymbolicUtilsExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ Returns the head (a function object) performed by an expression tree. Called onl
==#
function SymbolicUtils.operation(x::SymPyPythonCall.SymbolicObject)
@assert SymbolicUtils.istree(x)
nm = Symbol(SymPyPythonCall.Introspection.funcname(x))

nm = SymPyPythonCall.Introspection.funcname(x)
λ = get(SymPyPythonCall.Introspection.funcname2function, nm, nothing)
if isnothing(λ)
return getfield(Main, nm)
return getfield(Main, Symbol(nm))
else
return λ
end
Expand Down
4 changes: 2 additions & 2 deletions src/SymPyPythonCall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ include("conveniences.jl")
include("logical.jl")
include("matrix.jl")
include("assumptions.jl")
include("patternmatch.jl")
include("introspection.jl")
include("lambdify.jl")
include("introspection.jl")
include("patternmatch.jl")
include("plot_recipes.jl")
include("latexify_recipe.jl")
# export
Expand Down
38 changes: 6 additions & 32 deletions src/introspection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Module for Introspection
module Introspection

import SymPyPythonCall: Sym, asSymbolic
import SymPyPythonCall: Sym, asSymbolic, sympy_fn_julia_fn
using PythonCall
import PythonCall: Py
export args, func, funcname, class, classname, getmembers
Expand Down Expand Up @@ -47,40 +47,14 @@ end
class(x::T) where {T <: Union{Sym, Py}} = getattr(x, "__class__", nothing)
classname(x::T) where {T <: Union{Sym, Py}} = (cls = class(x); isnothing(cls) ? "" : cls.__name__)

# map funcname value (a string) to a function object
# special cases are in lambidfy.jl
const funcname2function = Dict(k => first(v) for (k,v) ∈ pairs(sympy_fn_julia_fn))


#function getmembers(x::T) where {T <: Union{Sym, PyObject}}
# Dict(u=>v for (u,v) in inspect.getmembers(x))
#end

## Map to get function object from type information
const funcname2function = (
Add = +,
Sub = -,
Mul = *,
Div = /,
Pow = ^,
re = real,
im = imag,
Abs = abs,
Min = min,
Max = max,
Poly = identity,
Piecewise = error, # replace
Order = (as...) -> 0,
And = (as...) -> all(as),
Or = (as...) -> any(as),
Less = <,
LessThan = <=,
StrictLessThan = <,
Equal = ==,
Equality = ==,
Unequality = !==,
StrictGreaterThan = >,
GreaterThan = >=,
Greater = >,
conjugate = conj,
atan2 = atan,
TupleArg = tuple,
Heaviside = (a...) -> (a[1] < 0 ? 0 : (a[1] > 0 ? 1 : (length(a) > 1 ? a[2] : NaN))),
)

end
91 changes: 57 additions & 34 deletions src/lambdify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,42 +36,48 @@ end
## As of newer sympy versions, this is no longer needed.
_PROD_(args...) = prod(args)

_ANY_(xs...) = any(xs)
_ALL_(xs...) = all(xs)
_ZERO_(xs...) = 0
_ANY_(xs...) = any(xs) # any∘tuple ?
_ALL_(xs...) = all(xs) # all∘tuple
_ZERO_(xs...) = 0 #
# not quite a match; NaN not θ(0) when evaluated at 0 w/o second argument
_HEAVISIDE_ = (a...) -> (a[1] < 0 ? 0 : (a[1] > 0 ? 1 : (length(a) > 1 ? a[2] : NaN)))
# _SYMPY_ALL_,
fn_map = Dict(
"Add" => :+,
"Sub" => :-,
"Mul" => :((as...)->prod(as)), #:*, # :(SymPy._PROD_)
"Div" => :/,
"Pow" => :^,
"re" => :real,
"im" => :imag,
"Abs" => :abs,
"Min" => :min,
"Max" => :max,
"Poly" => :identity,
"Piecewise" => :(SymPyPythonCall._piecewise),
"Order" => :(SymPyPythonCall._ZERO_), # :(as...) -> 0,
"And" => :(SymPyPythonCall._ALL_), #:((as...) -> all(as)), #:(&),
"Or" => :(SymPyPythonCall._ANY_), #:((as...) -> any(as)), #:(|),
"Less" => :(<),
"LessThan" => :(<=),
"StrictLessThan" => :(<),
"Equal" => :(==),
"Equality" => :(==),
"Unequality" => :(!==),
"StrictGreaterThan" => :(>),
"GreaterThan" => :(>=),
"Greater" => :(>),
"conjugate" => :conj,
"atan2" => :atan,
"Heaviside" => :(SymPyPythonCall._HEAVISIDE_),
_HEAVISIDE_(a...) = (a[1] < 0 ? 0 : (a[1] > 0 ? 1 : (length(a) > 1 ? a[2] : NaN)))

## Map to get function object from type information
# we may want fn or expression, Symbol(+) yields :+ but allocates to make a string
sympy_fn_julia_fn = Dict(
"Add" => (+, :+),
"Sub" => (-, :-),
"Mul" => (*, :*),
"Div" => (/, :/),
"Pow" => (^, :^),
"re" => (real, :real),
"im" => (imag, :imag),
"Abs" => (abs, :abs),
"Min" => (min, :min),
"Max" => (max, :max),
"Poly" => (identity, :identity),
"conjugate" => (conj, :conj),
"atan2" => (atan, :atan),
#
"Less" => (<, :(<)),
"LessThan" => (<=, :(<=)),
"StrictLessThan" => (<, :(<)),
"Equal" => (==, :(==)),
"Equality" => (==, :(==)),
"Unequality" => (!==, :(!==)),
"StrictGreaterThan" => (>, :(>)),
"GreaterThan" => (>=, :(>=)),
"Greater" => (>, :(>)),
#
"Piecewise" => (SymPyPythonCall._piecewise, :(SymPyPythonCall._piecewise)),
"Heaviside" => (SymPyPythonCall._HEAVISIDE_, :(SymPyPythonCall._HEAVISIDE_)),
"Order" => (SymPyPythonCall._ZERO_, :(SymPyPythonCall._ZERO_)),
"And" => (all∘tuple, :(SymPyPythonCall._ALL_)),
"Or" => (any∘tuple, :(SymPyPythonCall._ANY_)),
)

const fn_map = Dict(k => last(v) for (k,v) ∈ pairs(sympy_fn_julia_fn))

map_fn(key, fn_map) = haskey(fn_map, key) ? fn_map[key] : Symbol(key)

Base.convert(::Type{Expr}, x::SymbolicObject) = walk_expression(x)
Expand Down Expand Up @@ -210,7 +216,24 @@ This function will be about 2-3 times slower than `f`.
function lambdify(ex::Sym, vars=free_symbols(ex);
fns=Dict(), values=Dict(),
use_julia_code=false,
invoke_latest=true)
invoke_latest=true)
if isempty(vars)
# can't call N(ex) here...
flag = pygetattr(ex, "evalf", nothing)
if isnothing(flag)
val = pyconvert(Real, ex)
else
v = ex.evalf()
if pyconvert(Bool, v.is_real)
val = pyconvert(Real, v)
else
a,b = pyconvert.(Real, (real(v), imag(v)))
val = Complex(a, b)
end
end
return (ts...) -> val
end

body = convert_expr(ex, fns=fns, values=values, use_julia_code=use_julia_code)
ex = expr_to_function(body, vars)
if invoke_latest
Expand Down
7 changes: 7 additions & 0 deletions test/tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,13 @@ end
@test u(.5) == 1
@test u(1.5) == 0

# SymPy issue 567; constants
u = lambdify(Sym(1//2))
@test u() == u(1,2,3) == 1/2
@syms x
ex = integrate(sqrt(1 + (1/x)^2), (x, 1/sympy.E, sympy.E))
@test lambdify(ex)() ≈ 3.1961985135995072

# i2 = SymPy.lambdify_expr(x^2,name=:square)
# @test i2.head == :function
# @test i2.args[1].args[1] == :square
Expand Down