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 0084ae7..1e74b16 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -1,6 +1,9 @@ module TropicalNumbers -export content, neginf, posinf +using Random +using Random: Repetition, Sampler + +export content, neginf, posinf, inf, imp, not, fia, fli, fri, fii, inf_fast, ldiv_fast, imp_fast export TropicalTypes, AbstractSemiring export TropicalAndOr @@ -12,43 +15,22 @@ 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{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_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") 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`. @@ -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_algebra.jl b/src/abstract_semiring_algebra.jl new file mode 100644 index 0000000..8e82f30 --- /dev/null +++ b/src/abstract_semiring_algebra.jl @@ -0,0 +1,512 @@ +""" + 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 + +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 + +""" + AbstractQuantaleAlgebra <: AbstractSemiringAlgebra + +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 (+). 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)` + - `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)` + - `inf_rdiv_alg(::Type{T}, b, a, c)` + - `inf_imp_alg(::Type{T}, b, a, c)` + +""" +abstract type AbstractQuantaleAlgebra <: AbstractSemiringAlgebra end + +""" + AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end + +A quantale (R, +, ×, 0, 1) is called commutative if the multiplication operation (×) +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)` + - `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)` + - `inf_imp_alg(::Type{T}, b, a, c)` + +""" +abstract type AbstractCommutativeQuantaleAlgebra <: AbstractQuantaleAlgebra end + +""" + AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra + +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. 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)` + - `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)` + +""" +abstract type AbstractLatticeAlgebra <: AbstractCommutativeQuantaleAlgebra end + +""" + AbstractTropicalAlgebra <: AbstractCommutativeQuantaleAlgebra + +The tropical semirings + + - ([-∞, +∞], ∧, +, +∞, 0) + - ([-∞, +∞], ∨, +, -∞, 0) + - ([0, +∞], ∨, *, 0, 1) + +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 + +# --------- # +# Semirings # +# --------- # + +""" + 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} + +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} + +Get a multiplicative identity 1 of type `A`. +""" +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} + +Compute the sum a + b. +""" +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} + +Compute the sum a + b. +""" +function add_fast_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + 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 + +""" + mul_alg(::Type{T}, a, b) where {T <: AbstractSemiringAlgebra} + +Compute the product a × b. +""" +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} + +Compute the product a × b. +""" +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} + +Compute (a × b) + c. +""" +function mul_add_alg(::Type{T}, a, b, c) where {T <: AbstractSemiringAlgebra} + return add_alg(T, mul_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 # +# --------- # + +""" + 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} + +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} + +Compute the infimum a ∧ b. +""" +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} + +Compute infimum a ∧ b. +""" +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} + +Compute (a ∧ b) + c. +""" +function inf_add_alg(::Type{T}, a, b, c) where {T <: AbstractQuantaleAlgebra} + return add_alg(T, inf_alg(T, a, b), c) +end + +""" + ldiv_alg(::Type{T}, a, b) whre {T <: AbstractQuantaleAlgebra} + +Compute the residual a \\ b. +""" +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} + +Compute the residual a \\ b. +""" +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 + +""" + 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_alg(T, ldiv_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}, b, a) where {T <: AbstractQuantaleAlgebra} + +Compute the residual b / a. +""" +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} + +Compute the residual b / a. +""" +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} + +Compute (b / a) ∧ c. +""" +function inf_rdiv_alg(::Type{T}, b, a, c) where {T <: AbstractQuantaleAlgebra} + 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 + +""" + imp_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Compute the implication a → b. +""" +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 le_alg(T, a, b) + c = typemax_alg(T, A) + else + c = b + end + + return c +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) +end + +function imp_fast_alg(::Type{T}, a, b) where {T <: AbstractLatticeAlgebra} + return ldiv_fast_alg(T, a, b) +end + +""" + 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_alg(T, imp_alg(T, a, b), c) +end + +function inf_imp_alg(::Type{T}, a, b, c) where {T <: AbstractLatticeAlgebra} + return inf_ldiv_alg(T, a, b, c) +end + +function inf_imp_alg(::Type{T}, a, b::A, c::A) where {T <: AbstractTropicalAlgebra, A} + if le_alg(T, a, b) + d = c + else + d = inf_fast_alg(T, b, c) + end + + return d +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 rdiv_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 + +""" + 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)) +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 le_alg(T, a, zero_alg(T, A)) + c = typemax_alg(T, A) + else + c = zero_alg(T, A) + end + + return c +end + +""" + le_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + +Evaluate a ≤ b. +""" +function le_alg(::Type{T}, a, b) where {T <: AbstractQuantaleAlgebra} + return add_alg(T, a, b) == b +end + +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 + +""" + 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) & le_alg(T, a, b) +end + +function lt_alg(::Type{LatticeAlgebra{T}}, a, b) where {T <: AbstractQuantaleAlgebra} + return lt_alg(T, a, b) +end + +""" + 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ᵇ. +""" +pow_alg(::Type{T}, a, b) where {T <: AbstractTropicalAlgebra} diff --git a/src/counting_tropical.jl b/src/counting_tropical.jl index edeee91..168039b 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,46 +20,67 @@ 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.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.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, 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; 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...)) + +function Base.show(io::IO, t::CountingTropical) + print(io, (t.n, t.c)) + print(io, 'ₜ') + return +end -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)} -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) = 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 new file mode 100644 index 0000000..426b17c --- /dev/null +++ b/src/semiring.jl @@ -0,0 +1,370 @@ +""" + 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/) + +""" +struct Semiring{A <: AbstractSemiringAlgebra, T} <: Number + n::T +end + +struct SemiringSampler{A <: AbstractSemiringAlgebra, T, S <: Sampler{T}} <: Sampler{Semiring{A, T}} + s::S +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) +end + +function Semiring{A}(a::Semiring{A}) where {A <: AbstractSemiringAlgebra} + return Semiring{A}(a.n) +end + +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 + +function content(::Type{<:AbstractSemiring{T}}) where {T} + return T +end + +function Base.show(io::IO, a::AbstractSemiring) + print(io, a.n) + print(io, 'ₜ') + return +end + +function Base.isnan(a::AbstractSemiring) + return isnan(a.n) +end + +function Base.isinf(a::AbstractSemiring) + return isinf(a.n) +end + +function Base.isapprox(a::Semiring{A}, b::Semiring{A}; kw...) where {A <: AbstractSemiringAlgebra} + return isapprox(a.n, b.n; kw...) +end + +function Base.isapprox(a::AbstractArray{<:Semiring{A}}, b::AbstractArray{<:Semiring{A}}; kw...) where {A <: AbstractSemiringAlgebra} + return all(isapprox.(a, b; kw...)) +end + +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 + +# ------------------------ # +# 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 # +# --------- # + +function Base.zero(::Type{Semiring{A, T}}) where {A <: AbstractSemiringAlgebra, T} + n = zero_alg(A, T) + 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, 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, a.n, b.n) + return Semiring{A}(n) +end + +function Base.:*(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractSemiringAlgebra} + n = mul_alg(A, a.n, b.n) + return Semiring{A}(n) +end + +function Base.:*(a::T, b::Bool) where {T <: AbstractSemiring} + return ifelse(b, a, zero(T)) +end + +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} + 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, a.n, b.n, c.n) + 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 # +# --------- # + +function Base.typemin(::Type{T}) where {T <: AbstractQuantale} + return zero(T) +end + +function Base.typemin(::T) where {T <: AbstractQuantale} + return zero(T) +end + +function Base.typemax(::Type{Semiring{A, T}}) where {A <: AbstractQuantaleAlgebra, T} + n = typemax_alg(A, T) + return Semiring{A}(n) +end + +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 a ∧ b +end + +function inf_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = inf_fast_alg(A, a.n, b.n) + 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) +end + +function Base.:\(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = ldiv_alg(A, a.n, b.n) + return Semiring{A}(n) +end + +""" + ldiv_fast(a, b) + +Compute the residual a \\ b. +""" +function ldiv_fast(a, b) + return a \ b +end + +function ldiv_fast(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = ldiv_fast_alg(A, a.n, b.n) + return Semiring{A}(n) +end + +""" + fli(a, b, c) + +Compute (a \\ b) ∧ c. +""" +function fli(a, b, c) + return (a \ b) ∧ c +end + +function fli(a::Semiring{A}, b::Semiring{A}, c::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + 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, b.n, a.n) + return Semiring{A}(n) +end + +function Base.:/(b::T, a::Bool) where {T <: AbstractQuantale} + return ifelse(a, b, typemax(T)) +end + +function Base.:/(b::Bool, a::T) where {T <: AbstractQuantale} + return ifelse(b, inv(a), zero(T)) +end + +function Base.FastMath.div_fast(b::Semiring{A}, a::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + n = rdiv_fast_alg(A, b.n, a.n) + return Semiring{A}(n) +end + +""" + fri(b, a, c) + +Compute (b / a) ∧ c. +""" +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, b.n, a.n, c.n) + 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) +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 + +""" + 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) +end + +function Base.:<=(a::Semiring{A}, b::Semiring{A}) where {A <: AbstractQuantaleAlgebra} + 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) +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) +end + +# -------- # +# Tropical # +# -------- # + +function Base.:^(a::Semiring{A}, b::Number) where {A <: AbstractTropicalAlgebra} + n = pow_alg(A, a.n, b) + return Semiring{A}(n) +end + +function Base.:^(a::Semiring{A}, b::Integer) where {A <: AbstractTropicalAlgebra} + n = pow_alg(A, a.n, b) + return Semiring{A}(n) +end diff --git a/src/tropical_andor.jl b/src/tropical_andor.jl deleted file mode 100644 index 23e4794..0000000 --- a/src/tropical_andor.jl +++ /dev/null @@ -1,55 +0,0 @@ - - -""" - TropicalAndOr <: Number - -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ₜ -``` -""" -struct TropicalAndOr <: AbstractSemiring - n::Bool - TropicalAndOr(x::T) where T <: Bool = new(x) -end - -Base.show(io::IO, t::TropicalAndOr) = Base.print(io, "$(t.n)ₜ") - -Base.:*(a::TropicalAndOr, b::TropicalAndOr) = TropicalAndOr(a.n && b.n) - -Base.:+(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) - -# inverse and division -Base.inv(x::TropicalAndOr) = TropicalAndOr(!x.n) - -# bool type only have two values -Base.isapprox(x::TropicalAndOr, y::TropicalAndOr; kwargs...) = isapprox(x.n, y.n; kwargs...) diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl index 4a8bd92..d84979c 100644 --- a/src/tropical_bitwise.jl +++ b/src/tropical_bitwise.jl @@ -1,3 +1,5 @@ +struct Bitwise <: AbstractLatticeAlgebra end + """ TropicalBitwise{T} <: AbstractSemiring @@ -15,96 +17,53 @@ 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} <: AbstractSemiring - n::T -end +const TropicalBitwise = Semiring{Bitwise} +const TropicalAndOr = TropicalBitwise{Bool} -function TropicalBitwise(a::TropicalBitwise) - return TropicalBitwise(a.n) -end +add_alg(::Type{Bitwise}, a, b) = a | b +mul_alg(::Type{Bitwise}, a, b) = a & b +not_alg(::Type{Bitwise}, a) = ~a -function TropicalBitwise{T}(a::TropicalBitwise) where {T} - return TropicalBitwise{T}(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.show(io::IO, a::TropicalBitwise) - print(io, "$(a.n)ₛ") - return -end +ldiv_alg(::Type{Bitwise}, a, b) = b | ~a -function Base.isapprox(a::TropicalBitwise, b::TropicalBitwise; kw...) - return isapprox(a.n, b.n; kw...) -end +# --------------- # +# other operators # +# --------------- # -function Base.promote_rule(::Type{TropicalBitwise{U}}, ::Type{TropicalBitwise{V}}) where {U, V} - W = promote_type(U, V) - return TropicalBitwise{W} +function Base.isless(a::TropicalAndOr, b::TropicalAndOr) + return a < b end -function Base.:+(a::TropicalBitwise, b::TropicalBitwise) - n = a.n | b.n - return TropicalBitwise(n) -end +# -------- # +# printing # +# -------- # -function Base.:*(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) -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 - -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.show(io::IO, ::Type{TropicalBitwise{T}}) where {T} + print(io, "TropicalBitwise{$T}") + return end -function Base.:<(a::TropicalBitwise, b::TropicalBitwise) - return a != b && a <= b +function Base.show(io::IO, ::Type{TropicalBitwise}) + print(io, "TropicalBitwise") + return end -function Base.:>(a::TropicalBitwise, b::TropicalBitwise) - return b < a +function Base.show(io::IO, ::Type{TropicalAndOr}) + print(io, "TropicalBitwise") + return end diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl deleted file mode 100644 index 7e5866d..0000000 --- a/src/tropical_maxmin.jl +++ /dev/null @@ -1,89 +0,0 @@ -""" - 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ₛ -``` -""" -struct TropicalMaxMin{T} <: AbstractSemiring - 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.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) -end - -function Base.:*(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) -end - -function Base.typemin(::Type{TropicalMaxMin{T}}) where {T} - n = neginf(T) - return TropicalMaxMin(n) -end - -function Base.typemax(::Type{TropicalMaxMin{T}}) where {T} - n = posinf(T) - return TropicalMaxMin(n) -end diff --git a/src/tropical_maxmul.jl b/src/tropical_maxmul.jl index 8cdcc41..fd90946 100644 --- a/src/tropical_maxmul.jl +++ b/src/tropical_maxmul.jl @@ -1,7 +1,7 @@ - +struct MaxMul <: AbstractTropicalAlgebra end """ - TropicalMaxMul{T} <: AbstractSemiring + TropicalMaxMul{T} <: AbstractIdempotentSemiring{T} TropicalMaxMul is a semiring algebra, can be described by * TropicalMaxMul, (ℝ⁺, max, ⋅, 0, 1). @@ -16,55 +16,96 @@ 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} <: AbstractSemiring - 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 +const TropicalMaxMul = Semiring{MaxMul} + +add_alg(::Type{MaxMul}, a, b) = max(a, b) +inf_alg(::Type{MaxMul}, a, b) = min(a, b) + +# +# 0 * Inf = 0 +# Inf * 0 = 0 +# +mul_alg(::Type{MaxMul}, a, b) = a * b + +function mul_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = zero(T) + + if a == ⊥ && b == ⊤ || a == ⊤ && b == ⊥ + c = ⊥ + else + c = a * b end - function TropicalMaxMul{T1}(x::TropicalMaxMul{T2}) where {T1,T2} - new{T1}(T2(x.n)) + + return c +end + +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) +one_alg(::Type{MaxMul}, ::Type{T}) where {T} = one(T) + +# +# Inf / Inf = Inf +# 0 / 0 = Inf +# +ldiv_alg(::Type{MaxMul}, a, b) = b / a + +function ldiv_alg(::Type{MaxMul}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = zero(T) + + if a == b == ⊥ || a == b == ⊤ + c = ⊤ + else + c = b / a end + + return c end -Base.show(io::IO, t::TropicalMaxMul) = Base.print(io, "$(t.n)ₓ") +le_alg(::Type{MaxMul}, a, b) = a <= b +lt_alg(::Type{MaxMul}, a, b) = a < b -Base.:^(a::TropicalMaxMul, b::Real) = TropicalMaxMul(a.n ^ b) -Base.:^(a::TropicalMaxMul, b::Integer) = TropicalMaxMul(a.n ^ b) -Base.:*(a::TropicalMaxMul, b::TropicalMaxMul) = TropicalMaxMul(a.n * b.n) +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) -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}) +# -------------- # +# total ordering # +# -------------- # -Base.one(::Type{TropicalMaxMul{T}}) where T = TropicalMaxMul(one(T)) -Base.one(::TropicalMaxMul{T}) where T = one(TropicalMaxMul{T}) +function Base.isless(a::TropicalMaxMul, b::TropicalMaxMul) + return a < b +end -# inverse and division -Base.inv(x::TropicalMaxMul{T}) where T = TropicalMaxMul(one(T) / x.n) -Base.:/(x::TropicalMaxMul, y::TropicalMaxMul) = TropicalMaxMul(x.n / y.n) -Base.div(x::TropicalMaxMul, y::TropicalMaxMul) = TropicalMaxMul(x.n ÷ y.n) +# -------- # +# printing # +# -------- # -Base.isapprox(x::TropicalMaxMul, y::TropicalMaxMul; kwargs...) = isapprox(x.n, y.n; kwargs...) +function Base.show(io::IO, ::Type{TropicalMaxMul{T}}) where {T} + print(io, "TropicalMaxMul{$T}") + return +end -# promotion rules -Base.promote_rule(::Type{TropicalMaxMul{T1}}, b::Type{TropicalMaxMul{T2}}) where {T1, T2} = TropicalMaxMul{promote_type(T1,T2)} +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 8764760..4e7c658 100644 --- a/src/tropical_maxplus.jl +++ b/src/tropical_maxplus.jl @@ -1,11 +1,4 @@ -# 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) +struct MaxPlus <: AbstractTropicalAlgebra end """ TropicalMaxPlus{T} = Tropical{T} <: AbstractSemiring @@ -35,49 +28,129 @@ julia> zero(TropicalMaxPlusF64) -Infₜ ``` """ -struct Tropical{T} <: AbstractSemiring - 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)) +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) + +# +# -Inf + Inf = -Inf +# Inf + -Inf = -Inf +# +mul_alg(::Type{MaxPlus}, a, b) = a + b + +function mul_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) + + if a == ⊥ && b == ⊤ || a == ⊤ && b == ⊥ + c = ⊥ + else + c = a + b end + + return c end +inv_alg(::Type{MaxPlus}, a) = -a +pow_alg(::Type{MaxPlus}, a, b) = a * b -Base.show(io::IO, t::Tropical) = Base.print(io, "$(t.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) -Base.:^(a::Tropical, b::Real) = Tropical(a.n * b) -Base.:^(a::Tropical, b::Integer) = Tropical(a.n * b) -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 +# +# Inf - Inf = Inf +# -Inf - -Inf = Inf +# +ldiv_alg(::Type{MaxPlus}, a, b) = b - a + +function ldiv_alg(::Type{MaxPlus}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) + + if a == b == ⊥ || a == b == ⊤ + c = ⊤ else - Tropical(a.n + b.n) + c = b - a end + + return c +end + +le_alg(::Type{MaxPlus}, a, b) = a <= b +lt_alg(::Type{MaxPlus}, a, b) = a < b + +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) + +# -------------- # +# total ordering # +# -------------- # + +function Base.isless(a::TropicalMaxPlus, b::TropicalMaxPlus) + return a < b +end + +function Base.isless(a::TropicalMaxMin, b::TropicalMaxMin) + return a < b 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}) -Base.one(::Type{Tropical{T}}) where T = Tropical(zero(T)) -Base.one(::Tropical{T}) where T = one(Tropical{T}) +# -------- # +# printing # +# -------- # -# inverse and division -Base.inv(x::Tropical) = Tropical(-x.n) -Base.:/(x::Tropical, y::Tropical) = Tropical(x.n - y.n) -Base.div(x::Tropical, y::Tropical) = Tropical(x.n - y.n) +function Base.show(io::IO, ::Type{Tropical{T}}) where {T} + print(io, "Tropical{$T}") + return +end -Base.isapprox(x::Tropical, y::Tropical; kwargs...) = isapprox(x.n, y.n; kwargs...) +function Base.show(io::IO, ::Type{TropicalMaxMin{T}}) where {T} + print(io, "TropicalMaxMin{$T}") + return +end -# promotion rules -Base.promote_rule(::Type{Tropical{T1}}, b::Type{Tropical{T2}}) where {T1, T2} = Tropical{promote_type(T1,T2)} +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 40cbdfd..69087ab 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -1,4 +1,4 @@ - +struct MinPlus <: AbstractTropicalAlgebra end """ TropicalMinPlus{T} <: AbstractSemiring @@ -16,86 +16,96 @@ 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} <: AbstractSemiring - 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 +const TropicalMinPlus = Semiring{MinPlus} -Base.show(io::IO, t::TropicalMinPlus) = Base.print(io, "$(t.n)ₛ") +add_alg(::Type{MinPlus}, a, b) = min(a, b) +inf_alg(::Type{MinPlus}, a, b) = max(a, b) -Base.:^(a::TropicalMinPlus, b::Real) = TropicalMinPlus(a.n * b) -Base.:^(a::TropicalMinPlus, b::Integer) = TropicalMinPlus(a.n * b) -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 +# +# -Inf + Inf = Inf +# Inf + -Inf = Inf +# +mul_alg(::Type{MinPlus}, a, b) = a + b + +function mul_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) + + if a == ⊥ && b == ⊤ || a == ⊤ && b == ⊥ + c = ⊤ else - TropicalMinPlus(a.n + b.n) + c = a + b end + + return c end -Base.:+(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}) +inv_alg(::Type{MinPlus}, a) = -a +pow_alg(::Type{MinPlus}, a, b) = a * b -Base.one(::Type{TropicalMinPlus{T}}) where T = TropicalMinPlus(zero(T)) -Base.one(::TropicalMinPlus{T}) where T = one(TropicalMinPlus{T}) +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) -# inverse and division -Base.inv(x::TropicalMinPlus) = TropicalMinPlus(-x.n) -Base.:/(x::TropicalMinPlus, y::TropicalMinPlus) = TropicalMinPlus(x.n - y.n) -Base.div(x::TropicalMinPlus, y::TropicalMinPlus) = TropicalMinPlus(x.n - y.n) +# +# Inf - Inf = -Inf +# -Inf - -Inf = -Inf +# +ldiv_alg(::Type{MinPlus}, a, b) = b - a -# ordering -function Base.:(==)(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n == a.n -end +function ldiv_alg(::Type{MinPlus}, a::T, b::T) where {T <: Rational} + ⊤ = typemax(T) + ⊥ = typemin(T) -function Base.:>=(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n >= a.n -end + if a == b == ⊥ || a == b == ⊤ + c = ⊥ + else + c = b - a + end -function Base.:<=(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n <= a.n + return c end -function Base.:<(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n < a.n -end +le_alg(::Type{MinPlus}, a, b) = a >= b +lt_alg(::Type{MinPlus}, a, b) = a > b -function Base.:>(a::TropicalMinPlus, b::TropicalMinPlus) - return b.n > a.n -end +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 # +# --------------- # function Base.isless(a::TropicalMinPlus, b::TropicalMinPlus) - return isless(b.n, a.n) + return a < b end -Base.isapprox(x::TropicalMinPlus, y::TropicalMinPlus; kwargs...) = isapprox(x.n, y.n; kwargs...) +# -------- # +# printing # +# -------- # + +function Base.show(io::IO, ::Type{TropicalMinPlus{T}}) where {T} + print(io, "TropicalMinPlus{$T}") + return +end -# promotion rules -Base.promote_rule(::Type{TropicalMinPlus{T1}}, b::Type{TropicalMinPlus{T2}}) where {T1, T2} = TropicalMinPlus{promote_type(T1,T2)} +function Base.show(io::IO, ::Type{TropicalMinPlus}) + print(io, "TropicalMinPlus") + return +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 diff --git a/test/interface.jl b/test/interface.jl new file mode 100644 index 0000000..a9a6140 --- /dev/null +++ b/test/interface.jl @@ -0,0 +1,202 @@ +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 + @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 + + # 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} + 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) + + # 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)) + + # 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) + + # 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, + 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, + TropicalMaxPlusF64, + TropicalMaxMulF64, + TropicalAndOr, + TropicalBitwiseI64, + TropicalMaxMinF64, + ) + + for T in types + a = rand(T) + b = rand(T) + c = rand(T) + + test_type(a, 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_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 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) 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))