Groups and Monoids
The abstract types Group <: Monoid
encompass all multiplicative groups and monoids. Since these are already abstract, we skip the Abstract
prefix.
Assumptions
GroupsCore
implements some methods with default values, which may not be generally true for all groups. The intent is to limit the extent of the required interface. This requires special care when implementing groups/monoids that need to override these default methods.
The methods we currently predefine are:
GroupsCore.hasgens(::Monoid) = true
This is based on the broad assumption that reasonably generic functions manipulating groups/monoids can be implemented only with an access to a generating set.For finite groups/monoids only we define
Base.length(M) = order(Int, M)
In general length
should be used for iteration purposes only. If you are interested in the number of distinct elements of a groups/monoids, use order(::Type{<:Integer}, ::Group)
. For more information see Iteration.
Obligatory methods
Here we list the minimal set of functions that a group object must extend to implement the Monoid
interface:
Base.one(::Monoid)
and
GroupsCore.order
— Methodorder([::Type{T} = BigInt, ]M::Monoid) where T
Return the order of M
as an instance of T
. If M
is of infinite order, GroupsCore.InfiniteOrder
exception will be thrown.
Only arbitrary sized integers are required to return a mathematically correct answer.
GroupsCore.gens
— Methodgens(M::Monoid)
Return a random-access collection of generators of G
.
Iteration
If a group/monoid is defined by generators (i.e. hasgens(M)
returns true
) an important aspect of this interface is the iteration over a group.
Iteration over infinite objects seem to be useful only when the returned elements explore the whole group or monoid. To be precise, for the example of the free group $F_2 = ⟨a,b⟩$, one could implement iteration by sequence
\[a, a^2, a^3, \ldots,\]
which is arguably less useful than
\[a, b, a^{-1}, b^{-1}, ab, \ldots.\]
Therefore we put the following assumptions on iteration.
- Iteration is mandatory only if
hasgens(M)
returnstrue
. - The first element of the iteration (e.g. given by
Base.first
) is the group identity. - Iteration over an infinite group/monoid should exhaust every fixed radius ball around the identity (in word-length metric associated to
gens(M)
) in finite time. - There is no requirement that in the iteration sequence elements are returned only once.
These are just the conventions, the iteration interface consists of standard julia methods:
Base.IteratorSize
— MethodIteratorSize(::Type{M}) where {M <: Monoid}
Given the type of a monoid, return one of the following values:
Base.IsInfinite()
if all instances of groups of typeM
are infinite.Base.HasLength()
orBase.HasShape{N}()
if all instances are finite.Base.SizeUnknown()
otherwise, [the default].
In contrast to julia we default to Base.SizeUnknown()
to provide a mathematically correct fallback. If a group or monoid is finite by definition, implementing the correct IteratorSize
(i.e. Base.HasLength()
, or Base.HasShape{N}()
) will simplify several other methods, which will be then optimized to work only based on the type of the object. In particular when the information is derivable from the type, there is no need to extend Base.isfinite
.
In the case that IteratorSize(Gr) == IsInfinite()
, one should could Base.length(Gr)
to be a "best effort", length of the group/monoid iterator. For practical reasons the largest object you could iterate over in your lifetime is of order that fits well into an Int
($2^{63}$ nanoseconds comes to 290 years).
Additional methods
Base.isfinite
— Methodisfinite(M::Monoid)
Test whether monoid M
is finite.
The default implementation is based on Base.IteratorSize
. Only groups of returning Base.SizeUnknown()
should extend this method.
GroupsCore.istrivial
— Methodistrivial(M::Monoid)
Test whether monoid M
is trivial.
The default implementation is based on isfinite
and order
.
Random elements
We provide two methods for generating random elements of a group or monoid.
GroupsCore.ProductReplacementSampler
— TypeProductReplacementSampler
Implements Product Replacement Algorithm for a group or monoid generated by an explicit finite set of generators.
Product Replacement Algorithm performs a random walk on the graph of n
-generating tuples of a group (or monoid) with two accumulators. Each step consists of
- multiplying the right accumulator by a random generator,
- replacing one of the generators with the product with the right accumulator,
- multiplying the left accumulator (on the left) by a random generator.
The left accumulator is returned as a random element.
By default for a group with k
generators we set n = 2k + 10
and perform 10*n
scrambling steps before returning a random element.
PRASampler
provides provably uniformly distributed random elements for finite groups. For infinite groups RandomWordSampler
is used.
Using PRASampler
for an infinite group is ill-advised as the exponential growth of words during scrambling will result in excessive memory use and out-of-memory situation.
GroupsCore.RandomWordSampler
— TypeRandomWordSampler(S, distribution)
Return elements from a monoid represented by words of length in S
, obeying distribution
.
Usually for a monoid (group) S
is a (symmetric) generating set.
distribution
object must implement cdf(distribution, x::Float64)::Integer
.
For finite monoids or groups when uniformity of results is needed ProductReplacementSampler
should be used.
By default for finite monoids ProductReplacementSampler
is used and RandomWordSampler
following Poisson(λ=8)
is employed for inifinite ones.