From 1b2a4348bc43fe4b9574f53431075c4ef32b44a7 Mon Sep 17 00:00:00 2001 From: jverzani Date: Thu, 1 Jan 2026 11:52:35 -0500 Subject: [PATCH 1/5] more informative error; make some values const --- ext/SymEngineTermInterfaceExt.jl | 2 +- src/types.jl | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/SymEngineTermInterfaceExt.jl b/ext/SymEngineTermInterfaceExt.jl index b4c2749..4f96ba7 100644 --- a/ext/SymEngineTermInterfaceExt.jl +++ b/ext/SymEngineTermInterfaceExt.jl @@ -58,7 +58,7 @@ TermInterface.isexpr(x::SymEngine.SymbolicType) = TermInterface.iscall(x) ##TermInterface.issym(x::SymEngine.SymbolicType) = SymEngine.get_symengine_class(x) == :Symbol function TermInterface.operation(x::SymEngine.SymbolicType) - TermInterface.iscall(x) || error("$(typeof(x)) doesn't have an operation!") + TermInterface.iscall(x) || error("Not a call.") return julia_operations[SymEngine.get_type(x) + 1] end diff --git a/src/types.jl b/src/types.jl index f9fb92d..36098d3 100644 --- a/src/types.jl +++ b/src/types.jl @@ -245,18 +245,18 @@ convert(::Type{T}, val::T) where {T<:BasicType} = val ## some type unions possibly useful for dispatch ## Names here match those returned by get_symengine_class() -real_number_types = [:Integer, :RealDouble, :Rational, :RealMPFR] -complex_number_types = [:Complex, :ComplexDouble, :ComplexMPC] -number_types = vcat(real_number_types, complex_number_types) -BasicNumber = Union{[SymEngine.BasicType{Val{i}} for i in number_types]...} -BasicRealNumber = Union{[SymEngine.BasicType{Val{i}} for i in real_number_types]...} -BasicComplexNumber = Union{[SymEngine.BasicType{Val{i}} for i in complex_number_types]...} - -op_types = [:Mul, :Add, :Pow, :Symbol, :Const] -BasicOp = Union{[SymEngine.BasicType{Val{i}} for i in op_types]...} - -trig_types = [:Sin, :Cos, :Tan, :Csc, :Sec, :Cot, :ASin, :ACos, :ATan, :ACsc, :ASec, :ACot] -BasicTrigFunction = Union{[SymEngine.BasicType{Val{i}} for i in trig_types]...} +const real_number_types = [:Integer, :RealDouble, :Rational, :RealMPFR] +const complex_number_types = [:Complex, :ComplexDouble, :ComplexMPC] +const number_types = vcat(real_number_types, complex_number_types) +const BasicNumber = Union{[SymEngine.BasicType{Val{i}} for i in number_types]...} +const BasicRealNumber = Union{[SymEngine.BasicType{Val{i}} for i in real_number_types]...} +const BasicComplexNumber = Union{[SymEngine.BasicType{Val{i}} for i in complex_number_types]...} + +const op_types = [:Mul, :Add, :Pow, :Symbol, :Const] +const BasicOp = Union{[SymEngine.BasicType{Val{i}} for i in op_types]...} + +const trig_types = [:Sin, :Cos, :Tan, :Csc, :Sec, :Cot, :ASin, :ACos, :ATan, :ACsc, :ASec, :ACot] +const BasicTrigFunction = Union{[SymEngine.BasicType{Val{i}} for i in trig_types]...} From a5dfa932093ecc48cdb43cbf9d676d770dea51cd Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 28 Jan 2026 17:58:49 -0500 Subject: [PATCH 2/5] a few minor adjustments --- ext/SymEngineTermInterfaceExt.jl | 7 ++++++- src/mathfuns.jl | 2 ++ src/mathops.jl | 1 + src/numerics.jl | 6 +++++- src/subs.jl | 18 +++++++++++------- test/runtests.jl | 7 +++++++ 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/ext/SymEngineTermInterfaceExt.jl b/ext/SymEngineTermInterfaceExt.jl index 4f96ba7..2991f5b 100644 --- a/ext/SymEngineTermInterfaceExt.jl +++ b/ext/SymEngineTermInterfaceExt.jl @@ -57,9 +57,14 @@ TermInterface.isexpr(x::SymEngine.SymbolicType) = TermInterface.iscall(x) ##TermInterface.issym(x::SymEngine.SymbolicType) = SymEngine.get_symengine_class(x) == :Symbol +TermInterface.operation(x::SymEngine.SymFunction) = x function TermInterface.operation(x::SymEngine.SymbolicType) TermInterface.iscall(x) || error("Not a call.") - return julia_operations[SymEngine.get_type(x) + 1] + fn = julia_operations[SymEngine.get_type(x) + 1] + if ismissing(fn) && SymEngine.get_julia_class(x) == :functionsymbol + fn = SymEngine.SymFunction(Symbol(SymEngine.get_name(x))) + end + fn end function TermInterface.arguments(x::SymEngine.SymbolicType) diff --git a/src/mathfuns.jl b/src/mathfuns.jl index 06bbb3a..eff1d60 100644 --- a/src/mathfuns.jl +++ b/src/mathfuns.jl @@ -189,6 +189,8 @@ mutable struct SymFunction end SymFunction(s::Symbol) = SymFunction(string(s)) +Base.Symbol(s::SymFunction) = Symbol(s.name) +Base.nameof(s::SymFunction) = Symbol(s) function (f::SymFunction)(x::CVecBasic) a = Basic() diff --git a/src/mathops.jl b/src/mathops.jl index 9a0611a..2779717 100644 --- a/src/mathops.jl +++ b/src/mathops.jl @@ -129,6 +129,7 @@ end ## ## Conversions Base.convert(::Type{Basic}, x::Irrational{:π}) = PI Base.convert(::Type{Basic}, x::Irrational{:e}) = E +Base.convert(::Type{Basic}, x::Irrational{:ℯ}) = E Base.convert(::Type{Basic}, x::Irrational{:γ}) = EulerGamma Base.convert(::Type{Basic}, x::Irrational{:catalan}) = Catalan Base.convert(::Type{Basic}, x::Irrational{:φ}) = (1 + Basic(5)^Basic(1//2))/2 diff --git a/src/numerics.jl b/src/numerics.jl index af1cda6..0d9d71c 100644 --- a/src/numerics.jl +++ b/src/numerics.jl @@ -130,6 +130,8 @@ function N(::Val{<:Any}, b::Basic) imag(out) == Basic(0.0) ? N(real(out)) : N(out) end +unwrap_const(b) = is_constant(b) ? N(b) : b + ## deprecate N(::BasicType) N(b::BasicType{T}) where {T} = N(convert(Basic, b), T) @@ -279,7 +281,9 @@ function Base.isone(x::Basic) x == one(x) end - +## julia predicates we can mirror +Base.iseven(x::Basic) = is_a_Integer(x) && iseven(convert(Integer, x)) +Base.isodd(x::Basic) = is_a_Integer(x) && isodd(convert(Integer, x)) ## These should have support in symengine-wrapper, but currently don't trunc(x::Basic, args...) = Basic(trunc(N(x), args...)) diff --git a/src/subs.jl b/src/subs.jl index 2689cf0..9c99b3d 100644 --- a/src/subs.jl +++ b/src/subs.jl @@ -77,28 +77,32 @@ map_fn(key, fn_map) = haskey(fn_map, key) ? fn_map[key] : Symbol(lowercase(strin const julia_classes = map_fn.(symengine_classes, (fn_map,)) get_julia_class(x::Basic) = julia_classes[get_type(x) + 1] -Base.nameof(ex::Basic) = Symbol(toString(ex)) +get_julia_class(x::SymFunction) = Symbol(x) +#_convert(::Type{Expr}, ex::SymFunction) = Symbol(ex) function _convert(::Type{Expr}, ex::Basic) fn = get_symengine_class(ex) - if fn == :Symbol - return nameof(ex) + if fn ∈ (:Symbol,) + return Symbol(ex) elseif (fn in number_types) || (fn == :Constant) return N(ex) end + fn′ = get_julia_class(ex) + + if fn′ === :functionsymbol + fn′ = Symbol(get_name(ex)) + end as = get_args(ex) - fn′ = get_julia_class(ex) - Expr(:call, fn′, [_convert(Expr,a) for a in as]...) + Expr(:call, fn′, (_convert(Expr,a) for a in as)...) end - function convert(::Type{Expr}, ex::Basic) fn = get_symengine_class(ex) if fn == :Symbol - return Expr(:call, :*, nameof(ex), 1) + return Expr(:call, :*, Symbol(ex), 1) elseif (fn in number_types) || (fn == :Constant) return Expr(:call, :*, N(ex), 1) end diff --git a/test/runtests.jl b/test/runtests.jl index 34c884f..077d4b4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -92,12 +92,18 @@ u,v,w = x(2.1), x(1), x(0) @test isinteger(v) @test isone(v) @test iszero(w) +@test iseven(x) == iseven(u) == iseven(v) +@test iseven(w) +@test isodd(x) == isodd(w) == isodd(u) +@test isodd(v) @test (@allocated isreal(u)) == 0 @test (@allocated isinteger(v)) == 0 @test (@allocated isone(x)) == 0 @test (@allocated iszero(x)) == 0 @test (@allocated isone(v)) > 0 # checking v==zero(v) value allocates @test (@allocated iszero(w)) > 0 +@test (@allocated iseven(v)) > 0 +@test (@allocated isodd(v)) > 0 ## calculus x,y = symbols("x y") @@ -189,6 +195,7 @@ A = [x 2] @test lambdify(A, [x])(1) == [1 2] @test lambdify(A)(1) == [1 2] @test isa(convert.(Expr, [0 x x+1]), Array{Expr}) +@test all(x -> isa(x, Union{Number, Symbol, Expr}), SymEngine._convert.(Expr, [0 x x+1])) ## N, convert, _convert for val in samples From ec8ccab4138c836716e4deba8ae8e2df0d573c99 Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 28 Jan 2026 18:12:54 -0500 Subject: [PATCH 3/5] some tests --- test/runtests.jl | 16 ++++++++++++++++ test/test-SymbolicUtils.jl | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 077d4b4..f68cd00 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -342,6 +342,20 @@ t = BigFloat(1.23) @test Basic(:(-y)) == -y @test Basic(:(-2*(x-2*y))) == -2*(x-2*y) +# Check that constructing Basic from Irrational works +for a ∈ (:pi, :ℯ, :e, :φ, :γ, + MathConstants.eulergamma, + MathConstants.π, + MathConstants.catalan, + MathConstants.pi, + MathConstants.φ, + MathConstants.ℯ, + MathConstants.e, + MathConstants.golden, + MathConstants.γ) + @test Basic(a) isa Basic +end + @test Basic(0)/0 == NAN @test subs(1/x, x, 0) == Basic(1)/0 @@ -352,6 +366,8 @@ d = Dict(x=>y, y=>x) @test sin(PI/2-x) == cos(x) f = SymFunction("f") +@test nameof(f) == :f +@test_throws MethodError nameof(f(x)) @test string(f(x, y)) == "f(x, y)" @test string(f([x, y])) == "f(x, y)" @test string(f(2*x)) == "f(2*x)" diff --git a/test/test-SymbolicUtils.jl b/test/test-SymbolicUtils.jl index 0b25afc..b810b11 100644 --- a/test/test-SymbolicUtils.jl +++ b/test/test-SymbolicUtils.jl @@ -3,11 +3,12 @@ using SymEngine import TermInterface @testset "TermInterface" begin - @vars x + @vars x, ∫() @test !TermInterface.iscall(x) @test TermInterface.iscall(x^2) @test TermInterface.operation(sin(x)) == sin @test TermInterface.arguments(sin(x)) == [x] + @test nameof(TermInterface.operation(∫(sin(x), x))) == nameof(∫) end #= From dcac5d48d3bb071f87a41f6377ffe2458da7cbff Mon Sep 17 00:00:00 2001 From: jverzani Date: Wed, 28 Jan 2026 18:16:48 -0500 Subject: [PATCH 4/5] same defn for Symbol and nameof; bump version --- Project.toml | 2 +- src/mathfuns.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 64eaaab..8b6e77e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "SymEngine" uuid = "123dc426-2d89-5057-bbad-38513e3affd8" -version = "0.13.1" +version = "0.13.2" [deps] Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" diff --git a/src/mathfuns.jl b/src/mathfuns.jl index eff1d60..d83eb7e 100644 --- a/src/mathfuns.jl +++ b/src/mathfuns.jl @@ -190,7 +190,7 @@ end SymFunction(s::Symbol) = SymFunction(string(s)) Base.Symbol(s::SymFunction) = Symbol(s.name) -Base.nameof(s::SymFunction) = Symbol(s) +Base.nameof(s::SymFunction) = Symbol(s.name) function (f::SymFunction)(x::CVecBasic) a = Basic() From 6144e9e1e35138d61453f4dcadc0acc30bd4098c Mon Sep 17 00:00:00 2001 From: jverzani Date: Thu, 29 Jan 2026 16:24:49 -0500 Subject: [PATCH 5/5] avoid an error --- src/mathops.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mathops.jl b/src/mathops.jl index 2779717..40885a4 100644 --- a/src/mathops.jl +++ b/src/mathops.jl @@ -136,7 +136,7 @@ Base.convert(::Type{Basic}, x::Irrational{:φ}) = (1 + Basic(5)^Basic(1//2))/2 Base.convert(::Type{BasicType}, x::Irrational) = BasicType(convert(Basic, x)) ## Logical operators -Base.:<(x::SymbolicType, y::SymbolicType) = N(x) < N(y) +Base.:<(x::SymbolicType, y::SymbolicType) = is_constant(x) && is_constant(y) && N(x) < N(y) Base.:<(x::SymbolicType, y) = <(promote(x,y)...) Base.:<(x, y::SymbolicType) = <(promote(x,y)...)