add PoissonSampler for FPGroupElements

Words of length following the Poisson(λ=8) distribution are
chosen uniformly at random.
This commit is contained in:
Marek Kaluba 2024-02-12 12:16:03 +01:00
parent 5993cb328f
commit 148a472dd2
No known key found for this signature in database
GPG Key ID: 8BF1A3855328FC15
3 changed files with 63 additions and 11 deletions

View File

@ -27,6 +27,7 @@ include(joinpath("constructions", "constructions.jl"))
import .Constructions
include("types.jl")
include("rand.jl")
include("hashing.jl")
include("normalform.jl")
include("autgroups.jl")

62
src/rand.jl Normal file
View File

@ -0,0 +1,62 @@
"""
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

View File

@ -67,17 +67,6 @@ function GroupsCore.gens(G::AbstractFPGroup)
return [gens(G, i) for i in 1:GroupsCore.ngens(G)]
end
# TODO: ProductReplacementAlgorithm
function Base.rand(
rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:AbstractFPGroup},
)
l = rand(10:100)
G = rs[]
nletters = length(alphabet(G))
return FPGroupElement(word_type(G)(rand(1:nletters, l)), G)
end
function Base.isfinite(::AbstractFPGroup)
return (
@warn "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong"; false