From 24ea67d6b42e211387d970de78d400ec4d9a2cc0 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Mon, 21 Aug 2023 16:12:19 -0400 Subject: [PATCH 1/8] Sympy to symbolics file added --- ext/SymPySymbolicsExt.jl | 123 +++++++++++++++++++++++++++++++++++++++ ext/SymbolicsSymPyExt.jl | 3 + 2 files changed, 126 insertions(+) create mode 100644 ext/SymPySymbolicsExt.jl diff --git a/ext/SymPySymbolicsExt.jl b/ext/SymPySymbolicsExt.jl new file mode 100644 index 000000000..26f6f1df7 --- /dev/null +++ b/ext/SymPySymbolicsExt.jl @@ -0,0 +1,123 @@ +module SymPySymbolics +if isdefined(Base,:get_extension) + using Symbolics + using PythonCall + using CondaPkg +else + using ..Symbolics + using ..PythonCall + using ..CondaPkg +end + +CondaPkg.add("sympy") + +sp = pyimport("sympy") + +# rule functions +function pyconvert_rule_sympy_symbol(::Type{Symbolics.Num}, x::Py) + if !pyisinstance(x,sp.Symbol) + return PythonCall.pyconvert_unconverted() + end + name = PythonCall.pyconvert(Symbol,x.name) + return PythonCall.pyconvert_return(Symbolics.variable(name)) +end + +function pyconvert_rule_sympy_pow(::Type{Symbolics.Num}, x::Py) + if !pyisinstance(x,sp.Pow) + return PythonCall.pyconvert_unconverted() + end + expbase = pyconvert(Symbolics.Num,x.base) + exp = pyconvert(Symbolics.Num,x.exp) + return PythonCall.pyconvert_return(expbase^exp) +end + +function pyconvert_rule_sympy_mul(::Type{Symbolics.Num}, x::Py) + if !pyisinstance(x,sp.Mul) + return PythonCall.pyconvert_unconverted() + end + mult = reduce(*,PythonCall.pyconvert.(Symbolics.Num,x.args)) + return PythonCall.pyconvert_return(mult) +end + +function pyconvert_rule_sympy_add(::Type{Symbolics.Num}, x::Py) + if !pyisinstance(x,sp.Add) + return PythonCall.pyconvert_unconverted() + end + sum = reduce(+, PythonCall.pyconvert.(Symbolics.Num,x.args)) + return PythonCall.pyconvert_return(sum) +end + +function pyconvert_rule_sympy_equality(::Type{Symbolics.Equation}, x::Py) + if !pyisinstance(x,sp.Equality) + return PythonCall.pyconvert_unconverted() + end + rhs = pyconvert(Symbolics.Num,x.rhs) + lhs = pyconvert(Symbolics.Num,x.lhs) + return PythonCall.pyconvert_return(rhs ~ lhs) +end + +function pyconvert_rule_sympy_derivative(::Type{Symbolics.Num}, x::Py) + if !pyisinstance(x,sp.Derivative) + return PythonCall.pyconvert_unconverted() + end + variables = pyconvert.(Symbolics.Num,x.variables) + derivatives = prod(var -> Differential(var), variables) + expr = pyconvert(Symbolics.Num, x.expr) + return PythonCall.pyconvert_return(derivatives(expr)) +end + + +function pyconvert_rule_sympy_function(::Type{Symbolics.Num}, x::Py) + if !pyisinstance(x,sp.Function) + return PythonCall.pyconvert_unconverted() + end + name = pyconvert(String,x.name) + args = pyconvert.(Symbolics.Num,x.args) + symbolexpr = Expr(:call,Symbol(name),args...) + symbolicexpr = Num(parse_expr_to_symbolic(symbolexpr,Main)) + return PythonCall.pyconvert_return(symbolicexpr) +end + +# added rules +PythonCall.pyconvert_add_rule("sympy.core.power:Pow", Symbolics.Num, pyconvert_rule_sympy_pow) + +PythonCall.pyconvert_add_rule("sympy.core.symbol:Symbol", Symbolics.Num, pyconvert_rule_sympy_symbol) + +PythonCall.pyconvert_add_rule("sympy.core.mul:Mul", Symbolics.Num, pyconvert_rule_sympy_mul) + +PythonCall.pyconvert_add_rule("sympy.core.add:Add", Symbolics.Num, pyconvert_rule_sympy_add) + +PythonCall.pyconvert_add_rule("sympy.core.relational:Equality", Symbolics.Equation, pyconvert_rule_sympy_equality) + +PythonCall.pyconvert_add_rule("sympy.core.function:Derivative",Symbolics.Num, pyconvert_rule_sympy_derivative) + +PythonCall.pyconvert_add_rule("sympy.core.function:Function",Symbolics.Num, pyconvert_rule_sympy_function) + +# little tests +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t**t")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t + z + d")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t*z*9")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("5*t*z + 3*d + h/(b*5)")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t * n/z * t**4 * h**z + l*h - j")) + +PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(2,5, evaluate = False)")) + +PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(t*x + 5**x + 20/t, 90/t + t**4 - t*z)")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("Function('f')(x)")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("f(x,y)")) + +PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(f(x), 2*x +1)")) + +PythonCall.pyconvert(Symbolics.Num,sp.sympify("Derivative(f(x),x)")) + +PythonCall.pyconvert(Symbolics.Num,sp.sympify("Eq(Derivative(f(x),x), x")) + +end \ No newline at end of file diff --git a/ext/SymbolicsSymPyExt.jl b/ext/SymbolicsSymPyExt.jl index 630cd3ce7..ff59cadff 100644 --- a/ext/SymbolicsSymPyExt.jl +++ b/ext/SymbolicsSymPyExt.jl @@ -29,4 +29,7 @@ function Symbolics.symbolics_to_sympy(expr) end end + + + end #module From adf62f51c65c802214048ab3436c5062ae14ce5f Mon Sep 17 00:00:00 2001 From: jClugstor Date: Mon, 21 Aug 2023 16:13:32 -0400 Subject: [PATCH 2/8] got rid of extra lines --- ext/SymbolicsSymPyExt.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext/SymbolicsSymPyExt.jl b/ext/SymbolicsSymPyExt.jl index ff59cadff..630cd3ce7 100644 --- a/ext/SymbolicsSymPyExt.jl +++ b/ext/SymbolicsSymPyExt.jl @@ -29,7 +29,4 @@ function Symbolics.symbolics_to_sympy(expr) end end - - - end #module From cb7e95978ddff584cdad628da9093bb419d4bc1e Mon Sep 17 00:00:00 2001 From: jClugstor Date: Wed, 23 Aug 2023 11:11:41 -0400 Subject: [PATCH 3/8] Fixed functions --- ext/SymPySymbolicsExt.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ext/SymPySymbolicsExt.jl b/ext/SymPySymbolicsExt.jl index 26f6f1df7..88525c231 100644 --- a/ext/SymPySymbolicsExt.jl +++ b/ext/SymPySymbolicsExt.jl @@ -71,11 +71,10 @@ function pyconvert_rule_sympy_function(::Type{Symbolics.Num}, x::Py) if !pyisinstance(x,sp.Function) return PythonCall.pyconvert_unconverted() end - name = pyconvert(String,x.name) + name = pyconvert(Symbol,x.name) args = pyconvert.(Symbolics.Num,x.args) - symbolexpr = Expr(:call,Symbol(name),args...) - symbolicexpr = Num(parse_expr_to_symbolic(symbolexpr,Main)) - return PythonCall.pyconvert_return(symbolicexpr) + func = @variables $name(..) + return PythonCall.pyconvert_return(first(func)(args...)) end # added rules From d9cd245dc561b589dd9f62bc8965ad3cdc9ec78d Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 24 Aug 2023 10:25:06 -0400 Subject: [PATCH 4/8] Added sympy_to_symbolics, added test file --- ext/SymPySymbolicsExt.jl | 33 +++++---------------------- test/sympy_to_symbolics.jl | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 test/sympy_to_symbolics.jl diff --git a/ext/SymPySymbolicsExt.jl b/ext/SymPySymbolicsExt.jl index 88525c231..acfdc0605 100644 --- a/ext/SymPySymbolicsExt.jl +++ b/ext/SymPySymbolicsExt.jl @@ -66,7 +66,6 @@ function pyconvert_rule_sympy_derivative(::Type{Symbolics.Num}, x::Py) return PythonCall.pyconvert_return(derivatives(expr)) end - function pyconvert_rule_sympy_function(::Type{Symbolics.Num}, x::Py) if !pyisinstance(x,sp.Function) return PythonCall.pyconvert_unconverted() @@ -92,31 +91,11 @@ PythonCall.pyconvert_add_rule("sympy.core.function:Derivative",Symbolics.Num, py PythonCall.pyconvert_add_rule("sympy.core.function:Function",Symbolics.Num, pyconvert_rule_sympy_function) -# little tests -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t")) - -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t**t")) - -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t + z + d")) - -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t*z*9")) - -PythonCall.pyconvert(Symbolics.Num, sp.sympify("5*t*z + 3*d + h/(b*5)")) - -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t * n/z * t**4 * h**z + l*h - j")) - -PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(2,5, evaluate = False)")) - -PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(t*x + 5**x + 20/t, 90/t + t**4 - t*z)")) - -PythonCall.pyconvert(Symbolics.Num, sp.sympify("Function('f')(x)")) - -PythonCall.pyconvert(Symbolics.Num, sp.sympify("f(x,y)")) - -PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(f(x), 2*x +1)")) - -PythonCall.pyconvert(Symbolics.Num,sp.sympify("Derivative(f(x),x)")) - -PythonCall.pyconvert(Symbolics.Num,sp.sympify("Eq(Derivative(f(x),x), x")) +function sympy_to_symbolics(expr::Py) + if pyconvert(Bool,pytype(expr) == sp.core.relational.Equality) + return pyconvert(Symbolics.Equation,expr) + end + return pyconvert(Symbolics.Num,expr) +end end \ No newline at end of file diff --git a/test/sympy_to_symbolics.jl b/test/sympy_to_symbolics.jl new file mode 100644 index 000000000..ef2f75bb2 --- /dev/null +++ b/test/sympy_to_symbolics.jl @@ -0,0 +1,46 @@ +using Test +using SymPy +using Symbolics + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t**t")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t + z + d")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t*z*9")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("5*t*z + 3*d + h/(b*5)")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("t * n/z * t**4 * h**z + l*h - j")) + +PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(2,5, evaluate = False)")) + +PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(t*x + 5**x + 20/t, 90/t + t**4 - t*z)")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("Function('f')(x)")) + +PythonCall.pyconvert(Symbolics.Num, sp.sympify("f(x,y)")) + +PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(f(x), 2*x +1)")) + +PythonCall.pyconvert(Symbolics.Num,sp.sympify("Derivative(f(x),x)")) + +PythonCall.pyconvert(Symbolics.Num,sp.sympify("Eq(Derivative(f(x),x), x")) + +fol = sp.sympify("Eq(Derivative(x(t),t), (1 - x(t))/3.0)") +convertedsystem = PythonCall.pyconvert(Symbolics.Equation,fol) + +@named odesys = ODESystem(convertedsystem,t) + +sys = structural_simplify(odesys) + +prob = ODEProblem(sys, [0.0],(0.0,10.0)) + +using DifferentialEquations: solve +sol = solve(prob) + +using Plots + +plot(sol) + From 1b6f2af83a84cf180efd47798f89ff47e294db7e Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 24 Aug 2023 13:44:15 -0400 Subject: [PATCH 5/8] Added extension deps --- Project.toml | 2 ++ ext/SymPySymbolicsExt.jl | 4 ---- test/sympy_to_symbolics.jl | 45 ++++++++++++++------------------------ 3 files changed, 18 insertions(+), 33 deletions(-) diff --git a/Project.toml b/Project.toml index 8c085d797..bdc1f0985 100644 --- a/Project.toml +++ b/Project.toml @@ -35,9 +35,11 @@ TreeViews = "a2a6695c-b41b-5b7d-aed9-dbfdeacea5d7" [weakdeps] SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6" +PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" [extensions] SymbolicsSymPyExt = "SymPy" +SymPySymbolicsExt = "PythonCall" [compat] ArrayInterface = "6, 7" diff --git a/ext/SymPySymbolicsExt.jl b/ext/SymPySymbolicsExt.jl index acfdc0605..f4cef3851 100644 --- a/ext/SymPySymbolicsExt.jl +++ b/ext/SymPySymbolicsExt.jl @@ -9,10 +9,6 @@ else using ..CondaPkg end -CondaPkg.add("sympy") - -sp = pyimport("sympy") - # rule functions function pyconvert_rule_sympy_symbol(::Type{Symbolics.Num}, x::Py) if !pyisinstance(x,sp.Symbol) diff --git a/test/sympy_to_symbolics.jl b/test/sympy_to_symbolics.jl index ef2f75bb2..e813882ea 100644 --- a/test/sympy_to_symbolics.jl +++ b/test/sympy_to_symbolics.jl @@ -1,46 +1,33 @@ using Test -using SymPy +using PythonCall using Symbolics -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t")) +CondaPkg.add("sympy") -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t**t")) +sp = pyimport("sympy") -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t + z + d")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("t"))) == Symbol("t") -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t*z*9")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("t**t"))) == Symbol("^(t, t)") -PythonCall.pyconvert(Symbolics.Num, sp.sympify("5*t*z + 3*d + h/(b*5)")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("t + z + d"))) == Symbol("+(d, t, z)") -PythonCall.pyconvert(Symbolics.Num, sp.sympify("t * n/z * t**4 * h**z + l*h - j")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("t*z*9"))) == Symbol("*(9, t, z)") -PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(2,5, evaluate = False)")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("5*t*z + 3*d + h/(b*5)"))) == Symbol("+(3d, ((1//5)*h) / b, 5t*z)") -PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(t*x + 5**x + 20/t, 90/t + t**4 - t*z)")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("t * n/z * t**4 * h**z + l*h - j"))) == Symbol("+(-j, h*l, (n*(h^z)*(t^5)) / z)") -PythonCall.pyconvert(Symbolics.Num, sp.sympify("Function('f')(x)")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("Eq(2,5, evaluate = False)"))) -PythonCall.pyconvert(Symbolics.Num, sp.sympify("f(x,y)")) +@test Symbol(sympy_to_symbolics(sp.sympify("Eq(t*x + 5**x + 20/t, 90/t + t**4 - t*z)"))) == Symbol("t^4 + 90 / t - t*z ~ 20 / t + t*x + 5^x") -PythonCall.pyconvert(Symbolics.Equation, sp.sympify("Eq(f(x), 2*x +1)")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("Function('f')(x)"))) == Symbol("f(x)") -PythonCall.pyconvert(Symbolics.Num,sp.sympify("Derivative(f(x),x)")) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("f(x,y)"))) == Symbol("f(x, y)") -PythonCall.pyconvert(Symbolics.Num,sp.sympify("Eq(Derivative(f(x),x), x")) +@test Symbol((sympy_to_symbolics(sp.sympify("Eq(f(x), 2*x +1)")))) == Symbol("1 + 2x ~ f(x)") -fol = sp.sympify("Eq(Derivative(x(t),t), (1 - x(t))/3.0)") -convertedsystem = PythonCall.pyconvert(Symbolics.Equation,fol) - -@named odesys = ODESystem(convertedsystem,t) - -sys = structural_simplify(odesys) - -prob = ODEProblem(sys, [0.0],(0.0,10.0)) - -using DifferentialEquations: solve -sol = solve(prob) - -using Plots - -plot(sol) +@test Symbolics.tosymbol(sympy_to_symbolics(sp.sympify("Derivative(f(x),x)"))) == Symbol("fˍx(x)") +@test Symbol(sympy_to_symbolics(sp.sympify("Eq(Derivative(f(x),x), x)"))) == Symbol("x ~ Differential(x)(f(x))") From 254eb839db5ad9a4cafd13ecb7ebc98dd45d1b42 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 24 Aug 2023 14:17:07 -0400 Subject: [PATCH 6/8] Functions shouldn't need to know what sp is --- ext/SymPySymbolicsExt.jl | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/ext/SymPySymbolicsExt.jl b/ext/SymPySymbolicsExt.jl index f4cef3851..d46866a49 100644 --- a/ext/SymPySymbolicsExt.jl +++ b/ext/SymPySymbolicsExt.jl @@ -11,51 +11,33 @@ end # rule functions function pyconvert_rule_sympy_symbol(::Type{Symbolics.Num}, x::Py) - if !pyisinstance(x,sp.Symbol) - return PythonCall.pyconvert_unconverted() - end name = PythonCall.pyconvert(Symbol,x.name) return PythonCall.pyconvert_return(Symbolics.variable(name)) end function pyconvert_rule_sympy_pow(::Type{Symbolics.Num}, x::Py) - if !pyisinstance(x,sp.Pow) - return PythonCall.pyconvert_unconverted() - end expbase = pyconvert(Symbolics.Num,x.base) exp = pyconvert(Symbolics.Num,x.exp) return PythonCall.pyconvert_return(expbase^exp) end function pyconvert_rule_sympy_mul(::Type{Symbolics.Num}, x::Py) - if !pyisinstance(x,sp.Mul) - return PythonCall.pyconvert_unconverted() - end mult = reduce(*,PythonCall.pyconvert.(Symbolics.Num,x.args)) return PythonCall.pyconvert_return(mult) end function pyconvert_rule_sympy_add(::Type{Symbolics.Num}, x::Py) - if !pyisinstance(x,sp.Add) - return PythonCall.pyconvert_unconverted() - end sum = reduce(+, PythonCall.pyconvert.(Symbolics.Num,x.args)) return PythonCall.pyconvert_return(sum) end function pyconvert_rule_sympy_equality(::Type{Symbolics.Equation}, x::Py) - if !pyisinstance(x,sp.Equality) - return PythonCall.pyconvert_unconverted() - end rhs = pyconvert(Symbolics.Num,x.rhs) lhs = pyconvert(Symbolics.Num,x.lhs) return PythonCall.pyconvert_return(rhs ~ lhs) end function pyconvert_rule_sympy_derivative(::Type{Symbolics.Num}, x::Py) - if !pyisinstance(x,sp.Derivative) - return PythonCall.pyconvert_unconverted() - end variables = pyconvert.(Symbolics.Num,x.variables) derivatives = prod(var -> Differential(var), variables) expr = pyconvert(Symbolics.Num, x.expr) @@ -63,9 +45,6 @@ function pyconvert_rule_sympy_derivative(::Type{Symbolics.Num}, x::Py) end function pyconvert_rule_sympy_function(::Type{Symbolics.Num}, x::Py) - if !pyisinstance(x,sp.Function) - return PythonCall.pyconvert_unconverted() - end name = pyconvert(Symbol,x.name) args = pyconvert.(Symbolics.Num,x.args) func = @variables $name(..) From afa29035b4e0b0df8d9418a5b3e6e532da1b9b41 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 24 Aug 2023 15:27:19 -0400 Subject: [PATCH 7/8] added test set to runtests --- test/runtests.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/runtests.jl b/test/runtests.jl index 530e23af1..0288e8761 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -41,6 +41,7 @@ if GROUP == "All" || GROUP == "Core" @safetestset "Latexify Test" begin include("latexify.jl") end @safetestset "Domain Test" begin include("domains.jl") end @safetestset "SymPy Test" begin include("sympy.jl") end + @safetestset "SymPy to Symbolics Test" begin include("sympy_to_symbolics.jl") @safetestset "Inequality Test" begin include("inequality.jl") end @safetestset "Integral Test" begin include("integral.jl") end @safetestset "CartesianIndex Test" begin include("cartesianindex.jl") end From 48968c0a4d9262273176517b38f8f985fc4eb108 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 24 Aug 2023 15:42:23 -0400 Subject: [PATCH 8/8] added an end --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 0288e8761..60d41f7e6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -41,7 +41,7 @@ if GROUP == "All" || GROUP == "Core" @safetestset "Latexify Test" begin include("latexify.jl") end @safetestset "Domain Test" begin include("domains.jl") end @safetestset "SymPy Test" begin include("sympy.jl") end - @safetestset "SymPy to Symbolics Test" begin include("sympy_to_symbolics.jl") + @safetestset "SymPy to Symbolics Test" begin include("sympy_to_symbolics.jl") end @safetestset "Inequality Test" begin include("inequality.jl") end @safetestset "Integral Test" begin include("integral.jl") end @safetestset "CartesianIndex Test" begin include("cartesianindex.jl") end