Groups.jl/src/DirectPower.jl

321 lines
9.7 KiB
Julia
Raw Normal View History

2019-01-02 10:30:25 +01:00
export DirectPowerGroup, DirectPowerGroupElem
export MultiplicativeGroup, MltGrp, MltGrpElem
export AdditiveGroup, AddGrp, AddGrpElem
###############################################################################
#
# MltGrp/MltGrpElem & AddGrp/AddGrpElem
# (a thin wrapper for multiplicative/additive group of a Ring)
#
###############################################################################
for (Gr, Elem) in [(:MltGrp, :MltGrpElem), (:AddGrp, :AddGrpElem)]
@eval begin
struct $Gr{T<:AbstractAlgebra.Ring} <: AbstractAlgebra.Group
obj::T
end
struct $Elem{T<:AbstractAlgebra.RingElem} <: AbstractAlgebra.GroupElem
elt::T
end
==(g::$Elem, h::$Elem) = g.elt == h.elt
==(G::$Gr, H::$Gr) = G.obj == H.obj
elem_type(::Type{$Gr{T}}) where T = $Elem{elem_type(T)}
eltype(::Type{$Gr{T}}) where T = $Elem{elem_type(T)}
parent_type(::Type{$Elem{T}}) where T = $Gr{parent_type(T)}
parent(g::$Elem) = $Gr(parent(g.elt))
end
end
MultiplicativeGroup = MltGrp
AdditiveGroup = AddGrp
(G::MltGrp)(g::MltGrpElem) = MltGrpElem(G.obj(g.elt))
function (G::MltGrp)(g)
r = (G.obj)(g)
2018-07-30 14:59:11 +02:00
isunit(r) || throw(DomainError(
"Cannot coerce to multplicative group: $r is not invertible!"))
return MltGrpElem(r)
end
(G::AddGrp)(g) = AddGrpElem((G.obj)(g))
(G::MltGrp)() = MltGrpElem(G.obj(1))
(G::AddGrp)() = AddGrpElem(G.obj())
inv(g::MltGrpElem) = MltGrpElem(inv(g.elt))
inv(g::AddGrpElem) = AddGrpElem(-g.elt)
for (Elem, op) in ([:MltGrpElem, :*], [:AddGrpElem, :+])
@eval begin
^(g::$Elem, n::Integer) = $Elem(op(g.elt, n))
function *(g::$Elem, h::$Elem)
2018-07-30 14:59:11 +02:00
parent(g) == parent(h) || throw(DomainError(
"Cannot multiply elements of different parents"))
return $Elem($op(g.elt,h.elt))
end
end
end
2018-09-21 18:46:17 +02:00
show(io::IO, G::MltGrp) = print(io, "The multiplicative group of $(G.obj)")
show(io::IO, G::AddGrp) = print(io, "The additive group of $(G.obj)")
2018-09-21 18:46:17 +02:00
show(io::IO, g::Union{MltGrpElem, AddGrpElem}) = show(io, g.elt)
gens(F::AbstractAlgebra.Field) = elem_type(F)[gen(F)]
order(G::AddGrp{<:AbstractAlgebra.GFField}) = order(G.obj)
elements(G::AddGrp{F}) where F <: AbstractAlgebra.GFField = (G((i-1)*G.obj(1)) for i in 1:order(G))
order(G::MltGrp{<:AbstractAlgebra.GFField}) = order(G.obj) - 1
elements(G::MltGrp{F}) where F <: AbstractAlgebra.GFField = (G(i*G.obj(1)) for i in 1:order(G))
function iterate(G::AddGrp, s=0)
if s >= order(G)
return nothing
else
g, s = iterate(G.obj,s)
return G(g), s
end
end
function iterate(G::MltGrp, s=0)
if s > order(G)
return nothing
else
g, s = iterate(G.obj, s)
if g == G.obj()
g, s = iterate(G.obj, s)
end
return G(g), s
end
end
2017-06-22 14:21:25 +02:00
###############################################################################
#
2019-01-02 15:45:46 +01:00
# DirectPowerGroup / DirectPowerGroupElem Constructors
2017-06-22 14:21:25 +02:00
#
###############################################################################
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
DirectPowerGroup(G::Group, n::Int) <: Group
Implements `n`-fold direct product of `G`. The group operation is
2017-06-22 14:21:25 +02:00
`*` distributed component-wise, with component-wise identity as neutral element.
"""
2019-01-02 15:45:46 +01:00
struct DirectPowerGroup{N, T<:Group} <: Group
group::T
2017-06-22 14:21:25 +02:00
end
2019-01-02 15:45:46 +01:00
DirectPowerGroup(G::Gr, N::Int) where Gr<:Group = DirectPowerGroup{N,Gr}(G)
function DirectPower(G::Group, H::Group)
G == H || throw(DomainError(
"Direct Powers are defined only for the same groups"))
return DirectPowerGroup(G,2)
2017-06-22 14:21:25 +02:00
end
2019-01-02 15:45:46 +01:00
DirectPower(H::Group, G::DirectPowerGroup) = DirectPower(G,H)
2017-06-22 14:21:25 +02:00
2019-01-02 15:45:46 +01:00
function DirectPower(G::DirectPowerGroup{N}, H::Group) where N
G.group == H || throw(DomainError(
"Direct Powers are defined only for the same groups"))
return DirectPowerGroup(G.group, N+1)
end
function DirectPower(R::AbstractAlgebra.Ring, n::Int)
@warn "Creating DirectPower of the multilplicative group!"
return DirectPowerGroup(MultiplicativeGroup(R), n)
end
2017-06-22 14:21:25 +02:00
2019-01-02 15:45:46 +01:00
struct DirectPowerGroupElem{N, T<:GroupElem} <: GroupElem
elts::NTuple{N,T}
end
2017-06-22 14:21:25 +02:00
2019-01-02 15:45:46 +01:00
function DirectPowerGroupElem(v::Vector{GrEl}) where GrEl<:GroupElem
return DirectPowerGroupElem(tuple(v...))
end
2017-06-22 14:21:25 +02:00
###############################################################################
#
2019-01-02 15:45:46 +01:00
# Type and parent object methods
2017-06-22 14:21:25 +02:00
#
###############################################################################
2019-01-02 15:45:46 +01:00
elem_type(::Type{DirectPowerGroup{N,T}}) where {N,T} =
DirectPowerGroupElem{N, elem_type(T)}
2018-07-30 14:59:11 +02:00
2019-01-02 15:45:46 +01:00
parent_type(::Type{DirectPowerGroupElem{N,T}}) where {N,T} =
DirectPowerGroup{N, parent_type(T)}
2017-06-22 14:21:25 +02:00
2019-01-02 15:45:46 +01:00
parent(g::DirectPowerGroupElem{N, T}) where {N,T} =
DirectPowerGroup(parent(first(g.elts)), N)
###############################################################################
#
2019-01-02 15:45:46 +01:00
# AbstractVector interface
#
###############################################################################
2019-01-02 15:45:46 +01:00
size(g::DirectPowerGroupElem{N}) where N = (N,)
Base.IndexStyle(::Type{DirectPowerGroupElem}) = Base.LinearFast()
Base.getindex(g::DirectPowerGroupElem, i::Int) = g.elts[i]
2017-06-22 14:21:25 +02:00
###############################################################################
#
# Parent object call overloads
#
###############################################################################
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
(G::DirectPowerGroup)(a::Vector, check::Bool=true)
> Constructs element of the $n$-fold direct product group `G` by coercing each
2017-07-23 17:04:22 +02:00
> element of vector `a` to `G.group`. If `check` flag is set to `false` neither
> check on the correctness nor coercion is performed.
2017-06-22 14:21:25 +02:00
"""
2019-01-02 15:45:46 +01:00
function (G::DirectPowerGroup{N})(a::Vector, check::Bool=true) where N
if check
2019-01-02 15:45:46 +01:00
N == length(a) || throw(DomainError(
2019-01-02 10:30:25 +01:00
"Can not coerce to DirectPowerGroup: lengths differ"))
2018-07-30 14:59:11 +02:00
a = (G.group).(a)
end
2019-01-02 10:30:25 +01:00
return DirectPowerGroupElem(a)
2017-06-22 14:21:25 +02:00
end
2019-01-02 15:45:46 +01:00
function (G::DirectPowerGroup{N})(a::NTuple{N, GrEl}) where {N, GrEl}
return DirectPowerGroupElem(G.group.(a))
end
(G::DirectPowerGroup{N})(a::Vararg{GrEl, N}) where {N, GrEl} = DirectPowerGroupElem(G.group.(a))
2019-01-02 15:45:46 +01:00
function (G::DirectPowerGroup{N})() where N
return DirectPowerGroupElem(ntuple(i->G.group(),N))
end
2019-01-02 15:45:46 +01:00
(G::DirectPowerGroup)(g::DirectPowerGroupElem) = G(g.elts)
2017-06-22 14:21:25 +02:00
###############################################################################
#
# Basic manipulation
#
###############################################################################
2019-01-02 15:45:46 +01:00
function hash(G::DirectPowerGroup{N}, h::UInt) where N
return hash(G.group, hash(N, hash(DirectPowerGroup,h)))
2017-06-22 14:21:25 +02:00
end
2019-01-02 10:30:25 +01:00
function hash(g::DirectPowerGroupElem, h::UInt)
2019-01-02 15:45:46 +01:00
return hash(g.elts, hash(DirectPowerGroupElem, h))
2017-06-22 14:21:25 +02:00
end
###############################################################################
#
# String I/O
#
###############################################################################
2019-01-02 15:45:46 +01:00
function show(io::IO, G::DirectPowerGroup{N}) where N
print(io, "$(N)-fold direct product of $(G.group)")
2017-06-22 14:21:25 +02:00
end
2019-01-02 10:30:25 +01:00
function show(io::IO, g::DirectPowerGroupElem)
2018-07-30 14:05:47 +02:00
print(io, "[$(join(g.elts,","))]")
2017-06-22 14:21:25 +02:00
end
###############################################################################
#
# Comparison
#
###############################################################################
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
==(g::DirectPowerGroup, h::DirectPowerGroup)
2017-07-21 13:28:19 +02:00
> Checks if two direct product groups are the same.
"""
2019-01-02 15:45:46 +01:00
function (==)(G::DirectPowerGroup{N}, H::DirectPowerGroup{M}) where {N,M}
N == M || return false
2018-07-30 14:05:47 +02:00
G.group == H.group || return false
return true
2017-06-22 14:21:25 +02:00
end
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
==(g::DirectPowerGroupElem, h::DirectPowerGroupElem)
2017-07-21 13:28:19 +02:00
> Checks if two direct product group elements are the same.
2017-06-22 14:21:25 +02:00
"""
2019-01-02 15:45:46 +01:00
(==)(g::DirectPowerGroupElem, h::DirectPowerGroupElem) = g.elts == h.elts
2017-06-22 14:21:25 +02:00
###############################################################################
#
# Group operations
2017-06-22 14:21:25 +02:00
#
###############################################################################
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
*(g::DirectPowerGroupElem, h::DirectPowerGroupElem)
2017-06-22 14:21:25 +02:00
> Return the direct-product group operation of elements, i.e. component-wise
> operation as defined by `operations` field of the parent object.
"""
2019-01-02 15:45:46 +01:00
function *(g::DirectPowerGroupElem{N}, h::DirectPowerGroupElem{N}, check::Bool=true) where N
if check
2018-07-30 14:59:11 +02:00
parent(g) == parent(h) || throw(DomainError(
"Can not multiply elements of different groups!"))
end
2019-01-02 15:45:46 +01:00
return DirectPowerGroupElem(ntuple(i-> g.elts[i]*h.elts[i], N))
end
2019-01-02 10:30:25 +01:00
^(g::DirectPowerGroupElem, n::Integer) = Base.power_by_squaring(g, n)
2018-09-21 18:47:02 +02:00
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
inv(g::DirectPowerGroupElem)
2017-06-22 14:21:25 +02:00
> Return the inverse of the given element in the direct product group.
"""
2019-01-02 15:45:46 +01:00
function inv(g::DirectPowerGroupElem{N}) where {N}
return DirectPowerGroupElem(ntuple(i-> inv(g.elts[i]), N))
end
2017-06-22 14:21:25 +02:00
###############################################################################
#
# Misc
#
###############################################################################
struct DirectPowerIter{GrEl<:AbstractAlgebra.GroupElem}
N::Int
elts::Vector{GrEl}
totalorder::Int
orderG::Int
end
function DirectPowerIter(G::Gr, N::Integer) where {Gr<:AbstractAlgebra.Group}
return DirectPowerIter{elem_type(G)}(N, collect(G), order(G)^N, order(G))
end
length(DPIter::DirectPowerIter) = DPIter.totalorder
function iterate(DPIter::DirectPowerIter, state=0)
if state >= DPIter.totalorder
return nothing
end
idx = Tuple(CartesianIndices(ntuple(i -> DPIter.orderG, DPIter.N))[state+1])
2019-01-02 10:30:25 +01:00
return DirectPowerGroupElem([DPIter.elts[i] for i in idx]), state+1
end
2019-01-02 10:30:25 +01:00
eltype(::Type{DirectPowerIter{GrEl}}) where {GrEl} = DirectPowerGroupElem{GrEl}
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
elements(G::DirectPowerGroup)
2017-07-21 13:31:42 +02:00
> Returns `generator` that produces all elements of group `G` (provided that
> `G.group` implements the `elements` method).
2017-06-22 14:21:25 +02:00
"""
2019-01-02 10:30:25 +01:00
elements(G::DirectPowerGroup) = DirectPowerIter(G.group, G.n)
2017-06-22 14:21:25 +02:00
2018-09-21 18:08:44 +02:00
@doc doc"""
2019-01-02 10:30:25 +01:00
order(G::DirectPowerGroup)
2017-06-22 14:21:25 +02:00
> Returns the order (number of elements) in the group.
"""
2019-01-02 10:30:25 +01:00
order(G::DirectPowerGroup) = order(G.group)^G.n