From 6bb57f6976b23c1485f88bbe52116438ca4598ee Mon Sep 17 00:00:00 2001 From: Claus Fieker Date: Tue, 11 Jan 2022 16:34:49 +0100 Subject: [PATCH] add stuff to compute representations for PcGroups (any characteristic) This should work (possibly after the UngradeModule fix) ```@julia G = small_group(24, 12) z = Oscar.RepPc.reps(abelian_closure(QQ)[1], G) z = Oscar.RepPc.reps(cyclotomic_field(24)[1], G) z = Oscar.RepPc.reps(cyclotomic_field(4)[1], G) z = Oscar.RepPc.reps(FiniteField(3, 10)[1], G) ``` --- experimental/GModule/Cohomology.jl | 61 +++++++++--- experimental/GModule/GModule.jl | 151 +++++++++++++++++++++++++++-- src/Rings/AbelianClosure.jl | 2 +- 3 files changed, 196 insertions(+), 18 deletions(-) diff --git a/experimental/GModule/Cohomology.jl b/experimental/GModule/Cohomology.jl index 4e93f1be1f1a..1c79751be56e 100644 --- a/experimental/GModule/Cohomology.jl +++ b/experimental/GModule/Cohomology.jl @@ -5,21 +5,25 @@ import Oscar:action import AbstractAlgebra: Group, Module import Base: parent -#TODO: rename into GModule @attributes mutable struct GModule{gT,mT} G::gT M::mT ac::Vector{Map} # automorphisms of M, one for each generator of G - function GModule(G::T, ac::Vector{<:Map}) where {T <: Oscar.GAPGroup} - r = new{T,typeof(domain(ac[1]))}() + function GModule(M, G::T, ac::Vector{<:Map}) where {T <: Oscar.GAPGroup} + r = new{T,typeof(M)}() r.G = G r.ac = ac - r.M = domain(ac[1]) + r.M = M @assert all(x -> domain(x) == codomain(x) == r.M, ac) return r end + + function GModule(G::T, ac::Vector{<:Map}) where {T <: Oscar.GAPGroup} + return GModule(domain(ac[1]), G, ac) + end + F::Group # G as an Fp-group (if set) mF::GAPGroupHomomorphism # F -> G, maps F[i] to G[i] @@ -62,6 +66,21 @@ function Oscar.relations(F::FPGroup) return [(x, z) for x = R] end +function Oscar.relations(G::Oscar.GAPGroup) + f = GAP.Globals.IsomorphismFpGroupByGenerators(G.X, GAP.Globals.GeneratorsOfGroup(G.X)) + f !=GAP.Globals.fail || throw(ArgumentError("Could not convert group into a group of type FPGroup")) + H = FPGroup(GAP.Globals.Image(f)) + return relations(H) +end + +function Oscar.relations(G::PcGroup) + f = GAP.Globals.IsomorphismFpGroupByPcgs(GAP.Globals.FamilyPcgs(G.X), GAP.julia_to_gap("g")) + f !=GAP.Globals.fail || throw(ArgumentError("Could not convert group into a group of type FPGroup")) + H = FPGroup(GAP.Globals.Image(f)) + return relations(H) +end + + AbstractAlgebra.Group(C::GModule) = C.G AbstractAlgebra.Module(C::GModule) = C.M action(C::GModule) = C.ac @@ -183,6 +202,22 @@ function H_zero(C::GModule) return k, z end +#= TODO + - break out coboundaries and cochains + - depending on the module type: + - intersect yields an embedding (Z-module) or not GrpAb + - make sure that image/ kernel are consisten + - preimage + - issubset yields (for GrpAb) only true/ false, not the map + - issubgroup has the "wrong" order of arguments (and cannot apply + to modules) + - quo does ONLY work if B is a direct submodule of A (Z-modules) + - mat or matrix is used to get "the matrix" from a hom + - zero_hom/ zero_obj/ identity_hom is missing + - Janko-Module-Homs have different types, they probably need to + come under a common abstract type or be more selective +=# + function H_one(C::GModule) z = get_attribute(C, :H_one) if z !== nothing @@ -197,8 +232,8 @@ function H_one(C::GModule) X(r_i) corresponds to some map phi_i : M^r -> M phi_i = oplus h_j M for some homs h_j coming from the word in r so, a kernel computation again - =# + G = Group(C) n = ngens(G) M = Module(C) @@ -407,6 +442,11 @@ function Oscar.mat(M::FreeModuleHom{FreeMod{QabElem}, FreeMod{QabElem}}) return M.matrix end +#= Hulpke-Dietrich: +UNIVERSAL COVERS OF FINITE GROUPS +https://arxiv.org/pdf/1910.11453.pdf +almost the same as Holt +=# function H_two(C::GModule) z = get_attribute(C, :H_two) if z !== nothing @@ -693,7 +733,7 @@ function H_two(C::GModule) end """ -For a cohomology module `C` compute the `i`-th cohomology group +For a gmodule `C` compute the `i`-th cohomology group where `i` can be `0`, `1` or `2`. Together with the abstract module, a map is provided that will produce explicit cochains. @@ -817,6 +857,9 @@ Note: we do not check that this defined indeed a `ZZ[H]` module. function gmodule(H::Oscar.GAPGroup, ac::Vector{<:Map}) return GModule(H, ac) end +function gmodule(M, H::Oscar.GAPGroup, ac::Vector{<:Map}) + return GModule(M, H, ac) +end function _gmodule(k::AnticNumberField, H::PermGroup, mu::Map{GrpAbFinGen, FacElemMon{AnticNumberField}}) u = domain(mu) @@ -927,7 +970,7 @@ Oscar.group(C::GModule) = C.G #= TODO for Z, Z/nZ, F_p and F_q moduln -> find Fp-presentation - #done: for GrpAbFinGen -> find Fp-presentation + #done: for GrpAbFinGen -> find Fp-presentation #done: for a in H^2 find Fp-presentation for a in H^2 find (low degree) perm group using the perm group we have? Magma's DistinctExtensions @@ -938,9 +981,6 @@ Sort: - move (and fix) the ModuleHom stuff - add proper quo for Modules - group better: the GModule should - - cache the H^i's that are computed - features - inflation, restriction, long exact sequence @@ -954,7 +994,6 @@ Sort: - understand Derek Holt and use BSGS for large perm groups rather than the RWS (or use BSGS to get an RWS?) - GModule for - local field (add (trivial) and mult) - (S-)units diff --git a/experimental/GModule/GModule.jl b/experimental/GModule/GModule.jl index 15cf47838a9f..9dd5173d1ea8 100644 --- a/experimental/GModule/GModule.jl +++ b/experimental/GModule/GModule.jl @@ -28,8 +28,14 @@ function irreducible_modules(G::Oscar.GAPGroup) K = abelian_closure(QQ)[1] for m in im z = map(x->matrix(map(y->map(K, y), m(x.X))), gens(G)) - F = free_module(K, nrows(z[1])) - push!(IM, gmodule(G, map(x->hom(F, F, x), z))) + if ngens(G) == 0 + F = free_module(K, 0) + zz = typeof(hom(F, F, elem_type(F)[]))[] + else + F = free_module(K, nrows(z[1])) + zz = map(x->hom(F, F, x), z) + end + push!(IM, gmodule(F, G, zz)) end return IM end @@ -116,9 +122,13 @@ function gmodule(::typeof(CyclotomicField), C::GModule) for g = C.ac l = lcm(l, lcm(collect(map_entries(x->Hecke.iscyclotomic_type(parent(x.data))[2], mat(g))))) end - K = cyclotomic_field(l, cached = false)[1] + K = cyclotomic_field(base_ring(C), l)[1] F = free_module(K, dim(C)) - return gmodule(group(C), [hom(F, F, map_entries(x->K(x.data), mat(x))) for x = C.ac]) + if d == 0 + h = hom(F, F, elem_type(F)[]) + return gmodule(F, group(C), typeof(h)[hom(F, F, map_entries(x->K(x.data), mat(x))) for x = C.ac]) + end + return gmodule(F, group(C), [hom(F, F, map_entries(x->K(x.data), mat(x))) for x = C.ac]) end import Base: ^ @@ -133,12 +143,12 @@ end function ^(C::GModule{<:Any, Generic.FreeModule{QabElem}}, phi::Map{QabField, QabField}) F = free_module(codomain(phi), dim(C)) - return GModule(group(C), [hom(F, F, map_entries(phi, mat(x))) for x = C.ac]) + return GModule(F, group(C), [hom(F, F, map_entries(phi, mat(x))) for x = C.ac]) end function gmodule(::FlintRationalField, C::GModule{<:Any, Generic.FreeModule{nf_elem}}) F = free_module(QQ, dim(C)*degree(base_ring(C))) - return GModule(group(C), [hom(F, F, hvcat(dim(C), [representation_matrix(x) for x = transpose(mat(y))]...)) for y = C.ac]) + return GModule(F, group(C), [hom(F, F, hvcat(dim(C), [representation_matrix(x) for x = transpose(mat(y))]...)) for y = C.ac]) end function gmodule(k::Nemo.GaloisField, C::GModule{<:Any, Generic.FreeModule{fmpq}}) @@ -245,6 +255,9 @@ function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{ push!(t, hom_base(z1[i], z2[i])) end tt = [Hecke.modular_lift([t[i][j] for i=1:length(z1)], me) for j=1:length(t[1])] + if length(tt) == 0 + return [] + end @assert base_ring(tt[1]) == k if isone(pp) pp = fmpz(p) @@ -321,6 +334,39 @@ function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{ end end +function gmodule(K::AnticNumberField, M::GModule{<:Any, <:Generic.FreeModule{nf_elem}}) + F = free_module(K, dim(M)) + return gmodule(F, group(M), [hom(F, F, map_entries(K, mat(x))) for x = M.ac]) +end + +function (K::QabField)(a::nf_elem) + fl, f = Hecke.iscyclotomic_type(parent(a)) + @assert fl + return QabElem(a, f) +end + +function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{QabElem}} + C1 = gmodule(CyclotomicField, C) + D1 = gmodule(CyclotomicField, D) + fl, Cf = Hecke.iscyclotomic_type(base_ring(C1)) + @assert fl + fl, Df = Hecke.iscyclotomic_type(base_ring(D1)) + @assert fl + l = lcm(Cf, Df) + K, _ = cyclotomic_field(base_ring(C), l) + if l != Cf + C1 = gmodule(K, C1) + end + if l != Df + D1 = gmodule(K, D1) + end + h = hom_base(C1, D1) + if length(h) == 0 + return h + end + return map(x->map_entries(base_ring(C), x), h) +end + function gmodule(::FlintRationalField, C::GModule{<:Any, <:Generic.FreeModule{fmpz}}) F = free_module(QQ, dim(C)) return GModule(group(C), [hom(F, F, map_entries(QQ, mat(x))) for x = C.ac]) @@ -518,3 +564,96 @@ end #module GModuleFromGap using .GModuleFromGap export irreducible_modules, isabsolutely_irreducible, isdecomposable + +module RepPc +using Oscar + +Base.pairs(M::MatElem) = Base.pairs(IndexCartesian(), M) +Base.pairs(::IndexCartesian, M::MatElem) = Base.Pairs(M, CartesianIndices(axes(M))) + +function Hecke.roots(a::fq_nmod, i::Int) + kx, x = PolynomialRing(parent(a), cached = false) + return roots(x^i-a) +end + +#=TODO + - construct characters along the way as well? + - compare characters rather than the hom_base + - maybe reason from theory what reps are going to be new? + - conjugate to smallest field? + - allow trivial stuff +=# + +function reps(K, G::PcGroup) + s, ms = sub(G, [gens(G)[end]]) + o = Int(order(s)) + @assert isprime(o) + z = roots(K(1), o) + F = free_module(K, 1) + R = [gmodule(F, s, [hom(F, F, [r*F[1]])]) for r = z] + + for i=ngens(G)-1:-1:1 + h = G[i] + ns, mns = sub(G, gens(G)[i:end]) + p = Int(divexact(order(ns), order(s))) + @assert isprime(p) + new_R = [] + for r = R + F = r.M + rh = gmodule(group(r), [action(r, preimage(ms, x^h)) for x = gens(s)]) + l = Oscar.GModuleFromGap.hom_base(r, rh) + @assert length(l) <= 1 + nr = [] + Y = mat(action(r, preimage(ms, h^p))) + if length(l) == 1 + X = l[1] + Xp = X^p + #Brueckner: C*Xp == Y for some scalar C + i = findfirst(x->!iszero(x), Xp) + @assert !iszero(Y[i]) + C = divexact(Y[i], Xp[i]) + @assert C*Xp == Y + # I think they should always be roots of one here. + rt = roots(C, p) + Y = r.ac + for x = rt + nw = gmodule(F, ns, vcat([hom(F, F, x*X)], Y)) + push!(nr, nw) + end + else #need to extend dim + n = dim(r) + F = free_module(K, dim(r)*p) + z = zero_matrix(K, dim(F), dim(F)) + z[(p-1)*n+1:end, 1:n] = Y + for i=1:p-1 + z[(i-1)*n+1:i*n, i*n+1:(i+1)*n] = identity_matrix(K, n) + end + md = [hom(F, F, z)] + for g = gens(s) + z = zero_matrix(K, dim(F), dim(F)) + for j=1:p + Y = action(r, g) + z[(j-1)*n+1:j*n, (j-1)*n+1:j*n] = mat(Y) + g = preimage(ms, g^h) + end + push!(md, hom(F, F, z)) + end + push!(nr, gmodule(F, ns, md)) + end + for nw = nr + if any(x->length(Oscar.GModuleFromGap.hom_base(x, nw))>0, new_R) + continue + else + push!(new_R, nw) + end + end + end + s, ms = ns, mns + R = new_R + end + return R +end + +end #module RepPc + +using .RepPc diff --git a/src/Rings/AbelianClosure.jl b/src/Rings/AbelianClosure.jl index 01c4d8034a3a..be4c74dd4411 100644 --- a/src/Rings/AbelianClosure.jl +++ b/src/Rings/AbelianClosure.jl @@ -119,7 +119,7 @@ _variable(K::QabField) = K.s _variable(b::QabElem) = Expr(:call, Symbol(_variable(_Qab)), b.c) -function cyclotomic_field(K::QabField, c::Int) +function Hecke.cyclotomic_field(K::QabField, c::Int) if haskey(K.fields, c) k = K.fields[c] return k, gen(k)