From 4c0876438565e36fc78a38b8fbe05cc07ecfbcfe Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Fri, 18 Oct 2024 21:36:25 +0200 Subject: [PATCH] feat: group algebras with sparse representation --- docs/src/.vitepress/config.mts | 1 + docs/src/manual/algebras/groupalgebras.md | 49 +++++++ src/AlgAss/AbsAlgAss.jl | 15 +++ src/AlgAss/AlgGrp.jl | 62 +++++---- src/AlgAss/Elem.jl | 150 +++++++++++++++++----- src/AlgAss/Types.jl | 127 +++++++++++++----- src/Grp/GenGrp.jl | 6 +- src/GrpAb/GrpAbFinGen.jl | 2 + test/AlgAss/AlgGrp.jl | 31 +++++ test/AlgAss/Elem.jl | 9 ++ 10 files changed, 362 insertions(+), 90 deletions(-) create mode 100644 docs/src/manual/algebras/groupalgebras.md diff --git a/docs/src/.vitepress/config.mts b/docs/src/.vitepress/config.mts index 9609aaf411..e4c0427b9c 100644 --- a/docs/src/.vitepress/config.mts +++ b/docs/src/.vitepress/config.mts @@ -95,6 +95,7 @@ export default defineConfig({ { text: 'Introduction', link: 'manual/algebras/intro'}, { text: 'Basics', link: 'manual/algebras/basics'}, { text: 'Structure constant algebras', link: 'manual/algebras/structureconstant'}, + { text: 'Group algebras', link: 'manual/algebras/groupalgebras'}, { text: 'Ideals', link: 'manual/algebras/ideals'}, ] }, diff --git a/docs/src/manual/algebras/groupalgebras.md b/docs/src/manual/algebras/groupalgebras.md new file mode 100644 index 0000000000..d558ccc527 --- /dev/null +++ b/docs/src/manual/algebras/groupalgebras.md @@ -0,0 +1,49 @@ +# Group algebras + +```@meta +CurrentModule = Hecke +DocTestSetup = quote + using Hecke +end +``` + +As is natural, the basis of a group algebra $K[G]$ correspond to the elements of $G$ with respect +to some arbitrary ordering. + +## Creation + +```@docs +group_algebra(::Field, ::Group) +``` + +## Elements + +Given a group algebra `A` and an element of a group `g`, the corresponding group algebra element +can be constructed using the syntax `A(g)`. + +```jldoctest grpalgex1 +julia> G = abelian_group([2, 2]); a = G([0, 1]); + +julia> QG = group_algebra(QQ, G); + +julia> x = QG(a) +[0, 0, 1, 0] +``` + +Vice versa, one can obtain the coordinate of a group algebra element `x` with respect to a group +element `a` using the syntax `x[a]`. + +```jldoctest grpalgex1 +julia> x[a] +1 +``` + +It is also possible to create elements by specifying for each group element the corresponding coordinate either by a list of pairs or a dictionary: + +```jldoctest grpalgex1 +julia> QG(a => 2, zero(G) => 1) == 2 * QG(a) + 1 * QG(zero(G)) +true + +julia> QG(Dict(a => 2, zero(G) => 1)) == 2 * QG(a) + 1 * QG(zero(G)) +true +``` diff --git a/src/AlgAss/AbsAlgAss.jl b/src/AlgAss/AbsAlgAss.jl index 979ab75f20..0714afefd0 100644 --- a/src/AlgAss/AbsAlgAss.jl +++ b/src/AlgAss/AbsAlgAss.jl @@ -1,3 +1,18 @@ +################################################################################ +# +# Internal thingy +# +################################################################################ + +# check if elements are represented using a sparse row + +_is_sparse(A::AbstractAssociativeAlgebra) = false + +_is_sparse(A::GroupAlgebra) = A.sparse + +# because we are lazy +_is_dense(A::AbstractAssociativeAlgebra) = !_is_sparse(A) + _base_ring(A::AbstractAssociativeAlgebra) = base_ring(A) @doc raw""" diff --git a/src/AlgAss/AlgGrp.jl b/src/AlgAss/AlgGrp.jl index 1e1517f7ef..b9c144b547 100644 --- a/src/AlgAss/AlgGrp.jl +++ b/src/AlgAss/AlgGrp.jl @@ -32,8 +32,8 @@ group(A::GroupAlgebra) = A.group has_one(A::GroupAlgebra) = true -function (A::GroupAlgebra{T, S, R})(c::Vector{T}; copy::Bool = false) where {T, S, R} - length(c) != dim(A) && error("Dimensions don't match.") +function (A::GroupAlgebra{T, S, R})(c::Union{Vector{T}, SRow{T}}; copy::Bool = false) where {T, S, R} + c isa Vector && length(c) != dim(A) && error("Dimensions don't match.") return GroupAlgebraElem{T, typeof(A)}(A, copy ? deepcopy(c) : c) end @@ -52,6 +52,14 @@ function multiplication_table(A::GroupAlgebra; copy::Bool = true) end end +# get the underyling group operation, I wish this was part of the group interface + +_op(G::AbstractAlgebra.AdditiveGroup) = + + +_op(G::Group) = * + +_op(A::GroupAlgebra) = _op(group(A)) + ################################################################################ # # Construction @@ -59,17 +67,29 @@ end ################################################################################ @doc raw""" - group_algebra(K::Ring, G; op = *) -> GroupAlgebra + group_algebra(K::Ring, G::Group; cached::Bool = true) -> GroupAlgebra -Returns the group ring $K[G]$. -$G$ may be any set and `op` a group operation on $G$. -""" -group_algebra(K::Ring, G; op = *) = GroupAlgebra(K, G, op = op) +Return the group algebra of the group $G$ over the ring $R$. Shorthand syntax +for this construction is `R[G]`. -group_algebra(K::Ring, G::FinGenAbGroup) = GroupAlgebra(K, G) +# Examples -function group_algebra(K::Field, G; op = *) - A = GroupAlgebra(K, G, op = op) +```jldoctest +julia> QG = group_algebra(QQ, small_group(8, 5)) +Group algebra + of generic group of order 8 with multiplication table + over rational field +``` +""" +group_algebra(K::Ring, G; cached = true) = _group_algebra(K, G; op = *, cached) + +# one additional level of indirection to hide the non-user facing options +# `op` and `sparse`. +function _group_algebra(K::Ring, G; op = *, sparse = _use_sparse_group_algebra(G), cached::Bool = true) + A = GroupAlgebra(K, G; op = _op(G) , sparse = sparse, cached = cached) + if !(K isa Field) + return A + end if iszero(characteristic(K)) A.issemisimple = 1 else @@ -78,21 +98,19 @@ function group_algebra(K::Field, G; op = *) return A end -function group_algebra(K::Field, G::FinGenAbGroup) - A = group_algebra(K, G, op = +) - A.is_commutative = true - return A -end +#_group_algebra(K::Ring, G::FinGenAbGroup; op = +, cached::Bool = true, sparse::Bool = false) = GroupAlgebra(K, G, cached, sparse) -@doc raw""" - (K::Ring)[G::Group] -> GroupAlgebra - (K::Ring)[G::FinGenAbGroup] -> GroupAlgebra - -Returns the group ring $K[G]$. -""" getindex(K::Ring, G::Group) = group_algebra(K, G) getindex(K::Ring, G::FinGenAbGroup) = group_algebra(K, G) +function _use_sparse_group_algebra(G) + if !is_finite(G) + return true + else + return order(G) >= 1000 + end +end + ################################################################################ # # Commutativity @@ -629,7 +647,7 @@ const _reps = [(i=24,j=12,n=5,dims=(1,1,2,3,3), # ################################################################################ -mutable struct AbsAlgAssMorGen{S, T, U, V} <: Map{S, T, HeckeMap, AbsAlgAssMorGen} +mutable struct AbsAlgAssMorGen{S, T, U, V} <: Map{S, T, HeckeMap, Any}#AbsAlgAssMorGen} domain::S codomain::T tempdomain::U diff --git a/src/AlgAss/Elem.jl b/src/AlgAss/Elem.jl index 976dbfc966..908d3cd02a 100644 --- a/src/AlgAss/Elem.jl +++ b/src/AlgAss/Elem.jl @@ -4,6 +4,10 @@ # ################################################################################ +_is_sparse(a::AbstractAssociativeAlgebraElem) = _is_sparse(parent(a)) + +_is_dense(a::AbstractAssociativeAlgebraElem) = _is_dense(parent(a)) + function AbstractAlgebra.promote_rule(U::Type{<:AbstractAssociativeAlgebraElem{T}}, ::Type{S}) where {T, S} if AbstractAlgebra.promote_rule(T, S) === T return U @@ -90,7 +94,11 @@ function one(A::AbstractAssociativeAlgebra) if !has_one(A) error("Algebra does not have a one") end - return A(deepcopy(A.one)) # deepcopy needed by mul! + if _is_dense(A) + return A(deepcopy(A.one)) # deepcopy needed by mul! + else + return A(deepcopy(A.sparse_one)) + end end ################################################################################ @@ -151,20 +159,29 @@ end function +(a::AbstractAssociativeAlgebraElem{T}, b::AbstractAssociativeAlgebraElem{T}) where {T} parent(a) != parent(b) && error("Parents don't match.") - v = Vector{T}(undef, dim(parent(a))) - for i = 1:dim(parent(a)) - v[i] = coefficients(a, copy = false)[i] + coefficients(b, copy = false)[i] + if !_is_sparse(a) + v = Vector{T}(undef, dim(parent(a))) + for i = 1:dim(parent(a)) + v[i] = coefficients(a, copy = false)[i] + coefficients(b, copy = false)[i] + end + return parent(a)(v) + else + vv = a.coeffs_sparse + b.coeffs_sparse + return parent(a)(vv) end - return parent(a)(v) end function -(a::AbstractAssociativeAlgebraElem{T}, b::AbstractAssociativeAlgebraElem{T}) where {T} parent(a) != parent(b) && error("Parents don't match.") - v = Vector{T}(undef, dim(parent(a))) - for i = 1:dim(parent(a)) - v[i] = coefficients(a, copy = false)[i] - coefficients(b, copy = false)[i] + if _is_sparse(a) + return parent(a)(a.coeffs_sparse - b.coeffs_sparse) + else + v = Vector{T}(undef, dim(parent(a))) + for i = 1:dim(parent(a)) + v[i] = coefficients(a, copy = false)[i] - coefficients(b, copy = false)[i] + end + return parent(a)(v) end - return parent(a)(v) end function *(a::AssociativeAlgebraElem{T}, b::AssociativeAlgebraElem{T}) where {T} @@ -200,25 +217,53 @@ end function *(a::GroupAlgebraElem{T, S}, b::GroupAlgebraElem{T, S}) where {T, S} parent(a) != parent(b) && error("Parents don't match.") A = parent(a) - d = dim(A) - v = Vector{T}(undef, d) - for i in 1:d - v[i] = zero(base_ring(A)) - end - t = zero(base_ring(A)) - mt = multiplication_table(A, copy = false) - acoeff = coefficients(a, copy = false) - bcoeff = coefficients(b, copy = false) - for i in 1:d - if iszero(acoeff[i]) - continue + if !_is_sparse(a) + d = dim(A) + v = Vector{T}(undef, d) + for i in 1:d + v[i] = zero(base_ring(A)) end - for j in 1:d - k = mt[i, j] - v[k] = addmul!(v[k], acoeff[i], bcoeff[j], t) + t = zero(base_ring(A)) + mt = multiplication_table(A, copy = false) + acoeff = coefficients(a, copy = false) + bcoeff = coefficients(b, copy = false) + for i in 1:d + if iszero(acoeff[i]) + continue + end + for j in 1:d + k = mt[i, j] + v[k] = addmul!(v[k], acoeff[i], bcoeff[j], t) + end + end + return A(v) + else + op = _op(A) + s = sparse_row(base_ring(A)) + for (i, ci) in a.coeffs_sparse + r = sparse_row(base_ring(A), [(__elem_index(A, op(A.base_to_group[i],A.base_to_group[j])), ci * cj) for (j, cj) in b.coeffs_sparse]) + s += r + end + return A(s) + end +end + +################################################################################ +# +# Getindex for group algebra elements +# +################################################################################ + +function getindex(a::GroupAlgebraElem{S, GroupAlgebra{S, T, U}}, g::U) where {S, T, U} + if _is_sparse(a) + if !haskey(parent(a).group_to_base, g) + return zero(base_ring(parent(a))) + else + return a.coeffs_sparse[parent(a).group_to_base[g]] end + else + return a.coeffs[parent(a).group_to_base[g]] end - return A(v) end ################################################################################ @@ -303,6 +348,9 @@ mul!(c::AbstractAssociativeAlgebraElem{T}, a::Union{ Int, ZZRingElem }, b::Abstr function mul!(c::GroupAlgebraElem{T, S}, a::GroupAlgebraElem{T, S}, b::GroupAlgebraElem{T, S}) where {T, S} parent(a) != parent(b) && error("Parents don't match.") + if _is_sparse(a) + return a * b + end A = parent(a) d = dim(A) @@ -449,7 +497,11 @@ divexact_left(a::AbstractAssociativeAlgebraElem, b::AbstractAssociativeAlgebraEl ################################################################################ function *(a::AbstractAssociativeAlgebraElem{S}, b::S) where {S <: RingElem} - return typeof(a)(parent(a), coefficients(a, copy = false).* Ref(b)) + if !_is_sparse(a) + return typeof(a)(parent(a), coefficients(a, copy = false).* Ref(b)) + else + return typeof(a)(parent(a), a.coeffs_sparse * b) + end end *(b::S, a::AbstractAssociativeAlgebraElem{S}) where {S <: RingElem} = a*b @@ -630,7 +682,21 @@ end #end function (A::GroupAlgebra{T, S, R})(c::R) where {T, S, R} - return GroupAlgebraElem{T, typeof(A)}(A, deepcopy(c)) + return GroupAlgebraElem{T, typeof(A)}(A, c) +end + +function (A::GroupAlgebra{T, S, R})(d::Dict{R, <: Any}) where {T, S, R} + K = base_ring(A) + dd = sparse_row(base_ring(A), [(__elem_index(A, g), K(i)) for (g, i) in d]) + if _is_dense(A) + return GroupAlgebraElem{T, typeof(A)}(A, Vector(dd, dim(A))) + else + return GroupAlgebraElem{T, typeof(A)}(A, dd) + end +end + +function (A::GroupAlgebra{T, S, R})(x0::Pair{R, <: Any}, x::Vararg{U}) where {T, S, R, U <: Pair{R, <: Any}} + return A(Dict(x0, x...)) end # Generic.Mat needs it @@ -676,10 +742,23 @@ function show(io::IO, a::AbstractAssociativeAlgebraElem) if get(io, :compact, false) print(io, coefficients(a, copy = false)) else - print(io, "Element of ") - print(io, parent(a)) - print(io, " with coefficients ") - print(io, coefficients(a, copy = false)) + if _is_sparse(a) + sum = Expr(:call, :+) + if !iszero(a) + for (i, ci) in a.coeffs_sparse + push!(sum.args, + Expr(:call, :*, AbstractAlgebra.expressify(ci, context = io), + AbstractAlgebra.expressify(parent(a).base_to_group[i], context = IOContext(io, :compact => true)))) + end + end + print(io, AbstractAlgebra.expr_to_string(AbstractAlgebra.canonicalize(sum))) + else + ve = Expr(:vect) + for ci in coefficients(a, copy = false) + push!(ve.args, AbstractAlgebra.expressify(ci, context = io)) + end + print(io, AbstractAlgebra.expr_to_string(AbstractAlgebra.canonicalize(ve))) + end end end @@ -721,7 +800,11 @@ end function ==(a::AbstractAssociativeAlgebraElem{T}, b::AbstractAssociativeAlgebraElem{T}) where {T} parent(a) != parent(b) && return false - return coefficients(a, copy = false) == coefficients(b, copy = false) + if !_is_sparse(a) + return coefficients(a, copy = false) == coefficients(b, copy = false) + else + return a.coeffs_sparse == b.coeffs_sparse + end end ################################################################################ @@ -963,6 +1046,9 @@ end isone(a::AbstractAssociativeAlgebraElem) = a == one(parent(a)) function iszero(a::AbstractAssociativeAlgebraElem) + if _is_sparse(a) + return length(a.coeffs_sparse) == 0 + end return all(i -> iszero(i), coefficients(a, copy = false)) end diff --git a/src/AlgAss/Types.jl b/src/AlgAss/Types.jl index d69398e353..1da1e9a9a6 100644 --- a/src/AlgAss/Types.jl +++ b/src/AlgAss/Types.jl @@ -130,6 +130,15 @@ end @attributes mutable struct GroupAlgebra{T, S, R} <: AbstractAssociativeAlgebra{T} base_ring::Ring group::S + # We represent elements using a coefficient vector (which can be either + # sparse or dense), + # so all we have to keep track of is which group element corresponds to + # which basis element of the algebra + # This is what group_to_base, base_to_group are for. They realize the map + # G -> {1,...,n} + # {1,...n} -> G + # (In the sparse version, this map is constructed on demand) + group_to_base::Dict{R, Int} base_to_group::Vector{R} one::Vector{T} @@ -146,77 +155,113 @@ end maps_to_numberfields maximal_order - function GroupAlgebra(K::Ring, G::FinGenAbGroup, cached::Bool = true) - A = GroupAlgebra(K, G, op = +, cached = cached) + # For the sparse presentation + sparse::Bool + ind::Int # This is the number of group elements currently stored in + # group_to_base and base_to_group. + sparse_one # Store the sparse row for the one element + + function GroupAlgebra(K::Ring, G::FinGenAbGroup, cached::Bool = true, sparse::Bool = false) + A = GroupAlgebra(K, G; op = +, cached = cached, sparse = sparse) A.is_commutative = true return A end - function GroupAlgebra(K::Ring, G; op = *, cached = true) - return get_cached!(GroupAlgebraID, (K, G, op), cached) do + function GroupAlgebra(K::Ring, G; op = *, cached::Bool = true, sparse::Bool = false) + return get_cached!(GroupAlgebraID, (K, G, op, sparse), cached) do A = new{elem_type(K), typeof(G), elem_type(G)}() + A.sparse = sparse A.is_commutative = 0 A.is_simple = 0 A.issemisimple = 0 A.base_ring = K A.group = G - d = Int(order(G)) A.group_to_base = Dict{elem_type(G), Int}() - A.base_to_group = Vector{elem_type(G)}(undef, d) - A.mult_table = zeros(Int, d, d) - - i = 2 - for g in collect(G) - if isone(g) - A.group_to_base[deepcopy(g)] = 1 - A.base_to_group[1] = deepcopy(g) - continue - end - A.group_to_base[deepcopy(g)] = i - A.base_to_group[i] = deepcopy(g) - i += 1 + if !sparse + @assert is_finite(G) + d = order(Int, G) + A.base_to_group = Vector{elem_type(G)}(undef, d) + else + A.base_to_group = Vector{elem_type(G)}(undef, 1) end - v = Vector{elem_type(K)}(undef, d) - for i in 1:d - v[i] = zero(K) - end - v[1] = one(K) + if A.sparse + if G isa FinGenAbGroup + el = zero(G) + else + el = one(G) + end + A.group_to_base[el] = 1 + A.base_to_group[1] = el + A.sparse_one = sparse_row(K, [1], [one(K)]) + else + # dense + A.mult_table = zeros(Int, d, d) + i = 2 + for g in collect(G) + if isone(g) + A.group_to_base[deepcopy(g)] = 1 + A.base_to_group[1] = deepcopy(g) + continue + end + A.group_to_base[deepcopy(g)] = i + A.base_to_group[i] = deepcopy(g) + i += 1 + end - A.one = v + v = Vector{elem_type(K)}(undef, d) + for i in 1:d + v[i] = zero(K) + end + v[1] = one(K) + + A.one = v - for i = 1:d - for j = 1:d - l = op(A.base_to_group[i], A.base_to_group[j]) - A.mult_table[i, j] = A.group_to_base[l] + for i = 1:d + for j = 1:d + l = op(A.base_to_group[i], A.base_to_group[j]) + A.mult_table[i, j] = A.group_to_base[l] + end end - end - @assert all(A.mult_table[1, i] == i for i in 1:dim(A)) + @assert all(A.mult_table[1, i] == i for i in 1:dim(A)) + end return A end::GroupAlgebra{elem_type(K), typeof(G), elem_type(G)} end end -const GroupAlgebraID = AbstractAlgebra.CacheDictType{Tuple{Ring, Any, Any}, GroupAlgebra}() +const GroupAlgebraID = AbstractAlgebra.CacheDictType{Tuple{Ring, Any, Any, Bool}, GroupAlgebra}() mutable struct GroupAlgebraElem{T, S} <: AbstractAssociativeAlgebraElem{T} parent::S coeffs::Vector{T} + coeffs_sparse function GroupAlgebraElem{T, S}(A::S) where {T, S} z = new{T, S}() z.parent = A - z.coeffs = Vector{T}(undef, size(A.mult_table, 1)) - for i = 1:length(z.coeffs) - z.coeffs[i] = A.base_ring() + if !A.sparse + z.coeffs = Vector{T}(undef, size(A.mult_table, 1)) + for i = 1:length(z.coeffs) + z.coeffs[i] = A.base_ring() + end + else + z.coeffs_sparse = sparse_row(base_ring(A)) end return z end function GroupAlgebraElem{T, S}(A::S, g::U) where {T, S, U} - return A[A.group_to_base[g]] + if A.sparse + i = __elem_index(A, g) + a = GroupAlgebraElem{T, S}(A) + a.coeffs_sparse = sparse_row(base_ring(A), [i], [one(base_ring(A))]) + return a + else + return A[A.group_to_base[g]] + end end # This does not make a copy of coeffs @@ -226,8 +271,20 @@ mutable struct GroupAlgebraElem{T, S} <: AbstractAssociativeAlgebraElem{T} z.coeffs = coeffs return z end + + function GroupAlgebraElem{T, S}(A::S, coeffs::SRow{T}) where {T, S} + z = new{T, S}() + z.parent = A + z.coeffs_sparse = coeffs + return z + end end +__elem_index(A, g) = get!(A.group_to_base, g) do + push!(A.base_to_group, g) + return length(A.base_to_group) + end + ################################################################################ # # AbsAlgAssIdl diff --git a/src/Grp/GenGrp.jl b/src/Grp/GenGrp.jl index 3222ce0730..e8d9d12c72 100644 --- a/src/Grp/GenGrp.jl +++ b/src/Grp/GenGrp.jl @@ -199,6 +199,8 @@ end # ################################################################################ +is_finite(::MultTableGroup) = true + elem_type(::Type{MultTableGroup}) = MultTableGroupElem Base.hash(G::MultTableGroupElem, h::UInt) = Base.hash(G.i, h) @@ -256,10 +258,12 @@ end # ################################################################################ -function order(G::MultTableGroup) +function order(::Type{Int}, G::MultTableGroup) return size(G.mult_table, 1) end +order(G::MultTableGroup) = order(Int, G) + length(G::MultTableGroup) = order(G) ################################################################################ diff --git a/src/GrpAb/GrpAbFinGen.jl b/src/GrpAb/GrpAbFinGen.jl index a84c70118f..e0431f8627 100644 --- a/src/GrpAb/GrpAbFinGen.jl +++ b/src/GrpAb/GrpAbFinGen.jl @@ -586,6 +586,8 @@ function order(A::FinGenAbGroup) return prod(elementary_divisors(A)) end +order(::Type{Int}, A::FinGenAbGroup) = Int(order(A)) + ################################################################################ # # Exponent diff --git a/test/AlgAss/AlgGrp.jl b/test/AlgAss/AlgGrp.jl index 4b41f9cd2f..d5f923a1b7 100644 --- a/test/AlgAss/AlgGrp.jl +++ b/test/AlgAss/AlgGrp.jl @@ -73,4 +73,35 @@ end end end + + # abelian groups + + QG = group_algebra(QQ, abelian_group([2, 2])) + @test QG isa GroupAlgebra + @test QG !== Hecke._group_algebra(QQ, abelian_group([2, 2]); cached = false) + @test QG !== Hecke._group_algebra(QQ, abelian_group([2, 2]); sparse = true) + + QG = Hecke._group_algebra(QQ, abelian_group([2 for i in 1:10]); sparse = true) + @test QG isa GroupAlgebra + @test QG !== Hecke._group_algebra(QQ, abelian_group([2 for i in 1:10]); sparse = true, cached = false) + + # test sparse arithmetic + + let + G = SymmetricGroup(10) + QG = Hecke._group_algebra(QQ, G; sparse = true, cached = false) + for i in 1:10 + a = rand(G) + b = rand(G) + c = a * b + d = b * a + aa = QG(a) + bb = QG(b) + cc = QG(c) + dd = QG(d) + @test aa * bb == cc + @test bb * aa == dd + @test (aa + bb)^2 == QG(a)^2 + cc + dd + QG(b)^2 + end + end end diff --git a/test/AlgAss/Elem.jl b/test/AlgAss/Elem.jl index b08b5eb9af..4a7150a8b5 100644 --- a/test/AlgAss/Elem.jl +++ b/test/AlgAss/Elem.jl @@ -85,4 +85,13 @@ Hecke.add!(b,b) @test b == A(matrix(QQ, [6 8; 10 12])) end + + # fancy group algebra element constructor + G = abelian_group([2, 2]); a = G([0, 1]); + QG = Hecke._group_algebra(QQ, G; sparse = false); + @test QG(Dict(a => 2, zero(G) => 1)) == 2 * QG(a) + 1 * QG(zero(G)) + @test QG(a => ZZ(2), zero(G) => QQ(1)) == 2 * QG(a) + 1 * QG(zero(G)) + QG = Hecke._group_algebra(QQ, G; sparse = true); + @test QG(Dict(a => 2, zero(G) => 1)) == 2 * QG(a) + 1 * QG(zero(G)) + @test QG(a => ZZ(2), zero(G) => QQ(1)) == 2 * QG(a) + 1 * QG(zero(G)) end