diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index f49313b..2bacdb8 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -4,6 +4,22 @@ on: types: - created workflow_dispatch: + inputs: + lookback: + default: 3 +permissions: + actions: read + checks: read + contents: write + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + security-events: read + statuses: read jobs: TagBot: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7f439e..c75fd1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,24 @@ name: CI on: - - push - - pull_request + push: + branches: + - master + tags: ['*'] + pull_request: + workflow_dispatch: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: test: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} + timeout-minutes: 60 + permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created + actions: write + contents: read strategy: fail-fast: false matrix: @@ -17,24 +30,17 @@ jobs: arch: - x64 steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@v1 + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: actions/cache@v1 - env: - cache-name: cache-artifacts - with: - path: ~/.julia/artifacts - key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} - restore-keys: | - ${{ runner.os }}-test-${{ env.cache-name }}- - ${{ runner.os }}-test- - ${{ runner.os }}- + - uses: julia-actions/cache@v2 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v5 with: - file: lcov.info + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + slug: TensorBFS/TropicalNumbers.jl \ No newline at end of file diff --git a/README.md b/README.md index 8188530..d222d8e 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,11 @@ pkg> add TropicalNumbers A Topical algebra can be described as a tuple $(R, \oplus, \otimes, \mathbb{0}, \mathbb{1})$, where $R$ is the set, $\oplus$ and $\otimes$ are the opeartions and $\mathbb{0}$, $\mathbb{1}$ are their identity element, respectively. In this package, the following tropical algebras are implemented: * `TropicalAndOr`: $([T, F], \lor, \land, F, T)$; +* `TropicalBitwise`: $(\mathbb{T}, |, \&, 0, \sim 0)$ * `Tropical` (`TropicalMaxPlus`): $(\mathbb{R}, \max, +, -\infty, 0)$; * `TropicalMinPlus`: $(\mathbb{R}, \min, +, \infty, 0)$; * `TropicalMaxMul`: $(\mathbb{R}^+, \max, \times, 0, 1)$. +* `TropicalMaxMin`: $(\mathbb{R}^+, \max, \min, -\infty, \infty)$. ```julia julia> using TropicalNumbers diff --git a/src/TropicalNumbers.jl b/src/TropicalNumbers.jl index 74c1a89..0084ae7 100644 --- a/src/TropicalNumbers.jl +++ b/src/TropicalNumbers.jl @@ -5,9 +5,11 @@ export TropicalTypes, AbstractSemiring export TropicalAndOr export Tropical, TropicalF64, TropicalF32, TropicalF16, TropicalI64, TropicalI32, TropicalI16 -export TropicalMaxMul, TropicalMaxMulF64, TropicalMaxMulF32, TropicalMaxMulF16, TropicalMaxMulI64, TropicalMaxMulI32, TropicalMaxMulI16 export TropicalMaxPlus, TropicalMaxPlusF64, TropicalMaxPlusF32, TropicalMaxPlusF16, TropicalMaxPlusI64, TropicalMaxPlusI32, TropicalMaxPlusI16 export TropicalMinPlus, TropicalMinPlusF64, TropicalMinPlusF32, TropicalMinPlusF16, TropicalMinPlusI64, TropicalMinPlusI32, TropicalMinPlusI16 +export TropicalMaxMul, TropicalMaxMulF64, TropicalMaxMulF32, TropicalMaxMulF16, TropicalMaxMulI64, TropicalMaxMulI32, TropicalMaxMulI16 +export TropicalMaxMin, TropicalMaxMinF64, TropicalMaxMinF32, TropicalMaxMinF16, TropicalMaxMinI64, TropicalMaxMinI32, TropicalMaxMinI16 +export TropicalBitwise, TropicalBitwiseI64, TropicalBitwiseI32, TropicalBitwiseI16 export CountingTropical, CountingTropicalF16, CountingTropicalF32, CountingTropicalF64, CountingTropicalI16, CountingTropicalI32, CountingTropicalI64 @@ -41,6 +43,8 @@ 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}} @@ -53,17 +57,20 @@ for NBIT in [16, 32, 64] @eval const $(Symbol(:TropicalMaxPlus, :F, NBIT)) = TropicalMaxPlus{$(Symbol(:Float, NBIT))} @eval const $(Symbol(:TropicalMinPlus, :F, NBIT)) = TropicalMinPlus{$(Symbol(:Float, NBIT))} @eval const $(Symbol(:TropicalMaxMul, :F, NBIT)) = TropicalMaxMul{$(Symbol(:Float, NBIT))} + @eval const $(Symbol(:TropicalMaxMin, :F, NBIT)) = TropicalMaxMin{$(Symbol(:Float, NBIT))} @eval const $(Symbol(:CountingTropical, :F, NBIT)) = CountingTropical{$(Symbol(:Float, NBIT)),$(Symbol(:Float, NBIT))} @eval const $(Symbol(:Tropical, :I, NBIT)) = Tropical{$(Symbol(:Int, NBIT))} @eval const $(Symbol(:TropicalMaxPlus, :I, NBIT)) = TropicalMaxPlus{$(Symbol(:Int, NBIT))} @eval const $(Symbol(:TropicalMinPlus, :I, NBIT)) = TropicalMinPlus{$(Symbol(:Int, NBIT))} @eval const $(Symbol(:TropicalMaxMul, :I, NBIT)) = TropicalMaxMul{$(Symbol(:Int, NBIT))} + @eval const $(Symbol(:TropicalMaxMin, :I, NBIT)) = TropicalMaxMin{$(Symbol(:Int, NBIT))} + @eval const $(Symbol(:TropicalBitwise, :I, NBIT)) = TropicalBitwise{$(Symbol(:Int, NBIT))} @eval const $(Symbol(:CountingTropical, :I, NBIT)) = CountingTropical{$(Symbol(:Int, NBIT)),$(Symbol(:Float, NBIT))} end # alias -for T in [:Tropical, :TropicalMaxMul, :TropicalMinPlus, :CountingTropical] +for T in [:Tropical, :TropicalMaxMul, :TropicalMaxMin, :CountingTropical] for OP in [:>, :<, :(==), :>=, :<=, :isless] @eval Base.$OP(a::$T, b::$T) = $OP(a.n, b.n) end @@ -76,6 +83,16 @@ for T in [:Tropical, :TropicalMaxMul, :TropicalMinPlus, :CountingTropical] 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) @@ -88,7 +105,7 @@ for T in [:TropicalAndOr] end end -for T in [:Tropical, :TropicalMaxMul, :TropicalMinPlus, :CountingTropical] +for T in [:Tropical, :TropicalMinPlus, :TropicalMaxMul, :CountingTropical] @eval begin # this is for CUDA matmul Base.:(*)(a::$T, b::Bool) = b ? a : zero(a) @@ -100,4 +117,11 @@ for T in [:Tropical, :TropicalMaxMul, :TropicalMinPlus, :CountingTropical] 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/tropical_andor.jl b/src/tropical_andor.jl index ae588ef..23e4794 100644 --- a/src/tropical_andor.jl +++ b/src/tropical_andor.jl @@ -12,6 +12,8 @@ It maps * `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) diff --git a/src/tropical_bitwise.jl b/src/tropical_bitwise.jl new file mode 100644 index 0000000..4a8bd92 --- /dev/null +++ b/src/tropical_bitwise.jl @@ -0,0 +1,110 @@ +""" + TropicalBitwise{T} <: AbstractSemiring + +`TropicalBitwise` is a semiring algebra that parallelizes the [`TropicalAndOr`](@ref) algebra, +It can be described by +* TropicalBitwise, (ℝ, |, &, 0, ~0). + +It maps +* `+` to `|` +* `*` to `&` +* `0` to `0` +* `1` to `~0` + +Example +------------------------- +```jldoctest; setup=:(using TropicalNumbers) +julia> TropicalBitwise(1) + TropicalBitwise(3) +3ₛ + +julia> TropicalBitwise(1) * TropicalBitwise(3) +1ₛ + +julia> zero(TropicalBitwiseI64) +0ₛ + +julia> one(TropicalBitwiseI64) +-1ₛ +``` +""" +struct TropicalBitwise{T} <: AbstractSemiring + 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.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) +end + +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 +end + +function Base.:<(a::TropicalBitwise, b::TropicalBitwise) + return a != b && a <= b +end + +function Base.:>(a::TropicalBitwise, b::TropicalBitwise) + return b < a +end diff --git a/src/tropical_maxmin.jl b/src/tropical_maxmin.jl new file mode 100644 index 0000000..7e5866d --- /dev/null +++ b/src/tropical_maxmin.jl @@ -0,0 +1,89 @@ +""" + 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_minplus.jl b/src/tropical_minplus.jl index f9d1b16..40cbdfd 100644 --- a/src/tropical_minplus.jl +++ b/src/tropical_minplus.jl @@ -70,6 +70,31 @@ 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) +# 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.isless(a::TropicalMinPlus, b::TropicalMinPlus) + return isless(b.n, a.n) +end + Base.isapprox(x::TropicalMinPlus, y::TropicalMinPlus; kwargs...) = isapprox(x.n, y.n; kwargs...) # promotion rules diff --git a/test/runtests.jl b/test/runtests.jl index 7f43770..33e416f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,6 +17,14 @@ end include("tropical_maxplus.jl") end +@testset "tropical max min" begin + include("tropical_maxmin.jl") +end + +@testset "tropical bitwise" begin + include("tropical_bitwise.jl") +end + @testset "counting_tropical" begin include("counting_tropical.jl") end diff --git a/test/tropical_bitwise.jl b/test/tropical_bitwise.jl new file mode 100644 index 0000000..cbe97ed --- /dev/null +++ b/test/tropical_bitwise.jl @@ -0,0 +1,39 @@ +using Test +using TropicalNumbers + +@testset "TropicalBitwise max mul" begin + @test TropicalBitwise(3) * TropicalBitwise(4) == TropicalBitwise(0) + @test TropicalBitwise(3) + TropicalBitwise(4) == TropicalBitwise(7) + @test TropicalBitwise(4) + TropicalBitwise(1) == TropicalBitwise(5) + @test zero(TropicalBitwise(2)) == TropicalBitwise(0) + @test one(TropicalBitwise(2)) == TropicalBitwise(~0) + @test TropicalBitwise(2) ≈ TropicalBitwise(2) + @test TropicalBitwiseI32(2).n isa Int32 + @test TropicalBitwiseI16(2).n isa Int16 + @test TropicalBitwiseI64(2).n isa Int64 + + @test content(TropicalBitwise(3)) == 3 + @test TropicalBitwise{UInt32}(TropicalBitwise(0)) isa TropicalBitwise{UInt32} + println(TropicalBitwiseI64(3)) + + # promote and convert + t1 = TropicalBitwise(2) + t2 = TropicalBitwise(UInt32(2)) + @test TropicalBitwiseI32(t1) === TropicalBitwise(Int32(2)) + @test promote(t1, t2) === (t1, t1) + + @test content(TropicalBitwiseI64) == Int64 + + @test promote(TropicalBitwise{Int64}(1), TropicalBitwise{UInt64}(2)) == (TropicalBitwise{Int64}(1), TropicalBitwise{Int64}(2)) + @test promote(TropicalBitwise{Int64}(1)) == (TropicalBitwise{Int64}(1),) + @test promote_type(TropicalBitwise{Int64}, TropicalBitwiseI32) == TropicalBitwiseI64 + @test promote_type(TropicalBitwise{Int64}, TropicalBitwiseI32, TropicalBitwise{UInt32}) == TropicalBitwiseI64 + + x = TropicalBitwise(2) + @test x * true == x * one(x) + @test x * false == x * zero(x) + @test true * x == one(x) * x + @test false * x == zero(x) * x + + @test TropicalBitwise(0) < TropicalBitwise(10) <= TropicalBitwise(10) <= TropicalBitwise(~0) +end diff --git a/test/tropical_maxmin.jl b/test/tropical_maxmin.jl new file mode 100644 index 0000000..ac85e59 --- /dev/null +++ b/test/tropical_maxmin.jl @@ -0,0 +1,50 @@ +using Test +using TropicalNumbers + +@testset "TropicalMaxMin max min" begin + @test TropicalMaxMin(3) * TropicalMaxMin(4) == TropicalMaxMin(3) + @test TropicalMaxMin(3) + TropicalMaxMin(4) == TropicalMaxMin(4) + @test TropicalMaxMin(4) + TropicalMaxMin(1) == TropicalMaxMin(4) + @test zero(TropicalMaxMin(2)) == TropicalMaxMin(neginf(Int)) + @test zero(TropicalMaxMin(2.0)) == TropicalMaxMin(-Inf) + @test one(TropicalMaxMin(2)) == TropicalMaxMin(posinf(Int)) + @test TropicalMaxMin(2.0) ≈ TropicalMaxMin(2.0 + 1e-10) + @test TropicalMaxMin(2) ≈ TropicalMaxMin(2.0) + @test TropicalMaxMinF32(2.0).n isa Float32 + @test TropicalMaxMinF16(2.0).n isa Float16 + @test TropicalMaxMinF64(2.0).n isa Float64 + @test TropicalMaxMinI32(2).n isa Int32 + @test TropicalMaxMinI16(2).n isa Int16 + @test TropicalMaxMinI64(2).n isa Int64 + + @test TropicalMaxMin(0//3) * TropicalMaxMin(1//3) == TropicalMaxMin(0//3) + @test TropicalMaxMin(1//3) * TropicalMaxMin(0//3) == TropicalMaxMin(0//3) + @test TropicalMaxMin(1//3) * TropicalMaxMin(1//3) == TropicalMaxMin(1//3) + + @test TropicalMaxMin(1//3) * TropicalMaxMin(1//0) == TropicalMaxMin(1//3) + @test TropicalMaxMin(1//0) * TropicalMaxMin(1//1) == TropicalMaxMin(1//1) + @test content(TropicalMaxMin(3.0)) == 3.0 + @test TropicalMaxMin{Float32}(TropicalMaxMin(0.0)) isa TropicalMaxMin{Float32} + println(TropicalMaxMinF64(3)) + + # promote and convert + t1 = TropicalMaxMin(2) + t2 = TropicalMaxMin(2.0) + @test TropicalMaxMinF64(t1) === TropicalMaxMin(2.0) + @test promote(t1, t2) === (TropicalMaxMin(2.0), t2) + + @test content(TropicalMaxMinF64) == Float64 + + @test promote(TropicalMaxMin{Float64}(1), TropicalMaxMin{Int64}(2)) == (TropicalMaxMin{Float64}(1), TropicalMaxMin{Float64}(2)) + @test promote(TropicalMaxMin{Float64}(1)) == (TropicalMaxMin{Float64}(1),) + @test promote_type(TropicalMaxMin{Float64}, TropicalMaxMinF32) == TropicalMaxMinF64 + @test promote_type(TropicalMaxMin{Float64}, TropicalMaxMinF32, TropicalMaxMin{Int32}) == TropicalMaxMinF64 + + x = TropicalMaxMin(2.0) + @test x * true == x * one(x) + @test x * false == x * zero(x) + @test true * x == one(x) * x + @test false * x == zero(x) * x + @test isnan(TropicalMaxMin(NaN)) + @test !isnan(TropicalMaxMin(Inf)) +end diff --git a/test/tropical_minplus.jl b/test/tropical_minplus.jl index 139b660..d308500 100644 --- a/test/tropical_minplus.jl +++ b/test/tropical_minplus.jl @@ -64,4 +64,8 @@ using TropicalNumbers @test TropicalMinPlus(2.0) ^ 3.0 == TropicalMinPlus(2.0) * TropicalMinPlus(2.0) * TropicalMinPlus(2.0) @test TropicalMinPlus(2.0) ^ 3 == TropicalMinPlus(2.0) * TropicalMinPlus(2.0) * TropicalMinPlus(2.0) + + @test TropicalMinPlus(-Inf) > TropicalMinPlus(3.0) >= TropicalMinPlus(4.0) >= TropicalMinPlus(4.0) + @test TropicalMinPlus(4.0) <= TropicalMinPlus(4.0) < TropicalMinPlus(3.0) < TropicalMinPlus(-Inf) + @test isless(TropicalMinPlus(4.0), TropicalMinPlus(3.0)) end