diff --git a/experimental/GModule/Brueckner.jl b/experimental/GModule/Brueckner.jl index bbade84032c0..dfaa43e8fa15 100644 --- a/experimental/GModule/Brueckner.jl +++ b/experimental/GModule/Brueckner.jl @@ -33,7 +33,7 @@ function reps(K, G::Oscar.GAPGroup) if order(G) == 1 F = free_module(K, 1) h = hom(F, F, [F[1]]) - return [gmodule(F, G, typeof(h)[])] + return [gmodule(F, G, typeof(h)[h for i = gens(G)])] end pcgs = GAP.Globals.Pcgs(G.X) @@ -293,7 +293,7 @@ function Oscar.cokernel(h::Map) return quo(codomain(h), image(h)[1]) end -function Base.iterate(M::Union{Generic.FreeModule{T}, Generic.Submodule{T}}) where T <: FinFieldElem +function Base.iterate(M::AbstractAlgebra.FPModule{T}) where T <: FinFieldElem k = base_ring(M) if dim(M) == 0 return zero(M), iterate([1]) @@ -303,11 +303,11 @@ function Base.iterate(M::Union{Generic.FreeModule{T}, Generic.Submodule{T}}) whe return M(elem_type(k)[f[1][i] for i=1:dim(M)]), (f[2], p) end -function Base.iterate(::Union{Generic.FreeModule{fqPolyRepFieldElem}, Generic.Submodule{fqPolyRepFieldElem}}, ::Tuple{Int64, Int64}) +function Base.iterate(::AbstractAlgebra.FPModule{fqPolyRepFieldElem}, ::Tuple{Int64, Int64}) return nothing end -function Base.iterate(M::Union{Generic.FreeModule{T}, Generic.Submodule{T}}, st::Tuple{<:Tuple, <:Base.Iterators.ProductIterator}) where T <: FinFieldElem +function Base.iterate(M::AbstractAlgebra.FPModule{T}, st::Tuple{<:Tuple, <:Base.Iterators.ProductIterator}) where T <: FinFieldElem n = iterate(st[2], st[1]) if n === nothing return n @@ -315,14 +315,20 @@ function Base.iterate(M::Union{Generic.FreeModule{T}, Generic.Submodule{T}}, st: return M(elem_type(base_ring(M))[n[1][i] for i=1:dim(M)]), (n[2], st[2]) end -function Base.length(M::Union{Generic.FreeModule{T}, Generic.Submodule{T}}) where T <: FinFieldElem +function Base.length(M::AbstractAlgebra.FPModule{T}) where T <: FinFieldElem return Int(order(base_ring(M))^dim(M)) end -function Base.eltype(M::Union{Generic.FreeModule{T}, Generic.Submodule{T}}) where T <: FinFieldElem +function Base.eltype(M::AbstractAlgebra.FPModule{T}) where T <: FinFieldElem return elem_type(M) end +function Oscar.dim(M::AbstractAlgebra.Generic.DirectSumModule{<:FieldElem}) + return sum(dim(x) for x = M.m) +end + +Oscar.is_finite(M::AbstractAlgebra.FPModule{<:FinFieldElem}) = true + """ mp: G ->> Q C a F_p[Q]-module @@ -389,27 +395,30 @@ function lift(C::GModule, mp::Map) #projection of G. They are not all surjective. However, lets try: k, mk = kernel(s) allG = [] - z = get_attribute(C, :H_two)[1] + z = get_attribute(C, :H_two)[1] #tail (H2) -> cochain seen = Set{Tuple{elem_type(D), elem_type(codomain(mH2))}}() #TODO: the projection maps seem to be rather slow - in particular # as they SHOULD be trivial... for x = k epi = pDE[1](mk(x)) #the map - chn = pDE[2](mk(x)) #the tail data - if (epi,mH2(chn)) in seen + chn = mH2(pDE[2](mk(x))) #the tail data + if (epi,chn) in seen continue else - push!(seen, (epi, mH2(chn))) + push!(seen, (epi, chn)) end #TODO: not all "chn" yield distinct groups - the factoring by the # co-boundaries is missing # not all "epi" are epi, ie. surjective. The part of the thm # is missing... # (Thm 15, part b & c) (and the weird lemma) - @hassert :BruecknerSQ 2 all(x->all(y->sc(x, y)(chn) == last_c(x, y), gens(N)), gens(N)) +# @hassert :BruecknerSQ 2 all(x->all(y->sc(x, y)(chn) == last_c(x, y), gens(N)), gens(N)) + + @hassert :BruecknerSQ 2 preimage(z, z(chn)) == chn GG, GGinj, GGpro, GMtoGG = Oscar.GrpCoh.extension(PcGroup, z(chn)) + @assert is_surjective(GGpro) if get_assert_level(:BruecknerSQ) > 1 _GG, _ = Oscar.GrpCoh.extension(z(chn)) @assert is_isomorphic(GG, _GG) @@ -432,13 +441,15 @@ function lift(C::GModule, mp::Map) return d end l= [GMtoGG(reduce(gen(G, i)), pro[i](epi)) for i=1:ngens(G)] +# @show map(order, l), order(prod(l)) +# @show map(order, gens(G)), order(prod(gens(G))) - h = hom(G, GG, gens(G), [GMtoGG(reduce(gen(G, i)), pro[i](epi)) for i=1:ngens(G)]) + h = hom(G, GG, gens(G), l) if !is_surjective(h) - @show :darn +# @show :darn continue else - @show :bingo +# @show :bingo end push!(allG, h) end diff --git a/experimental/GModule/Cohomology.jl b/experimental/GModule/Cohomology.jl index 4de991e96f30..e61cc5a1ebf1 100644 --- a/experimental/GModule/Cohomology.jl +++ b/experimental/GModule/Cohomology.jl @@ -269,7 +269,7 @@ The induced module is returned as a product of copies of C. it also returns homomorphism. I this case a Z[G] linear map to the induced module is returned. """ -function induce(C::GModule, h::Map, D = nothing, mDC = nothing) +function induce(C::GModule{<:Oscar.GAPGroup, GrpAbFinGen}, h::Map, D = nothing, mDC = nothing) U = domain(h) G = codomain(h) @assert U == C.G @@ -337,6 +337,12 @@ function induce(C::GModule, h::Map, D = nothing, mDC = nothing) return iC, h end +function Oscar.quo(C::GModule{<:Any, <:Generic.FreeModule}, mDC::Generic.ModuleHomomorphism) + q, mq = Oscar.quo(C.M, image(mDC)[1]) + S = GModule(C.G, [hom(q, q, [mq(x(preimage(mq, t))) for t = gens(q)]) for x = C.ac]) + return S, mq +end + function Oscar.quo(C::GModule, mDC::Map{GrpAbFinGen, GrpAbFinGen}) q, mq = Oscar.quo(C.M, image(mDC)[1]) S = GModule(C.G, [GrpAbFinGenMap(pseudo_inv(mq)*x*mq) for x = C.ac]) @@ -346,6 +352,11 @@ function Oscar.quo(C::GModule, mDC::Map{GrpAbFinGen, GrpAbFinGen}) return S, mq end +function Oscar.direct_sum(M::AbstractAlgebra.Generic.DirectSumModule{T}, N::AbstractAlgebra.Generic.DirectSumModule{T}, mp::Vector{AbstractAlgebra.Generic.ModuleHomomorphism{T}}) where T + @assert length(M.m) == length(mp) == length(N.m) + return hom(M, N, cat(map(mat, mp)..., dims = (1,2))) +end + function Oscar.direct_product(C::GModule...; task::Symbol = :none) @assert task in [:sum, :prod, :both, :none] G = C[1].G @@ -366,6 +377,76 @@ function Oscar.direct_product(C::GModule...; task::Symbol = :none) end end +Oscar.direct_sum(C::GModule...; task::Symbol = :none) = Oscar.direct_product(C...; task = task) + +import Hecke.⊕ +⊕(C::GModule...) = Oscar.direct_sum(C...; task = :none) + +function Oscar.tensor_product(C::GModule{<:Any, GrpAbFinGen}...; task::Symbol = :map) + @assert all(x->x.G == C[1].G, C) + + T, mT = Oscar.tensor_product([x.M for x = C]...; task = :map) + TT = gmodule(T, C[1].G, [hom(T, T, [action(C[i], g) for i=1:length(C)]) for g = gens(C[1].G)]) + if task == :map + return TT, mT + else + return TT + end +end + +function Oscar.tensor_product(C::GModule{S, M}...; task::Symbol = :map) where S <: Oscar.GAPGroup where M <: AbstractAlgebra.Generic.FreeModule{<:Any} + @assert all(x->x.G == C[1].G, C) + @assert all(x->base_ring(x) == base_ring(C[1]), C) + + T, mT = Oscar.tensor_product([x.M for x = C]...; task = :map) + #this is slow. It should be done like in abelian groups using + #the tensor product of the maps + TT = gmodule(T, C[1].G, [hom(T, T, [mT(Tuple(action(C[i], g, preimage(mT, t)[i]) for i=1:length(C))) for t = gens(T)] ) for g = gens(C[1].G)]) + if task == :map + return TT, mT + else + return TT + end +end + +import Hecke.⊗ +⊗(C::GModule...) = Oscar.tensor_product(C...; task = :none) + +function Oscar.tensor_product(F::Generic.FreeModule{T}...; task = :none) where {T} + @assert all(x->base_ring(x) == base_ring(F[1]), F) + d = prod(rank(x) for x = F) + G = free_module(base_ring(F[1]), d) + if task == :none + return G + end + + g = vec(collect(Base.Iterators.ProductIterator(Tuple(gens(g) for g = reverse(F))))) + + function pure(g::Generic.FreeModuleElem...) + @assert length(g) == length(F) + @assert all(i-> parent(g[i]) == F[i], 1:length(F)) + + return G(vec(collect(prod(x) for x = Base.Iterators.product([h.v for h = reverse(g)]...)))) + end + function pure(T::Tuple) + return pure(T...) + end + + function inv_pure(t::Generic.FreeModuleElem) + p = Base.findall(i -> !iszero(t[i]), 1:ngens(G)) + if length(p) == 0 + return Tuple(collect(zero(g) for g = F)) + end + @assert length(p) == 1 + @assert t[p[1]] == 1 + return reverse(g[p[1]]) + end + + return G, MapFromFunc(Hecke.TupleParent(Tuple([zero(g) for g = F])), G, pure, inv_pure) +end + +⊗(F::Generic.FreeModule...) = Oscar.tensor_product(F...; task = :none) + function Oscar.restrict(C::GModule, U::Oscar.GAPGroup) fl, m = is_subgroup(U, C.G) @assert fl @@ -385,7 +466,8 @@ end export GModule, gmodule, word, fp_group, confluent_fp_group export action, cohomology_group, extension, pc_group -export induce, is_consistent, istwo_cocycle +export induce, is_consistent, istwo_cocycle, all_extensions +export split_extension, extension_with_abelian_kernel Oscar.dim(C::GModule) = rank(C.M) Oscar.base_ring(C::GModule) = base_ring(C.M) @@ -485,6 +567,18 @@ Oscar.Nemo.elem_type(::Type{AllCoChains{N,G,M}}) where {N,G,M} = CoChain{N,G,M} Oscar.Nemo.parent_type(::CoChain{N,G,M}) where {N,G,M}= AllCoChains{N,G,M} Oscar.parent(::CoChain{N,G,M}) where {N, G, M} = AllCoChains{N, G, M}() +function differential(C::CoChain{N, G, M}) where {N, G, M} + n = N+1 + d = [] + for g = Iterators.ProductIterator(Tuple([C.C.G for i=1:n])) + v = action(C.C, g[n], C(Tuple(g[i] for i=1:n-1))) + + sum((-1)^i* C(Tuple(vcat([g[j] for j=1:i-1], [g[i]*g[i+1]], [g[j] for j=i+2:n]))) for i=1:n-1; init = zero(C.C.M)) + + (-1)^n*C(Tuple(g[i] for i=2:n)) + push!(d, g=>v) + end + return CoChain{n, G, M}(C.C, Dict(d)) +end + function action(C::CoChain, g::PermGroupElem) C = deepcopy(C) for x = keys(C.d) @@ -498,6 +592,7 @@ end Evaluate a 0-cochain """ (C::CoChain{0})() = first(values(C.d)) +(C::CoChain{0})(::Tuple{}) = first(values(C.d)) #TODO: should this rather be a map from a 1-tuple of group elements? """ @@ -569,8 +664,11 @@ function H_zero(C::GModule) k = kernel(id - ac[1])[1] for i=2:length(ac) k = intersect(k, kernel(id - ac[i])[1]) + if isa(k, Tuple) #GrpAb: intersect yield ONLY intersection, + k = k[1] + end end - fl, inj = is_subgroup(k, M) + fl, inj = is_sub_with_data(k, M) @assert fl #this is fix, now it "should" be mod by norm? Only for Tate, see below @@ -730,13 +828,14 @@ function H_one(C::GModule) K = kernel(gg)[1] D = domain(gg) - lf, lft = is_subgroup(K, D) + lf, lft = is_sub_with_data(K, D) @assert lf Q, mQ = quo(K, image(g)[1]) M = Module(C) G = group(C) + n = ngens(G) z = MapFromFunc( Q, AllCoChains{1, elem_type(G), elem_type(M)}(), @@ -1019,7 +1118,7 @@ function H_two(C::GModule; force_rws::Bool = false, redo::Bool = false) E = D all_T = [] #need zero hom this is too slow - Z = hom(D, M, [M[0] for i=1:ngens(D)], check = false) + Z = hom(D, M, [zero(M) for i=1:ngens(D)], check = false) @vprint :GroupCohomology 2 "building relations...\n" for i = 1:length(R) @@ -1082,12 +1181,12 @@ function H_two(C::GModule; force_rws::Bool = false, redo::Bool = false) if length(all_T) == 0 Q = sub(M, elem_type(M)[])[1] - jinj = hom(M, Q, elem_type(Q)[Q[0] for m = gens(M)]) + jinj = hom(M, Q, elem_type(Q)[zero(Q) for m = gens(M)]) else Q, jinj = direct_product([M for i in all_T]..., task = :sum) end if length(all_T) == 0 - mm = hom(D, Q, elem_type(Q)[Q[0] for i=1:ngens(D)], check = false) + mm = hom(D, Q, elem_type(Q)[zero(Q) for i=1:ngens(D)], check = false) else mm = sum(all_T[i]*jinj[i] for i = 1:length(all_T)) end @@ -1132,7 +1231,7 @@ function H_two(C::GModule; force_rws::Bool = false, redo::Bool = false) end if length(r[2]) == 0 - S = hom(B, M, [M[0] for g = gens(B)], check = false) + S = hom(B, M, [zero(M) for g = gens(B)], check = false) elseif r[2][1] < 0 S = -B_pro[-r[2][1]]*iac[-r[2][1]] else @@ -1363,6 +1462,10 @@ function H_two(C::GModule; force_rws::Bool = false, redo::Bool = false) # to (0, t) where t is the tail for this rule end +function Base.iszero(x::AbstractAlgebra.Generic.ModuleHomomorphism) + return iszero(x.matrix) +end + function istwo_cocycle(c::CoChain{2}) C = c.C G = C.G @@ -1388,13 +1491,62 @@ iszero(a) || (@show g, h, k, a ; return false) return true end +""" +Computes H^3 via dimension-shifting: +There is a short exact sequence + 1 -> A -> Hom(Z[G], A) -> B -> 1 +thus + H^3(G, A) = H^2(G, B) +as Hom(Z[G], A) is induced hence has trivial cohomology. +Currently only the group is returned +""" +function H_three(C::GModule{<:Oscar.GAPGroup, <:Any}) + G = C.G + if isa(C.M, GrpAbFinGen) + zg, ac, em = Oscar.GModuleFromGap.natural_gmodule(GrpAbFinGen, G, ZZ) + elseif isa(C.M, AbstractAlgebra.FPModule{<:FieldElem}) + zg, ac, em = Oscar.GModuleFromGap.natural_gmodule(G, base_ring(C)) + else + error("unsupported module") + end + @assert is_consistent(zg) + H, mH = Oscar.GModuleFromGap.ghom(zg, C) + @assert is_consistent(H) + #from https://math.mit.edu/classes/18.786/2018/LectureNotes29.pdf + #around 29.8 + #Drew's notes. + #the augmentation map on the (canonical) generators is 1 + inj = hom(C.M, H.M, [preimage(mH, hom(zg.M, C.M, [c for g = gens(zg.M)])) for c = gens(C.M)]) + @assert is_G_hom(C, H, inj) + q, mq = quo(H, image(inj)[2]) +# return q, mq, inj, H + #possibly, to get 3-chains: + # 2 chain in q + # preimage mq 2 chain in H + # differential 3 chain in H + # preimage inj 3 chain in C + return H_two(q)[1] +end + +function is_right_G_module(C::GModule) + #tests if the action is right-linear + G = C.G + return all(action(action(C, g), h) == action(C, g*h) for g in gens(G), h in gens(G)) +end + +function is_left_G_module(C::GModule) + #tests if the action is left-linear + G = C.G + return all(action(C, h)*action(C, g) == action(C, g*h) for g = gens(G) for h = gens(G)) +end + """ For a gmodule `C` compute the `i`-th cohomology group - where `i` can be `0`, `1` or `2`. +where `i` can be `0`, `1` or `2`. (or `3` ...) Together with the abstract module, a map is provided that will produce explicit cochains. """ -function cohomology_group(C::GModule{PermGroup,GrpAbFinGen}, i::Int; Tate::Bool = false) +function cohomology_group(C::GModule, i::Int; Tate::Bool = false) #should also allow modules... if Tate @assert is_finite(group(C)) @@ -1409,6 +1561,8 @@ function cohomology_group(C::GModule{PermGroup,GrpAbFinGen}, i::Int; Tate::Bool return H_one(C) elseif i==2 return H_two(C) + elseif i==3 + return H_three(C) end error("only H^0, H^1 and H^2 are supported") end @@ -1422,10 +1576,18 @@ function fp_group(M::GrpAbFinGen) return domain(mp), mp end +function fp_group(M::AbstractAlgebra.FPModule{<:FinFieldElem}) + p, mp = pc_group(M, refine = false) + mf = isomorphism(FPGroup, p) + return codomain(mf), inv(mf)*mp +end + ######################################################### #XXX: should be in AA and supplemented by a proper quo -function Oscar.issubset(M::AbstractAlgebra.FPModule{T}, N::AbstractAlgebra.FPModule{T}) where T<:RingElement +Oscar.issubset(M::AbstractAlgebra.FPModule{T}, N::AbstractAlgebra.FPModule{T}) where T<:RingElement = is_submodule(M, N) + +function is_sub_with_data(M::AbstractAlgebra.FPModule{T}, N::AbstractAlgebra.FPModule{T}) where T<:RingElement fl = is_submodule(N, M) if fl return fl, hom(M, N, elem_type(N)[N(m) for m = gens(M)]) @@ -1433,6 +1595,7 @@ function Oscar.issubset(M::AbstractAlgebra.FPModule{T}, N::AbstractAlgebra.FPMod return fl, hom(M, N, elem_type(N)[zero(N) for m = gens(M)]) end end +is_sub_with_data(M::GrpAbFinGen, N::GrpAbFinGen) = is_subgroup(M, N) function Oscar.hom(V::Module, W::Module, v::Vector{<:ModuleElem}; check::Bool = true) if ngens(V) == 0 @@ -1461,6 +1624,8 @@ function Oscar.direct_product(M::Module...; task::Symbol = :none) error("illegal task") end +Base.:*(a::T, b::Generic.ModuleHomomorphism{T}) where {T} = hom(domain(b), codomain(b), a * mat(b)) +Base.:*(a::T, b::Generic.ModuleIsomorphism{T}) where {T} = hom(domain(b), codomain(b), a * mat(b)) Base.:+(a::Generic.ModuleHomomorphism, b::Generic.ModuleHomomorphism) = hom(domain(a), codomain(a), mat(a) + mat(b)) Base.:-(a::Generic.ModuleHomomorphism, b::Generic.ModuleHomomorphism) = hom(domain(a), codomain(a), mat(a) - mat(b)) Base.:-(a::Generic.ModuleHomomorphism) = hom(domain(a), codomain(a), -mat(a)) @@ -1469,7 +1634,11 @@ function Oscar.mat(M::FreeModuleHom{FreeMod{QQAbElem}, FreeMod{QQAbElem}}) return M.matrix end -function Oscar.id_hom(A::Generic.FreeModule) +function ==(a::Union{Generic.ModuleHomomorphism, Generic.ModuleIsomorphism}, b::Union{Generic.ModuleHomomorphism, Generic.ModuleIsomorphism}) + return mat(a) == mat(b) +end + +function Oscar.id_hom(A::AbstractAlgebra.FPModule) return Generic.ModuleIsomorphism(A, A, identity_matrix(base_ring(A), ngens(A))) end ########################################################### @@ -1597,11 +1766,11 @@ function (k::fqPolyRepField)(a::Vector) return k(polynomial(GF(Int(characteristic(k))), a)) end -function Oscar.order(F::Generic.FreeModule{<:FinFieldElem}) +function Oscar.order(F::AbstractAlgebra.FPModule{<:FinFieldElem}) return order(base_ring(F))^dim(F) end -function pc_group(M::Generic.FreeModule{<:FinFieldElem}; refine::Bool = true) +function pc_group(M::AbstractAlgebra.FPModule{<:FinFieldElem}; refine::Bool = true) k = base_ring(M) p = characteristic(k) @@ -1614,7 +1783,7 @@ function pc_group(M::Generic.FreeModule{<:FinFieldElem}; refine::Bool = true) B = PcGroup(GAP.Globals.GroupByRws(C)) FB = GAP.Globals.FamilyObj(GAP.Globals.Identity(B.X)) - function Julia_to_gap(a::Generic.FreeModuleElem{<:Union{fpFieldElem, FpFieldElem}}) + function Julia_to_gap(a::AbstractAlgebra.FPModuleElem{<:Union{fpFieldElem, FpFieldElem}}) r = ZZRingElem[] for i=1:ngens(M) if !iszero(a[i]) @@ -1626,7 +1795,7 @@ function pc_group(M::Generic.FreeModule{<:FinFieldElem}; refine::Bool = true) return g end - function Julia_to_gap(a::Generic.FreeModuleElem{<:Union{FqPolyRepFieldElem, fqPolyRepFieldElem}}) + function Julia_to_gap(a::AbstractAlgebra.FPModuleElem{<:Union{FqPolyRepFieldElem, fqPolyRepFieldElem}}) r = ZZRingElem[] for i=1:ngens(M) if !iszero(a[i]) @@ -1741,7 +1910,7 @@ function extension(c::CoChain{2,<:Oscar.GAPGroupElem}) return mQ(h1(underlying_word(g))*h2(underlying_word(preimage(mfM, m)))) end - return Q, inv(mfM)*MtoQ, QtoG, GMtoQ + return Q, pseudo_inv(mfM)*MtoQ, QtoG, GMtoQ end function extension(::Type{PcGroup}, c::CoChain{2,<:Oscar.PcGroupElem}) @@ -1883,6 +2052,117 @@ function extension(::Type{PcGroup}, c::CoChain{2,<:Oscar.PcGroupElem}) return Q, inv(mfM)*MtoQ, QtoG, GMtoQ end +""" + extension_with_abelian_kernel(X::Oscar.GAPGroup, M::Oscar.GAPGroup) + +For a group `K` and an abelian normal subgroup `M`, construct `M` as a +`X/M`-modul amd find the 2-cochain representing `X`. +""" +function extension_with_abelian_kernel(X::Oscar.GAPGroup, M::Oscar.GAPGroup) + g, pi = quo(X, M) + fl, i = is_subgroup(M, X) + phi = isomorphism(GrpAbFinGen, M) + A = codomain(phi) + gg = isomorphism(PermGroup, g) + C = gmodule(codomain(gg), [hom(A, A, [phi(preimage(i, i(preimage(phi, a))^preimage(pi, preimage(gg, x)))) for a = gens(A)]) for x = gens(codomain(gg))]) + + H2, mH2, _ = cohomology_group(C, 2) + + c = Dict( (gg(a), gg(b)) => phi(preimage(i, preimage(pi, a)*preimage(pi, b)*inv(preimage(pi, a*b)))) for a = g for b = g) + return C, CoChain{2, PermGroupElem, GrpAbFinGenElem}(C, c) +end + +""" +Let C be a G-module with G action on M. Then this function find the +subgroup of 'Aut(M) x Aut(G)' that is compatibel with the G-module +structure: + +Let 'h: G to Aut(M)' from C, then '(alpha, gamma) in Aut(M) x Aut(G)' +are compatible iff + for all 'a in M' and 'g in G' we have + 'h(gamma(g))(a) == alpha(inv(h(g))(alpha^-1(a)))' + +The group and the action on 2-cochains is returned. Cochains in the +same orbit parametrize the same group. +""" +function compatible_pairs(C::GModule) + #not exported + G = C.G + M = C.M + + autG = automorphism_group(G) + autM = automorphism_group(M) + + T, emb, pro = direct_product(autM, autG, morphisms = true) + + function action_on_chain(g::GAPGroupElem, c::CoChain{2, S, T}) where {S, T} + al = pro[1](C[2](g)) + ga = pro[2](C[2](g)) + d = Dict(ab=> al(c((inv(ga)(ab[1]), inv(ga)(ab[2])))) for ab = keys(c.d)) + return CoChain{2, S, T}(c.C, d) + end + + h = hom(G, autM, [autM(x) for x = C.ac]) + im = image(h)[1] + ke = kernel(h)[1] + + if order(im) == 1 + C = (T, x->x) + return T, action_on_chain + end + + N, mN = normalizer(autM, im) + S, mS = stabilizer(autG, ke, (x,y)->image(hom(y), x)[1]) + + NS, em, pr = direct_product(N, S, morphisms = true) + #from Holt/ Eick, ... Handbook of Computational Group Theory, P319 + C = stabilizer(NS, h, (x, y) -> hom(G, autM, + [inv(pr[1](y))*x(inv(pr[2](y))(g))*pr[1](y) for g = gens(G)])) + + #the more direct naive (and slow) approach... + #C = sub(T, [t for t in preimage(pro[1], N)[1] if all(ag -> action(C, pro[2](t)(ag[2]), ag[1]) == pro[1](t)(action(C, ag[2], inv(pro[1](t))(ag[1]))), Iterators.product(gens(M), gens(G)))]) + + return C[1], action_on_chain +end + +function split_extension(C::GModule) + #bypasses the Cohomolgy computation, hopefully + c = Dict((g, h) => zero(C.M) for g = C.G for h = C.G) + S = elem_type(C.G) + T = elem_type(C.M) + return extension(CoChain{2, S, T}(C, c)) +end + +function all_extensions(C::GModule) + @assert isfinite(C.M) + if gcd(order(C.M), order(C.G)) == 1 + return [split_extension(C)] + end + H2, mH2, _ = cohomology_group(C, 2) + if order(H2) == 1 + return [extension(mH2(zero(H2)))] + end + T, mT = compatible_pairs(C) + G = gset(T, (a, g) -> preimage(mH2, mT(g, mH2(a))), collect(H2), closed = true) + O = orbits(G) + all_G = [] + for o = O + push!(all_G, extension(mH2(representative(o)))[1]) + end + return all_G +end + +function all_extensions(M::GrpAbFinGen, G::PermGroup) #the cohomology wants it + A = automorphism_group(M) + l = GAP.Globals.AllHomomorphismClasses(G.X, A.X) + all_G = [] + for i = l + C = gmodule(G, [hom(A(i(g.X))) for g = gens(G)]) + append!(all_G, all_extensions(C)) + end + return all_G +end + function fp_group(c::CoChain{2}) return extension(c)[1] end @@ -1902,4 +2182,5 @@ using .GrpCoh export gmodule, fp_group, pc_group, induce, cohomology_group, extension export permutation_group, is_consistent, istwo_cocycle, GModule +export split_extension, all_extensions, extension_with_abelian_kernel diff --git a/experimental/GModule/GModule.jl b/experimental/GModule/GModule.jl index 06d4c28f9885..e7bee895510a 100644 --- a/experimental/GModule/GModule.jl +++ b/experimental/GModule/GModule.jl @@ -164,6 +164,25 @@ function __init__() set_verbose_level(:MinField, 0) end +function irreducible_modules(k::FinField, G::Oscar.GAPGroup) + h = Oscar.iso_oscar_gap(k) + hi = inv(h) + im = GAP.Globals.IrreducibleRepresentations(G.X, codomain(h)) + IM = GModule[] + for m in im + z = map(x->matrix(map(y->map(hi, y), m(x.X))), gens(G)) + 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 + function irreducible_modules(G::Oscar.GAPGroup) im = GAP.Globals.IrreducibleRepresentations(G.X) IM = GModule[] @@ -182,7 +201,7 @@ function irreducible_modules(G::Oscar.GAPGroup) return IM end -function Oscar.gmodule(::Type{AnticNumberField}, M::GModule{<:Oscar.GAPGroup, Generic.FreeModule{nf_elem}}) +function Oscar.gmodule(::Type{AnticNumberField}, M::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{nf_elem}}) k, mk = Hecke.subfield(base_ring(M), vec(collect(vcat(map(mat, M.ac)...)))) if k != base_ring(M) F = free_module(k, dim(M)) @@ -191,7 +210,7 @@ function Oscar.gmodule(::Type{AnticNumberField}, M::GModule{<:Oscar.GAPGroup, Ge return M end -function Oscar.gmodule(::Type{AnticNumberField}, M::GModule{<:Oscar.GAPGroup, Generic.FreeModule{QQAbElem{nf_elem}}}) +function Oscar.gmodule(::Type{AnticNumberField}, M::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{QQAbElem{nf_elem}}}) return gmodule(AnticNumberField, gmodule(CyclotomicField, M)) end @@ -212,7 +231,7 @@ function irreducible_modules(::Type{AnticNumberField}, G::Oscar.GAPGroup; minima end end -function _minimize(V::GModule{<:Oscar.GAPGroup, Generic.FreeModule{nf_elem}}) +function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{nf_elem}}) k, m = _character_field(V) chi = character(V) d = schur_index(chi) @@ -278,7 +297,7 @@ function gmodule(::typeof(CyclotomicField), C::GModule) return gmodule(F, group(C), [hom(F, F, map_entries(x->K(x.data), mat(x))) for x = C.ac]) end -function gmodule(k::Nemo.fpField, C::GModule{PermGroup, GrpAbFinGen}) +function gmodule(k::Nemo.fpField, C::GModule{<:Oscar.GAPGroup, GrpAbFinGen}) q, mq = quo(C.M, characteristic(k)) s, ms = snf(q) @@ -294,7 +313,7 @@ end import Base: ^ -function ^(C::GModule{<:Any, Generic.FreeModule{nf_elem}}, phi::Map{AnticNumberField, AnticNumberField}) +function ^(C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}, phi::Map{AnticNumberField, AnticNumberField}) F = free_module(codomain(phi), dim(C)) return GModule(group(C), [hom(F, F, map_entries(phi, mat(x))) for x = C.ac]) end @@ -303,52 +322,19 @@ function ^(C::GModule{<:Any, T}, h::Map{S, S}) where T <: S where S return GModule(group(C), [inv(h)*x*h for x = C.ac]) end -function ^(C::GModule{<:Any, Generic.FreeModule{QQAbElem}}, phi::Map{QQAbField, QQAbField}) +function ^(C::GModule{<:Any, <:AbstractAlgebra.FPModule{QQAbElem}}, phi::Map{QQAbField, QQAbField}) F = free_module(codomain(phi), dim(C)) return GModule(F, group(C), [hom(F, F, map_entries(phi, mat(x))) for x = C.ac]) end -function gmodule(::QQField, C::GModule{<:Any, Generic.FreeModule{nf_elem}}) +function gmodule(::QQField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}) F = free_module(QQ, dim(C)*degree(base_ring(C))) return GModule(F, group(C), [hom(F, F, hvcat(dim(C), [representation_matrix(x) for x = transpose(mat(y))]...)) for y = C.ac]) end -gmodule(k::Nemo.fpField, C::GModule{<:Any, Generic.FreeModule{fpFieldElem}}) = C +gmodule(k::Nemo.fpField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{fpFieldElem}}) = C -function gmodule(::Type{FinField}, C::GModule{<:Any, <:Generic.FreeModule{<:AlgClosureElem{<:FinField}}}) - - d = dim(C) - l = 1 - for g = C.ac - l = lcm(l, lcm(collect(map_entries(x->Hecke.degree(parent(x.data)), mat(g))))) - end - K = ext_of_degree(base_ring(C), l) - return gmodule(K, C) -end - -function gmodule(K::FinField, C::GModule{<:Any, <:Generic.FreeModule{<:AlgClosureElem{<:FinField}}}) - - d = dim(C) - F = free_module(K, d) - if d == 0 - h = hom(F, F, elem_type(F)[]) - return gmodule(F, group(C), typeof(h)[hom(F, F, map_entries(K, mat(x))) for x = C.ac]) - end - return gmodule(F, group(C), [hom(F, F, map_entries(K, mat(x))) for x = C.ac]) -end - -function gmodule(K::FinField, C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) - - d = dim(C) - F = free_module(K, d) - if d == 0 - h = hom(F, F, elem_type(F)[]) - return gmodule(F, group(C), typeof(h)[hom(F, F, map_entries(K, mat(x))) for x = C.ac]) - end - return gmodule(F, group(C), [hom(F, F, map_entries(K, mat(x))) for x = C.ac]) -end - -function _character(C::GModule{<:Any, <:Generic.FreeModule{<:AbstractAlgebra.FieldElem}}) +function _character(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:AbstractAlgebra.FieldElem}}) G = group(C) phi = epimorphism_from_free_group(G) ac = Oscar.GrpCoh.action(C) @@ -364,6 +350,7 @@ function _character(C::GModule{<:Any, <:Generic.FreeModule{<:AbstractAlgebra.Fie push!(chr, (c, K(n))) continue end + #use T = action(C, r) instead? p = preimage(phi, r) T = map_word(p, ac; genimgs_inv = iac) push!(chr, (c, trace(mat(T)))) @@ -371,19 +358,46 @@ function _character(C::GModule{<:Any, <:Generic.FreeModule{<:AbstractAlgebra.Fie return chr end -Oscar.character_field(C::GModule{<:Any, <:Generic.FreeModule{QQFieldElem}}) = QQ +""" +Returns Z[G] and a function f that, when applied to a G-module M will return +a map representing the action of Z[G] on M: + +f(C) yields the extension of g -> action(C, g) + +The third value returned is a Map between group elements and the index of the +corresponding module generator. +""" +function natural_gmodule(G::Oscar.GAPGroup, R::Ring) + M = free_module(R, Int(order(G))) + ge = collect(G) + ZG = gmodule(G, [hom(M, M, [M[findfirst(isequal(ge[i]*g), ge)] for i=1:length(ge)]) for g = gens(G)]) + return ZG, C->(x -> sum(x[i]*action(C, ge[i]) for i=1:length(ge))), + MapFromFunc(G, ZZ, x->ZZ(findfirst(isequal(x), ge)), + y->ge[Int(y)]) +end + +function natural_gmodule(::Type{GrpAbFinGen}, G::Oscar.GAPGroup, ::ZZRing) + M = free_abelian_group(order(Int, G)) + ge = collect(G) + ZG = gmodule(G, [hom(M, M, [M[findfirst(isequal(ge[i]*g), ge)] for i=1:length(ge)]) for g = gens(G)]) + return ZG, C->(x -> sum(x[i]*action(C, ge[i]) for i=1:length(ge))), + MapFromFunc(G, ZZ, x->ZZ(findfirst(isequal(x), ge)), + y->ge[Int(y)]) +end + +Oscar.character_field(C::GModule{<:Any, <:AbstractAlgebra.FPModule{QQFieldElem}}) = QQ -function _character_field(C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}) +function _character_field(C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}) val = _character(C) k, mkK = Hecke.subfield(base_ring(C), [x[2] for x = val]) return k, mkK end -function Oscar.character_field(C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}) +function Oscar.character_field(C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}) return _character_field(C)[1] end -function Oscar.character(C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}) +function Oscar.character(C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}) chr = _character(C) k, mkK = Hecke.subfield(base_ring(C), [x[2] for x = chr]) A = maximal_abelian_subfield(ClassField, k) @@ -394,17 +408,41 @@ function Oscar.character(C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}) return Oscar.class_function(group(C), [QQAb(em(preimage(mkK, x[2]))) for x = chr]) end -function Oscar.character(C::GModule{<:Any, <:Generic.FreeModule{QQFieldElem}}) +function Oscar.character(C::GModule{<:Any, <:AbstractAlgebra.FPModule{QQFieldElem}}) QQAb = abelian_closure(QQ)[1] return Oscar.class_function(group(C), [QQAb(x[2]) for x = _character(C)]) end -function Oscar.character(C::GModule{<:Any, <:Generic.FreeModule{<:AbstractAlgebra.FieldElem}}) - return Oscar.class_function(group(C), [base_ring(C)(x[2]) for x = _character(C)]) +function Oscar.natural_character(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) + G = C.G + tbl = character_table(G) + k = base_ring(C.M) + p = characteristic(k) + modtbl = mod(tbl, p) + ccl = conjugacy_classes(modtbl) # p-regular classes + h = Oscar.iso_oscar_gap(k) + + vals = [GAP.Globals.BrauerCharacterValue(GAP.Obj(map(h, mat(action(C, representative(x)))))) for x in ccl] + + return Oscar.class_function(modtbl, GAPWrap.ClassFunction(Oscar.GAPTable(modtbl), Oscar.GapObj(vals))) end +function Oscar.sub(C::GModule{<:Any, <:AbstractAlgebra.FPModule{T}}, m::MatElem{T}) where {T <: FinFieldElem} + + k = base_ring(C) + h = Oscar.iso_oscar_gap(k) + s = GAP.Globals.ShallowCopy(GAP.Obj(map(h, m))) + g = Gap(C) + x = GAP.Globals.MTX.SubGModule(g, s) + b = matrix([preimage(h, x[i, j]) for i in 1:GAPWrap.NrRows(x), j in 1:GAPWrap.NrCols(x)]) + + y = GAP.Globals.MTX.InducedActionSubmoduleNB(g, x) + F = free_module(k, nrows(b)) + return gmodule(F, Group(C), [hom(F, F, matrix([preimage(h, x[i, j]) for i in 1:GAPWrap.NrRows(x), j in 1:GAPWrap.NrCols(x)])) for x = y.generators]), hom(F, C.M, b) + return b +end -function gmodule(k::Nemo.fpField, C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) +function gmodule(k::Nemo.fpField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) F = free_module(k, dim(C)*degree(base_ring(C))) return GModule(F, group(C), [hom(F, F, hvcat(dim(C), [representation_matrix(x) for x = transpose(mat(y))]...)) for y = C.ac]) end @@ -413,11 +451,11 @@ function Hecke.frobenius(K::FinField, i::Int=1) MapFromFunc(K, K, x->Hecke.frobenius(x, i), y -> Hecke.frobenius(x, degree(K)-i)) end -function gmodule_minimal_field(C::GModule{<:Any, <:Generic.FreeModule{fpFieldElem}}) +function gmodule_minimal_field(C::GModule{<:Any, <:AbstractAlgebra.FPModule{fpFieldElem}}) return C end -function gmodule_minimal_field(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) +function gmodule_minimal_field(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) #always over char field K = base_ring(C) d = 0 @@ -431,11 +469,11 @@ function gmodule_minimal_field(C::GModule{<:Any, <:Generic.FreeModule{<:FinField return C end -function gmodule_minimal_field(C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}) +function gmodule_minimal_field(C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}) return _minimize(C) end -function gmodule_over(k::FinField, C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}; do_error::Bool = false) +function gmodule_over(k::FinField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}; do_error::Bool = false) #mathematically, k needs to contain the character field #only works for irreducible modules #requires rel cyclic Galois group, not really finite field... @@ -493,7 +531,7 @@ function gmodule_over(k::FinField, C::GModule{<:Any, <:Generic.FreeModule{<:FinF end #...now the same for number fields - and non-cyclic fields. -function gmodule_over(em::Map{AnticNumberField, AnticNumberField}, C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}; do_error::Bool = true) +function gmodule_over(em::Map{AnticNumberField, AnticNumberField}, C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}; do_error::Bool = true) K = base_ring(C) k = domain(em) @assert codomain(em) == K @@ -513,7 +551,7 @@ function gmodule_over(em::Map{AnticNumberField, AnticNumberField}, C::GModule{<: return gmodule(F, group(C), [hom(F, F, map_entries(t->preimage(em, t), x)) for x = ac]) end -function gmodule_over(::QQField, C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}; do_error::Bool = true) +function gmodule_over(::QQField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}; do_error::Bool = true) K = base_ring(C) A, mA = automorphism_group(PermGroup, K) ac = _two_cocycle(mA, C, do_error = do_error) @@ -555,7 +593,7 @@ module of the autmorphism group over the character field. If `mA` is given, it needs to map the automorphism group over the character field into the the automorphisms of the base ring. """ -function factor_set(C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}, mA::Union{Map, Nothing} = nothing) +function factor_set(C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}, mA::Union{Map, Nothing} = nothing) K = base_ring(C) if mA === nothing k, mkK = _character_field(C) @@ -571,7 +609,7 @@ function factor_set(C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}, mA::Union{ return c end -function _two_cocycle(mA::Map, C::GModule{<:Any, <:Generic.FreeModule{nf_elem}}; do_error::Bool = true, two_cycle::Bool = false) +function _two_cocycle(mA::Map, C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}; do_error::Bool = true, two_cycle::Bool = false) G = domain(mA) K = base_ring(C) @@ -795,19 +833,19 @@ function hilbert90_cyclic(A::MatElem{<:FinFieldElem}, s, os::Int) end end -function gmodule(k::Nemo.fpField, C::GModule{<:Any, Generic.FreeModule{QQFieldElem}}) +function gmodule(k::Nemo.fpField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{QQFieldElem}}) F = free_module(k, dim(C)) return GModule(group(C), [hom(F, F, map_entries(k, mat(x))) for x=C.ac]) end -function gmodule(mk::Map{AnticNumberField, <:FinField}, C::GModule{<:Any, Generic.FreeModule{nf_elem}}) +function gmodule(mk::Map{AnticNumberField, <:FinField}, C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}) k = codomain(mk) @assert domain(mk) == base_ring(C) F = free_module(k, dim(C)) return GModule(group(C), [hom(F, F, map_entries(mk, mat(x))) for x=C.ac]) end -function Hecke.modular_proj(C::GModule{T, Generic.FreeModule{nf_elem}}, me::Hecke.modular_env) where T +function Hecke.modular_proj(C::GModule{T, <:AbstractAlgebra.FPModule{nf_elem}}, me::Hecke.modular_env) where T R = [] z = map(x->(Hecke.modular_proj(x.matrix, me)), C.ac) for i=1:length(z[1]) @@ -819,7 +857,7 @@ function Hecke.modular_proj(C::GModule{T, Generic.FreeModule{nf_elem}}, me::Heck return R end -function Gap(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}, h=Oscar.iso_oscar_gap(base_ring(C))) +function Gap(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}, h=Oscar.iso_oscar_gap(base_ring(C))) z = get_attribute(C, :Gap) if z !== nothing return z @@ -829,27 +867,27 @@ function Gap(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}, h=Oscar.is return z end -function Oscar.is_irreducible(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) +function Oscar.is_irreducible(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) G = Gap(C) return GAP.Globals.MTX.IsIrreducible(G) end -function Oscar.is_absolutely_irreducible(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) +function Oscar.is_absolutely_irreducible(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) G = Gap(C) return GAP.Globals.MTX.IsAbsolutelyIrreducible(G) end -function is_decomposable(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) +function is_decomposable(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) G = Gap(C) return !GAP.Globals.MTX.IsIndecomposable(G) end """ - composition_factors_with_multiplicity(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) + composition_factors_with_multiplicity(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) Return the composition factors of `C` with their frequency. """ -function Oscar.composition_factors_with_multiplicity(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) +function Oscar.composition_factors_with_multiplicity(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) G = Gap(C) z = GAP.Globals.MTX.CollectedFactors(G) g = Group(C) @@ -866,14 +904,14 @@ function Oscar.composition_factors_with_multiplicity(C::GModule{<:Any, <:Generic end """ - indecomposition(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) + indecomposition(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) Return a decomposition of the module `C` into indecomposable summands as a list of pairs: - a direct indecomposable summand - a homomorphism (embedding) of the underlying free modules """ -function indecomposition(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}}) +function indecomposition(C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) G = Gap(C) z = GAP.Globals.MTX.Indecomposition(G) k = base_ring(C) @@ -887,21 +925,65 @@ function indecomposition(C::GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}} return CF end -function Oscar.hom(C::T, D::T) where T <: GModule{<:Any, <:Generic.FreeModule{<:FieldElem}} +""" +The group of Z[G]-homomorphisms as a k-module, not a k[G] one. (The G opetation +is trivial) +""" +function Oscar.hom(C::T, D::T) where T <: GModule{<:Any, <:AbstractAlgebra.FPModule{<:FieldElem}} b = hom_base(C, D) H, mH = hom(C.M, D.M) s, ms = sub(H, [H(vec(collect(x))) for x = b]) - return GModule(group(C), [hom(s, s, [preimage(ms, H(vec(collect(inv(mat(C.ac[i]))*g*mat(D.ac[i]))))) for g = b]) for i=1:ngens(group(C))]), ms, mH + return GModule(group(C), [hom(s, s, [preimage(ms, H(vec(collect(inv(mat(C.ac[i]))*g*mat(D.ac[i]))))) for g = b]) for i=1:ngens(group(C))]), ms * mH end -function Oscar.hom(F::Generic.FreeModule{T}, G::Generic.FreeModule{T}) where T +#a bad implementation of hom: as the fix module of ghom +#but we don't have he meataxe in general. +function Hecke.hom(C::GModule{T, GrpAbFinGen}, D::GModule{T, GrpAbFinGen}) where {T} + + H, mH = Oscar.GModuleFromGap.ghom(C, D) + q, mq = H_zero(H) + mmH = hom(q, H.M, [mq(x)() for x = gens(q)]) + return q, mmH*mH + return gmodule(C.G, [hom(q, q, gens(q)) for x = gens(C.G)]), mmH*mH +end + +""" +The G-module of all Z-module homomorphisms +""" +function ghom(C::GModule, D::GModule) + @assert C.G === D.G + H, mH = hom(C.M, D.M) + return gmodule(H, C.G, [hom(H, H, [preimage(mH, action(C, inv(g))*mH(h)*action(D, g)) for h = gens(H)]) for g = gens(C.G)]), mH +end + +function is_G_hom(C::GModule, D::GModule, H::Map) + return all([H(action(C, g, h)) == action(D, g, H(h)) for g = gens(C.G) for h = gens(C.M)]) +end + +#= +G = cyclic_group(PermGroup, 3) +A = abelian_group([3, 3]) +C = gmodule(G, [hom(A, A, [A[1], A[1]+A[2]])]) +is_consistent(C) + +zg, ac = Oscar.GModuleFromGap.natural_gmodule(G, ZZ) +zg = gmodule(GrpAbFinGen, zg) +H, mH = Oscar.GModuleFromGap.ghom(zg, C) +inj = hom(C.M, H.M, [preimage(mH, hom(zg.M, C.M, [ac(C)(g)(c) for g = gens(zg.M)])) for c = gens(C.M)]) + +q, mq = quo(H, image(inj)[2]) +=# + +Oscar.parent(H::AbstractAlgebra.Generic.ModuleHomomorphism{<:FieldElem}) = Hecke.MapParent(domain(H), codomain(H), "homomorphisms") + +function Oscar.hom(F::AbstractAlgebra.FPModule{T}, G::AbstractAlgebra.FPModule{T}) where T k = base_ring(F) @assert base_ring(G) == k H = free_module(k, dim(F)*dim(G)) return H, MapFromFunc(H, Hecke.MapParent(F, G, "homomorphisms"), x->hom(F, G, matrix(k, dim(F), dim(G), vec(collect(x.v)))), y->H(vec(collect(transpose(mat(y)))))) end -function hom_base(C::T, D::T) where T <: GModule{<:Any, <:Generic.FreeModule{<:FinFieldElem}} +function hom_base(C::GModule{S, <:AbstractAlgebra.FPModule{T}}, D::GModule{S, <:AbstractAlgebra.FPModule{T}}) where {S <: Oscar.GAPGroup, T <: FinFieldElem} @assert base_ring(C) == base_ring(D) h = Oscar.iso_oscar_gap(base_ring(C)) hb = GAP.Globals.MTX.BasisModuleHomomorphisms(Gap(C, h), Gap(D, h)) @@ -938,7 +1020,7 @@ on return. Currently assumes no bad primes. """ -function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{nf_elem}} +function hom_base(C::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}, D::GModule{<:Any, <:AbstractAlgebra.FPModule{nf_elem}}) @assert base_ring(C) == base_ring(D) p = Hecke.p_start @@ -1016,7 +1098,7 @@ end #T this belongs to Nemo and should be moved there Oscar.nbits(a::QQFieldElem) = nbits(numerator(a)) + nbits(denominator(a)) -function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{QQFieldElem}} +function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:AbstractAlgebra.FPModule{QQFieldElem}} @assert base_ring(C) == base_ring(D) p = Hecke.p_start @@ -1078,12 +1160,12 @@ 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}}) +function gmodule(K::AnticNumberField, M::GModule{<:Any, <:AbstractAlgebra.FPModule{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 hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{<:QQAbElem}} +function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:AbstractAlgebra.FPModule{<:QQAbElem}} C1 = gmodule(CyclotomicField, C) D1 = gmodule(CyclotomicField, D) fl, Cf = Hecke.is_cyclotomic_type(base_ring(C1)) @@ -1105,12 +1187,12 @@ function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{ return map(x->map_entries(base_ring(C), x), h) end -function gmodule(::QQField, C::GModule{<:Any, <:Generic.FreeModule{ZZRingElem}}) +function gmodule(::QQField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{ZZRingElem}}) F = free_module(QQ, dim(C)) return GModule(group(C), [hom(F, F, map_entries(QQ, mat(x))) for x = C.ac]) end -function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{ZZRingElem}} +function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:AbstractAlgebra.FPModule{ZZRingElem}} h = hom_base(gmodule(QQ, C), gmodule(QQ, D)) H = vcat([integral_split(matrix(QQ, 1, dim(C)^2, vec(collect(x))), ZZ)[1] for x = h]...) @@ -1118,7 +1200,7 @@ function hom_base(C::_T, D::_T) where _T <: GModule{<:Any, <:Generic.FreeModule{ return [matrix(ZZ, dim(C), dim(C), vec(collect(H[i, :]))) for i=1:nrows(H)] end -function gmodule(::ZZRing, C::GModule{<:Any, <:Generic.FreeModule{QQFieldElem}}) +function gmodule(::ZZRing, C::GModule{<:Any, <:AbstractAlgebra.FPModule{QQFieldElem}}) ma = map(mat, C.ac) M = identity_matrix(QQ, dim(C)) if dim(C) == 0 @@ -1139,11 +1221,11 @@ function gmodule(::ZZRing, C::GModule{<:Any, <:Generic.FreeModule{QQFieldElem}}) return gmodule(group(C), [integral_split(x, ZZ)[1] for x = action_matrices(D)]) end -function Base.transpose(C::GModule{<:Any, <:Generic.FreeModule}) +function Base.transpose(C::GModule{<:Any, <:AbstractAlgebra.FPModule}) return gmodule(group(C), [transpose(x) for x = action_matrices(C)]) end -function Oscar.dual(C::GModule{<:Any, <:Generic.FreeModule}) +function Oscar.dual(C::GModule{<:Any, <:AbstractAlgebra.FPModule}) D = gmodule(group(C), [inv(transpose(x)) for x = action_matrices(C)]) return D end @@ -1155,7 +1237,7 @@ end #to always get pos. def. one needs a different algorithm # - sum over group # - approximate reynolds as in invar thy (for number fields and Q/ Z) -function invariant_forms(C::GModule{<:Any, <:Generic.FreeModule}) +function invariant_forms(C::GModule{<:Any, <:AbstractAlgebra.FPModule}) D = Oscar.dual(C) h = hom_base(C, D) r, k = kernel(transpose(vcat([matrix(base_ring(C), 1, dim(C)^2, vec(x-transpose(x))) for x = h]...))) @@ -1173,26 +1255,38 @@ function Oscar.gmodule(G::Oscar.GAPGroup, v::Vector{<:MatElem}) end -function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, Generic.FreeModule{ZZRingElem}}) where {T <: Oscar.GAPGroup} +function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, AbstractAlgebra.FPModule{ZZRingElem}}) where {T <: Oscar.GAPGroup} A = free_abelian_group(rank(C.M)) return Oscar.gmodule(Group(C), [hom(A, A, mat(x)) for x = C.ac]) end -function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, Generic.FreeModule{FpFieldElem}}) where {T <: Oscar.GAPGroup} +function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, AbstractAlgebra.FPModule{FpFieldElem}}) where {T <: Oscar.GAPGroup} A = abelian_group([characteristic(base_ring(C)) for i=1:rank(C.M)]) return Oscar.gmodule(A, Group(C), [hom(A, A, map_entries(lift, mat(x))) for x = C.ac]) end -function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, Generic.FreeModule{fpFieldElem}}) where {T <: Oscar.GAPGroup} - A = abelian_group([characteristic(base_ring(C)) for i=1:rank(C.M)]) +function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, <:AbstractAlgebra.FPModule{fpFieldElem}}) where {T <: Oscar.GAPGroup} + A = abelian_group([characteristic(base_ring(C)) for i=1:dim(C.M)]) return Oscar.gmodule(A, Group(C), [hom(A, A, map_entries(lift, mat(x))) for x = C.ac]) end -function Oscar.abelian_group(M::Generic.FreeModule{fqPolyRepFieldElem}) +function Oscar.abelian_group(M::Generic.FreeModule{ZZRingElem}) + A = free_abelian_group(rank(M)) + return A, MapFromFunc(A, M, x->M(x.coeff), y->A(y.v)) +end + +function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, <:AbstractAlgebra.FPModule{ZZRingElem}}) where {T <: Oscar.GAPGroup} + A, mA = abelian_group(C.M) + return Oscar.gmodule(A, Group(C), [hom(A, A, mat(x)) for x = C.ac]) +end + + + +function Oscar.abelian_group(M::AbstractAlgebra.FPModule{fqPolyRepFieldElem}) k = base_ring(M) A = abelian_group([characteristic(k) for i = 1:dim(M)*degree(k)]) n = degree(k) - function to_A(m::Generic.FreeModuleElem{fqPolyRepFieldElem}) + function to_A(m::AbstractAlgebra.FPModuleElem{fqPolyRepFieldElem}) a = ZZRingElem[] for i=1:dim(M) c = m[i] @@ -1221,7 +1315,7 @@ function Oscar.gmodule(chi::Oscar.GAPGroupClassFunction) return gmodule(group(chi), [hom(F, F, x) for x = z]) end -function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, Generic.FreeModule{fqPolyRepFieldElem}}) where {T <: Oscar.GAPGroup} +function Oscar.gmodule(::Type{GrpAbFinGen}, C::GModule{T, AbstractAlgebra.FPModule{fqPolyRepFieldElem}}) where {T <: Oscar.GAPGroup} k = base_ring(C) A, mA = abelian_group(C.M) @@ -1251,15 +1345,15 @@ function Base.vec(M::MatElem) return r end -function Oscar.simplify(C::GModule{<:Any, <:Generic.FreeModule{QQFieldElem}}) +function Oscar.simplify(C::GModule{<:Any, <:AbstractAlgebra.FPModule{QQFieldElem}}) return gmodule(QQ, Oscar.simplify(gmodule(ZZ, C))[1]) end -function action_matrices(C::GModule{<:Any, <:Generic.FreeModule}) +function action_matrices(C::GModule{<:Any, <:AbstractAlgebra.FPModule}) return map(mat, action(C)) end -function Oscar.simplify(C::GModule{<:Any, <:Generic.FreeModule{ZZRingElem}}) +function Oscar.simplify(C::GModule{<:Any, <:AbstractAlgebra.FPModule{ZZRingElem}}) f = invariant_forms(C)[1] @assert all(i->det(f[1:i, 1:i])>0, 1:nrows(f)) m = map(mat, C.ac) @@ -1317,9 +1411,11 @@ function Hecke.induce_rational_reconstruction(a::ZZMatrix, pg::ZZRingElem; Error end export factor_set +export ghom export indecomposition export irreducible_modules export is_decomposable +export is_G_hom ## Fill in some stubs for Hecke @@ -1366,7 +1462,9 @@ end #module GModuleFromGap using .GModuleFromGap export factor_set +export ghom export indecomposition export irreducible_modules export is_decomposable +export is_G_hom diff --git a/experimental/GModule/GaloisCohomology.jl b/experimental/GModule/GaloisCohomology.jl index 9eef6fc01af3..295e6402cc06 100644 --- a/experimental/GModule/GaloisCohomology.jl +++ b/experimental/GModule/GaloisCohomology.jl @@ -7,7 +7,7 @@ import Base: parent import Oscar: direct_sum export is_coboundary, idel_class_gmodule, relative_brauer_group export local_invariants, global_fundamental_class, shrink -export local_index +export local_index, units_mod_ideal Oscar.elem_type(::Type{Hecke.NfMorSet{T}}) where {T <: Hecke.LocalField} = Hecke.LocalFieldMor{T, T} @@ -62,6 +62,25 @@ function Oscar.absolute_automorphism_group(::Type{PermGroup}, k) return codomain(mH), inv(mH)*mG end +""" + units_mod_ideal(I::NfOrdIdl; n_quo::Int = 0) -> GrpAbFinGen, Map{Grp, NfOrd} + +Computes the unit group of the order modulo `I`. If `n_quo` is non-zero, the quotient +modulo `n_quo` is computed. +""" +function units_mod_ideal(I::NfOrdIdl; n_quo::Int = 0) + #TODO: add places for sign condition (RatResidueRing in Magma) + # use the n_quo already in the creation. + R, mR = quo(order(I), I) + U, mU = unit_group(R) + if n_quo != 0 + u, mu = quo(U, n_quo) + U = u + mU = pseudo_inv(mu)*mU + end + return U, mU*pseudo_inv(mR) +end + """ The natural `ZZ[H]` module where `H`, a subgroup of the automorphism group acts on the ray class group. @@ -123,7 +142,7 @@ function Oscar.gmodule(H::PermGroup, mu::Map{GrpAbFinGen, FacElemMon{AnticNumber return _gmodule(base_ring(codomain(mu)), H, mu, mG) end -function Oscar.gmodule(H::PermGroup, mu::Hecke.MapUnitGrp{NfOrd}, mG = automorphism_group(PermGroup, k)[2]) +function Oscar.gmodule(H::PermGroup, mu::Map{GrpAbFinGen, NfOrd}, mG = automorphism_group(PermGroup, nf(codomain(mu)))[2]) #TODO: preimage for sunits can fail (inf. loop) if # (experimentally) the ideals in S are not coprime or include 1 # or if the s-unit is not in the image (eg. action and not closed set S) @@ -764,6 +783,7 @@ function idel_class_gmodule(k::AnticNumberField, s::Vector{Int} = Int[]; redo::B @hassert :GaloisCohomology 1 is_consistent(Et) iEt = Oscar.GrpCoh.induce(Et, mG_inf, E, id_hom(U)) else + #TODO: failing on x^8 + 70*x^4 + 15625 @vprint :GaloisCohomology 2 " .. complex field, hard case ..\n" mG_inf = Oscar.decomposition_group(k, complex_embeddings(k)[1], mG) G_inf = domain(mG_inf) @@ -787,8 +807,6 @@ function idel_class_gmodule(k::AnticNumberField, s::Vector{Int} = Int[]; redo::B #theta: theta = U[1] #should be a generator for torsion, torsion is even, #hence this elem cannot be a square - T = abelian_group([order(U[1]), 0]) - ac_T = hom(T, T, [sigma(U[1])[1]*T[1], T[1]+T[2]]) x = [preimage(mq, i) for i = x] y = [preimage(mq, i) for i = y] @@ -811,13 +829,14 @@ function idel_class_gmodule(k::AnticNumberField, s::Vector{Int} = Int[]; redo::B push!(not_inv, i) end end - + @assert length(not_inv) > 0 @assert length(not_inv) + length(inv) == length(x) x = vcat(x[not_inv], x[inv]) #reordering theta_i = vcat(theta_i[not_inv], theta_i[inv]) U_t, mU_t = sub(U, [U[1]]) + sm1 = hom(U_t, U, [sigma(mU_t(g)) - mU_t(g) for g = gens(U_t)]) eta_i = [preimage(sm1, theta - theta_i[i]) for i=1:length(not_inv)] @@ -826,10 +845,12 @@ function idel_class_gmodule(k::AnticNumberField, s::Vector{Int} = Int[]; redo::B im_psi = [U[1], x[1]+ eta_i[1]] for i=2:length(not_inv) push!(im_psi, x[i] - x[1] + eta_i[i] - eta_i[1]) + @assert sigma(im_psi[end]) == im_psi[end] #should be chosen to be pos. at place, flip signs... end for i=length(not_inv)+1:length(x) push!(im_psi, x[i]) + @assert sigma(im_psi[end]) == im_psi[end] #should be chosen to be pos. at place, flip signs... end for i=1:length(y) @@ -837,13 +858,16 @@ function idel_class_gmodule(k::AnticNumberField, s::Vector{Int} = Int[]; redo::B push!(im_psi, sigma(y[i])) end psi = hom(V, U, im_psi) + @assert all(i->psi(V[i]) == im_psi[i], 1:length(im_psi)) @assert is_bijective(psi) F = abelian_group([0 for i=2:length(x)]) Hecke.assure_has_hnf(F) W, pro, inj = direct_product(V, F, task = :both) @assert isdefined(W, :hnf) - ac = GrpAbFinGenMap(pro[1]*psi*sigma*pseudo_inv(psi)*inj[1])+ GrpAbFinGenMap(pro[2]*hom(F, W, GrpAbFinGenElem[inj[1](preimage(psi, x[i])) - inj[2](F[i-1]) for i=2:length(x)])) + ac = GrpAbFinGenMap(pro[1]*psi*sigma*pseudo_inv(psi)*inj[1]) + + GrpAbFinGenMap(pro[2]*hom(F, W, GrpAbFinGenElem[inj[1](V[i+1]) - inj[2](F[i-1]) for i=2:length(x)])) + Et = gmodule(G_inf, [ac]) @assert is_consistent(Et) mq = pseudo_inv(psi)*inj[1] @@ -1008,30 +1032,38 @@ julia> [describe(x[1]) for x = b] ``` """ -function Oscar.galois_group(A::ClassField, ::QQField; idel_parent::IdelParent = idel_class_gmodule(base_field(A))) +function Oscar.galois_group(A::ClassField, ::QQField; idel_parent::Union{IdelParent,Nothing} = nothing) m0, m_inf = defining_modulus(A) @assert length(m_inf) == 0 + if !is_normal(A) + A = normal_closure(A) + end mR = A.rayclassgroupmap mQ = A.quotientmap zk = order(m0) @req order(automorphism_group(nf(zk))[1]) == degree(zk) "base field must be normal" - if !Hecke.is_normal(A) - A = normal_closure(A) - mR = A.rayclassgroupmap - mQ = A.quotientmap - m0, m_inf = defining_modulus(A) - @assert length(m_inf) == 0 + if gcd(degree(A), degree(base_field(A))) == 1 + s, ms = split_extension(gmodule(A)) + return permutation_group(s)[1], ms + end + if idel_parent === nothing + idel_parent = idel_class_gmodule(base_field(A)) end + qI = cohomology_group(idel_parent, 2) q, mq = snf(qI[1]) a = qI[2](image(mq, q[1])) # should be a 2-cycle in q + @assert Oscar.GrpCoh.istwo_cocycle(a) gA = gmodule(A, idel_parent.mG) qA = cohomology_group(gA, 2) + n = degree(nf(zk)) aa = map_entries(a, parent = gA) do x + x = parent(x)(Hecke.mod_sym(x.coeff, gcd(n, degree(A)))) J = ideal(idel_parent, x, coprime = m0) mQ(preimage(mR, numerator(J)) - preimage(mR, denominator(J)*zk)) end + @assert Oscar.GrpCoh.istwo_cocycle(aa) return permutation_group(aa), (aa, gA) end @@ -1798,24 +1830,13 @@ function Oscar.cohomology_group(A::IdelParent, i::Int) return Oscar.cohomology_group(A.data[1], i) end -function Oscar.orbit(C::GModule{PermGroup, GrpAbFinGen}, o::GrpAbFinGenElem) - or = Set([o]) - done = false - while !done - sz = length(or) - done = true - for f = C.ac - while true - or = union(or, [f(x) for x = or]) - if length(or) == sz - break - end - done = false - sz = length(or) - end - end - end - return collect(or) +""" +For a C a G-module with operation on M and an element o of M, +compute the G-orbit (not the Z[G] orbit!) of o. +""" +function Oscar.orbit(C::GModule, o) + @assert parent(o) == C.M + return orbit(C.G, (x,y) -> action(C, y, x), o) end """ @@ -1834,7 +1855,7 @@ function shrink(C::GModule{PermGroup, GrpAbFinGen}, attempts::Int = 10) for i=1:attempts o = Oscar.orbit(q, rand(gens(q.M))) if length(o) == order(group(q)) - s, ms = sub(q.M, o) + s, ms = sub(q.M, collect(o)) if rank(s) == length(o) q, _mq = quo(q, ms) if first @@ -1893,5 +1914,5 @@ end end # module GrpCoh using .GaloisCohomology_Mod -export is_coboundary, idel_class_gmodule, relative_brauer_group +export is_coboundary, idel_class_gmodule, relative_brauer_group, units_mod_ideal diff --git a/experimental/GaloisGrp/src/Solve.jl b/experimental/GaloisGrp/src/Solve.jl index f7562c7a28bd..0e4f35313d84 100644 --- a/experimental/GaloisGrp/src/Solve.jl +++ b/experimental/GaloisGrp/src/Solve.jl @@ -11,7 +11,7 @@ end mutable struct SubField coeff_field::Union{Nothing, SubField} - fld::AbstractAlgebra.Field # Q or a NumField + fld::AbstractAlgebra.Field # Q or a NumField or not... grp::PermGroup #the fix group @@ -19,14 +19,14 @@ mutable struct SubField conj::Vector{PermGroupElem} # the absolute conjugates ts::ZZPolyRingElem # tschirnhaus - exact_den::Union{QQFieldElem, NumFieldElem} # in this field! f'(alpha) + exact_den::FieldElem #Union{QQFieldElem, NumFieldElem} # in this field! f'(alpha) dual_basis::Vector # symbolic: coeffs of f/t-pe basis::Vector # in fld, symbolic: [pe^i//exact_den for i=0:n-1] basis_abs::Vector #Caches: - num_basis::MatElem{qadic} - num_dual_basis::Vector{Vector{qadic}} + num_basis::MatElem{<:RingElem} # qadic or power series + num_dual_basis::Vector{Vector{<:RingElem}} #padic or power series function SubField() return new() @@ -177,13 +177,15 @@ function _fixed_field(C::GaloisCtx, S::SubField, U::PermGroup; invar=nothing, ma PE, _ = Oscar.GaloisGrp.relative_invariant(S.grp, U) end - rt = roots(C, 5) + rt = roots(C, Oscar.GaloisGrp.bound_to_precision(C, C.B)) ts = Oscar.GaloisGrp.find_transformation(rt, PE, t) B1 = length(t)*Oscar.GaloisGrp.upper_bound(C, PE^(1+length(t)), ts) B2 = dual_basis_bound(C, S) B = B2*B1 #maybe dual_basis_bound should do bound_ring stuff? pr = Oscar.GaloisGrp.bound_to_precision(C, B) - pr = min(pr, max_prec) + if isa(pr, Int) + pr = min(pr, max_prec) + end rt = roots(C, pr) if ts != gen(parent(ts)) rt = map(ts, rt) @@ -217,7 +219,8 @@ function _fixed_field(C::GaloisCtx, S::SubField, U::PermGroup; invar=nothing, ma f = power_sums_to_polynomial(pp) SS = SubField() - SS.fld, a = number_field(f, cached = false, check = false) + SS.fld, a =extension_field(f, cached = false, check = false) + f = defining_polynomial(SS.fld) SS.exact_den = derivative(f)(a) SS.basis = basis(SS.fld) KT, T = polynomial_ring(SS.fld, cached = false) @@ -232,6 +235,18 @@ function _fixed_field(C::GaloisCtx, S::SubField, U::PermGroup; invar=nothing, ma return SS end +function Oscar.extension_field(f::AbstractAlgebra.Generic.Poly{QQPolyRingElem}; cached::Bool, check::Bool) + C = base_ring(f) + Qt, t = RationalFunctionField(QQ, symbols(C)[1], cached = false) + ff = map_coefficients(x->x(t), f) + return extension_field(ff, cached = cached, check = check) +end + +function Oscar.extension_field(f::AbstractAlgebra.Generic.Poly{<:NumFieldElem}; cached::Bool, check::Bool) + return number_field(f; cached, check) +end + + function refined_derived_series(G::PermGroup) s = GAP.Globals.PcSeries(GAP.Globals.Pcgs(G.X)) return Oscar._as_subgroups(G,s) @@ -295,7 +310,7 @@ end #a bound on the largest conjugate of an absolute dual basis (product basis) function dual_basis_bound(C::GaloisCtx, S::SubField) if S.fld == QQ - return ZZRingElem(1) + return parent(C.B)(ZZRingElem(1)) end # return upper_bound(ZZRingElem, maximum(x->maximum(abs, Oscar.conjugates(x)), S.dual_basis))*dual_basis_bound(S.coeff_field) return maximum([length_bound(C, S, x) for x = S.dual_basis])*dual_basis_bound(C, S.coeff_field) @@ -316,6 +331,42 @@ function length_bound(C::GaloisCtx, S::SubField, x::Union{QQFieldElem,NumFieldEl B = Oscar.GaloisGrp.upper_bound(C, S.pe).val return sum(length_bound(C, S.coeff_field, coeff(f, i))*B^i for i=0:degree(f)) end + +function (R::AbstractAlgebra.Generic.PolyRing{AbstractAlgebra.Generic.RationalFunctionFieldElem{QQFieldElem, QQPolyRingElem}})(b::AbstractAlgebra.Generic.FunctionFieldElem{QQFieldElem}) + S = base_ring(R) + return map_coefficients(x->S(x, b.den), b.num, parent = R) +end + +function length_bound(C::GaloisCtx, S::SubField, x::AbstractAlgebra.Generic.RationalFunctionFieldElem) + R = parent(C.B) + if iszero(x) + return R(0) + end + + if degree(S.fld) == 1 + return R(1) + end + + f = parent(defining_polynomial(S.fld))(x) + @assert x.den == 1 + n = numerator(x) + @assert denominator(x) == 1 + return R(numerator(x)) +end + + +function length_bound(C::GaloisCtx, S::SubField, x::AbstractAlgebra.Generic.FunctionFieldElem) + R = parent(C.B) + if iszero(x) + return R(0) + end + f = parent(defining_polynomial(S.fld))(x) + + B = Oscar.GaloisGrp.upper_bound(C, S.pe) + return sum(length_bound(C, S.coeff_field, coeff(f, i))*B^i for i=0:degree(f)) +end + + function Hecke.length(x::NumFieldElem, abs_tol::Int = 32, T = arb) return sum(x^2 for x = Oscar.conjugates(x, abs_tol, T)) end @@ -369,7 +420,7 @@ For a cyclic extension K/k with Automorphism group generated by aut and a corresponding primitive n-th root of 1, find an isomorphic radical extension using Lagrange resolvents. """ -function as_radical_extension(K::NumField, aut::Map, zeta::NumFieldElem) +function as_radical_extension(K::NumField, aut::Map, zeta::NumFieldElem; simplify::Bool = !false) CHECK = get_assert_level(:SolveRadical) > 0 g = gen(K) @@ -390,14 +441,21 @@ function as_radical_extension(K::NumField, aut::Map, zeta::NumFieldElem) end s = coeff(r^d, 0) @hassert :SolveRadical 1 s == r^d + if simplify + p = parent(s) + k, ma = absolute_simple_field(p) + t = ma(evaluate(Hecke.reduce_mod_powers(preimage(ma, s), d))) + r = ma(root(preimage(ma, s//t), d))*r + s = t + @hassert :SolveRadical 1 s == r^d + end L, b = number_field(gen(parent(defining_polynomial(K)))^d-s, cached = false, check = CHECK) @assert base_field(L) == base_field(K) - global last_b = (L, K, r) return L, hom(L, K, r, check = CHECK) end -function Oscar.solve(f::QQPolyRingElem; max_prec::Int=typemax(Int)) - return solve(numerator(f), max_prec = max_prec) +function Oscar.solve(f::QQPolyRingElem; max_prec::Int=typemax(Int), simplify::Bool = false) + return solve(numerator(f), max_prec = max_prec, simplify = simplify) end @doc raw""" @@ -436,7 +494,7 @@ julia> solve(cyclotomic(12, x)) #zeta_12 as radical ``` """ -function Oscar.solve(f::ZZPolyRingElem; max_prec::Int=typemax(Int), show_radical::Bool = false) +function Oscar.solve(f::ZZPolyRingElem; max_prec::Int=typemax(Int), show_radical::Bool = false, simplify::Bool = false) #if poly is not monic, the roots are scaled (by default) to #make them algebraically integral. This has to be compensated #in a couple of places... @@ -534,6 +592,27 @@ function Oscar.solve(f::ZZPolyRingElem; max_prec::Int=typemax(Int), show_radical push!(h_data, gen(K)) h = hom(L, K, h_data..., check = CHECK) end + if simplify + s = -coeff(defining_polynomial(K), 0) + p = parent(s) + if p == QQ + lf = factor(s, ZZ) + t = prod([p for (p, k) = lf.fac if isodd(k)], init = ZZ(1)) * lf.unit + r = root(s//t, 2) + else + _, ma = absolute_simple_field(p) + t = ma(evaluate(Hecke.reduce_mod_powers(preimage(ma, s), 2))) + r = ma(root(preimage(ma, s//t), 2)) + end + K_, _ = radical_extension(2, t, check = false, cached = false) + if h_data[end] == gen(K) + h_data[end] = gen(K_)*r + else + h_data[end] = gen(K_)*r+coeff(h_data[end]-gen(K), 0) + end + K = K_ + h = hom(L, K, h_data..., check = CHECK) + end else @vtime :SolveRadical 2 Ra, hh = as_radical_extension(L, aut[i-length(pp)-1], zeta[findfirst(isequal(degree(L)), lp)]) #hh: new -> old @@ -557,23 +636,48 @@ function Oscar.solve(f::ZZPolyRingElem; max_prec::Int=typemax(Int), show_radical return K, Vector{Any}(map(h, R)) end +function basis_at_prec(C::GaloisCtx, S::SubField, pr) + rt = roots(C, pr) + if isdefined(S, :ts) + rt = map(S.ts, rt) + end + for i=1:length(S.conj) + S.num_basis[1, i] = one(parent(rt[1])) + c = S.conj[i] + p = evaluate(S.pe, c, rt) + S.num_basis[2, i] = p + for j=3:degree(S.fld) + S.num_basis[j, i] = p*S.num_basis[j-1, i] + end + end +end + function conjugates(C::GaloisCtx, S::SubField, a::NumFieldElem, pr::Int = 10) @assert parent(a) == S.fld if !isdefined(S, :num_basis) || precision(S.num_basis[1,1]) < pr - rt = roots(C, pr) - if isdefined(S, :ts) - rt = map(S.ts, rt) - end - for i=1:length(S.conj) - S.num_basis[1, i] = setprecision(S.num_basis[1, i], pr) - c = S.conj[i] - p = evaluate(S.pe, c, rt) - S.num_basis[2, i] = p - for j=3:degree(S.fld) - S.num_basis[j, i] = p*S.num_basis[j-1, i] - end - end + basis_at_prec(C, S, pr) end + + return conj_from_basis(C, S, a, pr) +end + +function conjugates(C::GaloisCtx, S::SubField, a::Generic.FunctionFieldElem, pr::Tuple{Int, Int} = (10, 5)) + @assert parent(a) == S.fld + if !isdefined(S, :num_basis) || precision(S.num_basis[1,1]) < pr[2] || precision(coeff(S.num_basis[1,1], 0)) < pr[1] + basis_at_prec(C, S, pr) + end + + return conj_from_basis(C, S, a, pr) +end + +function conjugates(C::GaloisCtx, S::SubField, a::Generic.RationalFunctionFieldElem, pr::Tuple{Int, Int} = (10, 5)) + r = roots(C, pr) + @assert denominator(a) == 1 + return [numerator(a)(gen(parent(r[1])))] +end + + +function conj_from_basis(C::GaloisCtx, S::SubField, a, pr) nb = S.num_basis K = base_ring(nb) coef = zero_matrix(K, 1, degree(S.fld)) @@ -590,7 +694,7 @@ function conjugates(C::GaloisCtx, S::SubField, a::NumFieldElem, pr::Int = 10) end -function dual_basis_conj(C::GaloisCtx, S::SubField, pr::Int = 10) +function dual_basis_conj(C::GaloisCtx, S::SubField, pr::Any = 10) r = roots(C, pr) if S.fld == QQ return [[parent(r[1])(1)]] diff --git a/src/NumberTheory/GaloisGrp/GaloisGrp.jl b/src/NumberTheory/GaloisGrp/GaloisGrp.jl index d1591b568098..995c9aceb025 100644 --- a/src/NumberTheory/GaloisGrp/GaloisGrp.jl +++ b/src/NumberTheory/GaloisGrp/GaloisGrp.jl @@ -381,10 +381,10 @@ mutable struct GaloisCtx{T} return r end - function GaloisCtx(f::ZZPolyRingElem) + function GaloisCtx(f::ZZPolyRingElem, field::Union{Nothing, AnticNumberField}) r = new{SymbolicRootCtx}() r.f = f - r.C = SymbolicRootCtx(f) + r.C = SymbolicRootCtx(f, field) r.B = add_ring()(leading_coefficient(f)*roots_upper_bound(f)) r.chn = Tuple{PermGroup, SLPoly, ZZPolyRingElem, Vector{PermGroupElem}}[] return r @@ -539,13 +539,19 @@ end mutable struct SymbolicRootCtx f::ZZPolyRingElem rt::Vector{nf_elem} - function SymbolicRootCtx(f::ZZPolyRingElem) + function SymbolicRootCtx(f::ZZPolyRingElem, ::Nothing) @assert ismonic(f) _, rt = splitting_field(f, do_roots = true) return new(f, rt) end + function SymbolicRootCtx(f::ZZPolyRingElem, field::AnticNumberField) + @assert ismonic(f) + rt = roots(f, field) + return new(f, rt) + end + function SymbolicRootCtx(f::QQPolyRingElem) - return SymbolicRootCtx(numerator(f)) + return SymbolicRootCtx(numerator(f), nothing) end end @@ -1322,9 +1328,16 @@ function sum_orbits(K, Qt_to_G, r) @assert all(isone, values(fg.fac)) O = [] + if isa(r[1], acb) + mm = collect(m) + end for f = keys(fg.fac) r = roots(map_coefficients(Qt_to_G, f)) - push!(O, [m[x] for x = r]) + if isa(r[1], acb) + push!(O, [mm[argmin(map(x->abs(x[1]-y), mm))][2] for y = r]) + else + push!(O, [m[x] for x = r]) + end end @vprint :GaloisGroup 2 "partitions: $O\n" return O @@ -1465,12 +1478,19 @@ function starting_group(GC::GaloisCtx, K::T; useSubfields::Bool = true) where T # the poset should make this trivial. @vprint :GaloisGroup 1 "computing group of subfield of degree $(degree(s))\n" local g, gc - if valuation(discriminant(s), GC.prime) > 0 + if isa(parent(c[1]), FlintLocalField) && + valuation(discriminant(s), GC.prime) > 0 throw(Hecke.BadPrime(GC.prime)) end g, gc = try Hecke.pushindent() - galois_group(s, prime = GC.prime) + if isa(c[1], NumFieldElem) + galois_group(s, algorithm = :Symbolic, field = parent(c[1])) + elseif isa(c[1], FlintLocalFieldElem) + galois_group(s, prime = GC.prime) + else + galois_group(s, algorithm = :Complex) + end catch e #can "legally" be BadPrime, will be dealt with one level up Hecke.popindent() rethrow() @@ -1481,13 +1501,24 @@ function starting_group(GC::GaloisCtx, K::T; useSubfields::Bool = true) where T pr = 5 r = roots(gc, pr, raw = true) - R = roots(GC,pr, raw = true) + R = roots(GC, pr, raw = true) gg = map_coefficients(x->map_coeff(GC, x), parent(K.pol)(ms(gen(s)))) d = map(gg, R) - f, mf = residue_field(parent(r[1])) - _F, mF = residue_field(parent(R[1])) - mfF = find_morphism(f, _F) + if isa(r[1], nf_elem) + @assert parent(r[1]) == parent(R[1]) + f = _F = parent(r[1]) + mf = mF = x->x + mfF = x->x + elseif isa(r[1], acb) + f = _F = parent(r[1]) + mf = mF = x->x + mfF = x->x + else + f, mf = residue_field(parent(r[1])) + _F, mF = residue_field(parent(R[1])) + mfF = find_morphism(f, _F) + end #we should have # - d == r (in the appropriate setting) # - order(Set(map(mF, d))) == order(Set(map(mf, r))) == degree(s) @@ -1529,9 +1560,9 @@ function starting_group(GC::GaloisCtx, K::T; useSubfields::Bool = true) where T #TODO: make generic!!! if isa(c[1], acb) d = map(conj, c) - si = [findfirst(y->abs(y-x) < 1e-9, c) for x = d] + si = [argmin(map(y->abs(y-x), c)) for x = d] elseif isa(c[1], nf_elem) #.. and use automorphism - si = collect(1:length(d)) + si = collect(1:length(c)) else d = map(frobenius, c) si = [findfirst(y->y==x, c) for x = d] @@ -1626,7 +1657,15 @@ function starting_group(GC::GaloisCtx, K::T; useSubfields::Bool = true) where T if length(bs) == 0 #primitive case: no subfields, no blocks, primitive group! push!(F, is_primitive, "primitivity") pc = parent(c[1]) - k, mk = residue_field(pc) + if isa(pc, NumField) + k = pc + mk = x->x + elseif isa(c[1], acb) + k = pc + mk = x->x + else + k, mk = residue_field(pc) + end O = sum_orbits(K, x->mk(pc(map_coeff(GC, x))), map(mk, c)) GC.start = (2, O) @@ -1907,7 +1946,7 @@ julia> roots(C, 2) (19^0 + O(19^2))*a + 11*19^0 + 19^1 + O(19^2) ``` """ -function galois_group(K::AnticNumberField, extra::Int = 5; useSubfields::Bool = true, pStart::Int = 2*degree(K), prime::Int = 0, algorithm::Symbol=:pAdic) +function galois_group(K::AnticNumberField, extra::Int = 5; useSubfields::Bool = true, pStart::Int = 2*degree(K), prime::Int = 0, algorithm::Symbol=:pAdic, field::Union{Nothing, AnticNumberField} = nothing) @assert algorithm in [:pAdic, :Complex, :Symbolic] @@ -1936,7 +1975,7 @@ function galois_group(K::AnticNumberField, extra::Int = 5; useSubfields::Bool = elseif algorithm == :Complex GC = GaloisCtx(Hecke.Globals.Zx(numerator(K.pol)), AcbField(20)) elseif algorithm == :Symbolic - GC = GaloisCtx(Hecke.Globals.Zx(numerator(K.pol))) + GC = GaloisCtx(Hecke.Globals.Zx(numerator(K.pol)), field) else error("wrong algorithm used") end @@ -2278,7 +2317,7 @@ function fixed_field(GC::GaloisCtx, U::PermGroup, extra::Int = 5) end #XXX: seems to be broken for reducible f, ie. intransitive groups a, T = relative_invariant(G, U) - r = roots(GC, 5) + r = roots(GC, bound_to_precision(GC, GC.B)) ts = find_transformation(r, a, T, RNG = MersenneTwister(1)) B = upper_bound(GC, a, ts) @@ -2476,7 +2515,7 @@ function galois_ideal(C::GaloisCtx, extra::Int = 5) #TODO: the subfields use, implicitly, special invariants, so # we should be able to avoid the chain G = symmetric_group(n) - if C.start[1] == 1 # start with intersection of wreath products + if isdefined(C, :start) && C.start[1] == 1 # start with intersection of wreath products _, g = slpoly_ring(ZZ, n) for bs = C.start[2] W = PermGroup(wreath_product(symmetric_group(length(bs[1])), symmetric_group(length(bs)))) diff --git a/src/NumberTheory/GaloisGrp/Qt.jl b/src/NumberTheory/GaloisGrp/Qt.jl index dd75c87180e5..9a8fa0dc5767 100644 --- a/src/NumberTheory/GaloisGrp/Qt.jl +++ b/src/NumberTheory/GaloisGrp/Qt.jl @@ -586,7 +586,7 @@ function isinteger(G::GaloisCtx, B::BoundRingElem{Tuple{ZZRingElem, Int, QQField p = bound_to_precision(G, B) p2 = min(p[2], precision(r)) - Qx = parent(numerator(gen(base_ring(G.f)))) + Qx = parent((gen(base_ring(G.f)))) if iszero(r) return true, Qx(0) end @@ -624,6 +624,10 @@ function isinteger(G::GaloisCtx, B::BoundRingElem{Tuple{ZZRingElem, Int, QQField return true, f(gen(parent(f))-G.data[2]) #.. and unshift end +function (a::Generic.RationalFunctionFieldElem)(b::RingElem) + return divexact(numerator(a)(b), denominator(a)(b)) +end + function Hecke.newton_polygon(f::Generic.Poly{<:Generic.RationalFunctionFieldElem{QQFieldElem}}) pt = Tuple{Int, Int}[] for i=0:degree(f) diff --git a/test/Experimental/gmodule.jl b/test/Experimental/gmodule.jl index 1918af5262ec..348b06add39b 100644 --- a/test/Experimental/gmodule.jl +++ b/test/Experimental/gmodule.jl @@ -41,8 +41,13 @@ end G = SL(2, 3) @test length(Oscar.RepPc.reps(abelian_closure(QQ)[1], G)) == 7 -end + @test length(Oscar.RepPc.reps(GF(7, 6), G)) == 7 + @test length(Oscar.RepPc.reps(GF(2, 6), G)) == 3 + G = fp_group(gens(G))[1] + q, mq = maximal_abelian_quotient(PcGroup, G) + @test length(Oscar.RepPc.brueckner(mq)) == 24 +end @testset "Experimental LocalH2" begin Qx, x = QQ["x"] @@ -118,3 +123,42 @@ end @test degree(base_ring(SS)) == 2 end + +@testset "Various" begin + @test length(all_extensions(abelian_group(2), cyclic_group(PermGroup, 2))) == 2 + k, a = cyclotomic_field(5) + zk = maximal_order(k) + + U, mU = Oscar.GaloisCohomology_Mod.units_mod_ideal(5*zk) + A, mA = automorphism_group(PermGroup, k) + + C = gmodule(A, mU) + U, mU = unit_group_fac_elem(zk) + C = gmodule(A, mU) + C = C ⊗ C + @test elementary_divisors(C.M) == ZZRingElem[10, 10, 10, 0] + + C = C ⊕ C + + D, _ = Oscar.GModuleFromGap.ghom(C, C) + @test dim(D) == 4 + + C = gmodule(GF(5), C) + i = indecomposition(C) + @test length(i) == 8 +end + +@testset "H^3" begin + # This is C = pi_2(X_P) for the standard presentation of Q_8 + # We know that H^3(G, C) = Z/|G|Z + genss = [@perm((1,2,6,3)(4,8,5,7)), @perm((1,4,6,5)(2,7,3,8))]; + G, = sub(symmetric_group(8), genss); + @test small_group_identification(G) == (8, 4) + mats = [[0, -1, 0, 0, 0, 0, 1, 0, -1, 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, -1, 1, 0, -1, 0, 0, 0, -1, 0, 1, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1]]; + F = free_abelian_group(7); + M1, M2 = matrix(ZZ, 7, 7, mats[1]), matrix(ZZ, 7, 7, mats[2]); + C = gmodule(G, [hom(F, F, M1), hom(F, F, M2)]); + q = cohomology_group(C, 3) + @test order(q) == 8 + @test is_cyclic(q) +end