JuliaSymbolics / Symbolics.jl

Symbolic programming for the next generation of numerical software
https://docs.sciml.ai/Symbolics/stable/
Other
1.36k stars 151 forks source link

Symbolics doesn't interact well with IntervalArithmetic #1157

Open dpsanders opened 3 months ago

dpsanders commented 3 months ago
julia> using IntervalArithmetic, Symbolics

julia> @variables x, y

julia> interval(1,2) * x
ERROR: TypeError: in Interval, in T, expected T<:Union{AbstractFloat, Rational}, got Type{Real}
Stacktrace:
 [1] promote_rule(::Type{Interval{Float64}}, ::Type{Real})
   @ IntervalArithmetic ~/Dropbox/packages/IntervalArithmetic/src/intervals/construction.jl:542
 [2] promote_type(::Type{Interval{Float64}}, ::Type{Real})
   @ Base ./promotion.jl:313
 [3] promote_symtype(::typeof(*), T::Type{Interval{Float64}}, S::Type{Real})
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/qyMYa/src/methods.jl:102
 [4] mul_t(a::Interval{Float64}, b::SymbolicUtils.BasicSymbolic{Real})
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/qyMYa/src/types.jl:1106
 [5] *(a::Interval{Float64}, b::SymbolicUtils.BasicSymbolic{Real})
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/qyMYa/src/types.jl:1152
 [6] *(a::Interval{Float64}, b::Num)
   @ Symbolics ~/.julia/packages/SymbolicUtils/qyMYa/src/methods.jl:58
 [7] top-level scope
   @ REPL[5]:1

Is there an easy fix for this? It looks like some type promotions are wrong, but I've tried various possible fixes and none have been successful.

ChrisRackauckas commented 3 months ago

We might need to change from using Real to a concrete RealSymbolic (which would be good for other things), then the promotion can be defined on that? Or define the promote_symtype?

dpsanders commented 3 months ago

Thanks, RealSymbolic sounds like a good idea yes; Real seems too general.

I guess I'll try fiddling with promote_symtype in the mean time...

bowenszhu commented 3 months ago

When you create a symbolic variable via Symbolics.jl, its default numeric type is Real.

using Symbolics
@variable x
typeof(Symbolics.value(x))   # SymbolicUtils.BasicSymbolic{Real}

interval(1, 2) is of type Interval{Float64}.

In IntervalArithmetic, const NumTypes is defined as Union{Rational,AbstractFloat} which is very strict. https://github.com/JuliaIntervals/IntervalArithmetic.jl/blob/ee8fd3d4b091d40fb5335b9c78bee737261faa87/src/intervals/construction.jl#L9

Calling IntervalArithmetic.promote_numtype(Interval{Float64}, Real) yields Real. https://github.com/JuliaIntervals/IntervalArithmetic.jl/blob/ee8fd3d4b091d40fb5335b9c78bee737261faa87/src/intervals/construction.jl#L53

promote_rule in IntervalArithmetic tries creating a Interval{Real} type, https://github.com/JuliaIntervals/IntervalArithmetic.jl/blob/ee8fd3d4b091d40fb5335b9c78bee737261faa87/src/intervals/construction.jl#L542-L543 which fails because the parameter T in the parametric type Interval{T} must be a subtype of const NumTypes = Union{Rational,AbstractFloat}. https://github.com/JuliaIntervals/IntervalArithmetic.jl/blob/ee8fd3d4b091d40fb5335b9c78bee737261faa87/src/intervals/construction.jl#L272

If you know the primitive type you want your symbolic variable to be, you can explicitly declare it. https://symbolics.juliasymbolics.org/stable/manual/types/ For example

using Symbolics
@variables z::Float64
typeof(Symbolics.value(z))   # SymbolicUtils.BasicSymbolic{Float64}

Then, the multiplication with IntervalArithmetic.Interval{Float64} works

using IntervalArithmetic, Symbolics
@variables z::Float64
res = interval(1, 2) * z
nothing

Note that you may get an error when you print res = interval(1, 2) * z in the above, which is a separate issue.

dpsanders commented 3 months ago

That's very helpful, thanks!