Usage
A semiring over a set $S$ is an algebraic structure $(S, \oplus, \otimes, \bar{0}, \bar{1})$ where $\oplus$ and $\otimes$ are two binary operators called "addition" and "multiplication" and $\bar{0}$ and $\bar{1}$ are their respective neutral element.
Simple example
Load the module
using Semirings
The usual workflow when is to first declare a semiring type and then perform a computation
S = LogSemiring{Float64,1}
(S(-2.3) + one(S)) * S(-1)
# output
LogSemiring{Float64, 1}(-0.904454535402037)
The neutral elements $\bar{0}$ and $\bar{1}$ can be instantiated with the zero
and one
functions respectively:
S = BoolSemiring
isone(one(S) + one(S)), iszero(zero(S) * one(S))
# output
(true, true)
Input / Output
Semirings can written and read from binary stream
S = TropicalSemiring{Float32}
io = IOBuffer()
write(io, S(2.4))
read(seekstart(io), S)
# output
TropicalSemiring{Float32}(2.4f0, 1)
It is also possible to create semiring S<:Semiring{T}
from a textual representation with parse
:
S = TropicalSemiring{Float32}
parse(S, "0.123")
# output
TropicalSemiring{Float32}(0.123f0, 1)
This will work only if the type T
wrapped by semiring type S
can be created from parse
.
Automatic Differentiation
Computation over sermiings should be differentiable (for semiring over numeric values) with reverse-mode differentiation backend relying upon ChainRulesCore.jl. Here is an example with Zygote.
using Semirings
using Zygote
S = LogSemiring{Float32,2}
gradient(S(1), S(2), S(3)) do x, y , z
val(z * (x + y))
end
# output
(SemiringTangent{Float64}(0.11920294090533493), SemiringTangent{Float64}(0.8807972175070349), SemiringTangent{Float32}(1.0f0))
Importantly, the function that needs to be differentiated should return the value of the semiring (not the semiring itself). Hence, your function should end with a call to val(...)
.