Skip to content

Commit 6a237d1

Browse files
authored
Merge pull request #27 from jverzani/lambdify
combine lambdify function; bump documenter
2 parents 8f7c8b0 + 8045d19 commit 6a237d1

File tree

8 files changed

+80
-97
lines changed

8 files changed

+80
-97
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SymPyPythonCall"
22
uuid = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c"
33
authors = ["jverzani <jverzani@gmail.com> and contributors"]
4-
version = "0.1.2"
4+
version = "0.1.3"
55

66
[deps]
77
CommonEq = "3709ef60-1bee-4518-9f2f-acd86f176c50"

docs/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[deps]
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
33
SymPyPythonCall = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c"
4+
5+
[compat]
6+
Documenter = "1"

docs/make.jl

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ using Documenter
88
makedocs(
99
sitename = "SymPyPythonCall",
1010
format = Documenter.HTML(),
11-
modules = [SymPyPythonCall]
11+
modules = [SymPyPythonCall],
12+
warnonly = [:missing_docs],
1213
)
1314

1415
# Documenter can also automatically deploy documentation to gh-pages.
@@ -17,27 +18,3 @@ makedocs(
1718
deploydocs(
1819
repo = "github.com/jverzani/SymPyPythonCall.jl.git"
1920
)
20-
21-
22-
#DocMeta.setdocmeta!(SymPyPythonCall, :DocTestSetup, :(using SymPyPythonCall); recursive=true)
23-
24-
# makedocs(;
25-
# modules=[SymPyPythonCall],
26-
# authors="jverzani <jverzani@gmail.com> and contributors",
27-
# repo="https://github.com/jverzani/SymPyPythonCall.jl/blob/{commit}{path}#{line}",
28-
# sitename="SymPyPythonCall.jl",
29-
# format=Documenter.HTML(;
30-
# prettyurls=get(ENV, "CI", "false") == "true",
31-
# canonical="https://jverzani.github.io/SymPyPythonCall.jl",
32-
# assets=String[],
33-
# ),
34-
# pages=[
35-
# "Home" => "index.md",
36-
# "Examples" => "introduction.md"
37-
# ],
38-
# )
39-
40-
# # deploydocs(;
41-
# repo="github.com/jverzani/SymPyPythonCall.jl",
42-
# devbranch="main",
43-
# )

ext/SymPyPythonCallSymbolicUtilsExt.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,10 @@ Returns the head (a function object) performed by an expression tree. Called onl
2222
==#
2323
function SymbolicUtils.operation(x::SymPyPythonCall.SymbolicObject)
2424
@assert SymbolicUtils.istree(x)
25-
nm = Symbol(SymPyPythonCall.Introspection.funcname(x))
26-
25+
nm = SymPyPythonCall.Introspection.funcname(x)
2726
λ = get(SymPyPythonCall.Introspection.funcname2function, nm, nothing)
2827
if isnothing(λ)
29-
return getfield(Main, nm)
28+
return getfield(Main, Symbol(nm))
3029
else
3130
return λ
3231
end

src/SymPyPythonCall.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ include("conveniences.jl")
3030
include("logical.jl")
3131
include("matrix.jl")
3232
include("assumptions.jl")
33-
include("patternmatch.jl")
34-
include("introspection.jl")
3533
include("lambdify.jl")
34+
include("introspection.jl")
35+
include("patternmatch.jl")
3636
include("plot_recipes.jl")
3737
include("latexify_recipe.jl")
3838
# export

src/introspection.jl

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
## Module for Introspection
33
module Introspection
44

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

50+
# map funcname value (a string) to a function object
51+
# special cases are in lambidfy.jl
52+
const funcname2function = Dict(k => first(v) for (k,v) pairs(sympy_fn_julia_fn))
53+
54+
5055
#function getmembers(x::T) where {T <: Union{Sym, PyObject}}
5156
# Dict(u=>v for (u,v) in inspect.getmembers(x))
5257
#end
5358

54-
## Map to get function object from type information
55-
const funcname2function = (
56-
Add = +,
57-
Sub = -,
58-
Mul = *,
59-
Div = /,
60-
Pow = ^,
61-
re = real,
62-
im = imag,
63-
Abs = abs,
64-
Min = min,
65-
Max = max,
66-
Poly = identity,
67-
Piecewise = error, # replace
68-
Order = (as...) -> 0,
69-
And = (as...) -> all(as),
70-
Or = (as...) -> any(as),
71-
Less = <,
72-
LessThan = <=,
73-
StrictLessThan = <,
74-
Equal = ==,
75-
Equality = ==,
76-
Unequality = !==,
77-
StrictGreaterThan = >,
78-
GreaterThan = >=,
79-
Greater = >,
80-
conjugate = conj,
81-
atan2 = atan,
82-
TupleArg = tuple,
83-
Heaviside = (a...) -> (a[1] < 0 ? 0 : (a[1] > 0 ? 1 : (length(a) > 1 ? a[2] : NaN))),
84-
)
8559

8660
end

src/lambdify.jl

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,42 +36,48 @@ end
3636
## As of newer sympy versions, this is no longer needed.
3737
_PROD_(args...) = prod(args)
3838

39-
_ANY_(xs...) = any(xs)
40-
_ALL_(xs...) = all(xs)
41-
_ZERO_(xs...) = 0
39+
_ANY_(xs...) = any(xs) # any∘tuple ?
40+
_ALL_(xs...) = all(xs) # all∘tuple
41+
_ZERO_(xs...) = 0 #
4242
# not quite a match; NaN not θ(0) when evaluated at 0 w/o second argument
43-
_HEAVISIDE_ = (a...) -> (a[1] < 0 ? 0 : (a[1] > 0 ? 1 : (length(a) > 1 ? a[2] : NaN)))
44-
# _SYMPY_ALL_,
45-
fn_map = Dict(
46-
"Add" => :+,
47-
"Sub" => :-,
48-
"Mul" => :((as...)->prod(as)), #:*, # :(SymPy._PROD_)
49-
"Div" => :/,
50-
"Pow" => :^,
51-
"re" => :real,
52-
"im" => :imag,
53-
"Abs" => :abs,
54-
"Min" => :min,
55-
"Max" => :max,
56-
"Poly" => :identity,
57-
"Piecewise" => :(SymPyPythonCall._piecewise),
58-
"Order" => :(SymPyPythonCall._ZERO_), # :(as...) -> 0,
59-
"And" => :(SymPyPythonCall._ALL_), #:((as...) -> all(as)), #:(&),
60-
"Or" => :(SymPyPythonCall._ANY_), #:((as...) -> any(as)), #:(|),
61-
"Less" => :(<),
62-
"LessThan" => :(<=),
63-
"StrictLessThan" => :(<),
64-
"Equal" => :(==),
65-
"Equality" => :(==),
66-
"Unequality" => :(!==),
67-
"StrictGreaterThan" => :(>),
68-
"GreaterThan" => :(>=),
69-
"Greater" => :(>),
70-
"conjugate" => :conj,
71-
"atan2" => :atan,
72-
"Heaviside" => :(SymPyPythonCall._HEAVISIDE_),
43+
_HEAVISIDE_(a...) = (a[1] < 0 ? 0 : (a[1] > 0 ? 1 : (length(a) > 1 ? a[2] : NaN)))
44+
45+
## Map to get function object from type information
46+
# we may want fn or expression, Symbol(+) yields :+ but allocates to make a string
47+
sympy_fn_julia_fn = Dict(
48+
"Add" => (+, :+),
49+
"Sub" => (-, :-),
50+
"Mul" => (*, :*),
51+
"Div" => (/, :/),
52+
"Pow" => (^, :^),
53+
"re" => (real, :real),
54+
"im" => (imag, :imag),
55+
"Abs" => (abs, :abs),
56+
"Min" => (min, :min),
57+
"Max" => (max, :max),
58+
"Poly" => (identity, :identity),
59+
"conjugate" => (conj, :conj),
60+
"atan2" => (atan, :atan),
61+
#
62+
"Less" => (<, :(<)),
63+
"LessThan" => (<=, :(<=)),
64+
"StrictLessThan" => (<, :(<)),
65+
"Equal" => (==, :(==)),
66+
"Equality" => (==, :(==)),
67+
"Unequality" => (!==, :(!==)),
68+
"StrictGreaterThan" => (>, :(>)),
69+
"GreaterThan" => (>=, :(>=)),
70+
"Greater" => (>, :(>)),
71+
#
72+
"Piecewise" => (SymPyPythonCall._piecewise, :(SymPyPythonCall._piecewise)),
73+
"Heaviside" => (SymPyPythonCall._HEAVISIDE_, :(SymPyPythonCall._HEAVISIDE_)),
74+
"Order" => (SymPyPythonCall._ZERO_, :(SymPyPythonCall._ZERO_)),
75+
"And" => (alltuple, :(SymPyPythonCall._ALL_)),
76+
"Or" => (anytuple, :(SymPyPythonCall._ANY_)),
7377
)
7478

79+
const fn_map = Dict(k => last(v) for (k,v) pairs(sympy_fn_julia_fn))
80+
7581
map_fn(key, fn_map) = haskey(fn_map, key) ? fn_map[key] : Symbol(key)
7682

7783
Base.convert(::Type{Expr}, x::SymbolicObject) = walk_expression(x)
@@ -210,7 +216,24 @@ This function will be about 2-3 times slower than `f`.
210216
function lambdify(ex::Sym, vars=free_symbols(ex);
211217
fns=Dict(), values=Dict(),
212218
use_julia_code=false,
213-
invoke_latest=true)
219+
invoke_latest=true)
220+
if isempty(vars)
221+
# can't call N(ex) here...
222+
flag = pygetattr(ex, "evalf", nothing)
223+
if isnothing(flag)
224+
val = pyconvert(Real, ex)
225+
else
226+
v = ex.evalf()
227+
if pyconvert(Bool, v.is_real)
228+
val = pyconvert(Real, v)
229+
else
230+
a,b = pyconvert.(Real, (real(v), imag(v)))
231+
val = Complex(a, b)
232+
end
233+
end
234+
return (ts...) -> val
235+
end
236+
214237
body = convert_expr(ex, fns=fns, values=values, use_julia_code=use_julia_code)
215238
ex = expr_to_function(body, vars)
216239
if invoke_latest

test/tests.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,13 @@ end
633633
@test u(.5) == 1
634634
@test u(1.5) == 0
635635

636+
# SymPy issue 567; constants
637+
u = lambdify(Sym(1//2))
638+
@test u() == u(1,2,3) == 1/2
639+
@syms x
640+
ex = integrate(sqrt(1 + (1/x)^2), (x, 1/sympy.E, sympy.E))
641+
@test lambdify(ex)() 3.1961985135995072
642+
636643
# i2 = SymPy.lambdify_expr(x^2,name=:square)
637644
# @test i2.head == :function
638645
# @test i2.args[1].args[1] == :square

0 commit comments

Comments
 (0)