From ff91a839e34e6a72deaa95e75a7954cfd0e496e6 Mon Sep 17 00:00:00 2001 From: john verzani Date: Thu, 28 Feb 2019 15:52:06 -0500 Subject: [PATCH] adjust to use getproperty over getindex with PyObject values (#254) * adjust to use getproperty over getindex with PyObject values * adjust to getproperty interface --- README.md | 76 +++++++++++++++++---------------------- REQUIRE | 2 +- src/SymPy.jl | 21 +++++------ src/assumptions.jl | 18 +++++----- src/core.jl | 11 +++--- src/display.jl | 10 +++--- src/dsolve.jl | 6 ++-- src/integrate.jl | 8 ++--- src/lambdify.jl | 2 +- src/logical.jl | 6 ++-- src/math.jl | 20 +++++------ src/mathops.jl | 8 ++--- src/matrix.jl | 16 ++++----- src/mpmath.jl | 21 ++++++----- src/patternmatch.jl | 16 ++++----- src/permutations.jl | 38 ++++++++++---------- src/physics.jl | 2 +- src/plot_recipes.jl | 2 +- src/poly.jl | 4 +-- src/sets.jl | 64 ++++++++++++++++----------------- src/subs.jl | 44 +++++++++++------------ src/types.jl | 8 ++--- src/utils.jl | 26 +++++++++++--- test/test-permutations.jl | 62 ++++++++++++++++---------------- test/tests.jl | 18 +++++----- 25 files changed, 252 insertions(+), 257 deletions(-) diff --git a/README.md b/README.md index db766228..f41eb50a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![SymPy](http://pkg.julialang.org/badges/SymPy_0.7.svg)](http://pkg.julialang.org/?pkg=SymPy&ver=0.7) +[![SymPy](http://pkg.julialang.org/badges/SymPy_0.7.svg)](http://pkg.julialang.org/?pkg=SymPy&ver=0.7) Linux: [![Build Status](https://travis-ci.org/JuliaPy/SymPy.jl.svg?branch=master)](https://travis-ci.org/JuliaPy/SymPy.jl)   @@ -10,7 +10,7 @@ Windows: [![Build Status](https://ci.appveyor.com/api/projects/status/github/Jul -The `SymPy` package (`http://sympy.org/`) is a Python library for symbolic mathematics. +The `SymPy` package (`http://sympy.org/`) is a Python library for symbolic mathematics. With the excellent `PyCall` package of `julia`, one has access to the many features of `SymPy` from within a `Julia` session. @@ -44,37 +44,37 @@ The only point to this package is that using `PyCall` to access a symbolic value `x`, take its sine, then evaluate at `pi`, say: ``` -using PyCall -@pyimport sympy +using PyCall +sympy = pyimport("sympy") x = sympy.Symbol("x") y = sympy.sin(x) -y[:subs](x, sympy.pi) |> float +z = y.subs(x, sympy.pi) +convert(Float64, z) ``` The `Symbol` and `sin` function of `SymPy` are found within the imported `sympy` object. They may be referenced with `Python`'s dot -notation. However, the `subs` method of the `y` object is accessed -differently, using indexing notation with a symbol. The call above -substitutes a value of `sympy.pi` for `x`. This leaves the object as a -`PyObject` storing a number which can be brought back into `julia` -through conversion, in this case with the `float` function. +notation. Similarly, the `subs` method of the `y` object may be +accessed with Python's dot nottation using PyCall's `getproperty` +overload to call the method. The call above substitutes a value of +`sympy.pi` for `x`. This leaves the object as a `PyObject` storing a +number which can be brought back into `julia` through conversion, in +this case through an explicit `convert` call. -The above isn't so awkward, but even more cumbersome is the similarly -simple task of finding `sin(pi*x)`. As this multiplication is done at -the python level and is not a method of `sympy` or the `x` object, we -need to evaluate python code. Here is one solution: + +Alternatively, `PyCall` now has a `*` method, so this could be done with ``` x = sympy.Symbol("x") -y = pycall(sympy.Mul, PyAny, sympy.pi, x) -z = sympy.sin(y) -z[:subs](x, 1) |> float +y = sympy.pi * x +z = sympy.sin(y) +convert(Float64, z.subs(x, 1)) ``` -This gets replaced by a more `julia`n syntax: +With `SymPy` this gets replaced by a more `julia`n syntax: ``` -using SymPy +using SymPy x = symbols("x") # or @vars x, Sym("x"), or Sym(:x) y = sin(pi*x) y(1) # Does subs(y, x, 1). Use y(x=>1) to be specific as to which symbol to substitute @@ -86,31 +86,26 @@ that working with symbolic expressions can use natural `julia` idioms. The final result here is a symbolic value of `0`, which prints as `0` and not `PyObject 0`. To convert it into a numeric value within `Julia`, the `N` function may be used, which acts like the -`float` call, only there is an attempt to preserve the variable type. +float conversion, only there is an attempt to preserve the variable type. -(There is a subtlety, the value of `pi` here (an `Irrational` in `Julia`) is converted to the -symbolic `PI`, but in general won't be if the math constant is coerced -to a floating point value before it encounters a symbolic object. It -is better to just use the symbolic value `PI`, an alias for `sympy.pi` -used above. A similar comment applies for `e`, where `E` should be -used.) +(There is a subtlety, the value of `pi` here (an `Irrational` in +`Julia`) is converted to the symbolic `PI`, but in general won't be if +the math constant is coerced to a floating point value before it +encounters a symbolic object. It is better to just use the symbolic +value `PI`, an alias for `sympy.pi` used above. A similar comment +applies for `e`, where `E` should be used.) However, for some tasks the `PyCall` interface is still needed, as only a portion of the `SymPy` interface is exposed. To call an -underlying SymPy method, the `getindex` method is overloaded for -`symbol` indices so that `ex[:meth_name](...)` dispatches to either to -SymPy's `ex.meth_name(...)` or `meth_name(ex, ...)`, as possible. - +underlying SymPy method, the `getproperty` method is overloaded so +that `ex.meth_name(...)` dispatches the method of the object and +`sympy.meth_name(...)` calls a function in the SymPy module. -There is a `sympy` string macro to simplify this a bit, with the call -looking like: `sympy"meth_name"(...)`, for example -`sympy"harmonic"(10)`. For another example, the above could also be done -through: +For example, we could have been more explicit and used: ``` -@vars x -y = sympy"sin"(pi * x) -y(1) +y = sympy.sin(pi * x) +y.subs(x, 1) ``` As calling the underlying SymPy function is not difficult, the @@ -136,14 +131,9 @@ det(a) Can be replaced with ``` -sympy"det"(a) +sympy.det(a) ``` Similarly for `trace`, `eigenvects`, ... . Note these are `sympy` methods, not `Julia` methods that have been ported. (Hence, `eigenvects` and not `eigvecs`.) - - - - - diff --git a/REQUIRE b/REQUIRE index 68b26a03..c5fe8ace 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,4 +1,4 @@ julia 0.7.0 -PyCall 1.7.1 +PyCall 1.90.0 RecipesBase 0.5.0 SpecialFunctions 0.3.4 diff --git a/src/SymPy.jl b/src/SymPy.jl index 992571d4..4038c2a7 100644 --- a/src/SymPy.jl +++ b/src/SymPy.jl @@ -241,7 +241,7 @@ for prop in union(core_object_properties, polynomial_predicates) prop_name = string(prop) - @eval ($prop)(ex::SymbolicObject) = PyObject(ex)[Symbol($prop_name)] + @eval ($prop)(ex::SymbolicObject) = getproperty(PyObject(ex), Symbol($prop_name)) eval(Expr(:export, prop)) end @@ -258,7 +258,7 @@ global call_sympy_fun(fn::PyCall.PyObject, args...; kwargs...) = PyCall.pycall(f ## These get coverted to PyAny for conversion via ## PyCall, others directly call `Sym`, as it is faster function sympy_meth(meth, args...; kwargs...) - ans = call_sympy_fun(sympy[string(meth)], args...; kwargs...) + ans = call_sympy_fun(getproperty(sympy, Symbol(string(meth))), args...; kwargs...) ## make nicer... try if isa(ans, Vector) @@ -277,7 +277,7 @@ end # pybuiltin(:int) and iterables # ## also PyObject(pybuiltin(:None)) function _sympy_meth(meth, args...; kwargs...) - out = PyCall.pycall(sympy[string(meth)], PyCall.PyObject, args...; kwargs...) + out = PyCall.pycall(getproperty(sympy, Symbol(string(meth))), PyCall.PyObject, args...; kwargs...) Sym(out) end @@ -333,7 +333,7 @@ end global object_meth(object::SymbolicObject, meth, args...; kwargs...) = begin - meth_or_prop = PyObject(object)[Symbol(meth)] + meth_or_prop = getproperty(PyObject(object),Symbol(meth)) if isa(meth_or_prop, PyCall.PyObject) call_sympy_fun(meth_or_prop, args...; kwargs...) # method else @@ -353,19 +353,20 @@ function __init__() ## mappings from PyObjects to types. copy!(combinatorics, PyCall.pyimport_conda("sympy.combinatorics", "sympy")) - pytype_mapping(combinatorics["permutations"]["Permutation"], SymPermutation) - pytype_mapping(combinatorics["perm_groups"]["PermutationGroup"], SymPermutationGroup) - polytype = sympy["polys"]["polytools"]["Poly"] + pytype_mapping(combinatorics."permutations"."Permutation", SymPermutation) + pytype_mapping(combinatorics."perm_groups"."PermutationGroup", SymPermutationGroup) + polytype = sympy.polys.polytools.Poly pytype_mapping(polytype, Sym) try - pytype_mapping(sympy["Matrix"], Array{Sym}) - pytype_mapping(sympy["matrices"]["MatrixBase"], Array{Sym}) + pytype_mapping(sympy."Matrix", Array{Sym}) + pytype_mapping(sympy."matrices"."MatrixBase", Array{Sym}) catch e end - basictype = sympy["basic"]["Basic"] + ## need "" here, as basictype = sympy.basic.Basic will not convert + basictype = sympy."basic"."Basic" pytype_mapping(basictype, Sym) diff --git a/src/assumptions.jl b/src/assumptions.jl index a179dade..2af920f9 100644 --- a/src/assumptions.jl +++ b/src/assumptions.jl @@ -70,7 +70,7 @@ ask(Q.positive(1 + x^2) # true -- must be postive now. The ask function uses tri-state logic, returning one of 3 values: `true`; `false`; or `nothing`, when the query is indeterminate. - + The construction of predicates is done through `Q` methods. These can be combined logically. For example, this will be `true`: @@ -85,7 +85,7 @@ The above use `&` as an infix operation for the binary operator Note: As `SymPy.jl` converts symbolic matrices into Julia's `Array` type and not as matrices within Python, the predicate functions from SymPy for matrices are not used, though a replacement is given. -""" +""" module Q import SymPy import PyCall @@ -143,7 +143,7 @@ for meth in Q_predicates # `$($nm)`: a SymPy function. # The SymPy documentation can be found through: http://docs.sympy.org/latest/search.html?q=$($nm) # """ -> - ($meth)(x) = PyCall.pycall(SymPy.sympy["Q"][$nm], SymPy.Sym, x)::SymPy.Sym + ($meth)(x) = PyCall.pycall(SymPy.sympy.Q.$nm, SymPy.Sym, x)::SymPy.Sym end end @@ -190,7 +190,7 @@ function orthogonal(M::Array{T,2}) where {T <: SymPy.Sym} no_nothing > 0 && return nothing return true end - + function unitary(M::Array{T,2}) where {T <: SymPy.Sym} vals = SymPy.simplify.(SymPy.simplify.(M*ctranspose(M)) .== one(T)) @@ -216,7 +216,7 @@ function normal(M::Array{T,2}) where {T <: SymPy.Sym} for val in vals a = SymPy.ask(val) if a == nothing - no_nothing += 1 + no_nothing += 1 elseif a == false return false end @@ -250,7 +250,7 @@ end upper_triangular(M::Array{T,2}) where {T <: SymPy.Sym} = SymPy.istriu(M) -lower_triangular(M::Array{T,2}) where {T <: SymPy.Sym} = SymPy.istril(M) +lower_triangular(M::Array{T,2}) where {T <: SymPy.Sym} = SymPy.istril(M) diagonal(M::Array{T,2}) where {T <: SymPy.Sym} = upper_triangular(M) && lower_triangular(M) triangular(M::Array{T,2}) where {T <: SymPy.Sym} = upper_triangular(M) || lower_triangular(M) @@ -260,7 +260,7 @@ function full_rank(M::Array{T,2}) where {T <: SymPy.Sym} m,n = size(M) m <= n || return full_rank(transpose(M)) - + rr, p = SymPy.rref(M) lr = rr[end, :] # is this zero? no_nothing = 0 @@ -281,7 +281,7 @@ function full_rank(M::Array{T,2}) where {T <: SymPy.Sym} else return true end - + end @@ -304,7 +304,7 @@ end function complex_elements(M::Array{T,2}) where {T <: SymPy.Sym} vals = real.(M) for val in vals - a = SymPy.ask(SymPy.sympy["Q"][:complex](val)) + a = SymPy.ask(SymPy.sympy."Q".complex(val)) (a == nothing || a == false) && return false end return true diff --git a/src/core.jl b/src/core.jl index c72e7930..b63c19f5 100644 --- a/src/core.jl +++ b/src/core.jl @@ -2,7 +2,7 @@ core_object_methods = (:as_poly, :atoms, :compare, :compare_pretty, :doit, :dummy_eq, # :count, - :has, + :has, :sort_key, :args_cnc, :as_coeff_Add, :as_coeff_Mul, :as_coeff_add, @@ -55,8 +55,8 @@ x = Sym("x") Eq(x, sin(x)) |> rhs ## sin(x) ``` """ -rhs(ex::Sym, args...; kwargs...) = PyObject(ex)[:rhs] -lhs(ex::Sym, args...; kwargs...) = PyObject(ex)[:lhs] +rhs(ex::Sym, args...; kwargs...) = PyObject(ex).rhs +lhs(ex::Sym, args...; kwargs...) = PyObject(ex).lhs @@ -65,9 +65,9 @@ lhs(ex::Sym, args...; kwargs...) = PyObject(ex)[:lhs] Return a vector of free symbols in an expression """ function free_symbols(ex::Union{T, Vector{T}}) where {T<:SymbolicObject} - fs = PyObject(ex)[:free_symbols] + fs = PyObject(ex).free_symbols ## are these a set? - if fs[:__class__][:__name__] == "set" + if fs.__class__.__name__ == "set" convert(Vector{Sym}, collect(fs)) else Sym[] @@ -77,4 +77,3 @@ end free_symbols(exs::Tuple) = free_symbols(Sym[ex for ex in exs]) export free_symbols - diff --git a/src/display.jl b/src/display.jl index a58ba53a..62d2b18b 100644 --- a/src/display.jl +++ b/src/display.jl @@ -12,15 +12,15 @@ Examples @vars x import Base.Docs.doc doc(sin(x)) # -doc(sympy[:sin]) # explicit module lookup -doc(SymPy.mpmath[:hypercomb]) # explicit module lookup +doc(sympy.sin) # explicit module lookup +doc(SymPy.mpmath.hypercomb) # explicit module lookup doc(Poly(x^2,x), :coeffs) # coeffs is an object method of the poly instance doc([x 1;1 x], :LUsolve) # LUsolve is a matrix method ``` """ Base.Docs.doc(x::SymbolicObject) = Base.Docs.doc(PyObject(x)) Base.Docs.doc(x::SymbolicObject, s::Symbol) = Base.Docs.doc(PyObject(x)[s]) -Base.Docs.doc(x::Array{T,N}, s::Symbol) where {T <: SymbolicObject, N} = Base.Docs.doc(PyObject(x)[s]) +Base.Docs.doc(x::Array{T,N}, s::Symbol) where {T <: SymbolicObject, N} = Base.Docs.doc(PyObject(x).s) ## Add some of SymPy's displays ## Some pretty printing @@ -29,7 +29,7 @@ Base.Docs.doc(x::Array{T,N}, s::Symbol) where {T <: SymbolicObject, N} = Base.Do #doc(x::SymbolicObject) = print(x[:__doc__]) "Map a symbolic object to a string" -_str(s::SymbolicObject) = s[:__str__]() +_str(s::SymbolicObject) = s.__str__() "Map an array of symbolic objects to a string" _str(a::AbstractArray{SymbolicObject}) = map(_str, a) @@ -55,7 +55,7 @@ Base.show(io::IO, s::Sym) = print(io, jprint(s)) ## We add show methods for the REPL (text/plain) and IJulia (text/latex) ## text/plain -show(io::IO, ::MIME"text/plain", s::SymbolicObject) = print(io, sympy["pretty"](s)) +show(io::IO, ::MIME"text/plain", s::SymbolicObject) = print(io, sympy.pretty(s)) show(io::IO, ::MIME"text/latex", x::Sym) = print(io, latex(x, mode="equation*")) function show(io::IO, ::MIME"text/latex", x::AbstractArray{Sym}) diff --git a/src/dsolve.jl b/src/dsolve.jl index 9ed27020..652da046 100644 --- a/src/dsolve.jl +++ b/src/dsolve.jl @@ -36,11 +36,11 @@ F,G,H = SymFunction("F, G, H") function SymFunction(x::T) where {T<:AbstractString} us = split(x, r",\s*") if length(us) > 1 - map(u -> SymFunction(sympy["Function"](u), 0), us) + map(u -> SymFunction(sympy."Function"(u), 0), us) else - SymFunction(sympy["Function"](x), 0) + SymFunction(sympy."Function"(x), 0) end -# u = sympy["Function"](x) +# u = sympy."Function"(x) # SymFunction(u, 0) end diff --git a/src/integrate.jl b/src/integrate.jl index a0b9a48e..d4032f81 100644 --- a/src/integrate.jl +++ b/src/integrate.jl @@ -32,7 +32,7 @@ integrals_instance_methods = (:as_sum, """ Create a parameterized curve for line integrals -[SymPy Documentation](http://docs.sympy.org/dev/modules/integrals/integrals.html#sympy.integrals.line_integrate) +[SymPy Documentation](http://docs.sympy.org/dev/modules/integrals/integrals.html#sympy.integrals.line_integrate) ``` @vars t x y C = Curve([exp(t)+1, exp(t)-1], (t, 0, log(Sym(2)))) @@ -40,7 +40,7 @@ line_integrate(x + y, C, [x,y]) ``` """ -Curve(exs::Vector{T}, p) where {T<:Sym} = sympy_meth(:Curve, exs, p) +Curve(exs::Vector{T}, p) where {T<:Sym} = sympy_meth(:Curve, exs, p) export Curve """ @@ -48,7 +48,7 @@ Dirac delta for integration [SymPy Documentation](http://docs.sympy.org/dev/modules/functions/special.html) """ -DiracDelta(x::Number) = pycall(sympy["DiracDelta"], PyAny, x) +DiracDelta(x::Number) = pycall(sympy.DiracDelta, PyAny, x) export DiracDelta """ @@ -58,7 +58,7 @@ Heaviside function for integration. [SymPy Documentation](http://docs.sympy.org/dev/modules/functions/special.html) """ -Heaviside(x::Number) = pycall(sympy["Heaviside"], PyAny, x) +Heaviside(x::Number) = pycall(sympy.Heaviside, PyAny, x) export Heaviside diff --git a/src/lambdify.jl b/src/lambdify.jl index b380f41e..d780120b 100644 --- a/src/lambdify.jl +++ b/src/lambdify.jl @@ -2,7 +2,7 @@ ## https://github.com/jverzani/SymPy.jl/issues/60 ## some tools, perhaps. Not exported for now. -funcname(x) = PyObject(x)[:func][:__name__] +funcname(x) = PyObject(x).func.__name__ srepr(x) = sympy_meth(:srepr, x) ## Mapping of SymPy Values into julia values diff --git a/src/logical.jl b/src/logical.jl index af2d375f..24cba316 100644 --- a/src/logical.jl +++ b/src/logical.jl @@ -25,9 +25,9 @@ end ## XXX Experimental! Not sure these are such a good idea ... ## but used with piecewise -Base.:&(x::Sym, y::Sym) = PyCall.pycall(PyObject(x)["__and__"], Sym, y) -Base.:|(x::Sym, y::Sym) = PyCall.pycall(PyObject(x)["__or__"], Sym, y) -!(x::Sym) = PyCall.pycall(x.x["__invert__"], Sym)::Sym +Base.:&(x::Sym, y::Sym) = PyCall.pycall(PyObject(x).__and__, Sym, y) +Base.:|(x::Sym, y::Sym) = PyCall.pycall(PyObject(x).__or__, Sym, y) +!(x::Sym) = PyCall.pycall(PyObject(x).__invert__, Sym)::Sym ## use ∨, ∧, ¬ for |,&,! (\vee, \wedge, \neg) ∨(x::Sym, y::Sym) = x | y diff --git a/src/math.jl b/src/math.jl index 05b86a55..d3ddbf01 100644 --- a/src/math.jl +++ b/src/math.jl @@ -40,12 +40,12 @@ for fn in (:cosd, :cotd, :cscd, :secd, :sind, :tand, :acosd, :acotd, :acscd, :asecd, :asind, :atand) rad_fn = string(fn)[1:end-1] - @eval ($fn)(x::Sym) = sympy[Symbol($rad_fn)](x * Sym(sympy["pi"])/180) + @eval ($fn)(x::Sym) = sympy.$rad_fn(x * Sym(sympy.pi)/180) end for fn in (:cospi, :sinpi) rad_fn = string(fn)[1:end-2] - @eval ($fn)(x::Sym) = sympy[Symbol($rad_fn)](x * Sym(sympy["pi"])) + @eval ($fn)(x::Sym) = sympy.$rad_fn(x * Sym(sympy.pi)) end Base.sincos(x::Sym) = (sin(x), cos(x)) @@ -224,8 +224,8 @@ Base.isinf(x::Sym) = try isinf(convert(Float64, x)) catch e false end Base.isnan(x::Sym) = try isnan(convert(Float64, x)) catch e false end ## we rename sympy.div -> polydiv -Base.div(x::Sym, y::SymOrNumber) = convert(Sym, sympy["floor"](x/convert(Sym,y))) -Base.rem(x::Sym, y::SymOrNumber) = x-Sym(y)*Sym(sympy["floor"](x/y)) +Base.div(x::Sym, y::SymOrNumber) = convert(Sym, sympy.floor(x/convert(Sym,y))) +Base.rem(x::Sym, y::SymOrNumber) = x-Sym(y)*Sym(sympy.floor.(x/y)) ## zero and one (zeros?) Base.zero(x::Sym) = Sym(0) @@ -300,20 +300,20 @@ global oo = Sym(pynull()) Base.convert(::Type{Sym}, x::Irrational{:π}) = PI Base.convert(::Type{Sym}, x::Irrational{:e}) = E -Base.convert(::Type{Sym}, x::Irrational{:γ}) = Sym(sympy["EulerGamma"]) -Base.convert(::Type{Sym}, x::Irrational{:catalan}) = Sym(sympy["Catalan"]) +Base.convert(::Type{Sym}, x::Irrational{:γ}) = Sym(sympy.EulerGamma) +Base.convert(::Type{Sym}, x::Irrational{:catalan}) = Sym(sympy.Catalan) Base.convert(::Type{Sym}, x::Irrational{:φ}) = (1 + Sym(5)^(1//2))/2 function init_math() "PI is a symbolic π. Using `julia`'s `pi` will give round off errors." - copy!(PI.x, sympy["pi"]) + copy!(PI.x, PyObject(sympy.pi)) "E is a symbolic `e`. Using `julia`'s `e` will give round off errors." - copy!(E.x, Sym(sympy["exp"](1)).x) + copy!(E.x, PyObject(sympy.exp(1))) "IM is a symbolic `im`" - copy!(IM.x, sympy["I"]) + copy!(IM.x, PyObject(sympy.I)) "oo is a symbolic infinity. Example: `integrate(exp(-x), x, 0, oo)`." - copy!(oo.x, sympy["oo"]) + copy!(oo.x, PyObject(sympy.oo)) end diff --git a/src/mathops.jl b/src/mathops.jl index e585716e..c67ff1ab 100644 --- a/src/mathops.jl +++ b/src/mathops.jl @@ -1,11 +1,11 @@ ## evaluate binary operations of symbolic objects ## XXX -- this may prove too narryw with the use of ::Sym -+(x::SymbolicObject, y::SymbolicObject) = pycall(sympy["Add"], Sym, x, y) -*(x::SymbolicObject, y::SymbolicObject) = pycall(sympy["Mul"], Sym, x, y) ++(x::SymbolicObject, y::SymbolicObject) = pycall(sympy.Add, Sym, x, y) +*(x::SymbolicObject, y::SymbolicObject) = pycall(sympy.Mul, Sym, x, y) -(x::SymbolicObject, y::SymbolicObject) = x + (-y) -(x::SymbolicObject) = (-1) * x /(x::SymbolicObject, y::SymbolicObject) = x * inv(y) -^(x::SymbolicObject, y::SymbolicObject) = pycall(sympy["Pow"], Sym, x, y)::Sym +^(x::SymbolicObject, y::SymbolicObject) = pycall(sympy.Pow, Sym, x, y)::Sym ^(x::SymbolicObject, y::Rational) = x^convert(Sym,y) #^(x::SymbolicObject, y::Integer) = x^convert(Sym,y) # no Union{Integer, Rational}, as that has ambiguity //(x::SymbolicObject, y::Int) = x / Sym(y) @@ -17,4 +17,4 @@ #inv(x::Sym) = x\one(x) #inv(x::Sym) = x^(-1) -inv(x::Sym) = pycall(sympy["Pow"], Sym, x, -1) +inv(x::Sym) = pycall(sympy.Pow, Sym, x, -1) diff --git a/src/matrix.jl b/src/matrix.jl index eab2a098..788097bd 100644 --- a/src/matrix.jl +++ b/src/matrix.jl @@ -5,7 +5,7 @@ ## this is achieved with the convert(Array{Sym}, o::PyObject) method below. ## Array{Sym} objects are converted into Python objects via -PyCall.PyObject(a::AbstractArray{Sym}) = pycall(sympy[:Matrix], PyObject, PyCall.array2py(a)) +PyCall.PyObject(a::AbstractArray{Sym}) = pycall(sympy.Matrix, PyObject, PyCall.array2py(a)) ## Matrix methods and objects @@ -13,7 +13,7 @@ PyCall.PyObject(a::AbstractArray{Sym}) = pycall(sympy[:Matrix], PyObject, PyCall ## For calling methods we have call_matrix_meth(M, :meth, ...) for M.meth(...) ## This helps, grabbing the M.meth part function getindex(s::AbstractArray{Sym}, i::Symbol) - PyObject(s)[i] + getproperty(PyObject(s),i) end @@ -21,15 +21,15 @@ end # though this may need to be done specially for some arguments that are passed in as collections global call_matrix_meth(object, meth, args...; kwargs...) = begin o = PyObject(object) - o[meth](args...; kwargs...) + getproperty(o,meth)(args...; kwargs...) end ## support for convert(Array{Sym}, o::PyObject) function matrix_size(x::PyObject) - a = x[:shape] # tuple + a = x.shape # tuple if isa(a, PyObject) - a = (a[:__getitem__](0), a[:__getitem__](1)) + a = (a.__getitem__(0), a.__getitem__(1)) end while a[end] == 1 a = a[1:end-1] @@ -154,7 +154,7 @@ for meth in matrix_properties # `$($meth_name)`: a SymPy function. # The SymPy documentation can be found through: http://docs.sympy.org/latest/search.html?q=$($meth_name) # """ -> - ($meth)(ex::Matrix{Sym}, args...; kwargs...) = ex[Symbol($meth_name)] + ($meth)(ex::Matrix{Sym}, args...; kwargs...) = getproperty(PyObject(ex),Symbol($meth_name)) end eval(Expr(:export, meth)) end @@ -176,7 +176,7 @@ function eigvecs(a::Matrix{Sym}) end out end -rref(a::Matrix{T}) where {T <: Integer} = N(rref(convert(Matrix{Sym}, a))) +rref(a::Matrix{T}) where {T <: Integer} = N(rref(convert(Matrix{Sym}, a))) rref(a::Matrix{Rational{T}}) where {T <: Integer} = N(rref(convert(Matrix{Sym}, a))) """ @@ -224,5 +224,3 @@ wronskian([u,v], x) # determinant of [u v; u' v'] wronskian(fs::Vector{T}, x::Sym, args...; kwargs...) where {T <: SymbolicObject} = sympy_meth(:wronskian, fs, x, args...; kwargs...) export GramSchmidt, hessian, wronskian - - diff --git a/src/mpmath.jl b/src/mpmath.jl index f6b801ff..36483fb7 100644 --- a/src/mpmath.jl +++ b/src/mpmath.jl @@ -2,8 +2,8 @@ ## These are not right!!! ## see hyper and meijerg to indicate what needs to be done for these special function ## they really need to be coordinated with `Julia`'s as well. -mpmath_fns = (:hyp0f1, - :hyp1f1, :hyp1f2, +mpmath_fns = (:hyp0f1, + :hyp1f1, :hyp1f2, :hyp2f0, :hyp2f1, :hyp2f2, :hyp2f3, :hyp3f2, :hypercomb, @@ -17,7 +17,7 @@ mpmath_fns = (:hyp0f1, :webere, :coulombc, :legenp, :legenq, - :chebyt, :chebyu, + :chebyt, :chebyu, :pcfd, :pcfu, :pcfv, :pcfw, :lommels1, :lommels2, :coulombf, :coulombg, @@ -25,7 +25,7 @@ mpmath_fns = (:hyp0f1, :whitm, :whitw, :scorergi, :scorerhi, :spherharm, - :airyaizero, :airybizero, + :airyaizero, :airybizero, :besseljzero, :besselyzero ) for fn in mpmath_fns @@ -36,13 +36,13 @@ end ## Call a function in the mpmath module, giving warning and returning NaN if module is not found ## (Doesn't need to be in init_mpmath?) -function mpmath_meth(meth, args...; kwargs...) +function mpmath_meth(meth, args...; kwargs...) if isa(mpmath, Nothing) warn("The mpmath module of Python is not installed. http://docs.sympy.org/dev/modules/mpmath/setup.html#download-and-installation") return(Sym(NaN)) end - - fn = mpmath[Symbol(meth)] + + fn = getproperty(mpmath, Symbol(meth)) ans = call_sympy_fun(fn, args...; kwargs...) ## make nicer... if isa(ans, Vector) @@ -66,12 +66,11 @@ function init_mpmath() copy!(mpmath, PyCall.pyimport_conda("mpmath", "mpmath")) if mpmath != PyCall.PyNULL() ## ignore warnings for now... - mpftype = mpmath["mpf"] + mpftype = mpmath."mpf" pytype_mapping(mpftype, BigFloat) ## Raises warning!!! - mpctype = mpmath["mpc"] + mpctype = mpmath."mpc" pytype_mapping(mpctype, Complex{BigFloat}) end - -end +end diff --git a/src/patternmatch.jl b/src/patternmatch.jl index 33fbe2b4..5ca79ea1 100644 --- a/src/patternmatch.jl +++ b/src/patternmatch.jl @@ -9,7 +9,7 @@ func(x*y) # Mul func(x+y) # Add ``` """ -func(ex::Sym) = Sym(PyObject(ex)[:func]) +func(ex::Sym) = Sym(PyObject(ex).func) export func @@ -30,15 +30,15 @@ sin(x) |> args ## (x,) [Invariant:](http://docs.sympy.org/dev/tutorial/manipulation.html) Every well-formed SymPy expression `ex` must either have `length(args(ex)) == 0` or -`func(ex)(args(ex)...) = ex`. +`func(ex)(args(ex)...) = ex`. """ -args(ex::Sym) = PyObject(ex)[:args] +args(ex::Sym) = ex.args """ `Wild(:x)`: create a "wild card" for pattern matching """ -Wild(x::AbstractString) = pycall(sympy["Wild"], PyAny, x) +Wild(x::AbstractString) = pycall(sympy.Wild, PyAny, x) Wild(x::Symbol) = Wild(string(x)) export Wild @@ -52,7 +52,7 @@ If a match is unsuccesful, returns an *empty* dictionary. (SymPy returns "nothin The order of the arguments follows `Julia`'s `match` function, not `SymPy`'s. This may change. """ function Base.match(pat::Sym, ex::Sym, args...; kwargs...) - out = ex[:match](pat, args...; kwargs...) + out = ex.match(pat, args...; kwargs...) out == nothing && return Dict() out end @@ -124,7 +124,7 @@ function Base.replace(ex::Sym, query::Sym, fn::Function; kwargs...) end function Base.replace(ex::Sym, query::Sym, value; kwargs...) - ex[:replace](query, value; kwargs...) + ex.replace(query, value; kwargs...) end ## curried form Base.replace(query::Sym, value; kwargs...) = ex -> replace(ex, query, value; kwargs...) @@ -141,7 +141,7 @@ rewrite(sin(x), "exp") # can use a string here ``` """ function rewrite(ex::Sym, args...; kwargs...) - ex[:rewrite](args...; kwargs...) + ex.rewrite(args...; kwargs...) end export rewrite @@ -163,7 +163,7 @@ xreplace(Integral(sin(x), x), x=> y) # bound and unbound are replaced ``` """ function xreplace(ex::Sym, rule::Dict, args...; kwargs...) - ex[:xreplace](rule, args...; kwargs...) + ex.xreplace(rule, args...; kwargs...) end xreplace(ex::Sym, xs::Pair...; kwargs...) = xreplace(ex, Dict(xs...); kwargs...) diff --git a/src/permutations.jl b/src/permutations.jl index 20c80d6f..11fd30be 100644 --- a/src/permutations.jl +++ b/src/permutations.jl @@ -148,17 +148,17 @@ function Permutation(x; kwargs...) x = collect(x) end _check_permutation_format(x) - SymPy.combinatorics[:permutations][:Permutation](x; kwargs...) + SymPy.combinatorics.permutations.Permutation(x; kwargs...) end function Permutation(i, j, xs...; kwargs...) Permutation([vcat(i,j,xs...)]; kwargs...) end -Permutation(;kwargs...) = SymPy.combinatorics[:permutations][:Permutation](; kwargs...) +Permutation(;kwargs...) = SymPy.combinatorics.permutations.Permutation(; kwargs...) export Permutation ## name? ## random permutation of {0,1,..., n-1} -randomperm(n) = SymPy.combinatorics[:permutations][:Permutation][:random](n) +randomperm(n) = SymPy.combinatorics.permutations.Permutation.random(n) export randomperm # ops import Base: +, -, *, /, ^, inv @@ -214,7 +214,7 @@ for meth in permutations_new_functions # `$($meth_name)`: a SymPy function. # The SymPy documentation can be found through: http://docs.sympy.org/latest/search.html?q=$($meth_name) # """ -> - ($meth)(args...; kwargs...) = SymPy.combinatorics[:permutations][:Permutation][$meth_name](args...; kwargs...) + ($meth)(args...; kwargs...) = getproperty(SymPy.combinatorics.permutations.Permutation,$meth_name)(args...; kwargs...) end eval(Expr(:export, meth)) end @@ -273,7 +273,7 @@ export transpositions ## properties ## Base methods -Base.size(p::SymPermutation) = p.x[:size] # property, not method call +Base.size(p::SymPermutation) = p.x.size # property, not method call ## non-base methods #import SymPy: is_even, is_odd @@ -296,7 +296,7 @@ for prop in (:is_Empty, # `$($prop_name)`: a SymPy function. # The SymPy documentation can be found through: http://docs.sympy.org/latest/search.html?q=$($prop_name) # """ -> - ($prop)(ex::SymPermutation) = PyCall.PyObject(ex)[Symbol($prop_name)] + ($prop)(ex::SymPermutation) = getproperty(PyCall.PyObject(ex),Symbol($prop_name)) end eval(Expr(:export, prop)) end @@ -305,12 +305,12 @@ _unflatten_cyclic_form(m::Matrix) = [m[i,:] for i in 1:size(m)[1]] _unflatten_cyclic_form(m) = m function cyclic_form(p::SymPermutation) - m = PyCall.PyObject(p)[:cyclic_form] + m = p.cyclic_form _unflatten_cyclic_form(m) end function full_cyclic_form(p::SymPermutation) - m = PyCall.PyObject(p)[:full_cyclic_form] + m = p.full_cyclic_form _unflatten_cyclic_form(m) end @@ -338,7 +338,7 @@ Differences: * use `collect(generate(G))` in place of `list(G.generate())` """ -PermutationGroup(args...; kwargs...) = SymPy.combinatorics[:perm_groups][:PermutationGroup](args...; kwargs...) +PermutationGroup(args...; kwargs...) = SymPy.combinatorics.perm_groups.PermutationGroup(args...; kwargs...) export PermutationGroup ## Algebra therof @@ -349,18 +349,18 @@ G1::SymPy.SymPermutationGroup * G2::SymPy.SymPermutationGroup = PyCall.py"$G1*$ Base.getindex(Gp::SymPy.SymPermutationGroup, i::Int) = PyCall.py"$(Gp.x)[$(i-1)]" # 1-base ## Special groups -SymmetricGroup(n::Int) = SymPy.combinatorics[:named_groups][:SymmetricGroup](n) -CyclicGroup(n::Int) = SymPy.combinatorics[:named_groups][:CyclicGroup](n) -DihedralGroup(n::Int) = SymPy.combinatorics[:named_groups][:DihedralGroup](n) -AlternatingGroup(n::Int) = SymPy.combinatorics[:named_groups][:AlternatingGroup](n) -AbelianGroup(args...) = SymPy.combinatorics[:named_groups][:AbelianGroup](args...) +SymmetricGroup(n::Int) = SymPy.combinatorics.named_groups.SymmetricGroup(n) +CyclicGroup(n::Int) = SymPy.combinatorics.named_groups.CyclicGroup(n) +DihedralGroup(n::Int) = SymPy.combinatorics.named_groups.DihedralGroup(n) +AlternatingGroup(n::Int) = SymPy.combinatorics.named_groups.AlternatingGroup(n) +AbelianGroup(args...) = SymPy.combinatorics.named_groups.AbelianGroup(args...) export SymmetricGroup, CyclicGroup, DihedralGroup, AlternatingGroup, AbelianGroup # special methods -SymPy.elements(gp::SymPy.SymPermutationGroup) = [a for a in PyCall.PyObject(gp)[:elements]] +SymPy.elements(gp::SymPy.SymPermutationGroup) = [a for a in gp.elements] """ random_element(gp, ...) @@ -446,7 +446,7 @@ export len Does G contain x. (In SymPy, this is `contains.) """ -Base.occursin(x, G::SymPermutationGroup; kwargs...) = (G[:contains](x; kwargs...) == Sym(true)) # was contains +Base.occursin(x, G::SymPermutationGroup; kwargs...) = (G.contains(x; kwargs...) == Sym(true)) # was contains ## These need PyVector in the call orbit(G::SymPy.SymPermutationGroup, alpha::Number, args...; kwargs...) = object_meth(G, :orbit, alpha, args...; kwargs...) @@ -488,18 +488,18 @@ for prop in permutation_group_properties # `$($prop_name)`: a SymPy function. # The SymPy documentation can be found through: http://docs.sympy.org/latest/search.html?q=$($prop_name) # """ -> - ($prop)(ex::SymPermutationGroup) = PyCall.PyObject(ex)[Symbol($prop_name)] + ($prop)(ex::SymPermutationGroup) = getproperty(PyCall.PyObject(ex),Symbol($prop_name)) end eval(Expr(:export, prop)) end -base(ex::SymPermutationGroup) = PyCall.PyObject(ex)[:base] +base(ex::SymPermutationGroup) = ex.base export base -strong_gens(D::SymPermutationGroup) = PyCall.PyObject(D)[:strong_gens] +strong_gens(D::SymPermutationGroup) = D.strong_gens export strong_gens diff --git a/src/physics.jl b/src/physics.jl index 05765e13..34b64ea3 100644 --- a/src/physics.jl +++ b/src/physics.jl @@ -37,7 +37,7 @@ for (m, meths) in physics # `$($meth_name)`: a SymPy function. # The SymPy documentation can be found through: http://docs.sympy.org/latest/search.html?q=$($meth_name) # """ -> - ($meth)(args...;kwargs...) = pycall(getindex($m, $meth_name), PyAny, map(Sym, args)..., + ($meth)(args...;kwargs...) = pycall(getproperty($m, $meth_name), PyAny, map(Sym, args)..., [k=>Sym(v) for (k,v) in pairs(kwargs)]...) end eval(Expr(:export, meth)) diff --git a/src/plot_recipes.jl b/src/plot_recipes.jl index 03fafafd..288ade33 100644 --- a/src/plot_recipes.jl +++ b/src/plot_recipes.jl @@ -236,7 +236,7 @@ function plot_parametric_surface(exs::Tuple{Sym,Sym,Sym}, args...; kwargs...) - SymPy.call_sympy_fun(sympy["plotting"]["plot3d_parametric_surface"], exs..., args...; kwargs...) + SymPy.call_sympy_fun(sympy.plotting.plot3d_parametric_surface, exs..., args...; kwargs...) end export plot_parametric_surface diff --git a/src/poly.jl b/src/poly.jl index 88983820..5e7b286c 100644 --- a/src/poly.jl +++ b/src/poly.jl @@ -133,7 +133,7 @@ coeffs(p) ## [a,b,c] ``` """ -Poly(x::Sym) = sympy["Poly"](x) +Poly(x::Sym) = sympy."Poly"(x) Poly(x::Sym, args...; kwargs...) = sympy_meth(:Poly, x, args...; kwargs...) export Poly @@ -236,7 +236,7 @@ polynomial_predicates = ( :is_zero) -is_primitive(x::Sym) = PyObject(x)[:is_primitive] +is_primitive(x::Sym) = x.is_primitive export is_primitive diff --git a/src/sets.jl b/src/sets.jl index 3e11346e..3c2870e8 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -51,13 +51,13 @@ sypmy_sets = nothing module S using SymPy function init_set() - S = sympy["S"] - global Reals = S["Reals"] - global UniversalSet = S["UniversalSet"] - global Naturals = S["Naturals"] - global Naturals0 = S["Naturals0"] - global Integers = S["Integers"] - global EmptySet = S["EmptySet"] + S = sympy.S + global Reals = S.Reals + global UniversalSet = S.UniversalSet + global Naturals = S.Naturals + global Naturals0 = S.Naturals0 + global Integers = S.Integers + global EmptySet = S.EmptySet end end @@ -76,7 +76,7 @@ function Base.convert(::Type{Set}, s::Sym) is_FiniteSet(s) || throw(ArgumentError("`s` must be a finite set")) s1 = Set(Any[u for u in s.x]) for el in copy(s1) - if !isa(el, Set) && !(el.x[:is_Symbol]) && is_FiniteSet(s) && length(el.x) > 1 + if !isa(el, Set) && !(el.x.is_Symbol) && is_FiniteSet(s) && length(el.x) > 1 ## replace python FiniteSet with julian Set setdiff!(s1, Set([el])) push!(s1, Set(convert(Set, el))) @@ -94,12 +94,12 @@ end "Find power set of set" -powerset(s::Sym) = s[:powerset]() +powerset(s::Sym) = s.powerset() "Is `x` in `I`?" -Base.in(x, I::Sym) = (I[:contains](x) == Sym(true)) +Base.in(x, I::Sym) = (I.contains(x) == Sym(true)) "Elements of finite set" @@ -108,77 +108,77 @@ export elements "Complement of set within the universe" -complement(I::Sym, U::Sym=S.Reals) = I[:complement](U) +complement(I::Sym, U::Sym=S.Reals) = I.complement(U) export complement "boundary, returnsa set" -boundary(I::Sym) = PyObject(I)[:boundary] +boundary(I::Sym) = I.boundary "Infinum of I" -inf(I::Sym) = PyObject(I)[:inf] +inf(I::Sym) = I.inf export inf "Intersection of two intervals" -Base.intersect(I::Sym, J::Sym) = I[:intersect](J) +Base.intersect(I::Sym, J::Sym) = I.intersect(J) "Are `I` and `J` disjoint?" -is_disjoint(I::Sym, J::Sym) = I[:is_disjoint](J) == Sym(true) +is_disjoint(I::Sym, J::Sym) = I.is_disjoint(J) == Sym(true) "Is `J` a proper subset of `I`?" -is_proper_subset(I::Sym, J::Sym) = I[:is_proper_subset](J) == Sym(true) +is_proper_subset(I::Sym, J::Sym) = I.is_proper_subset(J) == Sym(true) "Is `J` a subset of `I`?" -is_subset(I::Sym, J::Sym) = I[:is_subset](J) == Sym(true) +is_subset(I::Sym, J::Sym) = I.is_subset(J) == Sym(true) "Is `I` a superset of `J`?" -is_superset(I::Sym, J::Sym) = I[:is_superset](J) == Sym(true) +is_superset(I::Sym, J::Sym) = I.is_superset(J) == Sym(true) "Lebesgue mesuare of an interval" -measure(I::Sym) = PyObject(I)[:measure] +measure(I::Sym) = I.measure "Supremum of a set" -sup(I::Sym) = PyObject(I)[:sup] +sup(I::Sym) = I.sup "Union of two intervals" -Base.union(I::Sym, J::Sym) = I[:union](J) +Base.union(I::Sym, J::Sym) = I.union(J) "Symmetric difference of two intervals, in one or other, but not both: `(I ∪ J) \\ (I ∩ J)" Base.symdiff(I::Sym, J::Sym) = complement(intersection(I,J), union(I,J)) "rexpress I in terms of relations involving variable `x`" -as_relational(I::Sym, x::Sym) = I[:as_relational](x) +as_relational(I::Sym, x::Sym) = I.as_relational(x) # not implemented: `end` => `sup`, `left` => "Looks like (-oo, a)?" -is_left_unbounded(I::Sym) = PyObject(I)[:is_left_unbounded] == Sym(true) +is_left_unbounded(I::Sym) = I.is_left_unbounded == Sym(true) "Looks like (a, oo)?" -is_right_unbounded(I::Sym) = PyObject(I)[:is_right_unbounded] == Sym(true) +is_right_unbounded(I::Sym) = I.is_right_unbounded == Sym(true) "Looks like (a, b...?" -left_open(I::Sym) = PyObject(I)[:left_open] == Sym(true) +left_open(I::Sym) = I.left_open == Sym(true) "Looks like ..., b)?" -right_open(I::Sym) = PyObject(I)[:right_open] == Sym(true) +right_open(I::Sym) = I.right_open == Sym(true) "Is `I` a finite set?" -is_FiniteSet(I::Sym) = PyObject(I)[:is_FiniteSet] +is_FiniteSet(I::Sym) = I.is_FiniteSet "Is `I` an interval?" -is_Interval(I::Sym) = PyObject(I)[:is_Interval] +is_Interval(I::Sym) = I.is_Interval "Is `I` a union?" -is_Union(I::Sym) = PyObject(I)[:is_Union] +is_Union(I::Sym) = I.is_Union function init_sets() S.init_set() -end +end """ `FiniteSet(1,2,3)`, `FiniteSet(1:10...)`: create a finite set @@ -191,7 +191,7 @@ global ProductSet(args...) = sympy_meth(:ProductSet, args...) """ Means to filter a set to pull out elements by a condition. - + ConditionSet: A set of elements which satisfies a given condition. `ConditionSet(x, condition, S) = {x | condition(x) == true for x in S}` @@ -233,5 +233,3 @@ Interval(0,1,true, false) # (0,1] """ global Interval(l,r,left_open=false, right_open=false) = sympy_meth(:Interval, Sym(l), Sym(r), left_open ,right_open) - - diff --git a/src/subs.jl b/src/subs.jl index 8d4af1fa..79ea44f1 100644 --- a/src/subs.jl +++ b/src/subs.jl @@ -5,7 +5,7 @@ """ `subs` is used to subsitute a value in an expression with another -value. +value. Examples: @@ -54,9 +54,9 @@ There were some older convenience forms, but these are now deprecated, as they d subs(ex, :y, pi) # using a symbol, not a symbolic object subs(ex, x=1, y=pi) # using keyword argument, and not pairs ## or their curried or call forms -ex |> subs(:x, e) -ex |> subs(x=e) -ex(x=2, y=3) +ex |> subs(:x, e) +ex |> subs(x=e) +ex(x=2, y=3) ``` The `replace` function is related, but not identical to subs. @@ -83,8 +83,8 @@ subs(d::Pair...) = ex -> subs(ex, [(p.first, p.second) for p in d]...) ## helper, as :is_rational will find 1.2 rational... function _is_rational(ex::Sym) - ex.x[:is_rational] == nothing && return false - ex.x[:is_rational] && denom(ex).x[:is_integer] + ex.x.is_rational == nothing && return false + ex.x.is_rational && denom(ex).x.is_integer end ## evalf, n, N @@ -105,7 +105,7 @@ available symbolically, by calling `N` on the values. Using `SymPy` within `Julia` makes having two such functions useful: -* one to do the equivalent of SymPy's `evalf` function +* one to do the equivalent of SymPy's `evalf` function * one to *also* convert these expressions back into `Julia` objects. We use `N` to return a `Julia` object and `evalf` to return a symbolic @@ -144,9 +144,9 @@ function N(ex::Sym) ## XXX consolidate this and N(ex, digits) length(free_symbols(ex)) > 0 && return ex - + if is_integer(ex) == nothing - evalf_ex = ex[:evalf]() + evalf_ex = ex.evalf() if ex == evalf_ex return ex else @@ -154,30 +154,29 @@ function N(ex::Sym) end end if is_integer(ex) - for T in [Int, BigInt] + for T in (Int, BigInt) try (return(convert(T, ex))) catch e end end elseif _is_rational(ex) return N(numer(ex)) // N(denom(ex)) ## `convert(Rational, ex)))` fails on `Sym(4//3)` elseif is_real(ex) == true - for T in [Irrational, Float64] ## BigFloat??? + for T in (Irrational, Float64) ## BigFloat??? try (return(convert(T, ex))) catch e end end elseif is_complex(ex) == true try - r, i = ex[:re](), ex[:im]() - r, i = promote(N(r), N(i)) + r, i = promote(N.(ex.as_real_imag())...) return(Complex(r, i)) catch e end end - throw(DomainError()) + throw(DomainError(ex, "Object of type $(typeof(ex)) has no numeric conversion to a Julia object")) end N(x::Number) = x # implies N(x::Sym) = x if ... N(m::AbstractArray{Sym}) = map(N, m) """ -`N` can take a precision argument. +`N` can take a precision argument. When given as an integer greater than 16, we try to match the digits of accuracy using `BigFloat` precision on conversions to floating point. @@ -186,10 +185,10 @@ function N(x::Sym, digits::Int) ## check digits <= 16 && return(N(x)) if is_integer(x) == nothing - out = x[:evalf](digits) + out = x.evalf(digits) return( N(out, digits) ) end - + ex = evalf(x, digits) if is_integer(x) return(convert(BigInt, x)) @@ -198,16 +197,16 @@ function N(x::Sym, digits::Int) elseif is_real(x) == true p = round(Int,log2(10)*digits) - out = setprecision(p) do + out = setprecision(p) do convert(BigFloat, ex) end return(out) elseif is_complex(x) == true - r, i = ex[:re](), ex[:im]() + r, i = ex.as_real_imag() u, v = promote(N(r, digits), N(i, digits)) return(Complex(u, v)) end - + throw(DomainError()) end @@ -219,10 +218,7 @@ Unlike `N`, `evalf` returns an object of type `Sym`. Examples ``` x = Sym("x") -evalf(x, subs=Dict([(x,1/2)])) +evalf(x, subs=Dict([(x,1/2)])) ``` """ evalf(x::Sym, args...; kwargs...) = object_meth(x, :evalf, args...; kwargs...) - - - diff --git a/src/types.jl b/src/types.jl index bd4f8eed..87582f8d 100644 --- a/src/types.jl +++ b/src/types.jl @@ -57,8 +57,8 @@ convert(::Type{PyObject}, s::Sym) = s.x function convert(::Type{Tuple}, o::PyCall.PyObject) ## check that o is a tuple? ## PyCall.pytypeof(o) - n = o[:__len__]() - ntuple(i -> o[:__getitem__](i-1), n) + n = o.__len__() + ntuple(i -> o.__getitem__(i-1), n) end ## rational @@ -75,13 +75,13 @@ convert(::Type{T}, x::Sym) where {T <: Real} = convert(T, PyObject(x)) ## complex ## cf math.jl for `complex` of a value -## IM is SymPy's "i" (sympy["I"], not Python's +## IM is SymPy's "i" (sympy.I, not Python's ## Sym(PyCall.PyObject(im)) which gives 1j. function convert(::Type{Sym}, x::Complex) y = ifelse(isa(x, Complex{Bool}), real(x) + imag(x) * im, x) real(y) + imag(y) * IM end -convert(::Type{Complex{T}}, x::Sym) where {T} = complex(map(x -> convert(T, x), x[:as_real_imag]())...) +convert(::Type{Complex{T}}, x::Sym) where {T} = complex(map(x -> convert(T, x), x.as_real_imag())...) complex(::Type{Sym}) = Sym diff --git a/src/utils.jl b/src/utils.jl index 411987af..37bb917b 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -135,7 +135,7 @@ end ## length of object function length(x::SymbolicObject) - haskey(PyObject(x), :length) && return PyObject(x)[:length] + PyCall.hasproperty(PyObject(x), :length) && return PyObject(x).length sz = size(x) length(sz) == 0 && return(0) *(sz...) @@ -162,6 +162,22 @@ iterate(x::Sym) = (x.x, 0) iterate(x::Sym, state) = nothing +## Following recent changes to PyCall where: +# For o::PyObject, make o["foo"], o[:foo], and o.foo equivalent to o.foo in Python, +# with the former returning an raw PyObject and the latter giving the PyAny +# conversion. +# We do something similar to SymPy +# +# We only implement for symbols here, not strings +function Base.getproperty(o::T, s::Symbol) where {T <: SymbolicObject} + if !(s in fieldnames(T)) + getproperty(PyCall.PyObject(o), s) + else + getfield(o, s) + end +end + + @@ -189,12 +205,12 @@ x = Sym("x") """ function getindex(x::SymbolicObject, i::Symbol) - if haskey(PyObject(x), i) + if PyCall.hasproperty(PyObject(x), i) function __XXxxXX__(args...;kwargs...) # replace with generated name object_meth(x, i, args...; kwargs...) end return __XXxxXX__ - elseif haskey(sympy, i) + elseif PyCall.hasproperty(sympy, i) function __XXyyXX__(args...;kwargs...) sympy_meth(i, x, args...; kwargs...) end @@ -210,9 +226,9 @@ Base.hash(x::Sym) = hash(PyObject(x)) ## Helper function from PyCall.pywrap: function members(o::Union{PyObject, Sym}) - out = pycall(PyCall.inspect["getmembers"], PyObject, o) + out = pycall(PyCall.inspect.getmembers, PyObject, o) AbstractString[a for (a,b) in out] end " Return class name as a string " -classname(ex::Sym) = PyObject(ex)[:__class__][:__name__] +classname(ex::Sym) = ex.__class__.__name__ diff --git a/test/test-permutations.jl b/test/test-permutations.jl index 64afb375..1c3ec2bb 100644 --- a/test/test-permutations.jl +++ b/test/test-permutations.jl @@ -212,7 +212,7 @@ as = [Permutation(1, 2), Permutation(3, 5), Permutation(3, 4)] # The number of permutations on a set of n elements is given by n! and is called the cardinality. @test size(p) == 4 # 4 -@test cardinality(p) == 24 +@test cardinality(p) == 24 # 24 # A given permutation has a rank among all the possible permutations of the same elements, but what that rank is depends on how the permutations are enumerated. (There are a number of different methods of doing so.) The lexicographic rank is given by the rank method and this rank is used to increment a permutation with addition/subtraction: @@ -334,7 +334,7 @@ c = commutator(p, x); Id = Permutation(3) p = [(Id + i) for i in range(6)] for i in eachindex(p) - for j in eachindex(p) + for j in eachindex(p) c = commutator(p[i], p[j]) if p[i]*p[j] == p[j]*p[i] @assert c == I @@ -372,7 +372,7 @@ b = Permutation([2, 3, 5, 4, 1, 0]) #from sympy.combinatorics import Permutation @test cycle_structure(Permutation(3)) == Dict{Any,Any}(1=>4) -# {1: 4} +# {1: 4} @test cycle_structure(Permutation(0, 4, 3)(1, 2)(5, 6)) == Dict{Any,Any}(2=>2, 3=>1) # {2: 2, 3: 1} # cycles @@ -399,9 +399,9 @@ b = Permutation([2, 3, 5, 4, 1, 0]) #from sympy.combinatorics.permutations import Permutation p = Permutation([0, 3, 1, 2]) -@test cyclic_form(p) ==[[1, 3, 2]] +@test cyclic_form(p) ==[[1, 3, 2]] # [[1, 3, 2]] -@test cyclic_form(Permutation([1, 0, 2, 4, 3, 5])) == [[0, 1], [3, 4]] +@test cyclic_form(Permutation([1, 0, 2, 4, 3, 5])) == [[0, 1], [3, 4]] # [[0, 1], [3, 4]] @@ -455,7 +455,7 @@ p = Permutation([4, 0, 1, 3, 2]) # ['S', 'y', 'm', 'P', 'y'] ### XXX Passing in a method should be done via `pymethod`, though -## +## #Permutation.from_sequence('SymPy', key=lambda x: x.lower()) XXX # (4)(0 2)(1 3) @@ -508,11 +508,11 @@ r = Permutation([0, 2, 1, 4, 3]) #from sympy.combinatorics.permutations import Permutation p = josephus(3, 6, 1) -@test get_adjacency_matrix(p) == Sym[0 0 0 0 0 0; -0 0 0 0 1 0; -0 0 0 0 0 1; -0 1 0 0 0 0; -1 0 0 0 0 0; +@test get_adjacency_matrix(p) == Sym[0 0 0 0 0 0; +0 0 0 0 1 0; +0 0 0 0 0 1; +0 1 0 0 0 0; +1 0 0 0 0 0; 0 0 0 1 0 0] # Matrix([ # [0, 0, 0, 0, 0, 0], @@ -1037,7 +1037,7 @@ p = Permutation([0, 2, 1, 3]) a, b = Permutation(1,2,0), Permutation(0,2,1) ## XXX--->>> Permutation.rmul(a, [0, 2, 1]) == Permutation.rmul(a, b) # this fails, [0,2,1] not promoted -@test Permutation(rmul(a, b)...) == rmul(a, b) +@test Permutation(rmul(a, b)...) == rmul(a, b) # True # The reverse order of arguments will raise a TypeError. @@ -1266,7 +1266,7 @@ G = PermutationGroup([Permutation(0, 1, 3)(2, 4)]) # The randomized version (default) is of Las Vegas type. -# Parameters: +# Parameters: # base, strong_gens # The base and strong generating set. # pos @@ -1279,7 +1279,7 @@ G = PermutationGroup([Permutation(0, 1, 3)(2, 4)]) # The basic orbits, if known. # strong_gens_distr # The strong generators distributed by basic stabilizers, if known. -# Returns: +# Returns: # (base, strong_gens) # base is the new base, and strong_gens is a generating set relative to it. # See also schreier_sims @@ -1289,7 +1289,7 @@ G = PermutationGroup([Permutation(0, 1, 3)(2, 4)]) # Examples -# +# # # from sympy.combinatorics.named_groups import SymmetricGroup # # from sympy.combinatorics.testutil import _verify_bsgs # # from sympy.combinatorics.perm_groups import PermutationGroup @@ -1298,7 +1298,7 @@ schreier_sims(S) @test SymPy.Permutations.base(S) == [0, 1, 2] # [0, 1, 2] -_base, gens = baseswap(S, SymPy.Permutations.base(S), strong_gens(S), 1, randomized=false) +_base, gens = baseswap(S, SymPy.Permutations.base(S), strong_gens(S), 1, randomized=false) @test _base == [0,2,1] @test gens == [Permutation(0, 1, 2, 3), Permutation(3)(0, 1), Permutation(1, 3, 2), Permutation(2, 3), Permutation(1, 3)] @@ -1307,7 +1307,7 @@ _base, gens = baseswap(S, SymPy.Permutations.base(S), strong_gens(S), 1, randomi # # (2 3), (1 3)]) # # check that base, gens is a BSGS -# +# # ## XXX--->>> S1 = PermutationGroup(gens) # ## XXX--->>> _verify_bsgs(S1, base, gens) # True @@ -1400,7 +1400,7 @@ G = center(D) # It is naturally a subgroup of G; the centralizer of a permutation group is equal to the centralizer of any set of generators for that group, since any element commuting with the generators commutes with any product of the generators. -# Parameters: +# Parameters: # other # a permutation group/list of permutations/single permutation # See also subgroup_search @@ -1613,7 +1613,7 @@ collect(generate(G)) ## list(generate(G)) XXX # The derived series for a group GG is defined as G=G0>G1>G2>…G=G0>G1>G2>… where Gi=[Gi−1,Gi−1]Gi=[Gi−1,Gi−1], i.e. GiGi is the derived subgroup of Gi−1Gi−1, for i∈ℕi∈N. When we have Gk=Gk−1Gk=Gk−1 for some k∈ℕk∈N, the series terminates. -# Returns: +# Returns: # A list of permutation groups containing the members of the derived # series in the order G=G0,G1,G2,…G=G0,G1,G2,…. # See also derived_subgroup @@ -1685,9 +1685,7 @@ elements(p) # The permutation group given in the tetrahedron object is also true groups: -tet = SymPy.combinatorics[:tetrahedron] -G = PyCall.PyObject(tet)[:pgroup] -#G = tetrahedron.pgroup +G = SymPy.combinatorics.tetrahedron.pgroup @test is_group(G) # G.is_group # True @@ -2078,7 +2076,7 @@ D = DihedralGroup(10) # If S is a subset of a group G, the normal closure of A in G is defined as the intersection of all normal subgroups of G that contain A ([1], p.14). Alternatively, it is the group generated by the conjugates x^{-1}yx for x a generator of G and y a generator of the subgroup \left\langle S\right\rangle generated by S (for some chosen generating set for \left\langle S\right\rangle) ([1], p.73). -# Parameters: +# Parameters: # other # a subgroup/list of permutations/single permutation # k @@ -2126,7 +2124,7 @@ G = PermutationGroup([a]) @test Set(orbit(G, 0)) == Set([0,1,2]) # {0, 1, 2} -@test Set(orbit(G, [0, 4], "union")) == Set([0, 1, 2, 3, 4, 5, 6]) +@test Set(orbit(G, [0, 4], "union")) == Set([0, 1, 2, 3, 4, 5, 6]) # {0, 1, 2, 3, 4, 5, 6} @@ -2304,12 +2302,12 @@ basic_transversals(G) # schreier_sims_incremental(base=None, gens=None)[source] # Extend a sequence of points and generating set to a base and strong generating set. -# Parameters: +# Parameters: # base # The sequence of points to be extended to a base. Optional parameter with default value []. # gens # The generating set to be extended to a strong generating set relative to the base obtained. Optional parameter with default value self.generators. -# Returns: +# Returns: # (base, strong_gens) # base is the base obtained, and strong_gens is the strong generating set relative to it. The original parameters base, gens remain unchanged. # See also schreier_sims, schreier_sims_random @@ -2344,7 +2342,7 @@ _base[2] # The randomized Schreier-Sims algorithm takes the sequence base and the generating set gens, and extends base to a base, and gens to a strong generating set relative to that base with probability of a wrong answer at most 2−consec_succ2−consec_succ, provided the random generators are sufficiently random. -# Parameters: +# Parameters: # base # The sequence to be extended to a base. # gens @@ -2353,7 +2351,7 @@ _base[2] # The parameter defining the probability of a wrong answer. # _random_prec # An internal parameter used for testing purposes. -# Returns: +# Returns: # (base, strong_gens) # base is the base and strong_gens is the strong generating set relative to it. # See also schreier_sims @@ -2369,7 +2367,7 @@ _base[2] # from sympy.combinatorics.named_groups import SymmetricGroup S = SymmetricGroup(5) _base, _strong_gens = schreier_sims_random(S, consec_succ=5) -## _verify_bsgs(S, _base, _strong_gens) +## _verify_bsgs(S, _base, _strong_gens) # True @@ -2448,7 +2446,7 @@ D = DihedralGroup(4) # This is done by a depth-first search with respect to base images that uses several tests to prune the search tree. -# Parameters: +# Parameters: # prop # The property to be used. Has to be callable on group elements and always return True or False. It is assumed that all group elements satisfying prop indeed form a subgroup. # base @@ -2459,7 +2457,7 @@ D = DihedralGroup(4) # A list of callables of length equal to the length of base. These are used to rule out group elements by partial base images, so that tests[l](g) returns False if the element g is known not to satisfy prop base on where g sends the first l + 1 base points. # init_subgroup # if a subgroup of the sought group is known in advance, it can be passed to the function as this parameter. -# Returns: +# Returns: # res # The subgroup of all elements satisfying prop. The generating set for this group is guaranteed to be a strong generating set relative to the base base. # Notes @@ -2502,7 +2500,7 @@ a = Permutation([1, 2, 0]) b = Permutation([1, 0, 2]) G = PermutationGroup([a, b]) @test transitivity_degree(G) == 3 -# 3 +# 3 end diff --git a/test/tests.jl b/test/tests.jl index 232099c5..c90a7eba 100644 --- a/test/tests.jl +++ b/test/tests.jl @@ -140,11 +140,11 @@ using Base.MathConstants @test isa(N(r), Float64) @test isa(N(z), Integer) - ## method calls via getindex + ## method calls via getproperty p = (x-1)*(x-2) - @test p[:roots]() == Dict{Any,Any}(Sym(1) => 1, Sym(2)=> 1) # sympy.roots + @test sympy.roots(p) == Dict{Any,Any}(Sym(1) => 1, Sym(2)=> 1) # sympy.roots p = Poly(p, x) - @test p[:coeffs]() == Any[1,-3,2] # p.coeffs + @test p.coeffs() == Any[1,-3,2] # p.coeffs ## algebra @test SymPy.expand((x + 1)*(x + 2)) == x^2 + 3x + 2 # v0.7 deprecates expand, in v1.0 this is fine w/o qualifacation @@ -224,8 +224,8 @@ using Base.MathConstants summation(1/x^2, (x, 1, 10)) out = summation(1/x^2, (x, 1, 10)) out1 = sum([1//x^2 for x in 1:10]) - @test round(Integer, out.x[:p]) == out1.num - @test round(Integer, out.x[:q]) == out1.den + @test round(Integer, out.p) == out1.num + @test round(Integer, out.q) == out1.den ## Ops @@ -404,7 +404,7 @@ using Base.MathConstants Sym(big(2.0)) # may need mpmath (e.g., conda install mpmath) @test limit(besselj(1,1/x), x, 0) == Sym(0) - complex(N(hankel2(2, pi))) + complex(N(SymPy.mpmath.hankel2(2, pi))) bei(2, 3.5) bei(1+im, 2+3im) end @@ -438,9 +438,9 @@ using Base.MathConstants @test cse([x, x]) == (Any[], [x, x]) @test cse([x x; x x]) == (Any[], [x x; x x]) -## sympy"..."(...) -@vars x -@test sympy"sin"(1) == sin(Sym(1)) + ## sympy"..."(...) + @vars x + @test sympy"sin"(1) == sin(Sym(1)) end @testset "Fix past issues" begin