From 75c3df80d944c1a73f2a29bb15079da31b998a1b Mon Sep 17 00:00:00 2001 From: jverzani Date: Sun, 2 Mar 2025 17:45:21 -0500 Subject: [PATCH 1/4] more methods for real, imag --- src/numerics.jl | 21 ++++++++++++++++++--- test/runtests.jl | 9 +++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/numerics.jl b/src/numerics.jl index 421acbc..d32d223 100644 --- a/src/numerics.jl +++ b/src/numerics.jl @@ -169,16 +169,31 @@ as_numer_denom(x::BasicType) = as_numer_denom(Basic(x)) denominator(x::SymbolicType) = as_numer_denom(x)[2] numerator(x::SymbolicType) = as_numer_denom(x)[1] -## Complex +## Complex; real, imag for :Complex defined elsewhere via ccall +# MethodError if x not a number type. real(x::Basic) = Basic(real(SymEngine.BasicType(x))) -real(x::SymEngine.BasicType) = x +real(x::BasicType{Val{:Integer}}) = x +real(x::BasicType{Val{:RealDouble}}) = x +real(x::BasicType{Val{:RealMPFR}}) = x +real(x::BasicType{Val{:Rational}}) = x +function real(x::BasicType{Val{:Constant}}) + any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return Basic(x) + x == IM && return zero(x) + x == zoo && return oo +end + imag(x::Basic) = Basic(imag(SymEngine.BasicType(x))) imag(x::BasicType{Val{:Integer}}) = Basic(0) imag(x::BasicType{Val{:RealDouble}}) = Basic(0) imag(x::BasicType{Val{:RealMPFR}}) = Basic(0) imag(x::BasicType{Val{:Rational}}) = Basic(0) -imag(x::SymEngine.BasicType) = throw(InexactError()) +function imag(x::BasicType{Val{:Constant}}) + any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return zero(x) + x == IM && return one(x) + x == zoo && return oo +end + # Because of the definitions above, `real(x) == x` for `x::Basic` # such as `x = symbols("x")`. Thus, it is consistent to define the diff --git a/test/runtests.jl b/test/runtests.jl index 9855ce9..b2ca7f1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -285,6 +285,15 @@ expr = x^3 + 3*x^2*y + 3*x*y^2 + y^3 + 1 end end +@test real(Basic(1.5)) == 1.5 +@test isa(real(2 + 3IM), Basic) +@test real(2 + 3IM) == 2 + +@test imag(Basic(1.5)) == 0 +@test isa(imag(2 + 3IM), Basic) +@test imag(2 + 3IM) == Basic(3) + + @test round(Basic(3.14)) == 3.0 @test round(Basic(3.14); digits=1) == 3.1 From 17c2ee6abbec34b4b042acb5de256b397bec1ab8 Mon Sep 17 00:00:00 2001 From: jverzani Date: Mon, 3 Mar 2025 07:42:56 -0500 Subject: [PATCH 2/4] keep fallback --- src/numerics.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/numerics.jl b/src/numerics.jl index d32d223..9bb67fe 100644 --- a/src/numerics.jl +++ b/src/numerics.jl @@ -172,6 +172,7 @@ numerator(x::SymbolicType) = as_numer_denom(x)[1] ## Complex; real, imag for :Complex defined elsewhere via ccall # MethodError if x not a number type. real(x::Basic) = Basic(real(SymEngine.BasicType(x))) +real(x::BasicType{Val{:Symbol}}) = x # issue #273 has issue here real(x::BasicType{Val{:Integer}}) = x real(x::BasicType{Val{:RealDouble}}) = x real(x::BasicType{Val{:RealMPFR}}) = x From 2169999151dac762ceaac74b12eb05b5a05075c6 Mon Sep 17 00:00:00 2001 From: jverzani Date: Fri, 4 Apr 2025 19:33:15 -0400 Subject: [PATCH 3/4] Rework real/imag/conj --- src/numerics.jl | 45 +++++++++++++++++++++------------------ src/types.jl | 5 +++++ test/test-dense-matrix.jl | 6 +++--- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/numerics.jl b/src/numerics.jl index 853ae9a..55b8195 100644 --- a/src/numerics.jl +++ b/src/numerics.jl @@ -207,37 +207,40 @@ function _imag(b::Basic) return a end -real(x::Basic) = Basic(real(SymEngine.BasicType(x))) -real(x::BasicType{Val{:Symbol}}) = x # issue #273 has issue here -real(x::BasicType{Val{:Integer}}) = x -real(x::BasicType{Val{:RealDouble}}) = x -real(x::BasicType{Val{:RealMPFR}}) = x -real(x::BasicType{Val{:Rational}}) = x -function real(x::BasicType{Val{:Constant}}) + +real(x::Basic) = real(get_symengine_class_val(x), x) +real(::T, x) where {T<:RealNumberType} = x +real(::T, x) where {T<:ComplexNumberType} = _real(x) +function real(::Val{:Constant}, x) any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return Basic(x) x == IM && return zero(x) x == zoo && return oo end +real(::Val{<:Any},x) = real(evalf(x)) +real(::Val{:Symbol},x) = throw(ArgumentError("imag only defined on symbolic numbers")) - -imag(x::Basic) = Basic(imag(SymEngine.BasicType(x))) -imag(x::BasicType{Val{:Integer}}) = Basic(0) -imag(x::BasicType{Val{:RealDouble}}) = Basic(0) -imag(x::BasicType{Val{:RealMPFR}}) = Basic(0) -imag(x::BasicType{Val{:Rational}}) = Basic(0) -function imag(x::BasicType{Val{:Constant}}) - any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return zero(x) - x == IM && return one(x) +imag(x::Basic) = imag(get_symengine_class_val(x), x) +imag(::T, x) where {T<:RealNumberType} = Basic(0) +imag(::T, x) where {T<:ComplexNumberType} = _imag(x) +function imag(::Val{:Constant}, x) + any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return Basic(x) + x == IM && return zero(x) x == zoo && return oo end +imag(::Val{<:Any}, x) = imag(evalf(x)) +imag(::Val{:Symbol},x) = throw(ArgumentError("imag only defined on symbolic numbers")) -# Because of the definitions above, `real(x) == x` for `x::Basic` -# such as `x = symbols("x")`. Thus, it is consistent to define the -conj(x::Basic) = Basic(conj(SymEngine.BasicType(x))) -# To allow future extension, we define the fallback on `BasicType``. -conj(x::BasicType) = 2 * real(x.x) - x.x +conj(x::Basic) = conj(get_symengine_class_val(x), x) +conj(::T,x) where {T<:RealNumberType} = x +conj(::T,x) where {T<:ComplexNumberType} = _real(x) - _imag(x)*IM +function conj(::Val{:Constant}, x) + any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return x + x == IM && return zero(x) + x == zoo && return oo +end +conj(::Val{<:Any}, x) = 2*real(x) - x ## For generic programming in Julia float(x::Basic) = float(N(x)) diff --git a/src/types.jl b/src/types.jl index 86c6fb2..318fc41 100644 --- a/src/types.jl +++ b/src/types.jl @@ -241,10 +241,15 @@ convert(::Type{T}, val::T) where {T<:BasicType} = val 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]...} +NumberType = Union{(Val{i} for i in number_types)...} +RealNumberType = Union{(Val{i} for i in real_number_types)...} +ComplexNumberType = Union{(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]...} diff --git a/test/test-dense-matrix.jl b/test/test-dense-matrix.jl index 8ca1e5a..1f8a588 100644 --- a/test/test-dense-matrix.jl +++ b/test/test-dense-matrix.jl @@ -45,8 +45,8 @@ out = M \ b @test SymEngine.dense_matrix_eye(2,2,0) == Basic[1 0; 0 1] # dot product -@test dot(x, x) == x^2 -@test dot([1, x, 0], [y, -2, 1]) == y - 2x +#XXX@test dot(x, x) == x^2 +#XXX@test dot([1, x, 0], [y, -2, 1]) == y - 2x @testset "dense matrix" begin @vars a b c d x y @@ -55,4 +55,4 @@ out = M \ b res = A \ B @test res == [(x - b*(y - c*x/a)/(d - b*c/a))/a, (y - c*x/a)/(d - b*c/a)] -end \ No newline at end of file +end From 4fb2614a3f8451d3352cd9c306996f13b77079a1 Mon Sep 17 00:00:00 2001 From: jverzani Date: Sat, 5 Apr 2025 17:50:20 -0400 Subject: [PATCH 4/4] use deprecation, not error; cleanup --- src/SymEngine.jl | 1 + src/deprecated.jl | 25 +++++++++++++++++++++++++ src/mathops.jl | 2 ++ src/numerics.jl | 34 +++++++++++++++++++++++++--------- test/runtests.jl | 24 +++++++++++++++++------- test/test-dense-matrix.jl | 4 ---- test/test-deprecated.jl | 18 ++++++++++++++++++ 7 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 src/deprecated.jl create mode 100644 test/test-deprecated.jl diff --git a/src/SymEngine.jl b/src/SymEngine.jl index 8a1ebaa..e7be3cb 100644 --- a/src/SymEngine.jl +++ b/src/SymEngine.jl @@ -34,6 +34,7 @@ include("calculus.jl") include("recipes.jl") include("dense-matrix.jl") +include("deprecated.jl") function __init__() init_constants() end diff --git a/src/deprecated.jl b/src/deprecated.jl new file mode 100644 index 0000000..3aa6733 --- /dev/null +++ b/src/deprecated.jl @@ -0,0 +1,25 @@ +function real(::Val{<:Any}, x) + if is_constant(x) + real(evalf(x)) # keep this after removing deprecation + else + Base.depwarn( + """The `real` method for a symbol is deprecated. Its use is for + numeric values only.""", + :real; force=true + ) + x + end +end + +function imag(::Val{<:Any},x) + if is_constant(x) + imag(evalf(x)) # keep this after removing deprecation + else + Base.depwarn( + """The `imag` method for a symbol is deprecated. Its use is for + numeric values only.""", + :imag; force=true + ) + throw(InexactError()) # wrong as it was; seems like it should have been Basic(0) + end +end diff --git a/src/mathops.jl b/src/mathops.jl index 9a0611a..acfc8f1 100644 --- a/src/mathops.jl +++ b/src/mathops.jl @@ -126,6 +126,8 @@ function init_constants() @init_constant NAN nan end +RealConstants = (PI, E, EulerGamma, Catalan, GoldenRatio, oo) + ## ## Conversions Base.convert(::Type{Basic}, x::Irrational{:π}) = PI Base.convert(::Type{Basic}, x::Irrational{:e}) = E diff --git a/src/numerics.jl b/src/numerics.jl index 55b8195..1e1a7a3 100644 --- a/src/numerics.jl +++ b/src/numerics.jl @@ -212,30 +212,46 @@ real(x::Basic) = real(get_symengine_class_val(x), x) real(::T, x) where {T<:RealNumberType} = x real(::T, x) where {T<:ComplexNumberType} = _real(x) function real(::Val{:Constant}, x) - any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return Basic(x) + any(==(x), RealConstants) && return Basic(x) + x == NaN && return x x == IM && return zero(x) x == zoo && return oo end -real(::Val{<:Any},x) = real(evalf(x)) -real(::Val{:Symbol},x) = throw(ArgumentError("imag only defined on symbolic numbers")) +#= +function real(::Val{<:Any},x) + if is_constant(x) + real(evalf(x)) + else + throw(ArgumentError("The `real` method is only defined for numeric constants")) + end +end +=# imag(x::Basic) = imag(get_symengine_class_val(x), x) -imag(::T, x) where {T<:RealNumberType} = Basic(0) +imag(::T, x) where {T<:RealNumberType} = Basic(zero(x)) imag(::T, x) where {T<:ComplexNumberType} = _imag(x) function imag(::Val{:Constant}, x) - any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return Basic(x) + any(==(x), RealConstants) && return Basic(x) + x == NAN && return x x == IM && return zero(x) x == zoo && return oo end -imag(::Val{<:Any}, x) = imag(evalf(x)) -imag(::Val{:Symbol},x) = throw(ArgumentError("imag only defined on symbolic numbers")) - +#= after deprecation removed +function imag(::Val{<:Any},x) + if is_constant(x) + imag(evalf(x)) + else + throw(ArgumentError("The `imag` method is only defined for numeric constants")) + end +end +=# conj(x::Basic) = conj(get_symengine_class_val(x), x) conj(::T,x) where {T<:RealNumberType} = x conj(::T,x) where {T<:ComplexNumberType} = _real(x) - _imag(x)*IM function conj(::Val{:Constant}, x) - any(==(x), (PI, E, EulerGamma, Catalan, oo, NAN)) && return x + any(==(x), RealConstants) && return x + x == NAN && return x x == IM && return zero(x) x == zoo && return oo end diff --git a/test/runtests.jl b/test/runtests.jl index 2554963..ec36ae7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -386,14 +386,23 @@ expr = x^3 + 3*x^2*y + 3*x*y^2 + y^3 + 1 end end -@test real(Basic(1.5)) == 1.5 -@test isa(real(2 + 3IM), Basic) -@test real(2 + 3IM) == 2 - -@test imag(Basic(1.5)) == 0 -@test isa(imag(2 + 3IM), Basic) -@test imag(2 + 3IM) == Basic(3) +@testset "real/imag/conj" begin + @test real(Basic(1.5)) == 1.5 + @test isa(real(2 + 3IM), Basic) + @test real(2 + 3IM) == 2 + + @test imag(Basic(1.5)) == 0 + @test isa(imag(2 + 3IM), Basic) + @test imag(2 + 3IM) == Basic(3) + + for a ∈ (1, 1.0, 1//2, 2im, 3.14*im, 1 + im, 1.2 + 3im) # , pi + for λ ∈ (real, imag, conj) + u,v = Basic(λ(a)), λ(Basic(a)) + @test abs(u-v) == 0 # avoid Basic(0) ≠ Basic(0.0) + end + end +end @test round(Basic(3.14)) == 3.0 @test round(Basic(3.14); digits=1) == 3.1 @@ -424,4 +433,5 @@ end @test deserialized == data end +include("test-deprecated.jl") VERSION >= v"1.9.0" && include("test-allocations.jl") diff --git a/test/test-dense-matrix.jl b/test/test-dense-matrix.jl index 1f8a588..42ad50c 100644 --- a/test/test-dense-matrix.jl +++ b/test/test-dense-matrix.jl @@ -44,10 +44,6 @@ out = M \ b @test SymEngine.dense_matrix_eye(2,2,0) == Basic[1 0; 0 1] -# dot product -#XXX@test dot(x, x) == x^2 -#XXX@test dot([1, x, 0], [y, -2, 1]) == y - 2x - @testset "dense matrix" begin @vars a b c d x y A = [a b; c d] diff --git a/test/test-deprecated.jl b/test/test-deprecated.jl new file mode 100644 index 0000000..9f3059e --- /dev/null +++ b/test/test-deprecated.jl @@ -0,0 +1,18 @@ +using SymEngine +using LinearAlgebra +using Test + +@testset "real/imag/conj deprecation" begin + @vars x y + # dot product + @test dot(x, x) == x^2 + @test dot([1, x, 0], [y, -2, 1]) == y - 2x + + + @vars x + for ex = (x, x^2) + @test real(ex) == ex + @test_throws MethodError imag(ex) + @test conj(ex) == ex + end +end