63 lines
1.8 KiB
Julia
63 lines
1.8 KiB
Julia
"""
|
||
PoissonSampler
|
||
For a finitely presented group PoissonSampler returns group elements represented
|
||
by words of length at most `R ~ Poisson(λ)` chosen uniformly at random.
|
||
|
||
For finitely presented groups the Product Replacement Algorithm
|
||
(see `PRASampler` from `GroupsCore.jl`) doesn't make much sense due to
|
||
overly long words it produces. We therefore resort to a pseudo-random method,
|
||
where a word `w` of length `R` is chosen uniformly at random among all
|
||
words of length `R` where `R` follows the Poisson distribution.
|
||
|
||
!!! note
|
||
Due to the choice of the parameters (`λ=8`) and the floating point
|
||
arithmetic the sampler will always return group elements represented by
|
||
words of length at most `42`.
|
||
"""
|
||
struct PoissonSampler{G,T} <: Random.Sampler{T}
|
||
group::G
|
||
λ::Int
|
||
end
|
||
|
||
function PoissonSampler(G::AbstractFPGroup; λ)
|
||
return PoissonSampler{typeof(G),eltype(G)}(G, λ)
|
||
end
|
||
|
||
function __poisson_invcdf(val; λ)
|
||
# __poisson_pdf(k, λ) = λ^k * ℯ^-λ / factorial(k)
|
||
# pdf = ntuple(k -> __poisson_pdf(k - 1, λ), 21)
|
||
# cdf = accumulate(+, pdf)
|
||
# radius = something(findfirst(>(val), cdf) - 1, 0)
|
||
# this is the iterative version:
|
||
pdf = ℯ^-λ
|
||
cdf = pdf
|
||
k = 0
|
||
while cdf < val
|
||
k += 1
|
||
pdf = pdf * λ / k
|
||
cdf += pdf
|
||
end
|
||
return k
|
||
end
|
||
|
||
function Random.rand(rng::Random.AbstractRNG, sampler::PoissonSampler)
|
||
R = __poisson_invcdf(rand(rng); λ = sampler.λ)
|
||
|
||
G = sampler.group
|
||
n = length(alphabet(G))
|
||
W = word_type(G)
|
||
T = eltype(W)
|
||
|
||
letters = rand(rng, T(1):T(n), R)
|
||
word = W(letters, false)
|
||
return G(word)
|
||
end
|
||
|
||
function Random.Sampler(
|
||
RNG::Type{<:Random.AbstractRNG},
|
||
G::AbstractFPGroup,
|
||
repetition::Random.Repetition = Val(Inf),
|
||
)
|
||
return PoissonSampler(G; λ = 8)
|
||
end
|