From 6bdde570271217684844c7f58a5f433d5aef2a14 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Tue, 18 Nov 2025 17:05:27 -0500 Subject: [PATCH 01/23] Add abstract types. --- src/TropicalNumbers.jl | 91 ++------------------ src/abstract_semiring.jl | 177 +++++++++++++++++++++++++++++++++++++++ src/counting_tropical.jl | 8 +- src/tropical_andor.jl | 26 +++--- src/tropical_bitwise.jl | 57 ++++--------- src/tropical_maxmin.jl | 49 +++++------ src/tropical_maxmul.jl | 51 ++++++++--- src/tropical_maxplus.jl | 64 ++++++++------ src/tropical_minplus.jl | 71 +++++++--------- 9 files changed, 342 insertions(+), 252 deletions(-) create mode 100644 src/abstract_semiring.jl diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 0084ae7..259ef6d 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -1,6 +1,6 @@ module TropicalNumbers -export content, neginf, posinf +export content, neginf, posinf, sup, inf export TropicalTypes, AbstractSemiring export TropicalAndOr @@ -12,33 +12,15 @@ export TropicalMaxMin, TropicalMaxMinF64, TropicalMaxMinF32, TropicalMaxMinF16, export TropicalBitwise, TropicalBitwiseI64, TropicalBitwiseI32, TropicalBitwiseI16 export CountingTropical, CountingTropicalF16, CountingTropicalF32, CountingTropicalF64, CountingTropicalI16, CountingTropicalI32, CountingTropicalI64 +neginf(::Type{T}) where T = typemin(T) +neginf(::Type{T}) where T<:AbstractFloat = typemin(T) +neginf(::Type{T}) where T<:Rational = typemin(T) +neginf(::Type{T}) where T<:Integer = T(-999999) +neginf(::Type{Int16}) = Int16(-16384) +neginf(::Type{Int8}) = Int8(-64) +posinf(::Type{T}) where T = -neginf(T) -""" - AbstractSemiring <: Number - -A [`Semiring`](https://en.wikipedia.org/wiki/Semiring) is a set R equipped with two binary operations + and ⋅, called addition and multiplication, such that: - -* (R, +) is a monoid with identity element called 0; -* (R, ⋅) is a monoid with identity element called 1; -* Addition is commutative; -* Multiplication by the additive identity 0 annihilates ; -* Multiplication left- and right-distributes over addition; -* Explicitly stated, (R, +) is a commutative monoid. - -[`Tropical number`](https://en.wikipedia.org/wiki/Tropical_geometry) are a set of semiring algebras, described as -* (R, +, ⋅, 0, 1). -where R is the set, + and ⋅ are the opeartions and 0, 1 are their identity element, respectively. - -In this package, the following tropical algebras are implemented: -* TropicalAndOr, ([T, F], or, and, false, true); -* Tropical (TropicalMaxPlus), (ℝ, max, +, -Inf, 0); -* TropicalMinPlus, (ℝ, min, +, Inf, 0); -* TropicalMaxMul, (ℝ⁺, max, ⋅, 0, 1). - -We implemented fast tropical matrix multiplication in [`TropicalGEMM`](https://github.com/TensorBFS/TropicalGEMM.jl/) and [`CuTropicalGEMM`](https://github.com/ArrogantGao/CuTropicalGEMM.jl/). -""" -abstract type AbstractSemiring <: Number end - +include("abstract_semiring.jl") include("tropical_maxplus.jl") include("tropical_andor.jl") include("tropical_minplus.jl") @@ -69,59 +51,4 @@ for NBIT in [16, 32, 64] @eval const $(Symbol(:CountingTropical, :I, NBIT)) = CountingTropical{$(Symbol(:Int, NBIT)),$(Symbol(:Float, NBIT))} end -# alias -for T in [:Tropical, :TropicalMaxMul, :TropicalMaxMin, :CountingTropical] - for OP in [:>, :<, :(==), :>=, :<=, :isless] - @eval Base.$OP(a::$T, b::$T) = $OP(a.n, b.n) - end - @eval begin - content(x::$T) = x.n - content(x::Type{$T{X}}) where X = X - Base.isapprox(x::AbstractArray{<:$T}, y::AbstractArray{<:$T}; kwargs...) = all(isapprox.(x, y; kwargs...)) - Base.show(io::IO, ::MIME"text/plain", t::$T) = Base.show(io, t) - Base.isnan(x::$T) = isnan(content(x)) - end -end - -for T in [:TropicalMinPlus, :TropicalBitwise] - @eval begin - content(x::$T) = x.n - content(x::Type{$T{X}}) where X = X - Base.isapprox(x::AbstractArray{<:$T}, y::AbstractArray{<:$T}; kwargs...) = all(isapprox.(x, y; kwargs...)) - Base.show(io::IO, ::MIME"text/plain", t::$T) = Base.show(io, t) - Base.isnan(x::$T) = isnan(content(x)) - end -end - -for T in [:TropicalAndOr] - for OP in [:>, :<, :(==), :>=, :<=, :isless] - @eval Base.$OP(a::$T, b::$T) = $OP(a.n, b.n) - end - @eval begin - content(x::$T) = x.n - Base.isapprox(x::AbstractArray{<:$T}, y::AbstractArray{<:$T}; kwargs...) = all(isapprox.(x, y; kwargs...)) - Base.show(io::IO, ::MIME"text/plain", t::$T) = Base.show(io, t) - Base.isnan(x::$T) = isnan(content(x)) - end -end - -for T in [:Tropical, :TropicalMinPlus, :TropicalMaxMul, :CountingTropical] - @eval begin - # this is for CUDA matmul - Base.:(*)(a::$T, b::Bool) = b ? a : zero(a) - Base.:(*)(b::Bool, a::$T) = b ? a : zero(a) - Base.:(/)(a::$T, b::Bool) = b ? a : a / zero(a) - Base.:(/)(b::Bool, a::$T) = b ? inv(a) : zero(a) - Base.div(a::$T, b::Bool) = b ? a : a ÷ zero(a) - Base.div(b::Bool, a::$T) = b ? one(a) ÷ a : zero(a) - end -end - -for T in [:TropicalMaxMin, :TropicalBitwise] - @eval begin - Base.:(*)(a::$T, b::Bool) = b ? a : zero(a) - Base.:(*)(b::Bool, a::$T) = b ? a : zero(a) - end -end - end # module diff --git a/src/abstract_semiring.jl b/src/abstract_semiring.jl new file mode 100644 index 0000000..46aa0f1 --- /dev/null +++ b/src/abstract_semiring.jl @@ -0,0 +1,177 @@ +""" + AbstractSemiring{T} <: Number + +A [`semiring`](https://en.wikipedia.org/wiki/Semiring) is a quintuple (R, +, ×, 0, 1), where + +* (R, +, 0) is a commutative monoid +* (R, ×, 1) is a monoid +* multiplication (×) distributes over addition (+) +* zero (0) is absorbing + +This package implements the following semirings. + +* TropicalAndOr, ({0, 1}, or, and, 0, 1); +* TropicalMaxPlus, ([-∞, +∞], max, +, -∞, 0); +* TropicalMinPlus, ([-∞, +∞], min, +, +∞, 0); +* TropicalMaxMin, ([-∞, +∞], max, min, -∞, +∞) +* TropicalMaxMul, ([0, +∞], max, ×, 0, 1). + +Fast semiring matrix multiplication is implemented in the following libraries. + +* [`TropicalGEMM`](https://github.com/TensorBFS/TropicalGEMM.jl/) +* [`CuTropicalGEMM`](https://github.com/ArrogantGao/CuTropicalGEMM.jl/) + +""" +abstract type AbstractSemiring{T} <: Number end + +function content(::Type{<:AbstractSemiring{T}}) where {T} + return T +end + +function Base.show(io::IO, a::AbstractSemiring) + n = content(a) + print(io, "$(n)s") + return +end + +function Base.show(io::IO, ::MIME"text/plain", a::AbstractSemiring) + return show(io, a) +end + +function Base.isnan(x::AbstractSemiring) + return isnan(content(a)) +end + +function Base.isapprox(a::T, b::T; kw...) where {T <: AbstractSemiring} + return isapprox(content(a), content(b); kw...) +end + +function Base.isapprox(A::AbstractArray{<:AbstractSemiring}, B::AbstractArray{<:AbstractSemiring}; kw...) + return all(isapprox.(A, B; kw...)) +end + +function Base.typemin(::T) where {T <: AbstractSemiring} + return typemin(T) +end + +function Base.typemax(::T) where {T <: AbstractSemiring} + return typemax(T) +end + +function Base.zero(::T) where {T <: AbstractSemiring} + return zero(T) +end + +function Base.one(::T) where {T <: AbstractSemiring} + return one(T) +end + +function Base.inv(a::T) where {T <: AbstractSemiring} + return one(T) / a +end + +function invint(a::T) where {T <: AbstractSemiring} + return div(one(T), a) +end + +function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} + return b ? a : zero(T) +end + +function Base.:*(b::Bool, a::T) where {T <: AbstractSemiring} + return b ? a : zero(T) +end + +function Base.:/(a::T, b::Bool) where {T <: AbstractSemiring} + return b ? a : a / zero(T) +end + +function Base.div(a::T, b::Bool) where {T <: AbstractSemiring} + return b ? a : div(a, zero(T)) +end + +function Base.:/(b::Bool, a::T) where {T <: AbstractSemiring} + return b ? inv(a) : zero(T) +end + +function Base.div(b::Bool, a::T) where {T <: AbstractSemiring} + return b ? invint(a) : zero(T) +end + +function Base.:(==)(a::T, b::T) where {T <: AbstractSemiring} + return content(a) == content(b) +end + +function Base.isless(a::T, b::T) where {T <: AbstractSemiring} + return a < b || !(b >= a) || isless(content(a), content(b)) +end + +""" + AbstractIdempotentSemiring{T} <: AbstractSemiring{T} + +A semiring (R, +, ×, 0, 1) is additively idempotent if + +* x + x = x + +for all x ∈ R. +""" +abstract type AbstractIdempotentSemiring{T} <: AbstractSemiring{T} end + +function Base.zero(::Type{T}) where {T <: AbstractIdempotentSemiring} + return typemin(T) +end + +function Base.:+(a::T, b::T) where {T <: AbstractIdempotentSemiring} + return sup(a, b) +end + +function Base.:/(a::T, b::Bool) where {T <: AbstractIdempotentSemiring} + return b ? a : typemax(a) +end + +function Base.:div(a::T, b::Bool) where {T <: AbstractIdempotentSemiring} + return b ? a : typemax(a) +end + +function Base.:<=(a::T, b::T) where {T <: AbstractIdempotentSemiring} + return sup(a, b) == b +end + +function Base.:>=(a::T, b::T) where {T <: AbstractIdempotentSemiring} + return b <= a +end + +function Base.:<(a::T, b::T) where {T <: AbstractIdempotentSemiring} + return a != b && a <= b +end + +function Base.:>(a::T, b::T) where {T <: AbstractIdempotentSemiring} + return b < a +end + +""" + AbstractSimpleSemiring{T} <: AbstractIdemptentSemiring{T} + +A additively idempotent semiring (R, +, ×, 0, 1) is simple if + +* a × b = inf(a, b) + +for all a, b ∈ R. +""" +abstract type AbstractSimpleSemiring{T} <: AbstractIdempotentSemiring{T} end + +function Base.:*(a::T, b::T) where {T <: AbstractSimpleSemiring} + return inf(a, b) +end + +function Base.one(::Type{T}) where {T <: AbstractSimpleSemiring} + return typemax(T) +end + +function Base.inv(a::T) where {T <: AbstractSimpleSemiring} + return typemax(T) +end + +function invint(a::T) where {T <: AbstractSimpleSemiring} + return typemax(T) +end diff --git a/src/counting_tropical.jl b/src/counting_tropical.jl index edeee91..20330ad 100644 --- a/src/counting_tropical.jl +++ b/src/counting_tropical.jl @@ -1,5 +1,5 @@ """ - CountingTropical{T,CT} <: Number + CountingTropical{T,CT} <: AbstractSemiring{T} Counting tropical number type is also a semiring algebra. It is tropical algebra with one extra field for counting, it is introduced in [arXiv:2008.06888](https://arxiv.org/abs/2008.06888). @@ -20,7 +20,7 @@ julia> zero(CountingTropicalF64) (-Inf, 0.0)ₜ ``` """ -struct CountingTropical{T,CT} <: Number +struct CountingTropical{T,CT} <: AbstractSemiring{T} n::T c::CT end @@ -53,13 +53,11 @@ Base.typemin(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = Count Base.zero(::Type{CountingTropical{T}}) where T = zero(CountingTropical{T,T}) Base.zero(::Type{CountingTropical{T,CT}}) where {T<:Integer,CT} = CountingTropical(T(-999999), zero(CT)) Base.zero(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = typemin(CountingTropical{T, CT}) -Base.zero(::T) where T<:CountingTropical = zero(T) Base.one(::Type{CountingTropical{T}}) where T = one(CountingTropical{T,T}) Base.one(::Type{CountingTropical{T,CT}}) where {T<:Integer,CT} = CountingTropical(zero(T), one(CT)) Base.one(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = CountingTropical(zero(T), one(CT)) -Base.one(::T) where T<:CountingTropical = one(T) Base.isapprox(a::CountingTropical, b::CountingTropical; kwargs...) = isapprox(a.n, b.n; kwargs...) && isapprox(a.c, b.c; kwargs...) -Base.show(io::IO, t::CountingTropical) = Base.print(io, "$((t.n, t.c))ₜ") +Base.show(io::IO, t::CountingTropical) = Base.print(io, "$((t.n, t.c))s") Base.promote_rule(::Type{CountingTropical{T1,CT1}}, b::Type{CountingTropical{T2,CT2}}) where {T1,T2,CT1,CT2} = CountingTropical{promote_type(T1,T2), promote_type(CT1,CT2)} diff --git a/src/tropical_andor.jl b/src/tropical_andor.jl index 23e4794..20daba6 100644 --- a/src/tropical_andor.jl +++ b/src/tropical_andor.jl @@ -1,7 +1,7 @@ """ - TropicalAndOr <: Number + TropicalAndOr <: AbstractSimpleSemiring{Bool} TropicalAndOr is a semiring algebra, can be described by * TropicalAndOr, ([T, F], or, and, false, true). @@ -30,26 +30,22 @@ julia> zero(TropicalAndOr) falseₜ ``` """ -struct TropicalAndOr <: AbstractSemiring +struct TropicalAndOr <: AbstractSimpleSemiring{Bool} n::Bool TropicalAndOr(x::T) where T <: Bool = new(x) end -Base.show(io::IO, t::TropicalAndOr) = Base.print(io, "$(t.n)ₜ") +content(a::TropicalAndOr) = a.n -Base.:*(a::TropicalAndOr, b::TropicalAndOr) = TropicalAndOr(a.n && b.n) - -Base.:+(a::TropicalAndOr, b::TropicalAndOr) = TropicalAndOr(a.n || b.n) +inf(a::TropicalAndOr, b::TropicalAndOr) = TropicalAndOr(a.n && b.n) +sup(a::TropicalAndOr, b::TropicalAndOr) = TropicalAndOr(a.n || b.n) Base.typemin(::Type{TropicalAndOr}) = TropicalAndOr(false) -Base.zero(::Type{TropicalAndOr}) = typemin(TropicalAndOr) -Base.zero(::TropicalAndOr) = zero(TropicalAndOr) - -Base.one(::Type{TropicalAndOr}) = TropicalAndOr(true) -Base.one(::TropicalAndOr) = one(TropicalAndOr) +Base.typemax(::Type{TropicalAndOr}) = TropicalAndOr(true) -# inverse and division -Base.inv(x::TropicalAndOr) = TropicalAndOr(!x.n) +Base.:\(a::TropicalAndOr, b::TropicalAndOr) = b / a +Base.:/(b::TropicalAndOr, a::TropicalAndOr) = TropicalAndOr(b.n || !a.n) +Base.div(b::TropicalAndOr, a::TropicalAndOr) = b / a -# bool type only have two values -Base.isapprox(x::TropicalAndOr, y::TropicalAndOr; kwargs...) = isapprox(x.n, y.n; kwargs...) +# Base.inv(a::TropicalAndOr) = TropicalAndOr(!a.n) +# invint(a::TropicalAndOr) = inv(a) diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index 4a8bd92..3c6e9da 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -1,5 +1,5 @@ """ - TropicalBitwise{T} <: AbstractSemiring + TropicalBitwise{T} <: AbstractSimpleSemiring{T} `TropicalBitwise` is a semiring algebra that parallelizes the [`TropicalAndOr`](@ref) algebra, It can be described by @@ -27,7 +27,7 @@ julia> one(TropicalBitwiseI64) -1ₛ ``` """ -struct TropicalBitwise{T} <: AbstractSemiring +struct TropicalBitwise{T} <: AbstractSimpleSemiring{T} n::T end @@ -39,44 +39,23 @@ function TropicalBitwise{T}(a::TropicalBitwise) where {T} return TropicalBitwise{T}(a.n) end -function Base.show(io::IO, a::TropicalBitwise) - print(io, "$(a.n)ₛ") - return -end - -function Base.isapprox(a::TropicalBitwise, b::TropicalBitwise; kw...) - return isapprox(a.n, b.n; kw...) -end - function Base.promote_rule(::Type{TropicalBitwise{U}}, ::Type{TropicalBitwise{V}}) where {U, V} W = promote_type(U, V) return TropicalBitwise{W} end -function Base.:+(a::TropicalBitwise, b::TropicalBitwise) - n = a.n | b.n - return TropicalBitwise(n) +function content(a::TropicalBitwise) + return a.n end -function Base.:*(a::TropicalBitwise, b::TropicalBitwise) +function inf(a::TropicalBitwise, b::TropicalBitwise) n = a.n & b.n return TropicalBitwise(n) end -function Base.zero(::Type{T}) where {T <: TropicalBitwise} - return typemin(T) -end - -function Base.zero(::T) where {T <: TropicalBitwise} - return zero(T) -end - -function Base.one(::Type{T}) where {T <: TropicalBitwise} - return typemax(T) -end - -function Base.one(::T) where {T <: TropicalBitwise} - return one(T) +function sup(a::TropicalBitwise, b::TropicalBitwise) + n = a.n | b.n + return TropicalBitwise(n) end function Base.typemin(::Type{TropicalBitwise{T}}) where {T} @@ -89,22 +68,14 @@ function Base.typemax(::Type{TropicalBitwise{T}}) where {T} return TropicalBitwise(n) end -function Base.:(==)(a::TropicalBitwise, b::TropicalBitwise) - return a.n == b.n -end - -function Base.:>=(a::TropicalBitwise, b::TropicalBitwise) - return b.n <= a.n -end - -function Base.:<=(a::TropicalBitwise, b::TropicalBitwise) - return a.n | b.n == b.n +function Base.:\(a::TropicalBitwise, b::TropicalBitwise) + return b / a end -function Base.:<(a::TropicalBitwise, b::TropicalBitwise) - return a != b && a <= b +function Base.:/(b::TropicalBitwise, a::TropicalBitwise) + return TropicalBitwise(b.n | ~a.n) end -function Base.:>(a::TropicalBitwise, b::TropicalBitwise) - return b < a +function Base.div(b::TropicalBitwise, a::TropicalBitwise) + return b / a end diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl index 7e5866d..3f1ccc7 100644 --- a/src/tropical_maxmin.jl +++ b/src/tropical_maxmin.jl @@ -1,5 +1,5 @@ """ - TropicalMaxMin{T} <: AbstractSemiring + TropicalMaxMin{T} <: AbstractSimpleSemiring{T} TropicalMaxMin is a semiring algebra, can be described by * TropicalMaxMin, (ℝ, max, min, -Inf, Inf). @@ -26,7 +26,7 @@ julia> one(TropicalMaxMinF64) Infₛ ``` """ -struct TropicalMaxMin{T} <: AbstractSemiring +struct TropicalMaxMin{T} <: AbstractSimpleSemiring{T} n::T end @@ -38,44 +38,23 @@ function TropicalMaxMin{T}(a::TropicalMaxMin) where {T} return TropicalMaxMin{T}(a.n) end -function Base.show(io::IO, a::TropicalMaxMin) - print(io, "$(a.n)ₛ") - return -end - -function Base.isapprox(a::TropicalMaxMin, b::TropicalMaxMin; kw...) - return isapprox(a.n, b.n; kw...) -end - function Base.promote_rule(::Type{TropicalMaxMin{U}}, ::Type{TropicalMaxMin{V}}) where {U, V} W = promote_type(U, V) return TropicalMaxMin{W} end -function Base.:+(a::TropicalMaxMin, b::TropicalMaxMin) - n = max(a.n, b.n) - return TropicalMaxMin(n) +function content(a::TropicalMaxMin) + return a.n end -function Base.:*(a::TropicalMaxMin, b::TropicalMaxMin) +function inf(a::TropicalMaxMin, b::TropicalMaxMin) n = min(a.n, b.n) return TropicalMaxMin(n) end -function Base.zero(::Type{T}) where {T <: TropicalMaxMin} - return typemin(T) -end - -function Base.zero(::T) where {T <: TropicalMaxMin} - return zero(T) -end - -function Base.one(::Type{T}) where {T <: TropicalMaxMin} - return typemax(T) -end - -function Base.one(::T) where {T <: TropicalMaxMin} - return one(T) +function sup(a::TropicalMaxMin, b::TropicalMaxMin) + n = max(a.n, b.n) + return TropicalMaxMin(n) end function Base.typemin(::Type{TropicalMaxMin{T}}) where {T} @@ -87,3 +66,15 @@ function Base.typemax(::Type{TropicalMaxMin{T}}) where {T} n = posinf(T) return TropicalMaxMin(n) end + +function Base.:\(a::TropicalMaxMin, b::TropicalMaxMin) + return b / a +end + +function Base.:/(b::TropicalMaxMin, a::TropicalMaxMin) + return a <= b ? typemax(b) : b +end + +function Base.:div(b::TropicalMaxMin, a::TropicalMaxMin) + return b / a +end diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index 8cdcc41..fd27383 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -1,7 +1,7 @@ """ - TropicalMaxMul{T} <: AbstractSemiring + TropicalMaxMul{T} <: AbstractIdempotentSemiring{T} TropicalMaxMul is a semiring algebra, can be described by * TropicalMaxMul, (ℝ⁺, max, ⋅, 0, 1). @@ -28,7 +28,7 @@ julia> zero(TropicalMaxMulF64) 0.0ₓ ``` """ -struct TropicalMaxMul{T} <: AbstractSemiring +struct TropicalMaxMul{T} <: AbstractIdempotentSemiring{T} n::T function TropicalMaxMul{T}(x) where T new{T}(T(x)) @@ -44,27 +44,54 @@ struct TropicalMaxMul{T} <: AbstractSemiring end end -Base.show(io::IO, t::TropicalMaxMul) = Base.print(io, "$(t.n)ₓ") +content(a::TropicalMaxMul) = a.n Base.:^(a::TropicalMaxMul, b::Real) = TropicalMaxMul(a.n ^ b) Base.:^(a::TropicalMaxMul, b::Integer) = TropicalMaxMul(a.n ^ b) + +# +# 0 * Inf = 0 +# Inf * 0 = 0 +# Base.:*(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(a.n * b.n) -Base.:+(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(max(a.n, b.n)) -Base.typemin(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(zero(T)) -Base.typemax(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(posinf(T)) -Base.zero(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(zero(T)) -Base.zero(::TropicalMaxMul{T}) where T = zero(TropicalMaxMul{T}) +function Base.:*(a::T, b::T) where {T <: TropicalMaxMul{<:Rational}} + if a == typemin(T) && b == typemax(T) || a == typemax(T) && b == typemin(T) + c = typemin(T) + else + c = Tropical(a.n + b.n) + end + return c +end + +inf(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(min(a.n, b.n)) +sup(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(max(a.n, b.n)) +Base.typemin(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(neginf(T)) +Base.typemax(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(posinf(T)) Base.one(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(one(T)) -Base.one(::TropicalMaxMul{T}) where T = one(TropicalMaxMul{T}) # inverse and division -Base.inv(x::TropicalMaxMul{T}) where T = TropicalMaxMul(one(T) / x.n) +#Base.inv(x::TropicalMaxMul) = TropicalMaxMul(inv(x.n)) + +# +# Inf / Inf = Inf +# 0 / 0 = Inf +# +Base.:\(y::TropicalMaxMul, x::TropicalMaxMul) = x / y Base.:/(x::TropicalMaxMul, y::TropicalMaxMul) = TropicalMaxMul(x.n / y.n) -Base.div(x::TropicalMaxMul, y::TropicalMaxMul) = TropicalMaxMul(x.n ÷ y.n) -Base.isapprox(x::TropicalMaxMul, y::TropicalMaxMul; kwargs...) = isapprox(x.n, y.n; kwargs...) +function Base.:/(b::T, a::T) where {T <: TropicalMaxMul{<:Rational}} + if a == b == typemin(T) || a == b == typemax(T) + c = typemax(T) + else + c = Tropical(b.n - a.n) + end + + return c +end + +Base.div(x::TropicalMaxMul, y::TropicalMaxMul) = TropicalMaxMul(div(x.n, y.n)) # promotion rules Base.promote_rule(::Type{TropicalMaxMul{T1}}, b::Type{TropicalMaxMul{T2}}) where {T1, T2} = TropicalMaxMul{promote_type(T1,T2)} diff --git a/src/tropical_maxplus.jl b/src/tropical_maxplus.jl index 8764760..7b018fc 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -1,14 +1,5 @@ -# define the neginf and posinf -neginf(::Type{T}) where T = typemin(T) -neginf(::Type{T}) where T<:AbstractFloat = typemin(T) -neginf(::Type{T}) where T<:Rational = typemin(T) -neginf(::Type{T}) where T<:Integer = T(-999999) -neginf(::Type{Int16}) = Int16(-16384) -neginf(::Type{Int8}) = Int8(-64) -posinf(::Type{T}) where T = - neginf(T) - """ - TropicalMaxPlus{T} = Tropical{T} <: AbstractSemiring + TropicalMaxPlus{T} = Tropical{T} <: AbstractIdempotentSemiring{T} TropicalMaxPlus is a semiring algebra, can be described by * Tropical (TropicalMaxPlus), (ℝ, max, +, -Inf, 0). @@ -35,7 +26,7 @@ julia> zero(TropicalMaxPlusF64) -Infₜ ``` """ -struct Tropical{T} <: AbstractSemiring +struct Tropical{T} <: AbstractIdempotentSemiring{T} n::T Tropical{T}(x) where T = new{T}(T(x)) function Tropical(x::T) where T @@ -49,35 +40,54 @@ struct Tropical{T} <: AbstractSemiring end end - -Base.show(io::IO, t::Tropical) = Base.print(io, "$(t.n)ₜ") +content(a::Tropical) = a.n Base.:^(a::Tropical, b::Real) = Tropical(a.n * b) Base.:^(a::Tropical, b::Integer) = Tropical(a.n * b) + +# +# -Inf + Inf = -Inf +# Inf + -Inf = -Inf +# Base.:*(a::Tropical, b::Tropical) = Tropical(a.n + b.n) -function Base.:*(a::Tropical{<:Rational}, b::Tropical{<:Rational}) - if a.n.den == 0 - a - elseif b.n.den == 0 - b + +function Base.:*(a::T, b::T) where {T <: Tropical{<:Rational}} + if a == typemin(T) && b == typemax(T) || a == typemax(T) && b == typemin(T) + c = typemin(T) else - Tropical(a.n + b.n) + c = Tropical(a.n + b.n) end + + return c end -Base.:+(a::Tropical, b::Tropical) = Tropical(max(a.n, b.n)) -Base.typemin(::Type{Tropical{T}}) where T = Tropical(neginf(T)) -Base.zero(::Type{Tropical{T}}) where T = typemin(Tropical{T}) -Base.zero(::Tropical{T}) where T = zero(Tropical{T}) +inf(a::Tropical, b::Tropical) = Tropical(min(a.n, b.n)) +sup(a::Tropical, b::Tropical) = Tropical(max(a.n, b.n)) +Base.typemin(::Type{Tropical{T}}) where {T} = Tropical(neginf(T)) +Base.typemax(::Type{Tropical{T}}) where {T} = Tropical(posinf(T)) Base.one(::Type{Tropical{T}}) where T = Tropical(zero(T)) -Base.one(::Tropical{T}) where T = one(Tropical{T}) # inverse and division -Base.inv(x::Tropical) = Tropical(-x.n) +#Base.inv(x::Tropical) = Tropical(-x.n) + +# +# Inf - Inf = Inf +# -Inf - -Inf = Inf +# +Base.:\(y::Tropical, x::Tropical) = x / y Base.:/(x::Tropical, y::Tropical) = Tropical(x.n - y.n) -Base.div(x::Tropical, y::Tropical) = Tropical(x.n - y.n) -Base.isapprox(x::Tropical, y::Tropical; kwargs...) = isapprox(x.n, y.n; kwargs...) +function Base.:/(b::T, a::T) where {T <: Tropical{<:Rational}} + if a == b == typemin(T) || a == b == typemax(T) + c = typemax(T) + else + c = Tropical(b.n - a.n) + end + + return c +end + +Base.div(x::Tropical, y::Tropical) = x / y # promotion rules Base.promote_rule(::Type{Tropical{T1}}, b::Type{Tropical{T2}}) where {T1, T2} = Tropical{promote_type(T1,T2)} diff --git a/src/tropical_minplus.jl b/src/tropical_minplus.jl index 40cbdfd..b68877d 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -1,7 +1,7 @@ """ - TropicalMinPlus{T} <: AbstractSemiring + TropicalMinPlus{T} <: AbstractIdempotentSemiring{T} TropicalMinPlus is a semiring algebra, can be described by * TropicalMinPlus, (ℝ, min, +, Inf, 0). @@ -28,7 +28,7 @@ julia> zero(TropicalMinPlusF64) Infₛ ``` """ -struct TropicalMinPlus{T} <: AbstractSemiring +struct TropicalMinPlus{T} <: AbstractIdempotentSemiring{T} n::T TropicalMinPlus{T}(x) where T = new{T}(T(x)) function TropicalMinPlus(x::T) where T @@ -42,60 +42,53 @@ struct TropicalMinPlus{T} <: AbstractSemiring end end -Base.show(io::IO, t::TropicalMinPlus) = Base.print(io, "$(t.n)ₛ") +content(a::TropicalMinPlus) = a.n Base.:^(a::TropicalMinPlus, b::Real) = TropicalMinPlus(a.n * b) Base.:^(a::TropicalMinPlus, b::Integer) = TropicalMinPlus(a.n * b) + +# +# -Inf + Inf = Inf +# Inf + -Inf = Inf +# Base.:*(a::TropicalMinPlus, b::TropicalMinPlus) = TropicalMinPlus(a.n + b.n) -function Base.:*(a::TropicalMinPlus{<:Rational}, b::TropicalMinPlus{<:Rational}) - if a.n.den == 0 - a - elseif b.n.den == 0 - b + +function Base.:*(a::T, b::T) where {T <: TropicalMinPlus{<:Rational}} + if a == typemin(T) && b == typemax(T) || a == typemax(T) && b == typemin(T) + c = typemin(T) else - TropicalMinPlus(a.n + b.n) + c = Tropical(a.n + b.n) end + + return c end -Base.:+(a::TropicalMinPlus, b::TropicalMinPlus) = TropicalMinPlus(min(a.n, b.n)) +inf(a::TropicalMinPlus, b::TropicalMinPlus) = TropicalMinPlus(max(a.n, b.n)) +sup(a::TropicalMinPlus, b::TropicalMinPlus) = TropicalMinPlus(min(a.n, b.n)) Base.typemin(::Type{TropicalMinPlus{T}}) where T = TropicalMinPlus(posinf(T)) -Base.zero(::Type{TropicalMinPlus{T}}) where T = typemin(TropicalMinPlus{T}) -Base.zero(::TropicalMinPlus{T}) where T = zero(TropicalMinPlus{T}) - +Base.typemax(::Type{TropicalMinPlus{T}}) where T = TropicalMinPlus(neginf(T)) Base.one(::Type{TropicalMinPlus{T}}) where T = TropicalMinPlus(zero(T)) -Base.one(::TropicalMinPlus{T}) where T = one(TropicalMinPlus{T}) # inverse and division -Base.inv(x::TropicalMinPlus) = TropicalMinPlus(-x.n) +#Base.inv(x::TropicalMinPlus) = TropicalMinPlus(-x.n) + +# +# Inf - Inf = -Inf +# -Inf - -Inf = -Inf +# +Base.:\(y::TropicalMinPlus, x::TropicalMinPlus) = x / y Base.:/(x::TropicalMinPlus, y::TropicalMinPlus) = TropicalMinPlus(x.n - y.n) Base.div(x::TropicalMinPlus, y::TropicalMinPlus) = TropicalMinPlus(x.n - y.n) -# ordering -function Base.:(==)(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n == a.n -end - -function Base.:>=(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n >= a.n -end - -function Base.:<=(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n <= a.n -end - -function Base.:<(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n < a.n -end - -function Base.:>(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n > a.n -end +function Base.:/(b::T, a::T) where {T <: TropicalMinPlus{<:Rational}} + if a == b == typemin(T) || a == b == typemax(T) + c = typemax(T) + else + c = Tropical(b.n - a.n) + end -function Base.isless(a::TropicalMinPlus, b::TropicalMinPlus) - return isless(b.n, a.n) + return c end -Base.isapprox(x::TropicalMinPlus, y::TropicalMinPlus; kwargs...) = isapprox(x.n, y.n; kwargs...) - # promotion rules Base.promote_rule(::Type{TropicalMinPlus{T1}}, b::Type{TropicalMinPlus{T2}}) where {T1, T2} = TropicalMinPlus{promote_type(T1,T2)} From 831987df08199de4eaa1c86eda2ffa46bf10fe9d Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Wed, 19 Nov 2025 19:55:37 -0500 Subject: [PATCH 02/23] Refactor. --- src/TropicalNumbers.jl | 7 +- src/abstract_semiring.jl | 235 ++++++++++++++++++++----------- src/abstract_semiring_algebra.jl | 162 +++++++++++++++++++++ src/counting_tropical.jl | 20 ++- src/tropical_andor.jl | 42 +++--- src/tropical_bitwise.jl | 70 ++------- src/tropical_maxmin.jl | 72 +++------- src/tropical_maxmul.jl | 91 ++++++------ src/tropical_maxplus.jl | 92 ++++++------ src/tropical_minplus.jl | 92 ++++++------ test/tropical_andor.jl | 6 +- test/tropical_maxmul.jl | 11 +- test/tropical_maxplus.jl | 11 +- test/tropical_minplus.jl | 10 +- 14 files changed, 549 insertions(+), 372 deletions(-) create mode 100644 src/abstract_semiring_algebra.jl diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 259ef6d..8e8b456 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -1,6 +1,6 @@ module TropicalNumbers -export content, neginf, posinf, sup, inf +export content, neginf, posinf, inf, fli, fri, inf_fast, ldiv_fast, rdiv_fast, ∧ export TropicalTypes, AbstractSemiring export TropicalAndOr @@ -13,13 +13,12 @@ export TropicalBitwise, TropicalBitwiseI64, TropicalBitwiseI32, TropicalBitwiseI export CountingTropical, CountingTropicalF16, CountingTropicalF32, CountingTropicalF64, CountingTropicalI16, CountingTropicalI32, CountingTropicalI64 neginf(::Type{T}) where T = typemin(T) -neginf(::Type{T}) where T<:AbstractFloat = typemin(T) -neginf(::Type{T}) where T<:Rational = typemin(T) neginf(::Type{T}) where T<:Integer = T(-999999) neginf(::Type{Int16}) = Int16(-16384) neginf(::Type{Int8}) = Int8(-64) posinf(::Type{T}) where T = -neginf(T) +include("abstract_semiring_algebra.jl") include("abstract_semiring.jl") include("tropical_maxplus.jl") include("tropical_andor.jl") @@ -30,7 +29,7 @@ include("tropical_bitwise.jl") include("counting_tropical.jl") const TropicalTypes{T} = Union{CountingTropical{T}, Tropical{T}} -const TropicalMaxPlus = Tropical +const ∧ = inf # alias # defining constants like `TropicalF64`. diff --git a/src/abstract_semiring.jl b/src/abstract_semiring.jl index 46aa0f1..9a63e8c 100644 --- a/src/abstract_semiring.jl +++ b/src/abstract_semiring.jl @@ -22,156 +22,229 @@ Fast semiring matrix multiplication is implemented in the following libraries. * [`CuTropicalGEMM`](https://github.com/ArrogantGao/CuTropicalGEMM.jl/) """ -abstract type AbstractSemiring{T} <: Number end +abstract type AbstractSemiring <: Number end -function content(::Type{<:AbstractSemiring{T}}) where {T} - return T +function Base.zero(::T) where {T <: AbstractSemiring} + return zero(T) end -function Base.show(io::IO, a::AbstractSemiring) - n = content(a) - print(io, "$(n)s") - return +function Base.one(::T) where {T <: AbstractSemiring} + return one(T) end -function Base.show(io::IO, ::MIME"text/plain", a::AbstractSemiring) - return show(io, a) +function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} + return b ? a : zero(T) end -function Base.isnan(x::AbstractSemiring) - return isnan(content(a)) +function Base.:*(b::Bool, a::T) where {T <: AbstractSemiring} + return b ? a : zero(T) end -function Base.isapprox(a::T, b::T; kw...) where {T <: AbstractSemiring} - return isapprox(content(a), content(b); kw...) +struct Semiring{A <: AbstractSemiringAlgebra, T} <: AbstractSemiring + n::T + + function Semiring{A, T}(n) where {A <: AbstractSemiringAlgebra, T} + return new{A, T}(n) + end end -function Base.isapprox(A::AbstractArray{<:AbstractSemiring}, B::AbstractArray{<:AbstractSemiring}; kw...) - return all(isapprox.(A, B; kw...)) +function Semiring{A}(n::T) where {A <: AbstractSemiringAlgebra, T} + return Semiring{A, T}(n) end -function Base.typemin(::T) where {T <: AbstractSemiring} - return typemin(T) +function Semiring{A}(a::Semiring{A}) where {A <: AbstractSemiringAlgebra} + return Semiring{A}(content(a)) end -function Base.typemax(::T) where {T <: AbstractSemiring} - return typemax(T) +function Semiring{A, T}(a::Semiring{A}) where {A <: AbstractSemiringAlgebra, T} + return Semiring{A, T}(content(a)) end -function Base.zero(::T) where {T <: AbstractSemiring} - return zero(T) +function content(a::Semiring) + return a.n end -function Base.one(::T) where {T <: AbstractSemiring} - return one(T) +function content(::Type{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} + return T end -function Base.inv(a::T) where {T <: AbstractSemiring} - return one(T) / a +function Base.show(io::IO, a::Semiring) + print(io, content(a)) + return end -function invint(a::T) where {T <: AbstractSemiring} - return div(one(T), a) +function Base.isnan(a::Semiring) + return isnan(content(a)) end -function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} - return b ? a : zero(T) +function Base.isinf(a::Semiring) + return isinf(content(a)) end -function Base.:*(b::Bool, a::T) where {T <: AbstractSemiring} - return b ? a : zero(T) +function Base.:(==)(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + return content(a) == content(b) end -function Base.:/(a::T, b::Bool) where {T <: AbstractSemiring} - return b ? a : a / zero(T) +function Base.isapprox(a::Semiring{A}, b::Semiring{A}; kw...) where {A <: AbstractSemiringAlgebra} + return isapprox(content(a), content(b); kw...) end -function Base.div(a::T, b::Bool) where {T <: AbstractSemiring} - return b ? a : div(a, zero(T)) +function Base.isapprox(a::AbstractArray{<:Semiring{A}}, b::AbstractArray{<:Semiring{A}}; kw...) where {A <: AbstractSemiringAlgebra} + return all(isapprox.(a, b; kw...)) end -function Base.:/(b::Bool, a::T) where {T <: AbstractSemiring} - return b ? inv(a) : zero(T) +function Base.promote_rule(::Type{Semiring{A, T}}, ::Type{Semiring{A, U}}) where {A <: AbstractSemiringAlgebra, T, U} + V = promote_type(T, U) + return Semiring{A, V} end -function Base.div(b::Bool, a::T) where {T <: AbstractSemiring} - return b ? invint(a) : zero(T) +# --------- # +# Semirings # +# --------- # + +function Base.zero(::Type{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} + n = zero_alg(A, T) + return Semiring{A}(n) end -function Base.:(==)(a::T, b::T) where {T <: AbstractSemiring} - return content(a) == content(b) +function Base.one(::Type{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} + n = one_alg(A, T) + return Semiring{A}(n) end -function Base.isless(a::T, b::T) where {T <: AbstractSemiring} - return a < b || !(b >= a) || isless(content(a), content(b)) +function Base.:+(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + n = add_alg(A, content(a), content(b)) + return Semiring{A}(n) end -""" - AbstractIdempotentSemiring{T} <: AbstractSemiring{T} +function Base.FastMath.add_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + n = add_fast_alg(A, content(a), content(b)) + return Semiring{A}(n) +end -A semiring (R, +, ×, 0, 1) is additively idempotent if +function Base.:*(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + n = mul_alg(A, content(a), content(b)) + return Semiring{A}(n) +end -* x + x = x +function Base.FastMath.mul_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + n = mul_fast_alg(A, content(a), content(b)) + return Semiring{A}(n) +end -for all x ∈ R. -""" -abstract type AbstractIdempotentSemiring{T} <: AbstractSemiring{T} end +function Base.fma(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractSemiringAlgebra} + n = mul_add_alg(A, content(a), content(b), content(c)) + return Semiring{A}(n) +end -function Base.zero(::Type{T}) where {T <: AbstractIdempotentSemiring} - return typemin(T) +# --------- # +# Quantales # +# --------- # + +function Base.typemin(::Type{T}) where {T <: Semiring{<:AbstractQuantaleAlgebra}} + return zero(T) end -function Base.:+(a::T, b::T) where {T <: AbstractIdempotentSemiring} - return sup(a, b) +function Base.typemin(::T) where {T <: Semiring{<:AbstractQuantaleAlgebra}} + return zero(T) end -function Base.:/(a::T, b::Bool) where {T <: AbstractIdempotentSemiring} - return b ? a : typemax(a) +function Base.typemax(::Type{Semiring{A, T}}) where {A <: AbstractQuantaleAlgebra, T} + n = typemax_alg(A, T) + return Semiring{A}(n) end -function Base.:div(a::T, b::Bool) where {T <: AbstractIdempotentSemiring} - return b ? a : typemax(a) +function Base.typemax(::T) where {T <: Semiring{<:AbstractQuantaleAlgebra}} + return typemax(T) end -function Base.:<=(a::T, b::T) where {T <: AbstractIdempotentSemiring} - return sup(a, b) == b +function inf(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inf_alg(A, content(a), content(b)) + return Semiring{A}(n) end -function Base.:>=(a::T, b::T) where {T <: AbstractIdempotentSemiring} - return b <= a +function inf_fast(a, b) + return inf(a, b) end -function Base.:<(a::T, b::T) where {T <: AbstractIdempotentSemiring} - return a != b && a <= b +function inf_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inf_fast_alg(A, content(a), content(b)) + return Semiring{A}(n) end -function Base.:>(a::T, b::T) where {T <: AbstractIdempotentSemiring} - return b < a +function Base.:\(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = ldiv_alg(A, content(a), content(b)) + return Semiring{A}(n) end -""" - AbstractSimpleSemiring{T} <: AbstractIdemptentSemiring{T} +function ldiv_fast(a, b) + return a \ b +end -A additively idempotent semiring (R, +, ×, 0, 1) is simple if +function ldiv_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = ldiv_fast_alg(A, content(a), content(b)) + return Semiring{A}(n) +end -* a × b = inf(a, b) +function fli(a, b, c) + return (a \ b) ∧ c +end -for all a, b ∈ R. -""" -abstract type AbstractSimpleSemiring{T} <: AbstractIdempotentSemiring{T} end +function fli(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inf_ldiv_alg(A, content(a), content(b), content(c)) + return Semiring{A}(n) +end -function Base.:*(a::T, b::T) where {T <: AbstractSimpleSemiring} - return inf(a, b) +function Base.:/(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = rdiv_alg(A, content(b), content(a)) + return Semiring{A}(n) end -function Base.one(::Type{T}) where {T <: AbstractSimpleSemiring} - return typemax(T) +function Base.:/(b::T, a::Bool) where {T <: Semiring{<:AbstractQuantaleAlgebra}} + return a ? b : typemax(T) end -function Base.inv(a::T) where {T <: AbstractSimpleSemiring} - return typemax(T) +function Base.:/(b::Bool, a::T) where {T <: Semiring{<:AbstractQuantaleAlgebra}} + return b ? one(T) / a : zero(T) end -function invint(a::T) where {T <: AbstractSimpleSemiring} - return typemax(T) +function Base.FastMath.div_fast(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = rdiv_fast_alg(A, content(b), content(a)) + return Semiring{A}(n) +end + +function fri(b, a, c) + return (b / a) ∧ c +end + +function fri(b::Semiring{A}, a::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inf_rdiv_alg(A, content(b), content(a), content(c)) + return Semiring{A}(n) +end + +function Base.:<=(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + return leq_alg(A, content(a), content(b)) +end + +function Base.:<(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + return lt_alg(A, content(a), content(b)) +end + +# -------- # +# Tropical # +# -------- # + +function Base.:^(a::Semiring{A}, b::Number) where {A <: AbstractTropicalAlgebra} + n = exp_alg(A, content(a), b) + return Semiring{A}(n) +end + +function Base.:^(a::Semiring{A}, b::Integer) where {A <: AbstractTropicalAlgebra} + n = exp_alg(A, content(a), b) + return Semiring{A}(n) +end + +function Base.inv(a::Semiring{A}) where {A <: AbstractTropicalAlgebra} + n = inv_alg(A, content(a)) + return Semiring{A}(n) end diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl new file mode 100644 index 0000000..12f0f2b --- /dev/null +++ b/src/abstract_semiring_algebra.jl @@ -0,0 +1,162 @@ +abstract type AbstractSemiringAlgebra end + +abstract type AbstractQuantaleAlgebra <: AbstractSemiringAlgebra end + +abstract type AbstractLatticeAlgebra <: AbstractQuantaleAlgebra end + +abstract type AbstractTropicalAlgebra <: AbstractQuantaleAlgebra end + +# --------- # +# Semirings # +# --------- # + +""" + zero_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} +""" +zero_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} + +""" + one_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} +""" +one_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} + +""" + add_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} +""" +add_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + +""" + add_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} +""" +function add_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + c = add_alg(T, a, b) + return c +end + +""" + mul_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} +""" +mul_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + +""" + mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} +""" +function mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + c = mul_alg(T, a, b) + return c +end + +""" + mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} +""" +function mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} + return add_fast_alg(T, mul_fast_alg(T, a, b), c) +end + +# --------- # +# Quantales # +# --------- # + +""" + typemax_alg(::Type{T}, A::Type) where {T <: AbstractQuantaleAlgebra} +""" +typemax_alg(::Type{T}, A::Type) where {T <: AbstractQuantaleAlgebra} + +""" + inf_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +inf_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +""" + inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + c = inf_alg(T, a, b) + return c +end + +""" + ldiv_alg(::Type{T}, a, b) whre {T <: AbstractQuantaleAlgebra} +""" +ldiv_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +""" + ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +function ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + c = ldiv_alg(T, a, b) + return c +end + +""" + inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} +""" +function inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} + return inf_fast_alg(T, ldiv_fast_alg(T, a, b), c) +end + +""" + rdiv_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +rdiv_alg(::Type{T}, b, a) where {T <: AbstractQuantaleAlgebra} + +""" + rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +function rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + c = rdiv_alg(T, a, b) + return c +end + +""" + inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} +""" +function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} + return inf_fast_alg(T, rdiv_fast_alg(T, b, a), c) +end + +""" + leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +function leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return add_alg(T, a, b) == b +end + +""" + lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +function lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return a != b && leq_alg(T, a, b) +end + +# -------- # +# Lattices # +# -------- # + +function typemax_alg(::Type{T}, A::Type) where {T <: AbstractLatticeAlgebra} + return one_alg(T, A) +end + +function inf_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} + return mul_alg(T, a, b) +end + +function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} + return mul_fast_alg(T, a, b) +end + +# -------- # +# Tropical # +# -------- # + +""" + exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} +""" +exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} + +""" + inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} +""" +function inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} + return div_alg(T, one_alg(T, a), a) +end diff --git a/src/counting_tropical.jl b/src/counting_tropical.jl index 20330ad..35ee160 100644 --- a/src/counting_tropical.jl +++ b/src/counting_tropical.jl @@ -1,5 +1,5 @@ """ - CountingTropical{T,CT} <: AbstractSemiring{T} + CountingTropical{T,CT} <: AbstractSemiring Counting tropical number type is also a semiring algebra. It is tropical algebra with one extra field for counting, it is introduced in [arXiv:2008.06888](https://arxiv.org/abs/2008.06888). @@ -8,19 +8,19 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> CountingTropical(1.0, 5.0) + CountingTropical(3.0, 2.0) -(3.0, 2.0)ₜ +(3.0, 2.0) julia> CountingTropical(1.0, 5.0) * CountingTropical(3.0, 2.0) -(4.0, 10.0)ₜ +(4.0, 10.0) julia> one(CountingTropicalF64) -(0.0, 1.0)ₜ +(0.0, 1.0) julia> zero(CountingTropicalF64) -(-Inf, 0.0)ₜ +(-Inf, 0.0) ``` """ -struct CountingTropical{T,CT} <: AbstractSemiring{T} +struct CountingTropical{T,CT} <: AbstractSemiring n::T c::CT end @@ -58,6 +58,12 @@ Base.one(::Type{CountingTropical{T,CT}}) where {T<:Integer,CT} = CountingTropica Base.one(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = CountingTropical(zero(T), one(CT)) Base.isapprox(a::CountingTropical, b::CountingTropical; kwargs...) = isapprox(a.n, b.n; kwargs...) && isapprox(a.c, b.c; kwargs...) -Base.show(io::IO, t::CountingTropical) = Base.print(io, "$((t.n, t.c))s") +Base.show(io::IO, t::CountingTropical) = Base.print(io, (t.n, t.c)) Base.promote_rule(::Type{CountingTropical{T1,CT1}}, b::Type{CountingTropical{T2,CT2}}) where {T1,T2,CT1,CT2} = CountingTropical{promote_type(T1,T2), promote_type(CT1,CT2)} + +Base.isnan(a::CountingTropical) = isnan(a.n) +Base.isinf(a::CountingTropical) = isinf(a.n) +Base.:(==)(a::CountingTropical, b::CountingTropical) = a.n == b.n +Base.:<=(a::CountingTropical, b::CountingTropical) = a.n <= b.n +Base.:<(a::CountingTropical, b::CountingTropical) = a.n < b.n diff --git a/src/tropical_andor.jl b/src/tropical_andor.jl index 20daba6..bcc210e 100644 --- a/src/tropical_andor.jl +++ b/src/tropical_andor.jl @@ -1,7 +1,7 @@ - +struct AndOr <: AbstractLatticeAlgebra end """ - TropicalAndOr <: AbstractSimpleSemiring{Bool} + TropicalAndOr <: AbstractSemiring TropicalAndOr is a semiring algebra, can be described by * TropicalAndOr, ([T, F], or, and, false, true). @@ -18,34 +18,36 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalAndOr(true) + TropicalAndOr(false) -trueₜ +true julia> TropicalAndOr(true) * TropicalAndOr(false) -falseₜ +false julia> one(TropicalAndOr) -trueₜ +true julia> zero(TropicalAndOr) -falseₜ +false ``` """ -struct TropicalAndOr <: AbstractSimpleSemiring{Bool} - n::Bool - TropicalAndOr(x::T) where T <: Bool = new(x) -end +const TropicalAndOr = Semiring{AndOr, Bool} -content(a::TropicalAndOr) = a.n +add_alg(::Type{AndOr}, a::Bool, b::Bool) = a || b +mul_alg(::Type{AndOr}, a::Bool, b::Bool) = a && b -inf(a::TropicalAndOr, b::TropicalAndOr) = TropicalAndOr(a.n && b.n) -sup(a::TropicalAndOr, b::TropicalAndOr) = TropicalAndOr(a.n || b.n) +zero_alg(::Type{AndOr}, ::Type{Bool}) = false +one_alg(::Type{AndOr}, ::Type{Bool}) = true -Base.typemin(::Type{TropicalAndOr}) = TropicalAndOr(false) -Base.typemax(::Type{TropicalAndOr}) = TropicalAndOr(true) +ldiv_alg(::Type{AndOr}, a::Bool, b::Bool) = b || !a +rdiv_alg(::Type{AndOr}, b::Bool, a::Bool) = ldiv_alg(AndOr, a, b) -Base.:\(a::TropicalAndOr, b::TropicalAndOr) = b / a -Base.:/(b::TropicalAndOr, a::TropicalAndOr) = TropicalAndOr(b.n || !a.n) -Base.div(b::TropicalAndOr, a::TropicalAndOr) = b / a +leq_alg(::Type{AndOr}, a::Bool, b::Bool) = a <= b +lt_alg(::Type{AndOr}, a::Bool, b::Bool) = a < b -# Base.inv(a::TropicalAndOr) = TropicalAndOr(!a.n) -# invint(a::TropicalAndOr) = inv(a) +# --------------- # +# other operators # +# --------------- # + +function Base.isless(a::TropicalAndOr, b::TropicalAndOr) + return a < b +end diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index 3c6e9da..ae97586 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -1,7 +1,9 @@ +struct Bitwise <: AbstractLatticeAlgebra end + """ - TropicalBitwise{T} <: AbstractSimpleSemiring{T} + TropicalBitwise{T} <: AbstractSemiring -`TropicalBitwise` is a semiring algebra that parallelizes the [`TropicalAndOr`](@ref) algebra, +`TropicalBitwise` is a semiring algebra that parallelizes the [`TropicalBitwise`](@ref) algebra, It can be described by * TropicalBitwise, (ℝ, |, &, 0, ~0). @@ -15,67 +17,25 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalBitwise(1) + TropicalBitwise(3) -3ₛ +3 julia> TropicalBitwise(1) * TropicalBitwise(3) -1ₛ +1 julia> zero(TropicalBitwiseI64) -0ₛ +0 julia> one(TropicalBitwiseI64) --1ₛ +-1 ``` """ -struct TropicalBitwise{T} <: AbstractSimpleSemiring{T} - n::T -end - -function TropicalBitwise(a::TropicalBitwise) - return TropicalBitwise(a.n) -end - -function TropicalBitwise{T}(a::TropicalBitwise) where {T} - return TropicalBitwise{T}(a.n) -end - -function Base.promote_rule(::Type{TropicalBitwise{U}}, ::Type{TropicalBitwise{V}}) where {U, V} - W = promote_type(U, V) - return TropicalBitwise{W} -end - -function content(a::TropicalBitwise) - return a.n -end - -function inf(a::TropicalBitwise, b::TropicalBitwise) - n = a.n & b.n - return TropicalBitwise(n) -end - -function sup(a::TropicalBitwise, b::TropicalBitwise) - n = a.n | b.n - return TropicalBitwise(n) -end - -function Base.typemin(::Type{TropicalBitwise{T}}) where {T} - n = zero(T) - return TropicalBitwise(n) -end - -function Base.typemax(::Type{TropicalBitwise{T}}) where {T} - n = ~zero(T) - return TropicalBitwise(n) -end +const TropicalBitwise = Semiring{Bitwise} -function Base.:\(a::TropicalBitwise, b::TropicalBitwise) - return b / a -end +add_alg(::Type{Bitwise}, a, b) = a | b +mul_alg(::Type{Bitwise}, a, b) = a & b -function Base.:/(b::TropicalBitwise, a::TropicalBitwise) - return TropicalBitwise(b.n | ~a.n) -end +zero_alg(::Type{Bitwise}, ::Type{T}) where {T} = zero(T) +one_alg(::Type{Bitwise}, ::Type{T}) where {T} = ~zero(T) -function Base.div(b::TropicalBitwise, a::TropicalBitwise) - return b / a -end +ldiv_alg(::Type{Bitwise}, a, b) = b | ~a +rdiv_alg(::Type{Bitwise}, b, a) = ldiv_alg(Bitwise, a, b) diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl index 3f1ccc7..31938c2 100644 --- a/src/tropical_maxmin.jl +++ b/src/tropical_maxmin.jl @@ -1,5 +1,7 @@ +struct MaxMin <: AbstractLatticeAlgebra end + """ - TropicalMaxMin{T} <: AbstractSimpleSemiring{T} + TropicalMaxMin{T} <: AbstractSemiring TropicalMaxMin is a semiring algebra, can be described by * TropicalMaxMin, (ℝ, max, min, -Inf, Inf). @@ -14,67 +16,39 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMaxMin(1.0) + TropicalMaxMin(3.0) -3.0ₛ +3.0 julia> TropicalMaxMin(1.0) * TropicalMaxMin(3.0) -1.0ₛ +1.0 julia> zero(TropicalMaxMinF64) --Infₛ +-Inf julia> one(TropicalMaxMinF64) -Infₛ +Inf ``` """ -struct TropicalMaxMin{T} <: AbstractSimpleSemiring{T} - n::T -end - -function TropicalMaxMin(a::TropicalMaxMin) - return TropicalMaxMin(a.n) -end - -function TropicalMaxMin{T}(a::TropicalMaxMin) where {T} - return TropicalMaxMin{T}(a.n) -end - -function Base.promote_rule(::Type{TropicalMaxMin{U}}, ::Type{TropicalMaxMin{V}}) where {U, V} - W = promote_type(U, V) - return TropicalMaxMin{W} -end +const TropicalMaxMin = Semiring{MaxMin} -function content(a::TropicalMaxMin) - return a.n -end - -function inf(a::TropicalMaxMin, b::TropicalMaxMin) - n = min(a.n, b.n) - return TropicalMaxMin(n) -end +add_alg(::Type{MaxMin}, a, b) = max(a, b) +mul_alg(::Type{MaxMin}, a, b) = min(a, b) -function sup(a::TropicalMaxMin, b::TropicalMaxMin) - n = max(a.n, b.n) - return TropicalMaxMin(n) -end +zero_alg(::Type{MaxMin}, ::Type{T}) where {T} = neginf(T) +one_alg(::Type{MaxMin}, ::Type{T}) where {T} = posinf(T) -function Base.typemin(::Type{TropicalMaxMin{T}}) where {T} - n = neginf(T) - return TropicalMaxMin(n) -end +ldiv_alg(::Type{MaxMin}, a, b) = a <= b ? typemax(b) : b +rdiv_alg(::Type{MaxMin}, b, a) = ldiv_alg(MaxMin, a, b) -function Base.typemax(::Type{TropicalMaxMin{T}}) where {T} - n = posinf(T) - return TropicalMaxMin(n) -end +leq_alg(::Type{MaxMin}, b, a) = a <= b +lt_alg(::Type{MaxMin}, b, a) = a < b -function Base.:\(a::TropicalMaxMin, b::TropicalMaxMin) - return b / a -end +add_fast_alg(::Type{MaxMin}, a, b) = Base.FastMath.max_fast(a, b) +mul_fast_alg(::Type{MaxMin}, a, b) = Base.FastMath.min_fast(a, b) -function Base.:/(b::TropicalMaxMin, a::TropicalMaxMin) - return a <= b ? typemax(b) : b -end +# --------------- # +# other operators # +# --------------- # -function Base.:div(b::TropicalMaxMin, a::TropicalMaxMin) - return b / a +function Base.isless(a::TropicalMaxMin, b::TropicalMaxMin) + return a < b end diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index fd27383..d116583 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -1,4 +1,4 @@ - +struct MaxMul <: AbstractTropicalAlgebra end """ TropicalMaxMul{T} <: AbstractIdempotentSemiring{T} @@ -16,82 +16,81 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMaxMul(1.0) + TropicalMaxMul(3.0) -3.0ₓ +3.0 julia> TropicalMaxMul(1.0) * TropicalMaxMul(3.0) -3.0ₓ +3.0 julia> one(TropicalMaxMulF64) -1.0ₓ +1.0 julia> zero(TropicalMaxMulF64) -0.0ₓ +0.0 ``` """ -struct TropicalMaxMul{T} <: AbstractIdempotentSemiring{T} - n::T - function TropicalMaxMul{T}(x) where T - new{T}(T(x)) - end - function TropicalMaxMul(x::T) where T - new{T}(x) - end - function TropicalMaxMul{T}(x::TropicalMaxMul{T}) where T - x - end - function TropicalMaxMul{T1}(x::TropicalMaxMul{T2}) where {T1,T2} - new{T1}(T2(x.n)) - end -end - -content(a::TropicalMaxMul) = a.n +const TropicalMaxMul = Semiring{MaxMul} -Base.:^(a::TropicalMaxMul, b::Real) = TropicalMaxMul(a.n ^ b) -Base.:^(a::TropicalMaxMul, b::Integer) = TropicalMaxMul(a.n ^ b) +add_alg(::Type{MaxMul}, a, b) = max(a, b) +inf_alg(::Type{MaxMul}, a, b) = min(a, b) # # 0 * Inf = 0 # Inf * 0 = 0 # -Base.:*(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(a.n * b.n) +mul_alg(::Type{MaxMul}, a, b) = a * b +exp_alg(::Type{MaxMul}, a, b) = a ^ b +inv_alg(::Type{MaxMul}, a) = inv(a) -function Base.:*(a::T, b::T) where {T <: TropicalMaxMul{<:Rational}} - if a == typemin(T) && b == typemax(T) || a == typemax(T) && b == typemin(T) - c = typemin(T) +function mul_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = zero(T) + + if a == ⊥ && b == ⊤ || a == ⊤ && b == ⊥ + c = ⊥ else - c = Tropical(a.n + b.n) + c = a * b end return c end -inf(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(min(a.n, b.n)) -sup(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(max(a.n, b.n)) -Base.typemin(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(neginf(T)) -Base.typemax(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(posinf(T)) -Base.one(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(one(T)) - -# inverse and division -#Base.inv(x::TropicalMaxMul) = TropicalMaxMul(inv(x.n)) +zero_alg(::Type{MaxMul}, ::Type{T}) where {T} = zero(T) +typemax_alg(::Type{MaxMul}, ::Type{T}) where {T} = posinf(T) +one_alg(::Type{MaxMul}, ::Type{T}) where {T} = one(T) # # Inf / Inf = Inf # 0 / 0 = Inf # -Base.:\(y::TropicalMaxMul, x::TropicalMaxMul) = x / y -Base.:/(x::TropicalMaxMul, y::TropicalMaxMul) = TropicalMaxMul(x.n / y.n) +ldiv_alg(::Type{MaxMul}, a, b) = b / a -function Base.:/(b::T, a::T) where {T <: TropicalMaxMul{<:Rational}} - if a == b == typemin(T) || a == b == typemax(T) - c = typemax(T) +function ldiv_alg(::Type{MaxMul}, b::T, a::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = zero(T) + + if a == b == ⊥ || a == b == ⊤ + c = ⊤ else - c = Tropical(b.n - a.n) + c = b / a end return c end -Base.div(x::TropicalMaxMul, y::TropicalMaxMul) = TropicalMaxMul(div(x.n, y.n)) +rdiv_alg(::Type{MaxMul}, b, a) = ldiv_alg(MaxMul, a, b) + +leq_alg(::Type{MaxMul}, a, b) = a <= b +lt_alg(::Type{MaxMul}, a, b) = a < b -# promotion rules -Base.promote_rule(::Type{TropicalMaxMul{T1}}, b::Type{TropicalMaxMul{T2}}) where {T1, T2} = TropicalMaxMul{promote_type(T1,T2)} +add_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.max_fast(a, b) +mul_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.mul_fast(a, b) +ldiv_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.div_fast(b, a) +rdiv_fast_alg(::Type{MaxMul}, b, a) = ldiv_fast_alg(MaxMul, a, b) + +# --------------- # +# other operators # +# --------------- # + +function Base.isless(a::TropicalMaxMul, b::TropicalMaxMul) + return a < b +end diff --git a/src/tropical_maxplus.jl b/src/tropical_maxplus.jl index 7b018fc..a466bee 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -1,5 +1,7 @@ +struct MaxPlus <: AbstractTropicalAlgebra end + """ - TropicalMaxPlus{T} = Tropical{T} <: AbstractIdempotentSemiring{T} + TropicalMaxPlus{T} = Tropical{T} <: AbstractSemiring TropicalMaxPlus is a semiring algebra, can be described by * Tropical (TropicalMaxPlus), (ℝ, max, +, -Inf, 0). @@ -14,80 +16,82 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMaxPlus(1.0) + TropicalMaxPlus(3.0) -3.0ₜ +3.0 julia> TropicalMaxPlus(1.0) * TropicalMaxPlus(3.0) -4.0ₜ +4.0 julia> one(TropicalMaxPlusF64) -0.0ₜ +0.0 julia> zero(TropicalMaxPlusF64) --Infₜ +-Inf ``` """ -struct Tropical{T} <: AbstractIdempotentSemiring{T} - n::T - Tropical{T}(x) where T = new{T}(T(x)) - function Tropical(x::T) where T - new{T}(x) - end - function Tropical{T}(x::Tropical{T}) where T - x - end - function Tropical{T1}(x::Tropical{T2}) where {T1,T2} - new{T1}(T2(x.n)) - end -end - -content(a::Tropical) = a.n +const Tropical = Semiring{MaxPlus} +const TropicalMaxPlus = Tropical -Base.:^(a::Tropical, b::Real) = Tropical(a.n * b) -Base.:^(a::Tropical, b::Integer) = Tropical(a.n * b) +add_alg(::Type{MaxPlus}, a, b) = max(a, b) +inf_alg(::Type{MaxPlus}, a, b) = min(a, b) # # -Inf + Inf = -Inf # Inf + -Inf = -Inf # -Base.:*(a::Tropical, b::Tropical) = Tropical(a.n + b.n) +mul_alg(::Type{MaxPlus}, a, b) = a + b +exp_alg(::Type{MaxPlus}, a, b) = a * b +inv_alg(::Type{MaxPlus}, a) = -a + +function mul_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) -function Base.:*(a::T, b::T) where {T <: Tropical{<:Rational}} - if a == typemin(T) && b == typemax(T) || a == typemax(T) && b == typemin(T) - c = typemin(T) + if a == ⊥ && b == ⊤ || a == ⊤ && b == ⊥ + c = ⊥ else - c = Tropical(a.n + b.n) + c = a + b end return c end -inf(a::Tropical, b::Tropical) = Tropical(min(a.n, b.n)) -sup(a::Tropical, b::Tropical) = Tropical(max(a.n, b.n)) -Base.typemin(::Type{Tropical{T}}) where {T} = Tropical(neginf(T)) -Base.typemax(::Type{Tropical{T}}) where {T} = Tropical(posinf(T)) -Base.one(::Type{Tropical{T}}) where T = Tropical(zero(T)) - -# inverse and division -#Base.inv(x::Tropical) = Tropical(-x.n) +zero_alg(::Type{MaxPlus}, ::Type{T}) where {T} = neginf(T) +typemax_alg(::Type{MaxPlus}, ::Type{T}) where {T} = posinf(T) +one_alg(::Type{MaxPlus}, ::Type{T}) where {T} = zero(T) # # Inf - Inf = Inf # -Inf - -Inf = Inf # -Base.:\(y::Tropical, x::Tropical) = x / y -Base.:/(x::Tropical, y::Tropical) = Tropical(x.n - y.n) +ldiv_alg(::Type{MaxPlus}, a, b) = b - a + +function ldiv_alg(::Type{MaxPlus}, b::T, a::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) -function Base.:/(b::T, a::T) where {T <: Tropical{<:Rational}} - if a == b == typemin(T) || a == b == typemax(T) - c = typemax(T) + if a == b == ⊥ || a == b == ⊤ + c = ⊤ else - c = Tropical(b.n - a.n) + c = b - a end return c end -Base.div(x::Tropical, y::Tropical) = x / y +rdiv_alg(::Type{MaxPlus}, b, a) = ldiv_alg(MaxPlus, a, b) + +leq_alg(::Type{MaxPlus}, a, b) = a <= b +lt_alg(::Type{MaxPlus}, a, b) = a < b -# promotion rules -Base.promote_rule(::Type{Tropical{T1}}, b::Type{Tropical{T2}}) where {T1, T2} = Tropical{promote_type(T1,T2)} +add_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.max_fast(a, b) +mul_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.add_fast(a, b) +ldiv_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.sub_fast(b, a) +rdiv_fast_alg(::Type{MaxPlus}, b, a) = ldiv_fast_alg(MaxPlus, a, b) + +# --------------- # +# other operators # +# --------------- # + +function Base.isless(a::Tropical, b::Tropical) + return a < b +end diff --git a/src/tropical_minplus.jl b/src/tropical_minplus.jl index b68877d..ba48a32 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -1,7 +1,7 @@ - +struct MinPlus <: AbstractTropicalAlgebra end """ - TropicalMinPlus{T} <: AbstractIdempotentSemiring{T} + TropicalMinPlus{T} <: AbstractSemiring TropicalMinPlus is a semiring algebra, can be described by * TropicalMinPlus, (ℝ, min, +, Inf, 0). @@ -16,79 +16,81 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMinPlus(1.0) + TropicalMinPlus(3.0) -1.0ₛ +1.0 julia> TropicalMinPlus(1.0) * TropicalMinPlus(3.0) -4.0ₛ +4.0 julia> one(TropicalMinPlusF64) -0.0ₛ +0.0 julia> zero(TropicalMinPlusF64) -Infₛ +Inf ``` """ -struct TropicalMinPlus{T} <: AbstractIdempotentSemiring{T} - n::T - TropicalMinPlus{T}(x) where T = new{T}(T(x)) - function TropicalMinPlus(x::T) where T - new{T}(x) - end - function TropicalMinPlus{T}(x::TropicalMinPlus{T}) where T - x - end - function TropicalMinPlus{T1}(x::TropicalMinPlus{T2}) where {T1,T2} - new{T1}(T2(x.n)) - end -end - -content(a::TropicalMinPlus) = a.n +const TropicalMinPlus = Semiring{MinPlus} -Base.:^(a::TropicalMinPlus, b::Real) = TropicalMinPlus(a.n * b) -Base.:^(a::TropicalMinPlus, b::Integer) = TropicalMinPlus(a.n * b) +add_alg(::Type{MinPlus}, a, b) = min(a, b) +inf_alg(::Type{MinPlus}, a, b) = max(a, b) # # -Inf + Inf = Inf # Inf + -Inf = Inf # -Base.:*(a::TropicalMinPlus, b::TropicalMinPlus) = TropicalMinPlus(a.n + b.n) +mul_alg(::Type{MinPlus}, a, b) = a + b +exp_alg(::Type{MinPlus}, a, b) = a * b +inv_alg(::Type{MinPlus}, a) = -a + +function mul_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) -function Base.:*(a::T, b::T) where {T <: TropicalMinPlus{<:Rational}} - if a == typemin(T) && b == typemax(T) || a == typemax(T) && b == typemin(T) - c = typemin(T) + if a == ⊥ && b == ⊤ || a == ⊤ && b == ⊥ + c = ⊤ else - c = Tropical(a.n + b.n) + c = a + b end return c end -inf(a::TropicalMinPlus, b::TropicalMinPlus) = TropicalMinPlus(max(a.n, b.n)) -sup(a::TropicalMinPlus, b::TropicalMinPlus) = TropicalMinPlus(min(a.n, b.n)) -Base.typemin(::Type{TropicalMinPlus{T}}) where T = TropicalMinPlus(posinf(T)) -Base.typemax(::Type{TropicalMinPlus{T}}) where T = TropicalMinPlus(neginf(T)) -Base.one(::Type{TropicalMinPlus{T}}) where T = TropicalMinPlus(zero(T)) - -# inverse and division -#Base.inv(x::TropicalMinPlus) = TropicalMinPlus(-x.n) +zero_alg(::Type{MinPlus}, ::Type{T}) where {T} = posinf(T) +typemax_alg(::Type{MinPlus}, ::Type{T}) where {T} = neginf(T) +one_alg(::Type{MinPlus}, ::Type{T}) where {T} = zero(T) # # Inf - Inf = -Inf # -Inf - -Inf = -Inf # -Base.:\(y::TropicalMinPlus, x::TropicalMinPlus) = x / y -Base.:/(x::TropicalMinPlus, y::TropicalMinPlus) = TropicalMinPlus(x.n - y.n) -Base.div(x::TropicalMinPlus, y::TropicalMinPlus) = TropicalMinPlus(x.n - y.n) +ldiv_alg(::Type{MinPlus}, a, b) = b - a + +function ldiv_alg(::Type{MinPlus}, b::T, a::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) -function Base.:/(b::T, a::T) where {T <: TropicalMinPlus{<:Rational}} - if a == b == typemin(T) || a == b == typemax(T) - c = typemax(T) + if a == b == ⊥ || a == b == ⊤ + c = ⊥ else - c = Tropical(b.n - a.n) + c = b - a end return c end -# promotion rules -Base.promote_rule(::Type{TropicalMinPlus{T1}}, b::Type{TropicalMinPlus{T2}}) where {T1, T2} = TropicalMinPlus{promote_type(T1,T2)} +rdiv_alg(::Type{MinPlus}, b, a) = ldiv_alg(MinPlus, a, b) + +leq_alg(::Type{MinPlus}, a, b) = a >= b +lt_alg(::Type{MinPlus}, a, b) = a > b + +add_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.min_fast(a, b) +mul_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.add_fast(a, b) +ldiv_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.sub_fast(b, a) +rdiv_fast_alg(::Type{MinPlus}, b, a) = ldiv_fast_alg(MinPlus, a, b) + +# --------------- # +# other operators # +# --------------- # + +function Base.isless(a::TropicalMinPlus, b::TropicalMinPlus) + return a < b +end diff --git a/test/tropical_andor.jl b/test/tropical_andor.jl index a0ff738..f27fb0f 100644 --- a/test/tropical_andor.jl +++ b/test/tropical_andor.jl @@ -12,12 +12,10 @@ using TropicalNumbers @test TropicalAndOr(false) + TropicalAndOr(true) == TropicalAndOr(true) @test TropicalAndOr(false) + TropicalAndOr(false) == TropicalAndOr(false) - @test inv(TropicalAndOr(true)) == TropicalAndOr(false) - @test inv(TropicalAndOr(false)) == TropicalAndOr(true) + #@test inv(TropicalAndOr(true)) == TropicalAndOr(false) + #@test inv(TropicalAndOr(false)) == TropicalAndOr(true) @test zero(TropicalAndOr) == TropicalAndOr(false) @test one(TropicalAndOr) == TropicalAndOr(true) @test zero(TropicalAndOr) ≈ TropicalAndOr(false) - - println(TropicalAndOr(true)) end diff --git a/test/tropical_maxmul.jl b/test/tropical_maxmul.jl index b9fd18e..cadeccd 100644 --- a/test/tropical_maxmul.jl +++ b/test/tropical_maxmul.jl @@ -25,7 +25,6 @@ using TropicalNumbers @test TropicalMaxMul(1//0) * TropicalMaxMul(1//1) == TropicalMaxMul(1//0) @test content(TropicalMaxMul(3.0)) == 3.0 @test TropicalMaxMul{Float32}(TropicalMaxMul(0.0)) isa TropicalMaxMul{Float32} - println(TropicalMaxMulF64(3)) # promote and convert t1 = TropicalMaxMul(2) @@ -41,23 +40,23 @@ using TropicalNumbers @test promote_type(TropicalMaxMul{Float64}, TropicalMaxMulF32, TropicalMaxMul{Int32}) == TropicalMaxMulF64 @test TropicalMaxMul(3) / TropicalMaxMul(4) == TropicalMaxMul(3 / 4) - @test TropicalMaxMul(3) ÷ TropicalMaxMul(4) == TropicalMaxMul(3 ÷ 4) + #@test TropicalMaxMul(3) ÷ TropicalMaxMul(4) == TropicalMaxMul(3 ÷ 4) @test inv(TropicalMaxMul(3)) == TropicalMaxMul(1/3) x = TropicalMaxMul(2.0) @test x * true == x * one(x) @test x / true == x / one(x) - @test x ÷ true == x ÷ one(x) + #@test x ÷ true == x ÷ one(x) @test x * false == x * zero(x) @test x / false == x / zero(x) # @test x ÷ false == x / zero(x) - @test isnan(x ÷ false) + #@test isnan(x ÷ false) @test true * x == one(x) * x @test true / x == one(x) / x - @test true ÷ x == one(x) ÷ x + #@test true ÷ x == one(x) ÷ x @test false * x == zero(x) * x @test false / x == zero(x) / x - @test false ÷ x == zero(x) ÷ x + #@test false ÷ x == zero(x) ÷ x @test isnan(TropicalMaxMul(NaN)) @test !isnan(TropicalMaxMul(Inf)) diff --git a/test/tropical_maxplus.jl b/test/tropical_maxplus.jl index d40ff12..c42c722 100644 --- a/test/tropical_maxplus.jl +++ b/test/tropical_maxplus.jl @@ -34,7 +34,6 @@ using TropicalNumbers @test Tropical(-1//0) * Tropical(-1//1) == Tropical(-1//0) @test content(Tropical(3.0)) == 3.0 @test Tropical{Float32}(Tropical(0.0)) isa Tropical{Float32} - println(TropicalF64(3)) # promote and convert t1 = Tropical(2) @@ -50,22 +49,22 @@ using TropicalNumbers @test promote_type(Tropical{Float64}, TropicalF32, Tropical{Int32}) == TropicalF64 @test Tropical(3) / Tropical(4) == Tropical(-1) - @test Tropical(3) ÷ Tropical(4) == Tropical(-1) + #@test Tropical(3) ÷ Tropical(4) == Tropical(-1) @test inv(Tropical(3)) == Tropical(-3) x = Tropical(2.0) @test x * true == x * one(x) @test x / true == x / one(x) - @test x ÷ true == x ÷ one(x) + #@test x ÷ true == x ÷ one(x) @test x * false == x * zero(x) @test x / false == x / zero(x) - @test x ÷ false == x ÷ zero(x) + #@test x ÷ false == x ÷ zero(x) @test true * x == one(x) * x @test true / x == one(x) / x - @test true ÷ x == one(x) ÷ x + #@test true ÷ x == one(x) ÷ x @test false * x == zero(x) * x @test false / x == zero(x) / x - @test false ÷ x == zero(x) ÷ x + #@test false ÷ x == zero(x) ÷ x @test isnan(Tropical(NaN)) @test !isnan(Tropical(-Inf)) diff --git a/test/tropical_minplus.jl b/test/tropical_minplus.jl index d308500..2ee4bd4 100644 --- a/test/tropical_minplus.jl +++ b/test/tropical_minplus.jl @@ -43,22 +43,22 @@ using TropicalNumbers @test promote_type(TropicalMinPlus{Float64}, TropicalMinPlusF32, TropicalMinPlus{Int32}) == TropicalMinPlusF64 @test TropicalMinPlus(3) / TropicalMinPlus(4) == TropicalMinPlus(-1) - @test TropicalMinPlus(3) ÷ TropicalMinPlus(4) == TropicalMinPlus(-1) + #@test TropicalMinPlus(3) ÷ TropicalMinPlus(4) == TropicalMinPlus(-1) @test inv(TropicalMinPlus(3)) == TropicalMinPlus(-3) x = TropicalMinPlus(2.0) @test x * true == x * one(x) @test x / true == x / one(x) - @test x ÷ true == x ÷ one(x) + #@test x ÷ true == x ÷ one(x) @test x * false == x * zero(x) @test x / false == x / zero(x) - @test x ÷ false == x ÷ zero(x) + #@test x ÷ false == x ÷ zero(x) @test true * x == one(x) * x @test true / x == one(x) / x - @test true ÷ x == one(x) ÷ x + #@test true ÷ x == one(x) ÷ x @test false * x == zero(x) * x @test false / x == zero(x) / x - @test false ÷ x == zero(x) ÷ x + #@test false ÷ x == zero(x) ÷ x @test isnan(TropicalMinPlus(NaN)) @test !isnan(TropicalMinPlus(-Inf)) From 33eaf60d9cc2a32802075d7f757c304b3787f768 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Wed, 19 Nov 2025 21:30:10 -0500 Subject: [PATCH 03/23] Cosmetic changes. --- src/abstract_semiring_algebra.jl | 12 ++++-------- src/tropical_bitwise.jl | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index 12f0f2b..522f663 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -42,8 +42,7 @@ mul_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} """ function mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} - c = mul_alg(T, a, b) - return c + return mul_alg(T, a, b) end """ @@ -71,8 +70,7 @@ inf_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} - c = inf_alg(T, a, b) - return c + return inf_alg(T, a, b) end """ @@ -84,8 +82,7 @@ ldiv_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ function ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} - c = ldiv_alg(T, a, b) - return c + return ldiv_alg(T, a, b) end """ @@ -104,8 +101,7 @@ rdiv_alg(::Type{T}, b, a) where {T <: AbstractQuantaleAlgebra} rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ function rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} - c = rdiv_alg(T, a, b) - return c + return rdiv_alg(T, a, b) end """ diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index ae97586..76a2178 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -3,7 +3,7 @@ struct Bitwise <: AbstractLatticeAlgebra end """ TropicalBitwise{T} <: AbstractSemiring -`TropicalBitwise` is a semiring algebra that parallelizes the [`TropicalBitwise`](@ref) algebra, +`TropicalBitwise` is a semiring algebra that parallelizes the [`TropicalAndOr`](@ref) algebra, It can be described by * TropicalBitwise, (ℝ, |, &, 0, ~0). From 08349d6b202cba45d3dc452b7e556c622ae1ec2f Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Thu, 20 Nov 2025 12:41:41 -0500 Subject: [PATCH 04/23] Add interface tests. --- Project.toml | 8 +++- src/TropicalNumbers.jl | 3 ++ src/abstract_semiring.jl | 5 +++ src/tropical_maxmin.jl | 4 +- src/tropical_maxmul.jl | 2 +- src/tropical_maxplus.jl | 2 +- src/tropical_minplus.jl | 2 +- test/interface.jl | 93 ++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 4 ++ 9 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 test/interface.jl diff --git a/Project.toml b/Project.toml index 63cbbae..20c3a3b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,14 +1,18 @@ name = "TropicalNumbers" uuid = "b3a74e9c-7526-4576-a4eb-79c0d4c32334" -authors = ["GiggleLiu"] version = "0.6.4" +authors = ["GiggleLiu"] + +[deps] +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] +Random = "1.11.0" julia = "1" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test", "Documenter"] diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 8e8b456..7919aca 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -1,5 +1,8 @@ module TropicalNumbers +using Random +using Random: SamplerType + export content, neginf, posinf, inf, fli, fri, inf_fast, ldiv_fast, rdiv_fast, ∧ export TropicalTypes, AbstractSemiring diff --git a/src/abstract_semiring.jl b/src/abstract_semiring.jl index 9a63e8c..1f5a89a 100644 --- a/src/abstract_semiring.jl +++ b/src/abstract_semiring.jl @@ -81,6 +81,11 @@ function Base.isinf(a::Semiring) return isinf(content(a)) end +function Base.rand(rng::AbstractRNG, sampler::SamplerType{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} + n = rand(rng, T) + return Semiring{A}(n) +end + function Base.:(==)(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} return content(a) == content(b) end diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl index 31938c2..90d8d5d 100644 --- a/src/tropical_maxmin.jl +++ b/src/tropical_maxmin.jl @@ -39,8 +39,8 @@ one_alg(::Type{MaxMin}, ::Type{T}) where {T} = posinf(T) ldiv_alg(::Type{MaxMin}, a, b) = a <= b ? typemax(b) : b rdiv_alg(::Type{MaxMin}, b, a) = ldiv_alg(MaxMin, a, b) -leq_alg(::Type{MaxMin}, b, a) = a <= b -lt_alg(::Type{MaxMin}, b, a) = a < b +leq_alg(::Type{MaxMin}, a, b) = a <= b +lt_alg(::Type{MaxMin}, a, b) = a < b add_fast_alg(::Type{MaxMin}, a, b) = Base.FastMath.max_fast(a, b) mul_fast_alg(::Type{MaxMin}, a, b) = Base.FastMath.min_fast(a, b) diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index d116583..b3b7ac4 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -64,7 +64,7 @@ one_alg(::Type{MaxMul}, ::Type{T}) where {T} = one(T) # ldiv_alg(::Type{MaxMul}, a, b) = b / a -function ldiv_alg(::Type{MaxMul}, b::T, a::T) where {T <: Rational} +function ldiv_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} ⊤ = typemax(T) ⊥ = zero(T) diff --git a/src/tropical_maxplus.jl b/src/tropical_maxplus.jl index a466bee..ac1515f 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -65,7 +65,7 @@ one_alg(::Type{MaxPlus}, ::Type{T}) where {T} = zero(T) # ldiv_alg(::Type{MaxPlus}, a, b) = b - a -function ldiv_alg(::Type{MaxPlus}, b::T, a::T) where {T <: Rational} +function ldiv_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} ⊤ = typemax(T) ⊥ = typemin(T) diff --git a/src/tropical_minplus.jl b/src/tropical_minplus.jl index ba48a32..4f01098 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -64,7 +64,7 @@ one_alg(::Type{MinPlus}, ::Type{T}) where {T} = zero(T) # ldiv_alg(::Type{MinPlus}, a, b) = b - a -function ldiv_alg(::Type{MinPlus}, b::T, a::T) where {T <: Rational} +function ldiv_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} ⊤ = typemax(T) ⊥ = typemin(T) diff --git a/test/interface.jl b/test/interface.jl new file mode 100644 index 0000000..53f9ce1 --- /dev/null +++ b/test/interface.jl @@ -0,0 +1,93 @@ +function test_semiring(a::T, b::T, c::T) where {T <: AbstractSemiring} + # 1 is the multiplicative identity + @test one(T) * a ≈ a * one(T) ≈ a + + # 0 is the additive identity + @test zero(T) + a ≈ a + zero(T) ≈ a + + # 0 is absorbing + @test zero(T) * a ≈ a * zero(T) ≈ zero(T) + + # multiplication associates + @test (a * b) * c ≈ a * (b * c) + + # addition associates + @test (a + b) + c ≈ a + (b + c) + + # addition commutes + @test a + b ≈ b + a + + # multiplication right-distributes over addition + @test (a + b) * c ≈ a * c + b * c + + # multiplication left-distributes over addition + @test a * (b + c) ≈ a * b + a * c +end + +function test_quantale(a::T, b::T, c::T) where {T <: AbstractSemiring} + test_semiring(a, b, c) + + # ⊤ is the identity for infimum + @test typemax(T) ∧ a ≈ a ∧ typemax(T) ≈ a + + # addition and infimum are connected by the absorbtion law + @test a + (a ∧ b) ≈ a ∧ (a + b) ≈ a + + # lattice is residuated + @test (a * b <= c) == (b <= a \ c) == (a <= c / b) + + # partial order is reflexive + @test a <= a + + # strict order is irreflexive + @test !(a < a) + + # partial order is transitive + @test (a <= b && b <= c) <= (a <= c) + + # strict order is transitive + @test (a < b && b < c) <= (a < c) + + # partial ordering agrees with lattice structure + @test (a <= b) == (a ≈ a ∧ b) + @test (a <= b) == (b ≈ a + b) + + # strict ordering agrees with partial ordering + @test (a < b) == (a != b && a <= b) +end + +@testset "interface" begin + types = ( + TropicalMinPlusF64, + TropicalMaxPlusF64, + TropicalMaxMulF64, + TropicalAndOr, + TropicalBitwiseI64, + TropicalMaxMinF64, + ) + + for T in types + a = rand(T) + b = rand(T) + c = rand(T) + + test_quantale(a, b, c) + test_quantale(zero(T), b, c) + end + + types = ( + TropicalMinPlus{Rational{Int}}, + TropicalMaxPlus{Rational{Int}}, + TropicalMaxMul{Rational{Int}}, + ) + + a = 1 // 2 + b = 2 // 3 + c = 3 // 4 + + for T in types + test_quantale(T(a), T(b), T(c)) + test_quantale(zero(T), T(b), T(c)) + test_quantale(typemax(T), T(b), T(c)) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 33e416f..0ca2794 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -29,4 +29,8 @@ end include("counting_tropical.jl") end +@testset "interface" begin + include("interface.jl") +end + doctest(TropicalNumbers) From 300c4fbd4f0ee096d59e7b101be275920a7c2706 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Thu, 20 Nov 2025 13:03:30 -0500 Subject: [PATCH 05/23] Test fast operators. --- test/interface.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/interface.jl b/test/interface.jl index 53f9ce1..0b4f3e4 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,3 +1,5 @@ +using Base.FastMath: mul_fast, add_fast, div_fast + function test_semiring(a::T, b::T, c::T) where {T <: AbstractSemiring} # 1 is the multiplicative identity @test one(T) * a ≈ a * one(T) ≈ a @@ -56,6 +58,18 @@ function test_quantale(a::T, b::T, c::T) where {T <: AbstractSemiring} @test (a < b) == (a != b && a <= b) end +function test_fast(a::T, b::T, c::T) where {T <: AbstractSemiring} + @test a + b ≈ add_fast(a, b) + @test a * b ≈ mul_fast(a, b) + @test a ∧ b ≈ inf_fast(a, b) + @test a / b ≈ div_fast(a, b) + @test a \ b ≈ ldiv_fast(a, b) + + @test (a * b) + c ≈ fma(a, b, c) + @test (a \ b) ∧ c ≈ fli(a, b, c) + @test (a / b) ∧ c ≈ fri(a, b, c) +end + @testset "interface" begin types = ( TropicalMinPlusF64, @@ -71,6 +85,7 @@ end b = rand(T) c = rand(T) + test_fast(a, b, c) test_quantale(a, b, c) test_quantale(zero(T), b, c) end @@ -86,6 +101,7 @@ end c = 3 // 4 for T in types + test_fast(T(a), T(b), T(c)) test_quantale(T(a), T(b), T(c)) test_quantale(zero(T), T(b), T(c)) test_quantale(typemax(T), T(b), T(c)) From f2cd7151f8a47cc58b4ac2c496349b3054cf386a Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Thu, 20 Nov 2025 13:18:08 -0500 Subject: [PATCH 06/23] Test isless. --- test/interface.jl | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/interface.jl b/test/interface.jl index 0b4f3e4..bfb1e55 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -70,7 +70,40 @@ function test_fast(a::T, b::T, c::T) where {T <: AbstractSemiring} @test (a / b) ∧ c ≈ fri(a, b, c) end +function test_isless(a::T, b::T) where {T <: AbstractSemiring} + @test isless(a, b) == (a < b) +end + @testset "interface" begin + types = ( + TropicalMinPlusF64, + TropicalMaxPlusF64, + TropicalMaxMulF64, + TropicalAndOr, + TropicalMaxMinF64, + ) + + for T in types + a = rand(T) + b = rand(T) + + test_isless(a, b) + test_isless(a, a) + test_isless(a, zero(T)) + test_isless(a, typemax(T)) + end + + for T in types + a = rand(T) + b = rand(T) + c = rand(T) + + test_fast(a, b, c) + test_quantale(a, b, c) + test_quantale(zero(T), b, c) + end + + types = ( TropicalMinPlusF64, TropicalMaxPlusF64, @@ -104,6 +137,9 @@ end test_fast(T(a), T(b), T(c)) test_quantale(T(a), T(b), T(c)) test_quantale(zero(T), T(b), T(c)) + test_quantale(T(a), zero(T), zero(T)) test_quantale(typemax(T), T(b), T(c)) + test_quantale(typemax(T), zero(T), T(c)) + test_quantale(typemax(T), T(b), typemax(T)) end end From 0734ca8d9eb2d0cf6ca44de9e3dd5f3f268ce7a1 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Thu, 20 Nov 2025 13:31:40 -0500 Subject: [PATCH 07/23] Remove export. --- src/TropicalNumbers.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 7919aca..9262a99 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -3,7 +3,7 @@ module TropicalNumbers using Random using Random: SamplerType -export content, neginf, posinf, inf, fli, fri, inf_fast, ldiv_fast, rdiv_fast, ∧ +export content, neginf, posinf, inf, fli, fri, inf_fast, ldiv_fast, ∧ export TropicalTypes, AbstractSemiring export TropicalAndOr From fcbea4d6f5e690918b085cac492e8cecd8ba466e Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Thu, 20 Nov 2025 16:33:37 -0500 Subject: [PATCH 08/23] Various changes. --- src/TropicalNumbers.jl | 8 +- src/counting_tropical.jl | 15 ++- src/{abstract_semiring.jl => semiring.jl} | 110 +++++++++++----------- 3 files changed, 72 insertions(+), 61 deletions(-) rename src/{abstract_semiring.jl => semiring.jl} (74%) diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 9262a99..ff7fca2 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -15,14 +15,14 @@ export TropicalMaxMin, TropicalMaxMinF64, TropicalMaxMinF32, TropicalMaxMinF16, export TropicalBitwise, TropicalBitwiseI64, TropicalBitwiseI32, TropicalBitwiseI16 export CountingTropical, CountingTropicalF16, CountingTropicalF32, CountingTropicalF64, CountingTropicalI16, CountingTropicalI32, CountingTropicalI64 -neginf(::Type{T}) where T = typemin(T) -neginf(::Type{T}) where T<:Integer = T(-999999) +neginf(::Type{T}) where {T} = typemin(T) +neginf(::Type{T}) where {T <: Integer} = T(-999999) neginf(::Type{Int16}) = Int16(-16384) neginf(::Type{Int8}) = Int8(-64) -posinf(::Type{T}) where T = -neginf(T) +posinf(::Type{T}) where {T} = -neginf(T) include("abstract_semiring_algebra.jl") -include("abstract_semiring.jl") +include("semiring.jl") include("tropical_maxplus.jl") include("tropical_andor.jl") include("tropical_minplus.jl") diff --git a/src/counting_tropical.jl b/src/counting_tropical.jl index 35ee160..f67358e 100644 --- a/src/counting_tropical.jl +++ b/src/counting_tropical.jl @@ -1,5 +1,5 @@ """ - CountingTropical{T,CT} <: AbstractSemiring + CountingTropical{T,CT} <: Number Counting tropical number type is also a semiring algebra. It is tropical algebra with one extra field for counting, it is introduced in [arXiv:2008.06888](https://arxiv.org/abs/2008.06888). @@ -20,7 +20,7 @@ julia> zero(CountingTropicalF64) (-Inf, 0.0) ``` """ -struct CountingTropical{T,CT} <: AbstractSemiring +struct CountingTropical{T,CT} <: Number n::T c::CT end @@ -53,17 +53,26 @@ Base.typemin(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = Count Base.zero(::Type{CountingTropical{T}}) where T = zero(CountingTropical{T,T}) Base.zero(::Type{CountingTropical{T,CT}}) where {T<:Integer,CT} = CountingTropical(T(-999999), zero(CT)) Base.zero(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = typemin(CountingTropical{T, CT}) +Base.zero(::T) where {T <: CountingTropical} = zero(T) Base.one(::Type{CountingTropical{T}}) where T = one(CountingTropical{T,T}) Base.one(::Type{CountingTropical{T,CT}}) where {T<:Integer,CT} = CountingTropical(zero(T), one(CT)) Base.one(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = CountingTropical(zero(T), one(CT)) +Base.one(::T) where {T <: CountingTropical} = one(T) Base.isapprox(a::CountingTropical, b::CountingTropical; kwargs...) = isapprox(a.n, b.n; kwargs...) && isapprox(a.c, b.c; kwargs...) - +Base.isapprox(x::AbstractArray{<:CountingTropical}, y::AbstractArray{<:CountingTropical}; kwargs...) = all(isapprox.(x, y; kwargs...)) Base.show(io::IO, t::CountingTropical) = Base.print(io, (t.n, t.c)) Base.promote_rule(::Type{CountingTropical{T1,CT1}}, b::Type{CountingTropical{T2,CT2}}) where {T1,T2,CT1,CT2} = CountingTropical{promote_type(T1,T2), promote_type(CT1,CT2)} +content(a::CountingTropical) = a.n +content(::Type{<:CountingTropical{T}}) where {T} = T Base.isnan(a::CountingTropical) = isnan(a.n) Base.isinf(a::CountingTropical) = isinf(a.n) Base.:(==)(a::CountingTropical, b::CountingTropical) = a.n == b.n Base.:<=(a::CountingTropical, b::CountingTropical) = a.n <= b.n Base.:<(a::CountingTropical, b::CountingTropical) = a.n < b.n +Base.isless(a::CountingTropical, b::CountingTropical) = a < b +Base.:*(a::CountingTropical, b::Bool) = b ? a : zero(a) +Base.:*(b::Bool, a::CountingTropical) = b ? a : zero(a) +Base.:(/)(a::CountingTropical, b::Bool) = b ? a : a / zero(a) +Base.:(/)(b::Bool, a::CountingTropical) = b ? inv(a) : zero(a) diff --git a/src/abstract_semiring.jl b/src/semiring.jl similarity index 74% rename from src/abstract_semiring.jl rename to src/semiring.jl index 1f5a89a..1128c89 100644 --- a/src/abstract_semiring.jl +++ b/src/semiring.jl @@ -22,25 +22,7 @@ Fast semiring matrix multiplication is implemented in the following libraries. * [`CuTropicalGEMM`](https://github.com/ArrogantGao/CuTropicalGEMM.jl/) """ -abstract type AbstractSemiring <: Number end - -function Base.zero(::T) where {T <: AbstractSemiring} - return zero(T) -end - -function Base.one(::T) where {T <: AbstractSemiring} - return one(T) -end - -function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} - return b ? a : zero(T) -end - -function Base.:*(b::Bool, a::T) where {T <: AbstractSemiring} - return b ? a : zero(T) -end - -struct Semiring{A <: AbstractSemiringAlgebra, T} <: AbstractSemiring +struct Semiring{A <: AbstractSemiringAlgebra, T} <: Number n::T function Semiring{A, T}(n) where {A <: AbstractSemiringAlgebra, T} @@ -48,37 +30,41 @@ struct Semiring{A <: AbstractSemiringAlgebra, T} <: AbstractSemiring end end +const AbstractSemiring{T} = Semiring{<:AbstractSemiringAlgebra, T} +const AbstractQuantale{T} = Semiring{<:AbstractQuantaleAlgebra, T} +const AbstractLattice{T} = Semiring{<:AbstractLatticeAlgebra, T} + function Semiring{A}(n::T) where {A <: AbstractSemiringAlgebra, T} return Semiring{A, T}(n) end function Semiring{A}(a::Semiring{A}) where {A <: AbstractSemiringAlgebra} - return Semiring{A}(content(a)) + return Semiring{A}(a.n) end function Semiring{A, T}(a::Semiring{A}) where {A <: AbstractSemiringAlgebra, T} - return Semiring{A, T}(content(a)) + return Semiring{A, T}(a.n) end -function content(a::Semiring) +function content(a::AbstractSemiring) return a.n end -function content(::Type{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} +function content(::Type{<:AbstractSemiring{T}}) where {T} return T end -function Base.show(io::IO, a::Semiring) - print(io, content(a)) +function Base.show(io::IO, a::AbstractSemiring) + print(io, a.n) return end -function Base.isnan(a::Semiring) - return isnan(content(a)) +function Base.isnan(a::AbstractSemiring) + return isnan(a.n) end -function Base.isinf(a::Semiring) - return isinf(content(a)) +function Base.isinf(a::AbstractSemiring) + return isinf(a.n) end function Base.rand(rng::AbstractRNG, sampler::SamplerType{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} @@ -87,11 +73,11 @@ function Base.rand(rng::AbstractRNG, sampler::SamplerType{Semiring{A, T}}) where end function Base.:(==)(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} - return content(a) == content(b) + return a.n == b.n end function Base.isapprox(a::Semiring{A}, b::Semiring{A}; kw...) where {A <: AbstractSemiringAlgebra} - return isapprox(content(a), content(b); kw...) + return isapprox(a.n, b.n; kw...) end function Base.isapprox(a::AbstractArray{<:Semiring{A}}, b::AbstractArray{<:Semiring{A}}; kw...) where {A <: AbstractSemiringAlgebra} @@ -112,33 +98,49 @@ function Base.zero(::Type{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, return Semiring{A}(n) end +function Base.zero(::T) where {T <: AbstractSemiring} + return zero(T) +end + function Base.one(::Type{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} n = one_alg(A, T) return Semiring{A}(n) end +function Base.one(::T) where {T <: AbstractSemiring} + return one(T) +end + function Base.:+(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} - n = add_alg(A, content(a), content(b)) + n = add_alg(A, a.n, b.n) return Semiring{A}(n) end function Base.FastMath.add_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} - n = add_fast_alg(A, content(a), content(b)) + n = add_fast_alg(A, a.n, b.n) return Semiring{A}(n) end function Base.:*(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} - n = mul_alg(A, content(a), content(b)) + n = mul_alg(A, a.n, b.n) return Semiring{A}(n) end +function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} + return b ? a : zero(T) +end + +function Base.:*(b::Bool, a::T) where {T <: AbstractSemiring} + return b ? a : zero(T) +end + function Base.FastMath.mul_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} - n = mul_fast_alg(A, content(a), content(b)) + n = mul_fast_alg(A, a.n, b.n) return Semiring{A}(n) end function Base.fma(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractSemiringAlgebra} - n = mul_add_alg(A, content(a), content(b), content(c)) + n = mul_add_alg(A, a.n, b.n, c.n) return Semiring{A}(n) end @@ -146,11 +148,11 @@ end # Quantales # # --------- # -function Base.typemin(::Type{T}) where {T <: Semiring{<:AbstractQuantaleAlgebra}} +function Base.typemin(::Type{T}) where {T <: AbstractQuantale} return zero(T) end -function Base.typemin(::T) where {T <: Semiring{<:AbstractQuantaleAlgebra}} +function Base.typemin(::T) where {T <: AbstractQuantale} return zero(T) end @@ -159,12 +161,12 @@ function Base.typemax(::Type{Semiring{A, T}}) where {A <: AbstractQuantaleAlgebr return Semiring{A}(n) end -function Base.typemax(::T) where {T <: Semiring{<:AbstractQuantaleAlgebra}} +function Base.typemax(::T) where {T <: AbstractQuantale} return typemax(T) end function inf(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = inf_alg(A, content(a), content(b)) + n = inf_alg(A, a.n, b.n) return Semiring{A}(n) end @@ -173,12 +175,12 @@ function inf_fast(a, b) end function inf_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = inf_fast_alg(A, content(a), content(b)) + n = inf_fast_alg(A, a.n, b.n) return Semiring{A}(n) end function Base.:\(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = ldiv_alg(A, content(a), content(b)) + n = ldiv_alg(A, a.n, b.n) return Semiring{A}(n) end @@ -187,7 +189,7 @@ function ldiv_fast(a, b) end function ldiv_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = ldiv_fast_alg(A, content(a), content(b)) + n = ldiv_fast_alg(A, a.n, b.n) return Semiring{A}(n) end @@ -196,25 +198,25 @@ function fli(a, b, c) end function fli(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = inf_ldiv_alg(A, content(a), content(b), content(c)) + n = inf_ldiv_alg(A, a.n, b.n, c.n) return Semiring{A}(n) end function Base.:/(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = rdiv_alg(A, content(b), content(a)) + n = rdiv_alg(A, b.n, a.n) return Semiring{A}(n) end -function Base.:/(b::T, a::Bool) where {T <: Semiring{<:AbstractQuantaleAlgebra}} +function Base.:/(b::T, a::Bool) where {T <: AbstractQuantale} return a ? b : typemax(T) end -function Base.:/(b::Bool, a::T) where {T <: Semiring{<:AbstractQuantaleAlgebra}} +function Base.:/(b::Bool, a::T) where {T <: AbstractQuantale} return b ? one(T) / a : zero(T) end function Base.FastMath.div_fast(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = rdiv_fast_alg(A, content(b), content(a)) + n = rdiv_fast_alg(A, b.n, a.n) return Semiring{A}(n) end @@ -223,16 +225,16 @@ function fri(b, a, c) end function fri(b::Semiring{A}, a::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - n = inf_rdiv_alg(A, content(b), content(a), content(c)) + n = inf_rdiv_alg(A, b.n, a.n, c.n) return Semiring{A}(n) end function Base.:<=(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - return leq_alg(A, content(a), content(b)) + return leq_alg(A, a.n, b.n) end function Base.:<(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - return lt_alg(A, content(a), content(b)) + return lt_alg(A, a.n, b.n) end # -------- # @@ -240,16 +242,16 @@ end # -------- # function Base.:^(a::Semiring{A}, b::Number) where {A <: AbstractTropicalAlgebra} - n = exp_alg(A, content(a), b) + n = exp_alg(A, a.n, b) return Semiring{A}(n) end function Base.:^(a::Semiring{A}, b::Integer) where {A <: AbstractTropicalAlgebra} - n = exp_alg(A, content(a), b) + n = exp_alg(A, a.n, b) return Semiring{A}(n) end function Base.inv(a::Semiring{A}) where {A <: AbstractTropicalAlgebra} - n = inv_alg(A, content(a)) + n = inv_alg(A, a.n) return Semiring{A}(n) end From 35381addaf315b764b34f24782066678163decdc Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Thu, 20 Nov 2025 23:19:17 -0500 Subject: [PATCH 09/23] Small changes. --- src/semiring.jl | 12 ++++++++++-- src/tropical_maxmin.jl | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/semiring.jl b/src/semiring.jl index 1128c89..bba77bd 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -130,8 +130,8 @@ function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} return b ? a : zero(T) end -function Base.:*(b::Bool, a::T) where {T <: AbstractSemiring} - return b ? a : zero(T) +function Base.:*(a::Bool, b::T) where {T <: AbstractSemiring} + return b * a end function Base.FastMath.mul_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} @@ -237,6 +237,14 @@ function Base.:<(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlg return lt_alg(A, a.n, b.n) end +# -------- # +# Lattices # +# -------- # + +function Base.:/(b::Bool, a::T) where {T <: AbstractLattice} + return b ? one(T) : zero(T) +end + # -------- # # Tropical # # -------- # diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl index 90d8d5d..f378c16 100644 --- a/src/tropical_maxmin.jl +++ b/src/tropical_maxmin.jl @@ -36,7 +36,7 @@ mul_alg(::Type{MaxMin}, a, b) = min(a, b) zero_alg(::Type{MaxMin}, ::Type{T}) where {T} = neginf(T) one_alg(::Type{MaxMin}, ::Type{T}) where {T} = posinf(T) -ldiv_alg(::Type{MaxMin}, a, b) = a <= b ? typemax(b) : b +ldiv_alg(::Type{MaxMin}, a, b::T) where {T} = a <= b ? one_alg(MaxMin, T) : b rdiv_alg(::Type{MaxMin}, b, a) = ldiv_alg(MaxMin, a, b) leq_alg(::Type{MaxMin}, a, b) = a <= b From 417ca3d3220f375fc26d908a6760f7dd6a064128 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Fri, 21 Nov 2025 06:12:38 -0500 Subject: [PATCH 10/23] Fix random number generation. --- src/TropicalNumbers.jl | 2 +- src/semiring.jl | 29 +++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index ff7fca2..4a636fb 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -1,7 +1,7 @@ module TropicalNumbers using Random -using Random: SamplerType +using Random: Repetition, Sampler export content, neginf, posinf, inf, fli, fri, inf_fast, ldiv_fast, ∧ export TropicalTypes, AbstractSemiring diff --git a/src/semiring.jl b/src/semiring.jl index bba77bd..c995be9 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -24,10 +24,10 @@ Fast semiring matrix multiplication is implemented in the following libraries. """ struct Semiring{A <: AbstractSemiringAlgebra, T} <: Number n::T +end - function Semiring{A, T}(n) where {A <: AbstractSemiringAlgebra, T} - return new{A, T}(n) - end +struct SemiringSampler{A <: AbstractSemiringAlgebra, T, S <: Sampler{T}} <: Sampler{Semiring{A, T}} + s::S end const AbstractSemiring{T} = Semiring{<:AbstractSemiringAlgebra, T} @@ -46,6 +46,10 @@ function Semiring{A, T}(a::Semiring{A}) where {A <: AbstractSemiringAlgebra, T} return Semiring{A, T}(a.n) end +function SemiringSampler{A, T}(s::S) where {A <: AbstractSemiringAlgebra, T, S <: Sampler{T}} + return SemiringSampler{A, T, S}(s) +end + function content(a::AbstractSemiring) return a.n end @@ -67,11 +71,6 @@ function Base.isinf(a::AbstractSemiring) return isinf(a.n) end -function Base.rand(rng::AbstractRNG, sampler::SamplerType{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} - n = rand(rng, T) - return Semiring{A}(n) -end - function Base.:(==)(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} return a.n == b.n end @@ -89,6 +88,20 @@ function Base.promote_rule(::Type{Semiring{A, T}}, ::Type{Semiring{A, U}}) where return Semiring{A, V} end +# ------------------------ # +# Random Number Generation # +# ------------------------ # + +function Random.Sampler(::Type{R}, ::Type{Semiring{A, T}}, r::Repetition) where {R <: AbstractRNG, A <: AbstractSemiringAlgebra, T} + s = Sampler(R, T, r) + return SemiringSampler{A, T}(s) +end + +function Base.rand(rng::AbstractRNG, s::SemiringSampler{A}) where {A <: AbstractSemiringAlgebra} + n = rand(rng, s.s) + return Semiring{A}(n) +end + # --------- # # Semirings # # --------- # From bd5e7287fa1b3b57e50e72bf3003cccf03ea5227 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Fri, 21 Nov 2025 13:36:22 -0500 Subject: [PATCH 11/23] Merge `Bitwise` and `AndOr`. --- src/TropicalNumbers.jl | 1 - src/counting_tropical.jl | 59 +++++++++++++++++++++------------------- src/semiring.jl | 8 +++--- src/tropical_andor.jl | 53 ------------------------------------ src/tropical_bitwise.jl | 9 ++++++ src/tropical_maxmin.jl | 2 +- 6 files changed, 45 insertions(+), 87 deletions(-) delete mode 100644 src/tropical_andor.jl diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 4a636fb..3d4ee59 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -24,7 +24,6 @@ posinf(::Type{T}) where {T} = -neginf(T) include("abstract_semiring_algebra.jl") include("semiring.jl") include("tropical_maxplus.jl") -include("tropical_andor.jl") include("tropical_minplus.jl") include("tropical_maxmul.jl") include("tropical_maxmin.jl") diff --git a/src/counting_tropical.jl b/src/counting_tropical.jl index f67358e..d9c8351 100644 --- a/src/counting_tropical.jl +++ b/src/counting_tropical.jl @@ -1,5 +1,5 @@ """ - CountingTropical{T,CT} <: Number + CountingTropical{T, C} <: Number Counting tropical number type is also a semiring algebra. It is tropical algebra with one extra field for counting, it is introduced in [arXiv:2008.06888](https://arxiv.org/abs/2008.06888). @@ -20,49 +20,52 @@ julia> zero(CountingTropicalF64) (-Inf, 0.0) ``` """ -struct CountingTropical{T,CT} <: Number +struct CountingTropical{T,C} <: Number n::T - c::CT + c::C end -CountingTropical(x::T) where T<:Real = CountingTropical(x, one(T)) -CountingTropical{T1}(x) where {T1} = CountingTropical{T1,T1}(x) - -CountingTropical{T1,CT1}(x) where {T1, CT1} = CountingTropical{T1,CT1}(T1(x), one(CT1)) -CountingTropical{T1,CT1}(x::CountingTropical{T1,CT1}) where {T1,CT1} = x -CountingTropical{T1,CT1}(x::CountingTropical) where {T1,CT1} = CountingTropical(T1(x.n), CT1(x.c)) -CountingTropical{T1,CT1}(x::Tropical) where {T1,CT1} = CountingTropical{T1,CT1}(T1(TropicalNumbers.content(x)), one(CT1)) +CountingTropical(n::T) where {T <: Real} = CountingTropical{T}(n) +CountingTropical{T}(n) where {T} = CountingTropical{T, T}(n) +CountingTropical{T, C}(n) where {T, C} = CountingTropical{T, C}(n, one(C)) +CountingTropical{T, C}(a::Tropical) where {T, C} = CountingTropical{T, C}(a.n) +CountingTropical{T, C}(a::CountingTropical{T, C}) where {T, C} = a +CountingTropical{T, C}(a::CountingTropical) where {T, C} = CountingTropical{T, C}(a.n, a.c) Base.:*(a::CountingTropical, b::CountingTropical) = CountingTropical(a.n + b.n, a.c * b.c) Base.:^(a::CountingTropical, b::Real) = CountingTropical(a.n * b, a.c ^ b) Base.:^(a::CountingTropical, b::Integer) = CountingTropical(a.n * b, a.c ^ b) + function Base.:+(a::CountingTropical, b::CountingTropical) - n = max(a.n, b.n) if a.n > b.n + n = a.n c = a.c elseif a.n == b.n + n = a.n c = a.c + b.c else + n = b.n c = b.c end + CountingTropical(n, c) end -# inverse and division -Base.inv(x::CountingTropical) = CountingTropical(-x.n, x.c) -Base.typemin(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = CountingTropical(typemin(T), zero(CT)) -Base.zero(::Type{CountingTropical{T}}) where T = zero(CountingTropical{T,T}) -Base.zero(::Type{CountingTropical{T,CT}}) where {T<:Integer,CT} = CountingTropical(T(-999999), zero(CT)) -Base.zero(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = typemin(CountingTropical{T, CT}) +Base.inv(a::CountingTropical) = CountingTropical(-a.n, a.c) +Base.typemin(::Type{CountingTropical{T, C}}) where {T, C} = CountingTropical(neginf(T), zero(C)) + +Base.zero(::Type{CountingTropical{T, C}}) where {T, C} = typemin(CountingTropical{T, C}) +Base.zero(::Type{CountingTropical{T}}) where {T} = zero(CountingTropical{T, T}) Base.zero(::T) where {T <: CountingTropical} = zero(T) -Base.one(::Type{CountingTropical{T}}) where T = one(CountingTropical{T,T}) -Base.one(::Type{CountingTropical{T,CT}}) where {T<:Integer,CT} = CountingTropical(zero(T), one(CT)) -Base.one(::Type{CountingTropical{T,CT}}) where {T<:AbstractFloat,CT} = CountingTropical(zero(T), one(CT)) + +Base.one(::Type{CountingTropical{T, C}}) where {T, C} = CountingTropical(zero(T), one(C)) +Base.one(::Type{CountingTropical{T}}) where {T} = one(CountingTropical{T,T}) Base.one(::T) where {T <: CountingTropical} = one(T) -Base.isapprox(a::CountingTropical, b::CountingTropical; kwargs...) = isapprox(a.n, b.n; kwargs...) && isapprox(a.c, b.c; kwargs...) -Base.isapprox(x::AbstractArray{<:CountingTropical}, y::AbstractArray{<:CountingTropical}; kwargs...) = all(isapprox.(x, y; kwargs...)) -Base.show(io::IO, t::CountingTropical) = Base.print(io, (t.n, t.c)) -Base.promote_rule(::Type{CountingTropical{T1,CT1}}, b::Type{CountingTropical{T2,CT2}}) where {T1,T2,CT1,CT2} = CountingTropical{promote_type(T1,T2), promote_type(CT1,CT2)} +Base.isapprox(a::CountingTropical, b::CountingTropical; kw...) = isapprox(a.n, b.n; kw...) && isapprox(a.c, b.c; kw...) +Base.isapprox(x::AbstractArray{<:CountingTropical}, y::AbstractArray{<:CountingTropical}; kw...) = all(isapprox.(x, y; kw...)) + +Base.show(io::IO, t::CountingTropical) = Base.print(io, (t.n, t.c)) +Base.promote_rule(::Type{CountingTropical{T, C}}, b::Type{CountingTropical{U, D}}) where {T, U, C, D} = CountingTropical{promote_type(T, U), promote_type(C, D)} content(a::CountingTropical) = a.n content(::Type{<:CountingTropical{T}}) where {T} = T @@ -72,7 +75,7 @@ Base.:(==)(a::CountingTropical, b::CountingTropical) = a.n == b.n Base.:<=(a::CountingTropical, b::CountingTropical) = a.n <= b.n Base.:<(a::CountingTropical, b::CountingTropical) = a.n < b.n Base.isless(a::CountingTropical, b::CountingTropical) = a < b -Base.:*(a::CountingTropical, b::Bool) = b ? a : zero(a) -Base.:*(b::Bool, a::CountingTropical) = b ? a : zero(a) -Base.:(/)(a::CountingTropical, b::Bool) = b ? a : a / zero(a) -Base.:(/)(b::Bool, a::CountingTropical) = b ? inv(a) : zero(a) +Base.:*(a::CountingTropical, b::Bool) = ifelse(b, a, zero(a)) +Base.:*(a::Bool, b::CountingTropical) = b * a +Base.:/(b::CountingTropical, a::Bool) = ifelse(a, b, b / zero(a)) +Base.:/(b::Bool, a::CountingTropical) = ifelse(b, inv(a), zero(a)) diff --git a/src/semiring.jl b/src/semiring.jl index c995be9..1c27cfe 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -140,7 +140,7 @@ function Base.:*(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlg end function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} - return b ? a : zero(T) + return ifelse(b, a, zero(T)) end function Base.:*(a::Bool, b::T) where {T <: AbstractSemiring} @@ -221,11 +221,11 @@ function Base.:/(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlg end function Base.:/(b::T, a::Bool) where {T <: AbstractQuantale} - return a ? b : typemax(T) + return ifelse(a, b, typemax(T)) end function Base.:/(b::Bool, a::T) where {T <: AbstractQuantale} - return b ? one(T) / a : zero(T) + return ifelse(b, one(T) / a, zero(T)) end function Base.FastMath.div_fast(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} @@ -255,7 +255,7 @@ end # -------- # function Base.:/(b::Bool, a::T) where {T <: AbstractLattice} - return b ? one(T) : zero(T) + return ifelse(b, one(T), zero(T)) end # -------- # diff --git a/src/tropical_andor.jl b/src/tropical_andor.jl deleted file mode 100644 index bcc210e..0000000 --- a/src/tropical_andor.jl +++ /dev/null @@ -1,53 +0,0 @@ -struct AndOr <: AbstractLatticeAlgebra end - -""" - TropicalAndOr <: AbstractSemiring - -TropicalAndOr is a semiring algebra, can be described by -* TropicalAndOr, ([T, F], or, and, false, true). - -It maps -* `+` to `or` in regular algebra, -* `*` to `and` in regular algebra, -* `1` to `true` in regular algebra, -* `0` to `false` in regular algebra. - -For the parallel bit-wise version, see [`TropicalBitwise`](@ref). - -Example -------------------------- -```jldoctest; setup=:(using TropicalNumbers) -julia> TropicalAndOr(true) + TropicalAndOr(false) -true - -julia> TropicalAndOr(true) * TropicalAndOr(false) -false - -julia> one(TropicalAndOr) -true - -julia> zero(TropicalAndOr) -false -``` -""" -const TropicalAndOr = Semiring{AndOr, Bool} - -add_alg(::Type{AndOr}, a::Bool, b::Bool) = a || b -mul_alg(::Type{AndOr}, a::Bool, b::Bool) = a && b - -zero_alg(::Type{AndOr}, ::Type{Bool}) = false -one_alg(::Type{AndOr}, ::Type{Bool}) = true - -ldiv_alg(::Type{AndOr}, a::Bool, b::Bool) = b || !a -rdiv_alg(::Type{AndOr}, b::Bool, a::Bool) = ldiv_alg(AndOr, a, b) - -leq_alg(::Type{AndOr}, a::Bool, b::Bool) = a <= b -lt_alg(::Type{AndOr}, a::Bool, b::Bool) = a < b - -# --------------- # -# other operators # -# --------------- # - -function Base.isless(a::TropicalAndOr, b::TropicalAndOr) - return a < b -end diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index 76a2178..bbc5a9c 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -30,6 +30,7 @@ julia> one(TropicalBitwiseI64) ``` """ const TropicalBitwise = Semiring{Bitwise} +const TropicalAndOr = TropicalBitwise{Bool} add_alg(::Type{Bitwise}, a, b) = a | b mul_alg(::Type{Bitwise}, a, b) = a & b @@ -39,3 +40,11 @@ one_alg(::Type{Bitwise}, ::Type{T}) where {T} = ~zero(T) ldiv_alg(::Type{Bitwise}, a, b) = b | ~a rdiv_alg(::Type{Bitwise}, b, a) = ldiv_alg(Bitwise, a, b) + +# --------------- # +# other operators # +# --------------- # + +function Base.isless(a::TropicalAndOr, b::TropicalAndOr) + return a < b +end diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl index f378c16..61b15f4 100644 --- a/src/tropical_maxmin.jl +++ b/src/tropical_maxmin.jl @@ -36,7 +36,7 @@ mul_alg(::Type{MaxMin}, a, b) = min(a, b) zero_alg(::Type{MaxMin}, ::Type{T}) where {T} = neginf(T) one_alg(::Type{MaxMin}, ::Type{T}) where {T} = posinf(T) -ldiv_alg(::Type{MaxMin}, a, b::T) where {T} = a <= b ? one_alg(MaxMin, T) : b +ldiv_alg(::Type{MaxMin}, a, b::T) where {T} = ifelse(a <= b, one_alg(MaxMin, T), b) rdiv_alg(::Type{MaxMin}, b, a) = ldiv_alg(MaxMin, a, b) leq_alg(::Type{MaxMin}, a, b) = a <= b From 514b16e55bd9038b3395ed421befb0829d8b09bf Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Fri, 21 Nov 2025 14:20:52 -0500 Subject: [PATCH 12/23] Add tests and simplify. --- src/abstract_semiring_algebra.jl | 19 ++++++++++++------- src/counting_tropical.jl | 2 +- src/semiring.jl | 17 +++++------------ test/counting_tropical.jl | 10 +++++++++- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index 522f663..354baab 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -111,6 +111,13 @@ function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} return inf_fast_alg(T, rdiv_fast_alg(T, b, a), c) end +""" + inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} +""" +function inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} + return div_alg(T, one_alg(T, a), a) +end + """ leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ @@ -129,7 +136,7 @@ end # Lattices # # -------- # -function typemax_alg(::Type{T}, A::Type) where {T <: AbstractLatticeAlgebra} +function typemax_alg(::Type{T}, ::Type{A}) where {T <: AbstractLatticeAlgebra, A} return one_alg(T, A) end @@ -141,6 +148,10 @@ function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} return mul_fast_alg(T, a, b) end +function inv_alg(::Type{T}, a::A) where {T <: AbstractLatticeAlgebra, A} + return one_alg(T, A) +end + # -------- # # Tropical # # -------- # @@ -150,9 +161,3 @@ end """ exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} -""" - inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} -""" -function inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} - return div_alg(T, one_alg(T, a), a) -end diff --git a/src/counting_tropical.jl b/src/counting_tropical.jl index d9c8351..fe11bcf 100644 --- a/src/counting_tropical.jl +++ b/src/counting_tropical.jl @@ -77,5 +77,5 @@ Base.:<(a::CountingTropical, b::CountingTropical) = a.n < b.n Base.isless(a::CountingTropical, b::CountingTropical) = a < b Base.:*(a::CountingTropical, b::Bool) = ifelse(b, a, zero(a)) Base.:*(a::Bool, b::CountingTropical) = b * a -Base.:/(b::CountingTropical, a::Bool) = ifelse(a, b, b / zero(a)) +# Base.:/(b::CountingTropical, a::Bool) = ifelse(a, b, b / zero(a)) Base.:/(b::Bool, a::CountingTropical) = ifelse(b, inv(a), zero(a)) diff --git a/src/semiring.jl b/src/semiring.jl index 1c27cfe..dd7c66c 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -33,6 +33,7 @@ end const AbstractSemiring{T} = Semiring{<:AbstractSemiringAlgebra, T} const AbstractQuantale{T} = Semiring{<:AbstractQuantaleAlgebra, T} const AbstractLattice{T} = Semiring{<:AbstractLatticeAlgebra, T} +const AbstractTropical{T} = Semiring{<:AbstractTropicalAlgebra, T} function Semiring{A}(n::T) where {A <: AbstractSemiringAlgebra, T} return Semiring{A, T}(n) @@ -225,7 +226,7 @@ function Base.:/(b::T, a::Bool) where {T <: AbstractQuantale} end function Base.:/(b::Bool, a::T) where {T <: AbstractQuantale} - return ifelse(b, one(T) / a, zero(T)) + return ifelse(b, inv(a), zero(T)) end function Base.FastMath.div_fast(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} @@ -250,12 +251,9 @@ function Base.:<(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlg return lt_alg(A, a.n, b.n) end -# -------- # -# Lattices # -# -------- # - -function Base.:/(b::Bool, a::T) where {T <: AbstractLattice} - return ifelse(b, one(T), zero(T)) +function Base.inv(a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inv_alg(A, a.n) + return Semiring{A}(n) end # -------- # @@ -271,8 +269,3 @@ function Base.:^(a::Semiring{A}, b::Integer) where {A <: AbstractTropicalAlgebra n = exp_alg(A, a.n, b) return Semiring{A}(n) end - -function Base.inv(a::Semiring{A}) where {A <: AbstractTropicalAlgebra} - n = inv_alg(A, a.n) - return Semiring{A}(n) -end diff --git a/test/counting_tropical.jl b/test/counting_tropical.jl index 36be77a..a31d1c1 100644 --- a/test/counting_tropical.jl +++ b/test/counting_tropical.jl @@ -4,8 +4,13 @@ using TropicalNumbers @testset "counting tropical" begin ct1 = CountingTropical(2.0, 4.0) ct2 = CountingTropical(2.0, 3.0) + @test content(ct1) == 2.0 + @test content(typeof(ct1)) == Float64 + @test inv(ct1) == CountingTropical(-2.0, 4.0) @test ct1 * true == ct1 - @test ct1 * false == CountingTropical(-Inf, 1.0) + # @test ct1 / true == ct1 + @test false * ct1 == CountingTropical(-Inf, 1.0) + @test false / ct1 == CountingTropical(-Inf, 1.0) @test one(ct1) == CountingTropical(0.0, 1.0) @test zero(ct1) == CountingTropical(-Inf, 1.0) res1 = ct1 + ct2 @@ -34,9 +39,12 @@ using TropicalNumbers @test promote_type(CountingTropical{Float64,Float32}, CountingTropicalF32, CountingTropical{Int32,Int32}) == CountingTropical{Float64,Float32} @test one(CountingTropical{Float64}) == one(CountingTropicalF64) + @test isinf(CountingTropical(-Inf)) @test isnan(CountingTropical(NaN)) + @test !isinf(CountingTropical(NaN)) @test !isnan(CountingTropical(-Inf)) @test CountingTropical(2.0, 3.0) ^ 3.0 == CountingTropical(2.0, 3.0) * CountingTropical(2.0, 3.0) * CountingTropical(2.0, 3.0) @test CountingTropical(2.0, 3.0) ^ 3 == CountingTropical(2.0, 3.0) * CountingTropical(2.0, 3.0) * CountingTropical(2.0, 3.0) + @test CountingTropical(1.0, 2.0) <= CountingTropical(1.0, 1.0) < CountingTropical(2.0, 0.0) end From b843fdaa09ba3e226f66ae943a64bdd0eaf79a4d Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Fri, 21 Nov 2025 17:11:48 -0500 Subject: [PATCH 13/23] Implement Heyting algebra structure. --- src/TropicalNumbers.jl | 3 +- src/abstract_semiring_algebra.jl | 188 ++++++++++++++++++++++++++----- src/semiring.jl | 25 ++++ src/tropical_bitwise.jl | 2 +- src/tropical_maxmin.jl | 54 --------- src/tropical_maxmul.jl | 9 +- src/tropical_maxplus.jl | 49 ++++++-- src/tropical_minplus.jl | 4 +- test/interface.jl | 12 ++ 9 files changed, 246 insertions(+), 100 deletions(-) delete mode 100644 src/tropical_maxmin.jl diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 3d4ee59..4f76d3a 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -3,7 +3,7 @@ module TropicalNumbers using Random using Random: Repetition, Sampler -export content, neginf, posinf, inf, fli, fri, inf_fast, ldiv_fast, ∧ +export content, neginf, posinf, inf, imp, not, fia, fli, fri, fii, inf_fast, ldiv_fast, imp_fast, ∧ export TropicalTypes, AbstractSemiring export TropicalAndOr @@ -26,7 +26,6 @@ include("semiring.jl") include("tropical_maxplus.jl") include("tropical_minplus.jl") include("tropical_maxmul.jl") -include("tropical_maxmin.jl") include("tropical_bitwise.jl") include("counting_tropical.jl") diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index 354baab..77e1661 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -2,9 +2,13 @@ abstract type AbstractSemiringAlgebra end abstract type AbstractQuantaleAlgebra <: AbstractSemiringAlgebra end -abstract type AbstractLatticeAlgebra <: AbstractQuantaleAlgebra end +abstract type AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end -abstract type AbstractTropicalAlgebra <: AbstractQuantaleAlgebra end +abstract type AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra end + +abstract type AbstractTropicalAlgebra <: AbstractCommutativeQuantaleAlgebra end + +struct LatticeAlgebra{T <: AbstractQuantaleAlgebra} <: AbstractLatticeAlgebra end # --------- # # Semirings # @@ -15,22 +19,37 @@ abstract type AbstractTropicalAlgebra <: AbstractQuantaleAlgebra end """ zero_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} +function zero_alg(::Type{LatticeAlgebra{T}}, ::Type{A}) where {T <: AbstractQuantaleAlgebra, A} + return zero_alg(T, A) +end + """ one_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} """ one_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} +function one_alg(::Type{LatticeAlgebra{T}}, ::Type{A}) where {T <: AbstractQuantaleAlgebra, A} + return typemax_alg(T, A) +end + """ add_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} """ add_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} +function add_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return add_alg(T, a, b) +end + """ add_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} """ function add_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} - c = add_alg(T, a, b) - return c + return add_alg(T, a, b) +end + +function add_fast_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return add_fast_alg(T, a, b) end """ @@ -38,6 +57,10 @@ end """ mul_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} +function mul_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return inf_alg(T, a, b) +end + """ mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} """ @@ -45,6 +68,10 @@ function mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} return mul_alg(T, a, b) end +function mul_fast_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return inf_fast_alg(T, a, b) +end + """ mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} """ @@ -52,6 +79,11 @@ function mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} return add_fast_alg(T, mul_fast_alg(T, a, b), c) end +function mul_add_alg(::Type{LatticeAlgebra{T}}, a, b, c) where {T <: AbstractQuantaleAlgebra} + return inf_add_alg(T, a, b, c) +end + + # --------- # # Quantales # # --------- # @@ -61,11 +93,19 @@ end """ typemax_alg(::Type{T}, A::Type) where {T <: AbstractQuantaleAlgebra} +function typemax_alg(::Type{T}, ::Type{A}) where {T <: AbstractLatticeAlgebra, A} + return one_alg(T, A) +end + """ inf_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ inf_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +function inf_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} + return mul_alg(T, a, b) +end + """ inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ @@ -73,11 +113,26 @@ function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return inf_alg(T, a, b) end +function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} + return mul_fast_alg(T, a, b) +end + +""" + inf_add_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} +""" +function inf_add_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} + return add_fast_alg(T, inf_fast_alg(T, a, b), c) +end + """ ldiv_alg(::Type{T}, a, b) whre {T <: AbstractQuantaleAlgebra} """ ldiv_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +function ldiv_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return imp_alg(T, a, b) +end + """ ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ @@ -85,6 +140,14 @@ function ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return ldiv_alg(T, a, b) end +function ldiv_fast_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return imp_fast_alg(T, a, b) +end + +function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractCommutativeQuantaleAlgebra} + return inf_ldiv_alg(T, a, b, c) +end + """ inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} """ @@ -92,11 +155,19 @@ function inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} return inf_fast_alg(T, ldiv_fast_alg(T, a, b), c) end +function inf_ldiv_alg(::Type{LatticeAlgebra{T}}, a, b, c) where {T <: AbstractQuantaleAlgebra} + return inf_imp_alg(T, a, b, c) +end + """ rdiv_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ rdiv_alg(::Type{T}, b, a) where {T <: AbstractQuantaleAlgebra} +function rdiv_alg(::Type{T}, b, a) where {T <: AbstractCommutativeQuantaleAlgebra} + return ldiv_alg(T, a, b) +end + """ rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ @@ -104,6 +175,10 @@ function rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return rdiv_alg(T, a, b) end +function rdiv_fast_alg(::Type{T}, b, a) where {T <: AbstractCommutativeQuantaleAlgebra} + return ldiv_fast_alg(T, a, b) +end + """ inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} """ @@ -112,52 +187,111 @@ function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} end """ - inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} + imp_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ -function inv_alg(::Type{T}, a) where {T <: AbstractTropicalAlgebra} - return div_alg(T, one_alg(T, a), a) +imp_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +function imp_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} + return ldiv_alg(T, a, b) +end + +function imp_alg(::Type{T}, a, b::A) where {T <: AbstractTropicalAlgebra, A} + if leq_alg(T, a, b) + c = typemax_alg(T, A) + else + c = b + end + + return c end """ - leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + imp_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ -function leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} - return add_alg(T, a, b) == b +function imp_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return imp_alg(T, a, b) +end + +function imp_fast_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} + return ldiv_fast_alg(T, a, b) end """ - lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + inf_imp_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} """ -function lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} - return a != b && leq_alg(T, a, b) +function inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} + return inf_fast_alg(T, imp_fast_alg(T, a, b), c) end -# -------- # -# Lattices # -# -------- # - -function typemax_alg(::Type{T}, ::Type{A}) where {T <: AbstractLatticeAlgebra, A} - return one_alg(T, A) +function inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractLatticeAlgebra} + return inf_ldiv_alg(T, a, b, c) end -function inf_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} - return mul_alg(T, a, b) +function inf_imp_alg(::Type{T}, a, b::A, c::A) where {T <: AbstractTropicalAlgebra, A} + if leq_alg(T, a, b) + d = c + else + d = inf_fast_alg(T, b, c) + end + + return d end -function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} - return mul_fast_alg(T, a, b) +""" + inv_alg(::Type{T}, a) where {T <: AbstractQuantaleAlgebra} +""" +function inv_alg(::Type{T}, a::A) where {T <: AbstractQuantaleAlgebra, A} + return div_alg(T, one_alg(T, A), a) end function inv_alg(::Type{T}, a::A) where {T <: AbstractLatticeAlgebra, A} return one_alg(T, A) end -# -------- # -# Tropical # -# -------- # +""" + not_alg(::Type{T}, a) where {T <: AbstractQuantaleAlgebra} +""" +function not_alg(::Type{T}, a::A) where {T <: AbstractQuantaleAlgebra, A} + return imp_alg(T, a, zero_alg(T, A)) +end + +function not_alg(::Type{LatticeAlgebra{T}}, a) where {T <: AbstractQuantaleAlgebra} + return not_alg(T, a) +end + +function not_alg(::Type{T}, a::A) where {T <: AbstractTropicalAlgebra, A} + if leq_alg(T, a, zero_alg(T, A)) + c = typemax_alg(T, A) + else + c = zero_alg(T, A) + end + + return c +end + +""" + leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +function leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return add_alg(T, a, b) == b +end + +function leq_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return leq_alg(T, a, b) +end + +""" + lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +""" +function lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return a != b && leq_alg(T, a, b) +end + +function lt_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return lt_alg(T, a, b) +end """ exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} """ exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} - diff --git a/src/semiring.jl b/src/semiring.jl index dd7c66c..f629b9d 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -193,6 +193,11 @@ function inf_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAl return Semiring{A}(n) end +function fia(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inf_add_alg(A, a.n, b.n, c.n) + return Semiring{A}(n) +end + function Base.:\(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} n = ldiv_alg(A, a.n, b.n) return Semiring{A}(n) @@ -243,6 +248,21 @@ function fri(b::Semiring{A}, a::Semiring{A}, c::Semiring{A}) where {A <: Abstrac return Semiring{A}(n) end +function imp(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = imp_alg(A, a.n, b.n) + return Semiring{A}(n) +end + +function imp_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = imp_fast_alg(A, a.n, b.n) + return Semiring{A}(n) +end + +function fii(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inf_imp_alg(A, a.n, b.n, c.n) + return Semiring{A}(n) +end + function Base.:<=(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} return leq_alg(A, a.n, b.n) end @@ -256,6 +276,11 @@ function Base.inv(a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} return Semiring{A}(n) end +function not(a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = not_alg(A, a.n) + return Semiring{A}(n) +end + # -------- # # Tropical # # -------- # diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index bbc5a9c..353db94 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -34,12 +34,12 @@ const TropicalAndOr = TropicalBitwise{Bool} add_alg(::Type{Bitwise}, a, b) = a | b mul_alg(::Type{Bitwise}, a, b) = a & b +not_alg(::Type{Bitwise}, a) = ~a zero_alg(::Type{Bitwise}, ::Type{T}) where {T} = zero(T) one_alg(::Type{Bitwise}, ::Type{T}) where {T} = ~zero(T) ldiv_alg(::Type{Bitwise}, a, b) = b | ~a -rdiv_alg(::Type{Bitwise}, b, a) = ldiv_alg(Bitwise, a, b) # --------------- # # other operators # diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl deleted file mode 100644 index 61b15f4..0000000 --- a/src/tropical_maxmin.jl +++ /dev/null @@ -1,54 +0,0 @@ -struct MaxMin <: AbstractLatticeAlgebra end - -""" - TropicalMaxMin{T} <: AbstractSemiring - -TropicalMaxMin is a semiring algebra, can be described by -* TropicalMaxMin, (ℝ, max, min, -Inf, Inf). - -It maps -* `+` to `max` in regular algebra, -* `*` to `min` in regular algebra, -* `0` to `-Inf` in regular algebra (for integer content types, this is a small integer). -* `1` to `Inf` in regular algebra, (for integer content types, this is a large integer) - -Example -------------------------- -```jldoctest; setup=:(using TropicalNumbers) -julia> TropicalMaxMin(1.0) + TropicalMaxMin(3.0) -3.0 - -julia> TropicalMaxMin(1.0) * TropicalMaxMin(3.0) -1.0 - -julia> zero(TropicalMaxMinF64) --Inf - -julia> one(TropicalMaxMinF64) -Inf -``` -""" -const TropicalMaxMin = Semiring{MaxMin} - -add_alg(::Type{MaxMin}, a, b) = max(a, b) -mul_alg(::Type{MaxMin}, a, b) = min(a, b) - -zero_alg(::Type{MaxMin}, ::Type{T}) where {T} = neginf(T) -one_alg(::Type{MaxMin}, ::Type{T}) where {T} = posinf(T) - -ldiv_alg(::Type{MaxMin}, a, b::T) where {T} = ifelse(a <= b, one_alg(MaxMin, T), b) -rdiv_alg(::Type{MaxMin}, b, a) = ldiv_alg(MaxMin, a, b) - -leq_alg(::Type{MaxMin}, a, b) = a <= b -lt_alg(::Type{MaxMin}, a, b) = a < b - -add_fast_alg(::Type{MaxMin}, a, b) = Base.FastMath.max_fast(a, b) -mul_fast_alg(::Type{MaxMin}, a, b) = Base.FastMath.min_fast(a, b) - -# --------------- # -# other operators # -# --------------- # - -function Base.isless(a::TropicalMaxMin, b::TropicalMaxMin) - return a < b -end diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index b3b7ac4..0e9196b 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -38,8 +38,6 @@ inf_alg(::Type{MaxMul}, a, b) = min(a, b) # Inf * 0 = 0 # mul_alg(::Type{MaxMul}, a, b) = a * b -exp_alg(::Type{MaxMul}, a, b) = a ^ b -inv_alg(::Type{MaxMul}, a) = inv(a) function mul_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} ⊤ = typemax(T) @@ -54,6 +52,9 @@ function mul_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} return c end +exp_alg(::Type{MaxMul}, a, b) = a ^ b +inv_alg(::Type{MaxMul}, a) = inv(a) + zero_alg(::Type{MaxMul}, ::Type{T}) where {T} = zero(T) typemax_alg(::Type{MaxMul}, ::Type{T}) where {T} = posinf(T) one_alg(::Type{MaxMul}, ::Type{T}) where {T} = one(T) @@ -77,15 +78,13 @@ function ldiv_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} return c end -rdiv_alg(::Type{MaxMul}, b, a) = ldiv_alg(MaxMul, a, b) - leq_alg(::Type{MaxMul}, a, b) = a <= b lt_alg(::Type{MaxMul}, a, b) = a < b add_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.max_fast(a, b) mul_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.mul_fast(a, b) +inf_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.min_fast(a, b) ldiv_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.div_fast(b, a) -rdiv_fast_alg(::Type{MaxMul}, b, a) = ldiv_fast_alg(MaxMul, a, b) # --------------- # # other operators # diff --git a/src/tropical_maxplus.jl b/src/tropical_maxplus.jl index ac1515f..b4e5dd1 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -28,8 +28,38 @@ julia> zero(TropicalMaxPlusF64) -Inf ``` """ -const Tropical = Semiring{MaxPlus} -const TropicalMaxPlus = Tropical +const TropicalMaxPlus = Semiring{MaxPlus} +const Tropical = TropicalMaxPlus + +""" + TropicalMaxMin{T} <: AbstractSemiring + +TropicalMaxMin is a semiring algebra, can be described by +* TropicalMaxMin, (ℝ, max, min, -Inf, Inf). + +It maps +* `+` to `max` in regular algebra, +* `*` to `min` in regular algebra, +* `0` to `-Inf` in regular algebra (for integer content types, this is a small integer). +* `1` to `Inf` in regular algebra, (for integer content types, this is a large integer) + +Example +------------------------- +```jldoctest; setup=:(using TropicalNumbers) +julia> TropicalMaxMin(1.0) + TropicalMaxMin(3.0) +3.0 + +julia> TropicalMaxMin(1.0) * TropicalMaxMin(3.0) +1.0 + +julia> zero(TropicalMaxMinF64) +-Inf + +julia> one(TropicalMaxMinF64) +Inf +``` +""" +const TropicalMaxMin = Semiring{LatticeAlgebra{MaxPlus}} add_alg(::Type{MaxPlus}, a, b) = max(a, b) inf_alg(::Type{MaxPlus}, a, b) = min(a, b) @@ -39,8 +69,6 @@ inf_alg(::Type{MaxPlus}, a, b) = min(a, b) # Inf + -Inf = -Inf # mul_alg(::Type{MaxPlus}, a, b) = a + b -exp_alg(::Type{MaxPlus}, a, b) = a * b -inv_alg(::Type{MaxPlus}, a) = -a function mul_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} ⊤ = typemax(T) @@ -55,6 +83,9 @@ function mul_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} return c end +exp_alg(::Type{MaxPlus}, a, b) = a * b +inv_alg(::Type{MaxPlus}, a) = -a + zero_alg(::Type{MaxPlus}, ::Type{T}) where {T} = neginf(T) typemax_alg(::Type{MaxPlus}, ::Type{T}) where {T} = posinf(T) one_alg(::Type{MaxPlus}, ::Type{T}) where {T} = zero(T) @@ -78,20 +109,22 @@ function ldiv_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} return c end -rdiv_alg(::Type{MaxPlus}, b, a) = ldiv_alg(MaxPlus, a, b) - leq_alg(::Type{MaxPlus}, a, b) = a <= b lt_alg(::Type{MaxPlus}, a, b) = a < b add_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.max_fast(a, b) mul_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.add_fast(a, b) +inf_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.min_fast(a, b) ldiv_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.sub_fast(b, a) -rdiv_fast_alg(::Type{MaxPlus}, b, a) = ldiv_fast_alg(MaxPlus, a, b) # --------------- # # other operators # # --------------- # -function Base.isless(a::Tropical, b::Tropical) +function Base.isless(a::TropicalMaxPlus, b::TropicalMaxPlus) + return a < b +end + +function Base.isless(a::TropicalMaxMin, b::TropicalMaxMin) return a < b end diff --git a/src/tropical_minplus.jl b/src/tropical_minplus.jl index 4f01098..8009002 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -77,15 +77,13 @@ function ldiv_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} return c end -rdiv_alg(::Type{MinPlus}, b, a) = ldiv_alg(MinPlus, a, b) - leq_alg(::Type{MinPlus}, a, b) = a >= b lt_alg(::Type{MinPlus}, a, b) = a > b add_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.min_fast(a, b) mul_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.add_fast(a, b) +inf_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.max_fast(a, b) ldiv_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.sub_fast(b, a) -rdiv_fast_alg(::Type{MinPlus}, b, a) = ldiv_fast_alg(MinPlus, a, b) # --------------- # # other operators # diff --git a/test/interface.jl b/test/interface.jl index bfb1e55..63e9252 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -56,6 +56,15 @@ function test_quantale(a::T, b::T, c::T) where {T <: AbstractSemiring} # strict ordering agrees with partial ordering @test (a < b) == (a != b && a <= b) + + # lattice is a Heyting algebra + @test imp(a, a) == typemax(T) + @test a ∧ imp(a, b) == a ∧ b + @test b ∧ imp(a, b) == b + @test imp(a, b ∧ c) == imp(a, b) ∧ imp(a, c) + + # complement agrees with implication + @test not(a) == imp(a, zero(T)) end function test_fast(a::T, b::T, c::T) where {T <: AbstractSemiring} @@ -64,10 +73,13 @@ function test_fast(a::T, b::T, c::T) where {T <: AbstractSemiring} @test a ∧ b ≈ inf_fast(a, b) @test a / b ≈ div_fast(a, b) @test a \ b ≈ ldiv_fast(a, b) + @test imp(a, b) ≈ imp_fast(a, b) @test (a * b) + c ≈ fma(a, b, c) + @test (a ∧ b) + c ≈ fia(a, b, c) @test (a \ b) ∧ c ≈ fli(a, b, c) @test (a / b) ∧ c ≈ fri(a, b, c) + @test imp(a, b) ∧ c ≈ fii(a, b, c) end function test_isless(a::T, b::T) where {T <: AbstractSemiring} From 6bb3308d20939d292f6a3f77fb36b707ef56b063 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 09:36:53 -0500 Subject: [PATCH 14/23] Improve documentation. --- src/abstract_semiring_algebra.jl | 102 ++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index 77e1661..afa9167 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -1,13 +1,59 @@ +""" + AbstractSemiringAlgebra + +A [`semiring`](https://en.wikipedia.org/wiki/Semiring) is a quintuple (R, +, ×, 0, 1), where + + - (R, +, 0) is a commutative monoid + - (R, ×, 1) is a monoid + - multiplication (×) distributes over addition (+) + - zero (0) is absorbing +""" abstract type AbstractSemiringAlgebra end +""" + AbstractQuantaleAlgebra <: AbstractSemiringAlgebra + +A [`semiring`](https://en.wikipedia.org/wiki/Semiring) (R, +, ×, 0, 1) is called a +[`quantale`](https://en.wikipedia.org/wiki/Quantale) if it is additionally a complete +lattice whose supremum operation coincides with addition (+). +""" abstract type AbstractQuantaleAlgebra <: AbstractSemiringAlgebra end +""" + AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end + +A quantale (R, +, ×, 0, 1) is called commutative if the multiplication operation (×) +commutates. +""" abstract type AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end +""" + AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra + +A quantale (R, +, ×, 0, 1) is called Cartesian, if its infimum operation +coincides with multiplcation (×). +""" abstract type AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra end +""" + AbstractTropicalAlgebra <: AbstractCommutativeQuantaleAlgebra + +The tropical semirings + + - ([-∞, +∞], ∧, +, +∞, 0) + - ([-∞, +∞], ∨, +, -∞, 0) + - ([0, +∞], ∨, *, 0, 0) + +are commutative quantales with a well-defined exponentiation operation. +""" abstract type AbstractTropicalAlgebra <: AbstractCommutativeQuantaleAlgebra end +""" + LatticeAlgebra{T <: AbstractQuantaleAlgebra} <: AbstractLatticeAlgebra + +Transform a quantale into a Cartesian quantale by replacing multiplication (×) +with the infimum operation. +""" struct LatticeAlgebra{T <: AbstractQuantaleAlgebra} <: AbstractLatticeAlgebra end # --------- # @@ -16,6 +62,8 @@ struct LatticeAlgebra{T <: AbstractQuantaleAlgebra} <: AbstractLatticeAlgebra en """ zero_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} + +Get an additive identity 0 of type `A`. """ zero_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} @@ -25,6 +73,8 @@ end """ one_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} + +Get a multiplicative identity 1 of type `A`. """ one_alg(::Type{T}, A::Type) where {T <: AbstractSemiringAlgebra} @@ -34,6 +84,8 @@ end """ add_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + +Compute the sum a + b. """ add_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} @@ -43,6 +95,8 @@ end """ add_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + +Compute the sum a + b. """ function add_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} return add_alg(T, a, b) @@ -54,6 +108,8 @@ end """ mul_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + +Compute the product a × b. """ mul_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} @@ -63,6 +119,8 @@ end """ mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + +Compute the product a × b. """ function mul_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} return mul_alg(T, a, b) @@ -74,6 +132,8 @@ end """ mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} + +Compute (a × b) + c. """ function mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} return add_fast_alg(T, mul_fast_alg(T, a, b), c) @@ -90,6 +150,8 @@ end """ typemax_alg(::Type{T}, A::Type) where {T <: AbstractQuantaleAlgebra} + +Get a top element ⊤ of type `A`. """ typemax_alg(::Type{T}, A::Type) where {T <: AbstractQuantaleAlgebra} @@ -99,6 +161,8 @@ end """ inf_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Compute the infimum a ∧ b. """ inf_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} @@ -108,6 +172,8 @@ end """ inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Compute infimum a ∧ b. """ function inf_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return inf_alg(T, a, b) @@ -119,6 +185,8 @@ end """ inf_add_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} + +Compute (a ∧ b) + c. """ function inf_add_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} return add_fast_alg(T, inf_fast_alg(T, a, b), c) @@ -126,6 +194,8 @@ end """ ldiv_alg(::Type{T}, a, b) whre {T <: AbstractQuantaleAlgebra} + +Compute the residual a \\ b. """ ldiv_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} @@ -135,6 +205,8 @@ end """ ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Compute the residual a \\ b. """ function ldiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return ldiv_alg(T, a, b) @@ -150,6 +222,8 @@ end """ inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} + +Compute (a \\ b) ∧ c. """ function inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} return inf_fast_alg(T, ldiv_fast_alg(T, a, b), c) @@ -160,7 +234,9 @@ function inf_ldiv_alg(::Type{LatticeAlgebra{T}}, a, b, c) where {T <: AbstractQu end """ - rdiv_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + rdiv_alg(::Type{T}, b, a) where {T <: AbstractQuantaleAlgebra} + +Compute the residual b / a. """ rdiv_alg(::Type{T}, b, a) where {T <: AbstractQuantaleAlgebra} @@ -170,6 +246,8 @@ end """ rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Compute the residual b / a. """ function rdiv_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return rdiv_alg(T, a, b) @@ -181,6 +259,8 @@ end """ inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} + +Compute (b / a) ∧ c. """ function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} return inf_fast_alg(T, rdiv_fast_alg(T, b, a), c) @@ -188,6 +268,8 @@ end """ imp_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Compute the implication a → b. """ imp_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} @@ -207,6 +289,8 @@ end """ imp_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Compute the implication a → b. """ function imp_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return imp_alg(T, a, b) @@ -217,7 +301,9 @@ function imp_fast_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} end """ - inf_imp_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} + +Compute (a → b) ∧ c. """ function inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} return inf_fast_alg(T, imp_fast_alg(T, a, b), c) @@ -239,9 +325,11 @@ end """ inv_alg(::Type{T}, a) where {T <: AbstractQuantaleAlgebra} + +Compute the inverse 1 / a. """ function inv_alg(::Type{T}, a::A) where {T <: AbstractQuantaleAlgebra, A} - return div_alg(T, one_alg(T, A), a) + return rdiv_alg(T, one_alg(T, A), a) end function inv_alg(::Type{T}, a::A) where {T <: AbstractLatticeAlgebra, A} @@ -250,6 +338,8 @@ end """ not_alg(::Type{T}, a) where {T <: AbstractQuantaleAlgebra} + +Compute the psuedo-complement a → 0. """ function not_alg(::Type{T}, a::A) where {T <: AbstractQuantaleAlgebra, A} return imp_alg(T, a, zero_alg(T, A)) @@ -271,6 +361,8 @@ end """ leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Evaluate a ≤ b. """ function leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return add_alg(T, a, b) == b @@ -282,6 +374,8 @@ end """ lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Evaluate a < b. """ function lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return a != b && leq_alg(T, a, b) @@ -293,5 +387,7 @@ end """ exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} + +Compute the exponent aᵇ. """ exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} From d086191fcd293c62ad5f923d076a280740b0c248 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 09:38:46 -0500 Subject: [PATCH 15/23] Fix documentation. --- src/abstract_semiring_algebra.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index afa9167..a1e6da0 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -42,7 +42,7 @@ The tropical semirings - ([-∞, +∞], ∧, +, +∞, 0) - ([-∞, +∞], ∨, +, -∞, 0) - - ([0, +∞], ∨, *, 0, 0) + - ([0, +∞], ∨, *, 0, 1) are commutative quantales with a well-defined exponentiation operation. """ From 2749c509261647bbfd4ad9444178895684bcfd7f Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 10:07:50 -0500 Subject: [PATCH 16/23] Remove export. --- src/TropicalNumbers.jl | 2 +- test/interface.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 4f76d3a..1e74b16 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -3,7 +3,7 @@ module TropicalNumbers using Random using Random: Repetition, Sampler -export content, neginf, posinf, inf, imp, not, fia, fli, fri, fii, inf_fast, ldiv_fast, imp_fast, ∧ +export content, neginf, posinf, inf, imp, not, fia, fli, fri, fii, inf_fast, ldiv_fast, imp_fast export TropicalTypes, AbstractSemiring export TropicalAndOr diff --git a/test/interface.jl b/test/interface.jl index 63e9252..4456a46 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,4 +1,5 @@ using Base.FastMath: mul_fast, add_fast, div_fast +using TropicalNumbers: ∧ function test_semiring(a::T, b::T, c::T) where {T <: AbstractSemiring} # 1 is the multiplicative identity From 7b30849ab69e4f19738798ffac398014333dad91 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 10:18:09 -0500 Subject: [PATCH 17/23] Restore subscripts. --- src/counting_tropical.jl | 15 ++++++++++----- src/semiring.jl | 1 + src/tropical_bitwise.jl | 8 ++++---- src/tropical_maxmul.jl | 8 ++++---- src/tropical_maxplus.jl | 16 ++++++++-------- src/tropical_minplus.jl | 8 ++++---- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/counting_tropical.jl b/src/counting_tropical.jl index fe11bcf..168039b 100644 --- a/src/counting_tropical.jl +++ b/src/counting_tropical.jl @@ -8,16 +8,16 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> CountingTropical(1.0, 5.0) + CountingTropical(3.0, 2.0) -(3.0, 2.0) +(3.0, 2.0)ₜ julia> CountingTropical(1.0, 5.0) * CountingTropical(3.0, 2.0) -(4.0, 10.0) +(4.0, 10.0)ₜ julia> one(CountingTropicalF64) -(0.0, 1.0) +(0.0, 1.0)ₜ julia> zero(CountingTropicalF64) -(-Inf, 0.0) +(-Inf, 0.0)ₜ ``` """ struct CountingTropical{T,C} <: Number @@ -64,7 +64,12 @@ Base.one(::T) where {T <: CountingTropical} = one(T) Base.isapprox(a::CountingTropical, b::CountingTropical; kw...) = isapprox(a.n, b.n; kw...) && isapprox(a.c, b.c; kw...) Base.isapprox(x::AbstractArray{<:CountingTropical}, y::AbstractArray{<:CountingTropical}; kw...) = all(isapprox.(x, y; kw...)) -Base.show(io::IO, t::CountingTropical) = Base.print(io, (t.n, t.c)) +function Base.show(io::IO, t::CountingTropical) + print(io, (t.n, t.c)) + print(io, 'ₜ') + return +end + Base.promote_rule(::Type{CountingTropical{T, C}}, b::Type{CountingTropical{U, D}}) where {T, U, C, D} = CountingTropical{promote_type(T, U), promote_type(C, D)} content(a::CountingTropical) = a.n diff --git a/src/semiring.jl b/src/semiring.jl index f629b9d..4942a4e 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -61,6 +61,7 @@ end function Base.show(io::IO, a::AbstractSemiring) print(io, a.n) + print(io, 'ₜ') return end diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index 353db94..ddfe56b 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -17,16 +17,16 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalBitwise(1) + TropicalBitwise(3) -3 +3ₜ julia> TropicalBitwise(1) * TropicalBitwise(3) -1 +1ₜ julia> zero(TropicalBitwiseI64) -0 +0ₜ julia> one(TropicalBitwiseI64) --1 +-1ₜ ``` """ const TropicalBitwise = Semiring{Bitwise} diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index 0e9196b..485fb87 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -16,16 +16,16 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMaxMul(1.0) + TropicalMaxMul(3.0) -3.0 +3.0ₜ julia> TropicalMaxMul(1.0) * TropicalMaxMul(3.0) -3.0 +3.0ₜ julia> one(TropicalMaxMulF64) -1.0 +1.0ₜ julia> zero(TropicalMaxMulF64) -0.0 +0.0ₜ ``` """ const TropicalMaxMul = Semiring{MaxMul} diff --git a/src/tropical_maxplus.jl b/src/tropical_maxplus.jl index b4e5dd1..2665568 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -16,16 +16,16 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMaxPlus(1.0) + TropicalMaxPlus(3.0) -3.0 +3.0ₜ julia> TropicalMaxPlus(1.0) * TropicalMaxPlus(3.0) -4.0 +4.0ₜ julia> one(TropicalMaxPlusF64) -0.0 +0.0ₜ julia> zero(TropicalMaxPlusF64) --Inf +-Infₜ ``` """ const TropicalMaxPlus = Semiring{MaxPlus} @@ -47,16 +47,16 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMaxMin(1.0) + TropicalMaxMin(3.0) -3.0 +3.0ₜ julia> TropicalMaxMin(1.0) * TropicalMaxMin(3.0) -1.0 +1.0ₜ julia> zero(TropicalMaxMinF64) --Inf +-Infₜ julia> one(TropicalMaxMinF64) -Inf +Infₜ ``` """ const TropicalMaxMin = Semiring{LatticeAlgebra{MaxPlus}} diff --git a/src/tropical_minplus.jl b/src/tropical_minplus.jl index 8009002..56350fc 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -16,16 +16,16 @@ Example ------------------------- ```jldoctest; setup=:(using TropicalNumbers) julia> TropicalMinPlus(1.0) + TropicalMinPlus(3.0) -1.0 +1.0ₜ julia> TropicalMinPlus(1.0) * TropicalMinPlus(3.0) -4.0 +4.0ₜ julia> one(TropicalMinPlusF64) -0.0 +0.0ₜ julia> zero(TropicalMinPlusF64) -Inf +Infₜ ``` """ const TropicalMinPlus = Semiring{MinPlus} From 13474f2286c15c2611fd6a8ed8a7d7a3d4c9cb58 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 10:30:10 -0500 Subject: [PATCH 18/23] Improve printing. --- src/tropical_bitwise.jl | 19 +++++++++++++++++++ src/tropical_maxmul.jl | 14 ++++++++++++++ src/tropical_maxplus.jl | 24 ++++++++++++++++++++++++ src/tropical_minplus.jl | 14 ++++++++++++++ test/interface.jl | 3 +++ 5 files changed, 74 insertions(+) diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index ddfe56b..d84979c 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -48,3 +48,22 @@ ldiv_alg(::Type{Bitwise}, a, b) = b | ~a function Base.isless(a::TropicalAndOr, b::TropicalAndOr) return a < b end + +# -------- # +# printing # +# -------- # + +function Base.show(io::IO, ::Type{TropicalBitwise{T}}) where {T} + print(io, "TropicalBitwise{$T}") + return +end + +function Base.show(io::IO, ::Type{TropicalBitwise}) + print(io, "TropicalBitwise") + return +end + +function Base.show(io::IO, ::Type{TropicalAndOr}) + print(io, "TropicalBitwise") + return +end diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index 485fb87..b60d584 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -93,3 +93,17 @@ ldiv_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.div_fast(b, a) function Base.isless(a::TropicalMaxMul, b::TropicalMaxMul) return a < b end + +# -------- # +# printing # +# -------- # + +function Base.show(io::IO, ::Type{TropicalMaxMul{T}}) where {T} + print(io, "TropicalMaxMul{$T}") + return +end + +function Base.show(io::IO, ::Type{TropicalMaxMul}) + print(io, "TropicalMaxMul") + return +end diff --git a/src/tropical_maxplus.jl b/src/tropical_maxplus.jl index 2665568..e030fd1 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -128,3 +128,27 @@ end function Base.isless(a::TropicalMaxMin, b::TropicalMaxMin) return a < b end + +# -------- # +# printing # +# -------- # + +function Base.show(io::IO, ::Type{Tropical{T}}) where {T} + print(io, "Tropical{$T}") + return +end + +function Base.show(io::IO, ::Type{TropicalMaxMin{T}}) where {T} + print(io, "TropicalMaxMin{$T}") + return +end + +function Base.show(io::IO, ::Type{Tropical}) + print(io, "Tropical") + return +end + +function Base.show(io::IO, ::Type{TropicalMaxMin}) + print(io, "TropicalMaxMin") + return +end diff --git a/src/tropical_minplus.jl b/src/tropical_minplus.jl index 56350fc..ff47bce 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -92,3 +92,17 @@ ldiv_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.sub_fast(b, a) function Base.isless(a::TropicalMinPlus, b::TropicalMinPlus) return a < b end + +# -------- # +# printing # +# -------- # + +function Base.show(io::IO, ::Type{TropicalMinPlus{T}}) where {T} + print(io, "TropicalMinPlus{$T}") + return +end + +function Base.show(io::IO, ::Type{TropicalMinPlus}) + print(io, "TropicalMinPlus") + return +end diff --git a/test/interface.jl b/test/interface.jl index 4456a46..c6874a7 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -134,6 +134,9 @@ end test_fast(a, b, c) test_quantale(a, b, c) test_quantale(zero(T), b, c) + + @test isnothing(println(T)) + @test isnothing(println(a)) end types = ( From 0295ca16f632d64ee569d902b41520f4ba6c7063 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 10:56:10 -0500 Subject: [PATCH 19/23] Add tests. --- test/interface.jl | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/test/interface.jl b/test/interface.jl index c6874a7..e0dc537 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -87,6 +87,26 @@ function test_isless(a::T, b::T) where {T <: AbstractSemiring} @test isless(a, b) == (a < b) end +@testset "printing" begin + types = ( + TropicalMinPlus, + TropicalMaxPlus, + TropicalMaxMul, + TropicalBitwise, + TropicalMaxMin, + TropicalMinPlusF64, + TropicalMaxPlusF64, + TropicalMaxMulF64, + TropicalAndOr, + TropicalBitwiseI64, + TropicalMaxMinF64, + ) + + for T in types + @test repr(T) isa String + end +end + @testset "interface" begin types = ( TropicalMinPlusF64, @@ -116,7 +136,6 @@ end test_quantale(zero(T), b, c) end - types = ( TropicalMinPlusF64, TropicalMaxPlusF64, @@ -134,9 +153,6 @@ end test_fast(a, b, c) test_quantale(a, b, c) test_quantale(zero(T), b, c) - - @test isnothing(println(T)) - @test isnothing(println(a)) end types = ( From f9b3845f00f47067a7b13d53538c1c46be2773c4 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 12:31:20 -0500 Subject: [PATCH 20/23] Adjust docstring. --- src/abstract_semiring_algebra.jl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index a1e6da0..cb73b51 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -1,7 +1,7 @@ """ AbstractSemiringAlgebra -A [`semiring`](https://en.wikipedia.org/wiki/Semiring) is a quintuple (R, +, ×, 0, 1), where +A [semiring](https://en.wikipedia.org/wiki/Semiring) is a quintuple (R, +, ×, 0, 1), where - (R, +, 0) is a commutative monoid - (R, ×, 1) is a monoid @@ -13,9 +13,9 @@ abstract type AbstractSemiringAlgebra end """ AbstractQuantaleAlgebra <: AbstractSemiringAlgebra -A [`semiring`](https://en.wikipedia.org/wiki/Semiring) (R, +, ×, 0, 1) is called a -[`quantale`](https://en.wikipedia.org/wiki/Quantale) if it is additionally a complete -lattice whose supremum operation coincides with addition (+). +A semiring (R, +, ×, 0, 1) is called a [quantale](https://en.wikipedia.org/wiki/Quantale) if +it is additionally a [complete lattice](https://en.wikipedia.org/wiki/Complete_lattice) whose +supremum operation coincides with addition (+). """ abstract type AbstractQuantaleAlgebra <: AbstractSemiringAlgebra end @@ -30,8 +30,9 @@ abstract type AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end """ AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra -A quantale (R, +, ×, 0, 1) is called Cartesian, if its infimum operation -coincides with multiplcation (×). +A [complete lattice](https://en.wikipedia.org/wiki/Complete_lattice) (R, ≤) can be thought +of as a semiring (R, +, ×, 0, 1), where addition (+) is the supremum operation, multiplication +(×) is the infimum operation, 0 is the bottom element, and 1 is the top element. """ abstract type AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra end From 7526c1cd1eda06ec66d97762946161207fd2136a Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 14:33:12 -0500 Subject: [PATCH 21/23] Improve documentation. --- src/abstract_semiring_algebra.jl | 94 +++++++++++++++++++++++++++++++- src/semiring.jl | 59 +++++++++++++++++++- 2 files changed, 149 insertions(+), 4 deletions(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index cb73b51..e5ebc65 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -7,6 +7,21 @@ A [semiring](https://en.wikipedia.org/wiki/Semiring) is a quintuple (R, +, ×, 0 - (R, ×, 1) is a monoid - multiplication (×) distributes over addition (+) - zero (0) is absorbing + +To create a new semiring, define a concrete subtype `T <: AbstractSemiringAlgebra` +as well as the following methods. + + - `zero_alg(::Type{T}, A)` + - `one_alg(::Type{T}, A)` + - `add_alg(::Type{T}, a, b)` + - `mul_alg(::Type{T}, a, b)` + +The following additional methods are optional. + + - `add_fast_alg(::Type{T}, a, b)` + - `mul_fast_alg(::Type{T}, a, b)` + - `mul_add_alg(::Type{T}, a, b, c)` + """ abstract type AbstractSemiringAlgebra end @@ -15,7 +30,35 @@ abstract type AbstractSemiringAlgebra end A semiring (R, +, ×, 0, 1) is called a [quantale](https://en.wikipedia.org/wiki/Quantale) if it is additionally a [complete lattice](https://en.wikipedia.org/wiki/Complete_lattice) whose -supremum operation coincides with addition (+). +supremum operation coincides with addition (+). To create a new quantale, define a concrete +subtype `T <: AbstractQuantaleAlgebra` as well as the following methods. + + - `zero_alg(::Type{T}, A)` + - `one_alg(::Type{T}, A)` + - `typemax_alg(::Type{T}, A)` + - `add_alg(::Type{T}, a, b)` + - `mul_alg(::Type{T}, a, b)` + - `inf_alg(::Type{T}, a, b)` + - `ldiv_alg(::Type{T}, a, b)` + - `rdiv_alg(::Type{T}, b, a)` + - `imp_alg(::Type{T}, a, b)` + +The following additional methods are optional. + + - `inv_alg(::Type{T}, a)` + - `not_alg(::Type{T}, a)` + - `add_fast_alg(::Type{T}, a, b)` + - `mul_fast_alg(::Type{T}, a, b)` + - `inf_fast_alg(::Type{T}, a, b)` + - `ldiv_fast_alg(::Type{T}, a, b)` + - `rdiv_fast_alg(::Type{T}, b, a)` + - `imp_fast_alg(::Type{T}, a, b)` + - `mul_add_alg(::Type{T}, a, b, c)` + - `inf_add_alg(::Type{T}, a, b, c)` + - `inf_ldiv_alg(::Type{T}, a, b, c)` + - `inf_rdiv_alg(::Type{T}, b, a, c)` + - `inf_imp_alg(::Type{T}, b, a, c)` + """ abstract type AbstractQuantaleAlgebra <: AbstractSemiringAlgebra end @@ -23,7 +66,32 @@ abstract type AbstractQuantaleAlgebra <: AbstractSemiringAlgebra end AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end A quantale (R, +, ×, 0, 1) is called commutative if the multiplication operation (×) -commutates. +commutates. To create a new commutative quantale, define a concrete subtype +`T <: AbstractCommutativeQuantaleAlgebra` as well as the following methods. + + - `zero_alg(::Type{T}, A)` + - `one_alg(::Type{T}, A)` + - `typemax_alg(::Type{T}, A)` + - `add_alg(::Type{T}, a, b)` + - `mul_alg(::Type{T}, a, b)` + - `inf_alg(::Type{T}, a, b)` + - `ldiv_alg(::Type{T}, a, b)` + - `imp_alg(::Type{T}, a, b)` + +The following additional methods are optional. + + - `inv_alg(::Type{T}, a)` + - `not_alg(::Type{T}, a)` + - `add_fast_alg(::Type{T}, a, b)` + - `mul_fast_alg(::Type{T}, a, b)` + - `inf_fast_alg(::Type{T}, a, b)` + - `ldiv_fast_alg(::Type{T}, a, b)` + - `imp_fast_alg(::Type{T}, a, b)` + - `mul_add_alg(::Type{T}, a, b, c)` + - `inf_add_alg(::Type{T}, a, b, c)` + - `inf_ldiv_alg(::Type{T}, a, b, c)` + - `inf_imp_alg(::Type{T}, b, a, c)` + """ abstract type AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end @@ -32,7 +100,27 @@ abstract type AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end A [complete lattice](https://en.wikipedia.org/wiki/Complete_lattice) (R, ≤) can be thought of as a semiring (R, +, ×, 0, 1), where addition (+) is the supremum operation, multiplication -(×) is the infimum operation, 0 is the bottom element, and 1 is the top element. +(×) is the infimum operation, 0 is the bottom element, and 1 is the top element. To create a new +complete lattice, define a concrete subtype `T <: AbstractLatticeAlgebra` as well +as the following methods. + + - `zero_alg(::Type{T}, A)` + - `one_alg(::Type{T}, A)` + - `add_alg(::Type{T}, a, b)` + - `mul_alg(::Type{T}, a, b)` + - `ldiv_alg(::Type{T}, a, b)` + +The following additional methods are optional. + + - `inv_alg(::Type{T}, a)` + - `not_alg(::Type{T}, a)` + - `add_fast_alg(::Type{T}, a, b)` + - `mul_fast_alg(::Type{T}, a, b)` + - `ldiv_fast_alg(::Type{T}, a, b)` + - `imp_fast_alg(::Type{T}, a, b)` + - `mul_add_alg(::Type{T}, a, b, c)` + - `inf_ldiv_alg(::Type{T}, a, b, c)` + """ abstract type AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra end diff --git a/src/semiring.jl b/src/semiring.jl index 4942a4e..ab0c659 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -180,13 +180,23 @@ function Base.typemax(::T) where {T <: AbstractQuantale} return typemax(T) end +""" + inf(a, b) + +Compute the infimum a ∧ b. +""" function inf(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} n = inf_alg(A, a.n, b.n) return Semiring{A}(n) end +""" + inf_fast(a, b) + +Compute the infimum a ∧ b. +""" function inf_fast(a, b) - return inf(a, b) + return a ∧ b end function inf_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} @@ -194,6 +204,15 @@ function inf_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAl return Semiring{A}(n) end +""" + fia(a, b, c) + +Compute (a ∧ b) + c. +""" +function fia(a, b, c) + return (a ∧ b) + c +end + function fia(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} n = inf_add_alg(A, a.n, b.n, c.n) return Semiring{A}(n) @@ -204,6 +223,11 @@ function Base.:\(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlg return Semiring{A}(n) end +""" + ldiv_fast(a, b) + +Compute the residual a \\ b. +""" function ldiv_fast(a, b) return a \ b end @@ -213,6 +237,11 @@ function ldiv_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleA return Semiring{A}(n) end +""" + fli(a, b, c) + +Compute (a \\ b) ∧ c. +""" function fli(a, b, c) return (a \ b) ∧ c end @@ -240,6 +269,11 @@ function Base.FastMath.div_fast(b::Semiring{A}, a::Semiring{A}) where {A <: Abst return Semiring{A}(n) end +""" + fri(b, a, c) + +Compute (b / a) ∧ c. +""" function fri(b, a, c) return (b / a) ∧ c end @@ -249,6 +283,11 @@ function fri(b::Semiring{A}, a::Semiring{A}, c::Semiring{A}) where {A <: Abstrac return Semiring{A}(n) end +""" + imp(a, b) + +Compute the implication a → b. +""" function imp(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} n = imp_alg(A, a.n, b.n) return Semiring{A}(n) @@ -259,6 +298,15 @@ function imp_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAl return Semiring{A}(n) end +""" + fii(a, b, c) + +Compute (a → b) ∧ c. +""" +function fii(a, b, c) + return imp(a, b) ∧ c +end + function fii(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} n = inf_imp_alg(A, a.n, b.n, c.n) return Semiring{A}(n) @@ -277,6 +325,15 @@ function Base.inv(a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} return Semiring{A}(n) end +""" + not(a) + +Compute the psuedo-complement a → 0. +""" +function not(a) + return imp(a, zero(a)) +end + function not(a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} n = not_alg(A, a.n) return Semiring{A}(n) From 300a2f83bc2b68eb6c531f216fe64e175a181d0a Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 14:58:51 -0500 Subject: [PATCH 22/23] Remove short circuit. --- src/abstract_semiring_algebra.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index e5ebc65..292fbd6 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -467,7 +467,7 @@ end Evaluate a < b. """ function lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} - return a != b && leq_alg(T, a, b) + return (a != b) & leq_alg(T, a, b) end function lt_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} From aa3992766ae41807c424c2a75719f516a4bc36be Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 22 Nov 2025 17:59:10 -0500 Subject: [PATCH 23/23] More fast functions. --- src/abstract_semiring_algebra.jl | 68 +++++++++++++----- src/semiring.jl | 30 ++++++-- src/tropical_maxmul.jl | 20 +++--- src/tropical_maxplus.jl | 20 +++--- src/tropical_minplus.jl | 17 +++-- test/interface.jl | 119 +++++++++++++++++++------------ 6 files changed, 176 insertions(+), 98 deletions(-) diff --git a/src/abstract_semiring_algebra.jl b/src/abstract_semiring_algebra.jl index 292fbd6..8e82f30 100644 --- a/src/abstract_semiring_algebra.jl +++ b/src/abstract_semiring_algebra.jl @@ -47,12 +47,16 @@ The following additional methods are optional. - `inv_alg(::Type{T}, a)` - `not_alg(::Type{T}, a)` + - `le_alg(::Type{T}, a, b)` + - `lt_alg(::Type{T}, a, b)` - `add_fast_alg(::Type{T}, a, b)` - `mul_fast_alg(::Type{T}, a, b)` - `inf_fast_alg(::Type{T}, a, b)` - `ldiv_fast_alg(::Type{T}, a, b)` - `rdiv_fast_alg(::Type{T}, b, a)` - `imp_fast_alg(::Type{T}, a, b)` + - `le_fast_alg(::Type{T}, a, b)` + - `lt_fast_alg(::Type{T}, a, b)` - `mul_add_alg(::Type{T}, a, b, c)` - `inf_add_alg(::Type{T}, a, b, c)` - `inf_ldiv_alg(::Type{T}, a, b, c)` @@ -82,11 +86,15 @@ The following additional methods are optional. - `inv_alg(::Type{T}, a)` - `not_alg(::Type{T}, a)` + - `le_alg(::Type{T}, a, b)` + - `lt_alg(::Type{T}, a, b)` - `add_fast_alg(::Type{T}, a, b)` - `mul_fast_alg(::Type{T}, a, b)` - `inf_fast_alg(::Type{T}, a, b)` - `ldiv_fast_alg(::Type{T}, a, b)` - `imp_fast_alg(::Type{T}, a, b)` + - `le_fast_alg(::Type{T}, a, b)` + - `lt_fast_alg(::Type{T}, a, b)` - `mul_add_alg(::Type{T}, a, b, c)` - `inf_add_alg(::Type{T}, a, b, c)` - `inf_ldiv_alg(::Type{T}, a, b, c)` @@ -114,10 +122,14 @@ The following additional methods are optional. - `inv_alg(::Type{T}, a)` - `not_alg(::Type{T}, a)` + - `le_alg(::Type{T}, a, b)` + - `lt_alg(::Type{T}, a, b)` - `add_fast_alg(::Type{T}, a, b)` - `mul_fast_alg(::Type{T}, a, b)` - `ldiv_fast_alg(::Type{T}, a, b)` - `imp_fast_alg(::Type{T}, a, b)` + - `le_fast_alg(::Type{T}, a, b)` + - `lt_fast_alg(::Type{T}, a, b)` - `mul_add_alg(::Type{T}, a, b, c)` - `inf_ldiv_alg(::Type{T}, a, b, c)` @@ -225,7 +237,7 @@ end Compute (a × b) + c. """ function mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} - return add_fast_alg(T, mul_fast_alg(T, a, b), c) + return add_alg(T, mul_alg(T, a, b), c) end function mul_add_alg(::Type{LatticeAlgebra{T}}, a, b, c) where {T <: AbstractQuantaleAlgebra} @@ -278,7 +290,7 @@ end Compute (a ∧ b) + c. """ function inf_add_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} - return add_fast_alg(T, inf_fast_alg(T, a, b), c) + return add_alg(T, inf_alg(T, a, b), c) end """ @@ -305,17 +317,13 @@ function ldiv_fast_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuan return imp_fast_alg(T, a, b) end -function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractCommutativeQuantaleAlgebra} - return inf_ldiv_alg(T, a, b, c) -end - """ inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} Compute (a \\ b) ∧ c. """ function inf_ldiv_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} - return inf_fast_alg(T, ldiv_fast_alg(T, a, b), c) + return inf_alg(T, ldiv_alg(T, a, b), c) end function inf_ldiv_alg(::Type{LatticeAlgebra{T}}, a, b, c) where {T <: AbstractQuantaleAlgebra} @@ -352,7 +360,11 @@ end Compute (b / a) ∧ c. """ function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} - return inf_fast_alg(T, rdiv_fast_alg(T, b, a), c) + return inf_alg(T, rdiv_alg(T, b, a), c) +end + +function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractCommutativeQuantaleAlgebra} + return inf_ldiv_alg(T, a, b, c) end """ @@ -367,7 +379,7 @@ function imp_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} end function imp_alg(::Type{T}, a, b::A) where {T <: AbstractTropicalAlgebra, A} - if leq_alg(T, a, b) + if le_alg(T, a, b) c = typemax_alg(T, A) else c = b @@ -395,7 +407,7 @@ end Compute (a → b) ∧ c. """ function inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} - return inf_fast_alg(T, imp_fast_alg(T, a, b), c) + return inf_alg(T, imp_alg(T, a, b), c) end function inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractLatticeAlgebra} @@ -403,7 +415,7 @@ function inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractLatticeAlgebra} end function inf_imp_alg(::Type{T}, a, b::A, c::A) where {T <: AbstractTropicalAlgebra, A} - if leq_alg(T, a, b) + if le_alg(T, a, b) d = c else d = inf_fast_alg(T, b, c) @@ -439,7 +451,7 @@ function not_alg(::Type{LatticeAlgebra{T}}, a) where {T <: AbstractQuantaleAlgeb end function not_alg(::Type{T}, a::A) where {T <: AbstractTropicalAlgebra, A} - if leq_alg(T, a, zero_alg(T, A)) + if le_alg(T, a, zero_alg(T, A)) c = typemax_alg(T, A) else c = zero_alg(T, A) @@ -449,16 +461,25 @@ function not_alg(::Type{T}, a::A) where {T <: AbstractTropicalAlgebra, A} end """ - leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + le_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} Evaluate a ≤ b. """ -function leq_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} +function le_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} return add_alg(T, a, b) == b end -function leq_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} - return leq_alg(T, a, b) +function le_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return le_alg(T, a, b) +end + +""" + le_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Evaluate a ≤ b. +""" +function le_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return le_alg(T, a, b) end """ @@ -467,7 +488,7 @@ end Evaluate a < b. """ function lt_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} - return (a != b) & leq_alg(T, a, b) + return (a != b) & le_alg(T, a, b) end function lt_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} @@ -475,8 +496,17 @@ function lt_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlg end """ - exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} + lt_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Evaluate a < b. +""" +function lt_fast_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return Base.FastMath.ne_fast(a, b) & le_fast_alg(T, a, b) +end + +""" + pow_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} Compute the exponent aᵇ. """ -exp_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} +pow_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} diff --git a/src/semiring.jl b/src/semiring.jl index ab0c659..426b17c 100644 --- a/src/semiring.jl +++ b/src/semiring.jl @@ -73,10 +73,6 @@ function Base.isinf(a::AbstractSemiring) return isinf(a.n) end -function Base.:(==)(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} - return a.n == b.n -end - function Base.isapprox(a::Semiring{A}, b::Semiring{A}; kw...) where {A <: AbstractSemiringAlgebra} return isapprox(a.n, b.n; kw...) end @@ -159,6 +155,18 @@ function Base.fma(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: Ab return Semiring{A}(n) end +function Base.:(==)(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + return a.n == b.n +end + +function Base.FastMath.eq_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + return Base.FastMath.eq_fast(a.n, b.n) +end + +function Base.FastMath.ne_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + return Base.FastMath.ne_fast(a.n, b.n) +end + # --------- # # Quantales # # --------- # @@ -313,13 +321,21 @@ function fii(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: Abstrac end function Base.:<=(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} - return leq_alg(A, a.n, b.n) + return le_alg(A, a.n, b.n) +end + +function Base.FastMath.le_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + return le_fast_alg(A, a.n, b.n) end function Base.:<(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} return lt_alg(A, a.n, b.n) end +function Base.FastMath.lt_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + return lt_fast_alg(A, a.n, b.n) +end + function Base.inv(a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} n = inv_alg(A, a.n) return Semiring{A}(n) @@ -344,11 +360,11 @@ end # -------- # function Base.:^(a::Semiring{A}, b::Number) where {A <: AbstractTropicalAlgebra} - n = exp_alg(A, a.n, b) + n = pow_alg(A, a.n, b) return Semiring{A}(n) end function Base.:^(a::Semiring{A}, b::Integer) where {A <: AbstractTropicalAlgebra} - n = exp_alg(A, a.n, b) + n = pow_alg(A, a.n, b) return Semiring{A}(n) end diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index b60d584..fd90946 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -52,8 +52,8 @@ function mul_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} return c end -exp_alg(::Type{MaxMul}, a, b) = a ^ b inv_alg(::Type{MaxMul}, a) = inv(a) +pow_alg(::Type{MaxMul}, a, b) = a ^ b zero_alg(::Type{MaxMul}, ::Type{T}) where {T} = zero(T) typemax_alg(::Type{MaxMul}, ::Type{T}) where {T} = posinf(T) @@ -78,17 +78,19 @@ function ldiv_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} return c end -leq_alg(::Type{MaxMul}, a, b) = a <= b +le_alg(::Type{MaxMul}, a, b) = a <= b lt_alg(::Type{MaxMul}, a, b) = a < b -add_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.max_fast(a, b) -mul_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.mul_fast(a, b) -inf_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.min_fast(a, b) -ldiv_fast_alg(::Type{MaxMul}, a, b) = Base.FastMath.div_fast(b, a) +add_fast_alg(::Type{MaxMul}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.max_fast(a, b) +mul_fast_alg(::Type{MaxMul}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.mul_fast(a, b) +inf_fast_alg(::Type{MaxMul}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.min_fast(a, b) +ldiv_fast_alg(::Type{MaxMul}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.div_fast(b, a) +le_fast_alg(::Type{MaxMul}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.le_fast(a, b) +lt_fast_alg(::Type{MaxMul}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.lt_fast(a, b) -# --------------- # -# other operators # -# --------------- # +# -------------- # +# total ordering # +# -------------- # function Base.isless(a::TropicalMaxMul, b::TropicalMaxMul) return a < b diff --git a/src/tropical_maxplus.jl b/src/tropical_maxplus.jl index e030fd1..4e7c658 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -83,8 +83,8 @@ function mul_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} return c end -exp_alg(::Type{MaxPlus}, a, b) = a * b inv_alg(::Type{MaxPlus}, a) = -a +pow_alg(::Type{MaxPlus}, a, b) = a * b zero_alg(::Type{MaxPlus}, ::Type{T}) where {T} = neginf(T) typemax_alg(::Type{MaxPlus}, ::Type{T}) where {T} = posinf(T) @@ -109,17 +109,19 @@ function ldiv_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} return c end -leq_alg(::Type{MaxPlus}, a, b) = a <= b +le_alg(::Type{MaxPlus}, a, b) = a <= b lt_alg(::Type{MaxPlus}, a, b) = a < b -add_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.max_fast(a, b) -mul_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.add_fast(a, b) -inf_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.min_fast(a, b) -ldiv_fast_alg(::Type{MaxPlus}, a, b) = Base.FastMath.sub_fast(b, a) +add_fast_alg(::Type{MaxPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.max_fast(a, b) +mul_fast_alg(::Type{MaxPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.add_fast(a, b) +inf_fast_alg(::Type{MaxPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.min_fast(a, b) +ldiv_fast_alg(::Type{MaxPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.sub_fast(b, a) +le_fast_alg(::Type{MaxPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.le_fast(a, b) +lt_fast_alg(::Type{MaxPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.lt_fast(a, b) -# --------------- # -# other operators # -# --------------- # +# -------------- # +# total ordering # +# -------------- # function Base.isless(a::TropicalMaxPlus, b::TropicalMaxPlus) return a < b diff --git a/src/tropical_minplus.jl b/src/tropical_minplus.jl index ff47bce..69087ab 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -38,8 +38,6 @@ inf_alg(::Type{MinPlus}, a, b) = max(a, b) # Inf + -Inf = Inf # mul_alg(::Type{MinPlus}, a, b) = a + b -exp_alg(::Type{MinPlus}, a, b) = a * b -inv_alg(::Type{MinPlus}, a) = -a function mul_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} ⊤ = typemax(T) @@ -54,6 +52,9 @@ function mul_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} return c end +inv_alg(::Type{MinPlus}, a) = -a +pow_alg(::Type{MinPlus}, a, b) = a * b + zero_alg(::Type{MinPlus}, ::Type{T}) where {T} = posinf(T) typemax_alg(::Type{MinPlus}, ::Type{T}) where {T} = neginf(T) one_alg(::Type{MinPlus}, ::Type{T}) where {T} = zero(T) @@ -77,13 +78,15 @@ function ldiv_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} return c end -leq_alg(::Type{MinPlus}, a, b) = a >= b +le_alg(::Type{MinPlus}, a, b) = a >= b lt_alg(::Type{MinPlus}, a, b) = a > b -add_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.min_fast(a, b) -mul_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.add_fast(a, b) -inf_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.max_fast(a, b) -ldiv_fast_alg(::Type{MinPlus}, a, b) = Base.FastMath.sub_fast(b, a) +add_fast_alg(::Type{MinPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.min_fast(a, b) +mul_fast_alg(::Type{MinPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.add_fast(a, b) +inf_fast_alg(::Type{MinPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.max_fast(a, b) +ldiv_fast_alg(::Type{MinPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.sub_fast(b, a) +le_fast_alg(::Type{MinPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.le_fast(b, a) +lt_fast_alg(::Type{MinPlus}, a::AbstractFloat, b::AbstractFloat) = Base.FastMath.lt_fast(b, a) # --------------- # # other operators # diff --git a/test/interface.jl b/test/interface.jl index e0dc537..a9a6140 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,5 +1,5 @@ -using Base.FastMath: mul_fast, add_fast, div_fast -using TropicalNumbers: ∧ +using Base.FastMath: mul_fast, add_fast, div_fast, eq_fast, ne_fast, le_fast, lt_fast +using TropicalNumbers: ∧, AbstractQuantale, AbstractLattice, AbstractTropical function test_semiring(a::T, b::T, c::T) where {T <: AbstractSemiring} # 1 is the multiplicative identity @@ -25,6 +25,13 @@ function test_semiring(a::T, b::T, c::T) where {T <: AbstractSemiring} # multiplication left-distributes over addition @test a * (b + c) ≈ a * b + a * c + + # fast operations + @test a + b ≈ add_fast(a, b) + @test a * b ≈ mul_fast(a, b) + + # ternary operations + @test (a * b) + c ≈ fma(a, b, c) end function test_quantale(a::T, b::T, c::T) where {T <: AbstractSemiring} @@ -66,27 +73,76 @@ function test_quantale(a::T, b::T, c::T) where {T <: AbstractSemiring} # complement agrees with implication @test not(a) == imp(a, zero(T)) -end -function test_fast(a::T, b::T, c::T) where {T <: AbstractSemiring} - @test a + b ≈ add_fast(a, b) - @test a * b ≈ mul_fast(a, b) + # fast operations + @test (a == b) == eq_fast(a, b) + @test (a != b) == ne_fast(a, b) + @test (a <= b) == le_fast(a, b) + @test (a < b) == lt_fast(a, b) @test a ∧ b ≈ inf_fast(a, b) @test a / b ≈ div_fast(a, b) @test a \ b ≈ ldiv_fast(a, b) @test imp(a, b) ≈ imp_fast(a, b) - @test (a * b) + c ≈ fma(a, b, c) + # ternary operations @test (a ∧ b) + c ≈ fia(a, b, c) @test (a \ b) ∧ c ≈ fli(a, b, c) @test (a / b) ∧ c ≈ fri(a, b, c) @test imp(a, b) ∧ c ≈ fii(a, b, c) end +function test_commutative_quantale(a::T, b::T, c::T) where {T <: AbstractQuantale} + test_quantale(a, b, c) + + # multiplication is commutative + @test a * b == b * a + + # residuation is commutative + @test a \ b == b / a +end + +function test_lattice(a::T, b::T, c::T) where {T <: AbstractLattice} + test_commutative_quantale(a, b, c) + + # product is infimum + @test a ∧ b ≈ a * b + + # implication is residuation + @test a \ b ≈ imp(a, b) +end + +function test_tropical(a::T, b::T, c::T) where {T <: AbstractTropical} + test_commutative_quantale(a, b, c) + + # integer powers + @test a ^ 1 ≈ a + @test a ^ 3 ≈ a * a * a + + # real powers + @test a ^ 1.0 ≈ a + @test a ^ 3.0 ≈ a * a * a +end + function test_isless(a::T, b::T) where {T <: AbstractSemiring} @test isless(a, b) == (a < b) end +function test_type(a::T, b::T, c::T) where {T <: AbstractSemiring} + if T <: AbstractTropical + test_tropical(a, b, c) + elseif T <: AbstractLattice + test_lattice(a, b, c) + elseif T <: AbstractQuantale + test_quantale(a, b, c) + else + test_semiring(a, b, c) + end + + if T <: Union{AbstractTropical, TropicalMaxMin, TropicalAndOr} + test_isless(a, b) + end +end + @testset "printing" begin types = ( TropicalMinPlus, @@ -108,34 +164,6 @@ end end @testset "interface" begin - types = ( - TropicalMinPlusF64, - TropicalMaxPlusF64, - TropicalMaxMulF64, - TropicalAndOr, - TropicalMaxMinF64, - ) - - for T in types - a = rand(T) - b = rand(T) - - test_isless(a, b) - test_isless(a, a) - test_isless(a, zero(T)) - test_isless(a, typemax(T)) - end - - for T in types - a = rand(T) - b = rand(T) - c = rand(T) - - test_fast(a, b, c) - test_quantale(a, b, c) - test_quantale(zero(T), b, c) - end - types = ( TropicalMinPlusF64, TropicalMaxPlusF64, @@ -150,28 +178,25 @@ end b = rand(T) c = rand(T) - test_fast(a, b, c) - test_quantale(a, b, c) - test_quantale(zero(T), b, c) + test_type(a, b, c) end types = ( TropicalMinPlus{Rational{Int}}, TropicalMaxPlus{Rational{Int}}, - TropicalMaxMul{Rational{Int}}, - ) + TropicalMaxMul{Rational{Int}}, + ) a = 1 // 2 b = 2 // 3 c = 3 // 4 for T in types - test_fast(T(a), T(b), T(c)) - test_quantale(T(a), T(b), T(c)) - test_quantale(zero(T), T(b), T(c)) - test_quantale(T(a), zero(T), zero(T)) - test_quantale(typemax(T), T(b), T(c)) - test_quantale(typemax(T), zero(T), T(c)) - test_quantale(typemax(T), T(b), typemax(T)) + test_type(T(a), T(b), T(c)) + test_type(zero(T), T(b), T(c)) + test_type(T(a), zero(T), zero(T)) + test_type(typemax(T), T(b), T(c)) + test_type(typemax(T), zero(T), T(c)) + test_type(typemax(T), T(b), typemax(T)) end end