Hi ! Thanks for the link I’ll watch.
Here is the miminal code extracted from my project. I’ll try with the debugger but it seems to me this problem has been introduced since Julia 1.4.2 because I tested on versions 0.7 1.2.x 1.3.x and got the correct answers.
MP means Max-Plus: a tropical algebra (see here for more information http://www.stanczyk.pro/mpa/download.php?id=max-plus-1.7&ex=pdf )
This code redefines operators + as operator max() and operator * as operator *. We define neutral and absorbing elements zeros() as -Inf and one() as 0. I did not include conversion and promotion (not used in this basic example). The magic in Julia is that all complex algebra functions are automatically working … when there is no regression introduced by newer version
using LinearAlgebra, SparseArrays
struct MP
v::Float64
end
Base.zero(::Type{MP}) = MP(typemin(Float64))
Base.zero(x::MP) = zero(typeof(x))
Base.one(::Type{MP}) = MP(zero(Float64))
Base.one(x::MP) = one(typeof(x))
Base.:(+)(x::MP, y::MP) = MP(max(x.v, y.v))
Base.:(+)(x::MP, y::Real) = MP(max(x.v, y))
Base.:(+)(x::Real, y::MP) = MP(max(x, y.v))
Base.:(*)(x::MP, y::MP) = MP(x.v + y.v)
Base.:(*)(x::MP, y::Real) = MP(x.v + y)
Base.:(*)(x::Real, y::MP) = MP(x + y.v)
#Not needed for this example:
#MP(x::MP) = MP(x.v)
#Base.promote_rule(::Type{MP}, ::Type{U}) where {U} = MP
#Base.convert(::MP, x::Number) = MP(x)
This code gives the good result:
julia> A=[MP(1) MP(2); MP(3) MP(4)]
julia> A*A
2×2 Array{MP,2}:
MP(5.0) MP(6.0)
MP(7.0) MP(8.0)
This one does not give the correct answer since Julia 1.4.2 (1.5.x, 1.6.0):
julia> sparse(A)*A
2×2 Matrix{MP}:
MP(6.0) MP(7.0)
MP(8.0) MP(9.0)
When commenting
#Base.:(+)(x::MP, y::Real) = MP(max(x.v, y))
#Base.:(+)(x::Real, y::MP) = MP(max(x, y.v))
#Base.:(*)(x::MP, y::Real) = MP(x.v + y)
#Base.:(*)(x::Real, y::MP) = MP(x + y.v)
I’ll have this interesting error:
ERROR: MethodError: no method matching *(::MP, ::Bool)
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
*(::StridedArray{P, N} where N, ::Real) where P<:Dates.Period at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/Dates/src/deprecated.jl:44
*(::Union{SparseVector{Tv, Ti}, SubArray{Tv, 1, var"#s832", Tuple{Base.Slice{Base.OneTo{Int64}}, Int64}, false} where var"#s832"<:SparseArrays.AbstractSparseMatrixCSC{Tv, Ti}, SubArray{Tv, 1, var"#s832", Tuple{Base.Slice{Base.OneTo{Int64}}}, false} where var"#s832"<:AbstractSparseVector{Tv, Ti}} where {Tv, Ti}, ::Number) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/SparseArrays/src/sparsevector.jl:1448
...
Stacktrace:
[1] mul!(C::Matrix{MP}, A::SparseMatrixCSC{MP, Int64}, B::Matrix{MP}, α::Bool, β::Bool)
@ SparseArrays /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/SparseArrays/src/linalg.jl:41
[2] *(A::SparseMatrixCSC{MP, Int64}, B::Matrix{MP})
@ SparseArrays /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/SparseArrays/src/linalg.jl:53
[3] top-level scope
@ REPL[12]:1
This is not good since we are not supposed to call *(::MP, ::Bool)
bool is not for Max-Plus algebra. Note that we may want to write MP(1.0) + 3.0
just for the user to avoid typing too many characters 3.0 meaning MP(3.0)) but MP(1.0) + false
has nonsense.
By the way this is a second regression I found. Identity matrix that used to be created with the eye()
function was made like this:
[ one() zero() zero()
zero() one() zero()
zero() zero() one()]
Since eye()
no longer exist, replaced in Julia 1.x (I guess) by I
which is not working because AGAIN with introduction boolean instead of zero() and one()
julia> I
UniformScaling{Bool}
true*I
and therefore Identity are no longer working in Max-Plus. I succeeded to make a workaround in my package but I have to find a real fix. See Matrix identity not calling one() ? · Issue #33036 · JuliaLang/julia · GitHub