Symbolics.jl icon indicating copy to clipboard operation
Symbolics.jl copied to clipboard

Small inconsistencies

Open andreasvarga opened this issue 2 years ago • 3 comments

Working with Symbolics.jl, I came accross to the following small inconsistency (see the true value in the (1,2) element):

julia> F
2×2 Matrix{Num}:
  0.0                 1.0
 -1.0 - 10.0cos(t)  -24.0 - 19.0sin(t)

julia> inv(F)
2×2 Matrix{Num}:
 (24.0 + 19.0sin(t)) / (-1.0 - 10.0cos(t))  true / (-1.0 - 10.0cos(t))
   1.0                                        0.0

Also, checking the result produces a (1,2) element of apparently different type from the rest:

julia> Symbolics.simplify.(inv(F)*F)
2×2 Matrix{Num}:
 1.0  0
 0.0  1.0

Also

julia> F\F
2×2 Matrix{Num}:
 1    0.0
 0.0  1.0

julia> F/F
2×2 Matrix{Num}:
 1.0  0.0
 0.0  1

generate entries of different nature.

I wonder if this is the expected behaviour.

andreasvarga avatar Aug 09 '22 14:08 andreasvarga

https://github.com/JuliaSymbolics/Symbolics.jl/blob/da72404e92ad6894697f741747bfd828ff9df516/src/num.jl#L1-L3

Num is a wrapper for Real, SymbolicUtils.Symbolic, etc. The symtype T of SymbolicUtils.Symbolic{T} and the numeric types of integers and floating-point numbers are ignored because of Num.

Bool true

inv(A::StridedMatrix{T}) calls inv!(A::LU{T,<:StridedMatrix}) https://github.com/JuliaLang/julia/blob/686afd3e41a797c070000d2d402ea224b4b25a0b/stdlib/LinearAlgebra/src/dense.jl#L893

inv!(A::LU{T,<:StridedMatrix}) calls Matrix{T}(s::UniformScaling, dims::Dims{2}) with s = I where I is the identity matrix (see doc) https://github.com/JuliaLang/julia/blob/686afd3e41a797c070000d2d402ea224b4b25a0b/stdlib/LinearAlgebra/src/lu.jl#L514

Matrix{T}(s::UniformScaling, dims::Dims{2}) calls T(s.λ) where s = I https://github.com/JuliaLang/julia/blob/686afd3e41a797c070000d2d402ea224b4b25a0b/stdlib/LinearAlgebra/src/uniformscaling.jl#L498

I.λ is true of type Bool which indicates the simplest one. T is normally Int64, Float64, etc. So T(s.λ) essentially converts true to a one of the desired numeric type T.

However, in our case T is Num, so T(true) actually calls the constructor of Num, instead of doing type conversion.

Int64 1

one(::Type{T}) https://github.com/JuliaLang/julia/blob/70656e214882ccb8744386e2f08db1e1eea46919/base/number.jl#L346 https://github.com/JuliaSymbolics/Symbolics.jl/blob/da72404e92ad6894697f741747bfd828ff9df516/src/num.jl#L158

When some functions, such as lu, call one internally, the result is just a 64-bit integer Int64 1.

using Symbolics
@variables x::Float64
x_one = one(x) # Int64 1
t = typeof(x) # Num
xt_one = one(t) # Int64 1
y = Num(3.4)
y_one = one(y) # Int64 1

Fix

We can maybe add a parametric type to Num{T}, but this doesn't seem easy.

bowenszhu avatar Aug 09 '22 22:08 bowenszhu

Num{T} <: T would be ideal but it's not possible in the language. We've discussed with the core compiler team and I don't think it'll be able to do this anytime soon. So Num <: Number, which is where the oddities stem from. These are a bit hard to fix without overloading every single fallback individually (which we can do)

ChrisRackauckas avatar Aug 11 '22 00:08 ChrisRackauckas

The true is gone now, but

using Symbolics
F = [0.0 1.0; -1.0-10.0*cos(t) -24.0-19.0*sin(t)]
inv(F)

outputs mixed floats and integers:

2×2 Matrix{Num}:
 (-24.0 - 19.0sin(t)) / (1.0 + 10.0cos(t))  -1.0 / (1.0 + 10.0cos(t))
    1                                        0.0

hersle avatar May 09 '24 19:05 hersle