diff --git a/experimental/GModule/src/Cohomology.jl b/experimental/GModule/src/Cohomology.jl index 51af4ffaf8f2..9e0bfe52f294 100644 --- a/experimental/GModule/src/Cohomology.jl +++ b/experimental/GModule/src/Cohomology.jl @@ -1944,7 +1944,7 @@ the corresponding elt in the extension. If the gmodule is defined via a pc-group and the 1st argument is the `Type{PcGroup}`, the resulting group is also pc. """ -function extension(c::CoChain{2,<:Oscar.GAPGroupElem}) +function extension(::Type{FPGroup}, c::CoChain{2,<:Oscar.GAPGroupElem}) C = c.C G = Group(C) F = codomain(isomorphism(FPGroup, G, on_gens=true)) @@ -2253,7 +2253,7 @@ function split_extension(C::GModule) 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)) + return extension(FPGroup, CoChain{2, S, T}(C, c)) end function split_extension(::Type{PcGroup}, C::GModule{<:PcGroupElem}) @@ -2396,7 +2396,7 @@ function pc_group(c::CoChain{2, <:Oscar.PcGroupElem}) end function Oscar.permutation_group(c::CoChain{2}) - g = extension(c)[1] + g = extension(FPGroup, c)[1] return permutation_group(g) end diff --git a/experimental/GModule/src/GModule.jl b/experimental/GModule/src/GModule.jl index dd0ffe79e62a..0d620735e4e0 100644 --- a/experimental/GModule/src/GModule.jl +++ b/experimental/GModule/src/GModule.jl @@ -1,13 +1,13 @@ isdefined(Oscar, :word) || function word end include("Cohomology.jl") +include("Types.jl") include("GaloisCohomology.jl") include("GrpExt.jl") include("Misc.jl") module GModuleFromGap using Oscar -using Hecke import Hecke: data #XXX: clash of names! @@ -43,7 +43,7 @@ julia> C = gmodule(CyclotomicField, C); julia> h = subfields(base_ring(C), degree = 2)[1][2]; julia> restriction_of_scalars(C, h) -G-module for G acting on vector space of dimension 4 over number field +(G-module for G acting on vector space of dimension 4 over number field, Map: C -> g-module for G acting on vector space of dimension 4 over number field) julia> restriction_of_scalars(C, QQ) G-module for G acting on vector space of dimension 8 over QQ @@ -56,9 +56,15 @@ function restriction_of_scalars(M::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.F @assert codomain(phi) == base_ring(M) d = divexact(degree(codomain(phi)), degree(domain(phi))) F = free_module(domain(phi), dim(M)*d) - _, _, rep = relative_field(phi) + _, coord, rep = relative_field(phi) - return GModule(F, group(M), [hom(F, F, hvcat(dim(M), [rep(x) for x in transpose(matrix(y))]...)) for y in M.ac]) + D = GModule(F, group(M), [hom(F, F, hvcat(dim(M), [rep(x) for x in transpose(matrix(y))]...)) for y in M.ac]) + #the blow-up function is not a "nice" module hom as tis is used + #to make from a K-Module to e.g. a QQ-module, so the map + #will be QQ-linear and we'd need to get QQ-gens from a K-module + #also: pre-image is not working (not implemented) (needs more info from + #relative_field) + return D, hom(M, D, MapFromFunc(M.M, D.M, x->D.M(vcat([coord(t) for t = x.v[1,:]]...))); check = false) end function restriction_of_scalars(C::GModule{<:Any, <:AbstractAlgebra.FPModule{AbsSimpleNumFieldElem}}, ::QQField) @@ -281,7 +287,7 @@ function invariant_lattice_classes(M::GModule{<:Oscar.GAPGroup, <:AbstractAlgebr pG = p.*gens(M.M) for s in S x, mx = sub(M.M, vcat(pG, [M.M(map_entries(x->lift(ZZ, x), s[i:i, :])) for i in 1:nrows(s)])) - r = (sub(M, mx), mx) + r = (sub(M, mx)[1], mx) if any(x->is_isomorphic(r[1], x[1]), res) continue else @@ -313,7 +319,7 @@ function maximal_submodule_bases(M::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra. return res end -function maximal_submodules(M::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) +function Oscar.maximal_submodules(M::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) return [sub(M, s) for s = maximal_submodule_bases(M)] end @@ -496,7 +502,7 @@ function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{AbsSi k char field | Q - + So: V is given as G -> GL(n, K) This is represented by sigma: Gal(K/k)^2 -> K a 2-chain @@ -520,7 +526,7 @@ function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{AbsSi split by A in GL(n, K) Now, technically, A V A^-1 has values in Gl(n, E) Step 7: - Replacing V -> A V A^-1 changes + Replacing V -> A V A^-1 changes X_g -> A^g X A^-1 As A V A^-1 is in GL(n, E), A^g X A^-1 can be normalized (mult. by scalar in K) to be in Gl(n, E) @@ -543,7 +549,7 @@ function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{AbsSi d = reduce(lcm, [x[2] for x = ld], init = 1) s = subfields(base_ring(V)) - + s = [x for x in s if degree(x[1]) >= d*degree(k)] sort!(s, lt = (a,b) -> degree(a[1]) < degree(b[1])) for (m, mm) in s @@ -600,7 +606,7 @@ function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{AbsSi #we need Gal(E/k) as the quotient of A/U q, mq = quo(domain(mA), U) X = Dict( g => map_entries(mA(preimage(mq, g)), AA) * X[preimage(mq, g)] * AAi for g = q) - for (g, x) = X + for (g, x) = X lf = findfirst(!iszero, x) x *= inv(x[lf]) X[g] = map_entries(pseudo_inv(mm), x) @@ -636,7 +642,7 @@ function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{AbsSi LD = Dict{AbsSimpleNumFieldOrderIdeal, Int}() LI = Dict{AbsSimpleNumFieldEmbedding, Int}() for (p, d) = ld - if p == -1 + if p == -1 @assert d == 2 if signature(k)[2] == 0 for e = real_embeddings(k) @@ -659,13 +665,13 @@ function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{AbsSi C, mC = automorphism_group(PermGroup, EF) gE = mE_EF(E[1]) hBC = hom(C, B, [[b for b = B if mC(c)(gE) == mE_EF(mB(b)(E[1]))][1] for c = gens(C)]) - gF = mF_EF(F[1]) - U, mU = sub(C, [c for c = C if mC(c)(gF) == gF]) + gF = mF_EF(F[1]) + U, mU = sub(C, [c for c = C if mC(c)(gF) == gF]) MEF = MultGrp(EF) #inflate s = Dict{NTuple{2, elem_type(U)}, elem_type(MEF)}((f, g) => MEF(mE_EF(s[(hBC(f), hBC(g))])) for f = U for g = U) - + D = gmodule(U, [hom(MEF, MEF, mC(mU(x))) for x = gens(U)]) Sigma = CoChain{2,PermGroupElem, MultGrpElem{AbsSimpleNumFieldElem}}(D, s) @@ -673,7 +679,7 @@ function _minimize(V::GModule{<:Oscar.GAPGroup, <:AbstractAlgebra.FPModule{AbsSi @assert fl #inflate X X = Dict( g => map_entries(mE_EF, X[preimage(h, hBC(mU(g)))]) *mu(g).data for g = U) - @hassert :MinField 1 isone_cochain(X, mU*mC) + @hassert :MinField 1 isone_cochain(X, mU*mC) @vtime :MinField 2 BB, BBi = hilbert90_generic(X, mU*mC) c = content_ideal(BB) sd = Hecke.short_elem(inv(c)) @@ -697,7 +703,8 @@ end function irreducible_modules(::QQField, G::Oscar.GAPGroup) #if cyclo is not minimal, this is not irreducible z = irreducible_modules(CyclotomicField, G) - return [gmodule(QQ, descent_to_minimal_degree_field(m)) for m in z] + temp = map(x -> galois_orbit_sum(character(x)), z) + return [gmodule(QQ, descent_to_minimal_degree_field(z[i])) for i in unique(i -> temp[i], 1:length(temp))] end function irreducible_modules(::ZZRing, G::Oscar.GAPGroup) @@ -926,7 +933,8 @@ function Oscar.sub(C::GModule{<:Any, <:AbstractAlgebra.FPModule{T}}, m::MatElem{ 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) + D = 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]) + return D, hom(C, D, b) return b end @@ -936,9 +944,19 @@ function Oscar.sub(M::GModule{<:Any, <:AbstractAlgebra.FPModule{T}}, f::Abstract @assert codomain(f) == M.M S = domain(f) Sac = [hom(S, S, [preimage(f, h(f(x))) for x in gens(S)]) for h in M.ac] - return gmodule(S, M.G, Sac) + D = gmodule(S, M.G, Sac) + return D, hom(D, M, f) +end + +function Oscar.sub(M::GModule{<:Any, FinGenAbGroup}, f::FinGenAbGroupHom) + @assert codomain(f) == M.M + S = domain(f) + Sac = [hom(S, S, [preimage(f, h(f(x))) for x in gens(S)]) for h in M.ac] + D = gmodule(S, M.G, Sac) + return D, hom(D, M, f) end + function gmodule(k::Nemo.FinField, C::GModule{<:Any, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) @assert absolute_degree(k) == 1 F = free_module(k, dim(C)*absolute_degree(base_ring(C))) @@ -1324,14 +1342,14 @@ function Oscar.is_coboundary(c::CoChain{1,PermGroupElem,MultGrpElem{AbsSimpleNum cnt = 0 while true local Y - while true + while true Y = rand(K, -5:5) iszero(Y) || break end cnt += 1 S = sum(mA(emb(g))(Y)*c((g,)).data for g = G) is_zero(S) || return true, mK(S) - if cnt > 10 + if cnt > 10 error("should not happen") end end @@ -1824,8 +1842,8 @@ end function invariant_forms(C::GModule{<:Any, <:AbstractAlgebra.FPModule}) D = Oscar.dual(C) h = hom_base(C, D) - k = kernel(transpose(reduce(vcat, [matrix(base_ring(C), 1, dim(C)^2, _vec(x-transpose(x))) for x = h]))) - return [sum(h[i]*k[i, j] for i=1:length(h)) for j=1:ncols(k)] + k = kernel((reduce(vcat, [matrix(base_ring(C), 1, dim(C)^2, _vec(x-transpose(x))) for x = h]))) + return [sum(h[i]*k[j,i] for i=1:length(h)) for j=1:nrows(k)] end function Oscar.is_isomorphic(A::GModule{T, <:AbstractAlgebra.FPModule{<:FinFieldElem}}, B::GModule{T, <:AbstractAlgebra.FPModule{<:FinFieldElem}}) where T @@ -1934,12 +1952,22 @@ function action_matrices(C::GModule{<:Any, <:AbstractAlgebra.FPModule}) end function Oscar.simplify(C::GModule{<:Any, <:AbstractAlgebra.FPModule{ZZRingElem}}) - f = invariant_forms(C)[1] +# f = invariant_forms(C)[1] +#thsi will not give pos. def. forms!!! we need to go via Reynolds. # @assert all(i->det(f[1:i, 1:i])>0, 1:nrows(f)) m = map(matrix, C.ac) S = identity_matrix(ZZ, dim(C)) while true + f = zero_matrix(ZZ, dim(C), dim(C)) + for i=gens(C.G) + x = action(C, i) + f = f + matrix(x)*transpose(matrix(x)) + end +# @assert is_symmetric(f) +# @assert is_positive_definite(f) L, T = lll_gram_with_transform(f) +# @assert L == T*f*transpose(T) + Ti = inv(T) n = [T*x*Ti for x = m] if length(string(n)) >= length(string(m)) diff --git a/experimental/GModule/src/GrpExt.jl b/experimental/GModule/src/GrpExt.jl index 3f8f08b8e4c6..669543b4850f 100644 --- a/experimental/GModule/src/GrpExt.jl +++ b/experimental/GModule/src/GrpExt.jl @@ -4,6 +4,7 @@ using Oscar import Base: *, ==, one, rand, show, iterate export GrpExt, GrpExtElem export commutator_decomposition_map +import Oscar.GAPWrap """ A type representing the group extension by a 2-co-cycle. @@ -48,11 +49,191 @@ _module(P::GrpExt) = gmodule(P).M _group(P::GrpExt) = gmodule(P).G Oscar.parent(g::GrpExtElem) = g.P +Oscar.elem_type(::Type{GrpExt{S, T}}) where {S, T} = GrpExtElem{S, T} +function Oscar.extension(c::Oscar.GrpCoh.CoChain{2,<:Oscar.GAPGroupElem}) + return GrpExt(c) +end + +#g in H +# -> g in G where the gens to be used in G are different +function shiftgens(g::FPGroupElem, G, offset) + w = GapObj(g) + famG = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(GapObj(G))) + if GAP.Globals.IsLetterAssocWordRep(w) + l = copy(GAP.Globals.LetterRepAssocWord(w)) + for i in 1:length(l) + if l[i] > 0 + l[i] = l[i] + offset + else + l[i] = l[i] - offset + end + end + ll = GAP.Globals.AssocWordByLetterRep(famG, l) + else + l = copy(GAPWrap.ExtRepOfObj(w)) + for i in 1:2:length(l) + l[i] = l[i] + offset + end + ll = GAPWrap.ObjByExtRep(famG, l) + end + return FPGroupElem(G, ll) +end + +function Oscar.isomorphism(::Type{FPGroup}, E::GrpExt) + c = E.c + C = c.C + G = C.G + mGF = Oscar.isomorphism(FPGroup, G, on_gens=true) #G -> F + F = codomain(mGF) + M = C.M + ac = action(C) + mfM = inv(Oscar.isomorphism(FPGroup, M)) + fM = domain(mfM) + + N = free_group(ngens(G) + ngens(fM)) + + s = map(x->shiftgens(x, N, ngens(G)), relators(fM)) + for R = relators(F) + t = map_word(R, gens(E)[1:ngens(G)]) + push!(s, shiftgens(R, N, 0)*(shiftgens(preimage(mfM, t.m), N, ngens(G)))) + end + for i=1:ngens(G) + for j=1:ngens(fM) + #g[i]*t = m[j]*g[i] = g[i] m[j]^g[i] = m[j] g[i] (cancellation in conj) + t = preimage(mfM, ac[i](gen(M, j))) + push!(s, gen(N, ngens(G)+j)*gen(N, i)*inv(shiftgens(t, N, ngens(G))) * inv(gen(N, i))) + end + end + Q, mQ = quo(N, s) + @assert ngens(Q) == ngens(N) + function EtoQ(x::GrpExtElem) + w = mGF(x.g) + ww = map_word(w, gens(E)[1:ngens(G)]) #this performs a collection + #and will transform x.g into + #canonical form + return mQ(shiftgens(w, N, 0)*shiftgens(preimage(mfM, x.m-ww.m), N, ngens(G))) + + end + return MapFromFunc(E, Q, EtoQ, y->map_word(y, gens(E))) + #the projection will be hom(Q, G, vcat(gens(G), ones(G, ???) + #the injection should be hom(M, Q, gens(Q)[ngens(G)+1:end]) + #both can/ should be handled by shiftgens (or sylable/ create) +end + +# convert syllables in canonical form into exponent vector +#Thomas +function exponent_vector(sylls::Vector{Pair{Int64, ZZRingElem}}, n) + res = zeros(ZZRingElem, n) + for pair in sylls + @assert res[pair.first] == 0 #just to make sure + res[pair.first] = pair.second + end + return res +end + +function Oscar.isomorphism(::Type{PcGroup}, E::GrpExt) + c = E.c + C = c.C + G = C.G + @assert isa(G, PcGroup) + M = C.M + mMf = Oscar.isomorphism(PcGroup, M) # M -> PcGroup + #TODO: this group is internal only. Should it be made available? + #TODO: this should be using the new syllable/ expo vector interface and + # not do any group arithmetic + fM = codomain(mMf) + + cM = collector(fM) + cG = collector(G) + nG = ngens(G) + cE = collector(nG + ngens(fM)) + + set_relative_orders!(cE, vcat(get_relative_orders(cG), get_relative_orders(cM))) + + for i=1:ngens(fM) + set_power!(cE, i + nG, [g[1] + nG => g[2] for g = get_power(cM, i)]) + end + + #TODO: if all is well, then the tails used to compute H^2 will match exactly + #the data required here - thus no computation will be required + #well for the conjugates the action on the module + for i=1:nG + #XXX: only since we don't have GrpEltElem^ZZRingElem (yet) + x = E[i]^Int(get_relative_order(cG, i)) + t = vcat(Oscar.syllables(x.g), [ g[1] + nG => g[2] for g = Oscar.syllables(mMf(x.m))]) + set_power!(cE, i, t) + end + + for i=1:nG + for j=i+1:nG+ngens(fM) + x = E[j]^E[i] + t = vcat(Oscar.syllables(x.g), [ g[1] + nG => g[2] for g = Oscar.syllables(mMf(x.m))]) + set_conjugate!(cE, j, i, t) + end + end + + Q = pc_group(cE) + + function EtoQ(x::GrpExtElem) + @assert parent(x) == E + t = vcat(Oscar.syllables(x.g), [ g[1] + nG => g[2] for g = Oscar.syllables(mMf(x.m))]) + return Q(t) + end + + function QtoE(x::PcGroupElem) + #implicitly assumes the syllables to be a normalized expo vector + #in particular the module part needs to be in the end + @assert parent(x) == Q + s = Oscar.syllables(x) + a = Pair{Int, ZZRingElem}[] + b = Pair{Int, ZZRingElem}[] + for i = s + if i[1] <= nG + push!(a, i) + else + push!(b, i[1]-nG => i[2]) + end + end + return E(G(a), preimage(mMf, fM(b))) + end + #XXX: this is not yet good: + # - we use elt -> syllable + # - but create elt from exponent vector (well this is hidden) + # why is the collector using syllables + # + #the collector has a type parameter for the exponents. This is "ignored" + #here + + return MapFromFunc(E, Q, EtoQ, QtoE) #y->map_word(y, gens(E))) +end +#= +Thomas: +Fuer die Umkehrabbildung kann man statt y -> map_word(y, gens(E)) +dann y in Syllables auspacken, an der richtigen Stelle in zwei Teile +aufteilen und die mit Hilfe der Kollektoren cM und cG in Elemente +verpacken, aus denen dann ein GrpExtElem gebaut wird. + +=# function one(G::GrpExt) return GrpExtElem(G, one(_group(G)), zero(_module(G))) end +function Oscar.gen(G::GrpExt, i::Int) + i == 0 && return one(G) + i < 0 && return inv(gen(G, -i)) + i > ngens(G) && error("index out of range") + gr = _group(G) + mo = _module(G) + if i <= ngens(gr) + return GrpExtElem(G, gen(gr, i), zero(mo)) + end + return GrpExtElem(G, one(gr), gen(mo, i-ngens(gr))) +end + +function Oscar.ngens(G::GrpExt) + return ngens(_group(G)) + ngens(_module(G)) +end function Oscar.gens(G::GrpExt) gr = _group(G) mo = _module(G) @@ -77,6 +258,9 @@ end function ==(g::GrpExtElem, h::GrpExtElem) @assert g.P === h.P + #XXX: will this work for FPGroupElems? They may not be normalized + # if the element is obtained through E(g, m) + # if it comes from arithmetic, I think it will be fine return g.g == h.g && g.m == h.m end @@ -122,13 +306,13 @@ function Oscar.is_stem_extension(P::GrpExt) G = _group(P) # part of algo is explained in Cohomology.jl # - # a commutator (comm(X, Y)) does not depend on X,m and Y.m + # a commutator (comm(X, Y)) does not depend on X.m and Y.m # We need to check M <= P' - # in P, M ;ooks like (1, M) + # in P, M looks like (1, M) # any word evaluated in P will have the G part as the evaluation # only on the G part (the G part is the projection, hence a hom) # any word evaluated in P to get a (1, M) will be a relator of G' - # Writig such a relator as a word in commutators make the evaluation + # Writing such a relator as a word in commutators make the evaluation # depend only on the co-chain, not on other choices. E, mE = derived_subgroup(G) h = commutator_decomposition_map(mE) diff --git a/experimental/GModule/src/Misc.jl b/experimental/GModule/src/Misc.jl index dcca294ca93a..bd04a1d6cc6e 100644 --- a/experimental/GModule/src/Misc.jl +++ b/experimental/GModule/src/Misc.jl @@ -245,6 +245,9 @@ Hecke.restrict(::Hecke.NumFieldEmb, ::Map{QQField, AbsSimpleNumField}) = complex function relative_field(m::Map{<:AbstractAlgebra.Field, <:AbstractAlgebra.Field}) k = domain(m) K = codomain(m) + if k == base_field(K) + return defining_polynomial(K), Hecke.coordinates, representation_matrix + end @assert base_field(k) == base_field(K) kt, t = polynomial_ring(k, cached = false) f = defining_polynomial(K) diff --git a/experimental/GModule/src/Types.jl b/experimental/GModule/src/Types.jl new file mode 100644 index 000000000000..2191f26a2116 --- /dev/null +++ b/experimental/GModule/src/Types.jl @@ -0,0 +1,92 @@ +mutable struct GModuleHom{ G, T1, T2} <: Map{GModule{G, T1}, GModule{G, T2}, OscarMap, GModuleHom} + + GM1::GModule{G, T1} + GM2::GModule{G, T2} + module_map::Map + + function GModuleHom( + M1::GModule, + M2::GModule, + mp::Map; + check::Bool = false + ) + # Need to require that + # 1. Both GModules have the same group + # 2. The group action is respected + @req M1.G === M2.G "groups need to be identical" + @req domain(mp) === M1.M && codomain(mp) === M2.M "map need to map 1st module into 2nd" + #not every hom is a G-Hom...that is what check is supposed to do - eventually + #see 2. + if check #only works if mp is a morphism so that "*" and "==" are doing + #s.th. useful + @assert all(g->action(M1, g)*mp == mp*action(M2, g), gens(M1.G)) + end + + return new{typeof(M1.G), typeof(M1.M), typeof(M2.M)}(M1, M2, mp) + end +end + +function hom(M1::GModule{T}, M2::GModule{T}, mp::Map; check::Bool = true) where T <: AbstractAlgebra.Group + return GModuleHom(M1, M2, mp; check) +end + +function hom(M1::GModule{T}, M2::GModule{T}, mp::MatElem; check::Bool = true) where T <: AbstractAlgebra.Group + return GModuleHom(M1, M2, hom(M1.M, M2.M, mp); check) +end + +domain(M::GModuleHom) = M.GM1 +codomain(M::GModuleHom) = M.GM2 +parent(M::GModuleHom) = Hecke.MapParent(domain(M), codomain(M), "homomorphisms") + +mutable struct GModuleElem{T} + parent::GModule + data::T +end + +parent(a::GModuleElem) = a.parent + +function (C::GModule)(a::Union{ModuleElem, FinGenAbGroupElem}) + @req parent(a) === C.M "wrong parent for $a" + return GModuleElem(C, a) +end + +function ==(a::GModuleElem, b::GModuleElem) + @req parent(a) === parent(b) "parents differ" + return a.data == b.data +end + +function hash(a::GModuleElem, u::UInt) + return hash(a.data, u) +end + +function +(a::GModuleElem, b::GModuleElem) + @req parent(a) === parent(b) "parents differ" + return GModuleElem(parent(a), a.data + b.data) +end + +function -(a::GModuleElem, b::GModuleElem) + @req parent(a) === parent(b) "parents differ" + return GModuleElem(parent(a), a.data - b.data) +end + +function -(a::GModuleElem) + return GModuleElem(parent(a), -a.data) +end + +function *(a::GModuleElem, g::GroupElem) + @req parent(a).G === parent(g) "group element has wrong parent" + return GModuleElem(parent(a), action(parent(a), g, a.data)) +end + +function (A::GModuleHom)(a::GModuleElem) + @req parent(a) === domain(A) "element has wrong parent" + return GModuleElem(codomain(A), A.module_map(a.data)) +end + +function kernel(A::GModuleHom) + return sub(domain(A), kernel(A.module_map)[2]) +end + +function image(A::GModuleHom) + return sub(codomain(A), image(A.module_map)[2]) +end diff --git a/experimental/GModule/test/runtests.jl b/experimental/GModule/test/runtests.jl index 63864ed432c3..9da7d19a35dc 100644 --- a/experimental/GModule/test/runtests.jl +++ b/experimental/GModule/test/runtests.jl @@ -38,7 +38,7 @@ end @test length(z) == 5 z = irreducible_modules(ZZ, G) - @test length(z) == 5 + @test length(z) == 3 l = irreducible_modules(AbsSimpleNumField, small_group(48, 17), minimal_degree = true) ds = degree.(base_ring.(l)) @@ -130,7 +130,7 @@ end H2 = Oscar.GrpCoh.cohomology_group(M, 2) x = collect(H2[1])[1] c = H2[2](x) - e = extension(c) + e = extension(FPGroup, c) GG = e[1] @test order(GG) == 216 @test GG isa FPGroup @@ -189,7 +189,7 @@ end X = cyclic_group(4) M, _ = sub(X, [X[1]^2]) C, c = extension_with_abelian_kernel(X, M) - @test is_isomorphic(extension(c)[1], X) + @test is_isomorphic(extension(FPGroup, c)[1], X) end @testset "Experimental Schur" begin @@ -203,7 +203,7 @@ end M = trivial_gmodule(G, Z2) h, mh = cohomology_group(M, 2) @test Set(is_stem_extension(Oscar.GrpExt(mh(x))) for x = h) == Set([0,1]) - @test Set(is_stem_extension(extension(mh(x))[2]) for x = h) == Set([0,1]) + @test Set(is_stem_extension(extension(mh(x))) for x = h) == Set([0,1]) end diff --git a/src/GAP/iso_oscar_gap.jl b/src/GAP/iso_oscar_gap.jl index 7d87de40db34..a1e7af95d8fd 100644 --- a/src/GAP/iso_oscar_gap.jl +++ b/src/GAP/iso_oscar_gap.jl @@ -239,7 +239,8 @@ end # in Oscar and GAP, respectively. # (Cyclotomic fields are easier to handle than general number fields.) function _iso_oscar_gap_field_cyclotomic_functions(FO::AbsSimpleNumField, FG::GapObj) - N = conductor(FO) + flag, N = Hecke.is_cyclotomic_type(FO) + @req flag "FO was not constructed as a cyclotomic field" cycpol = GAPWrap.CyclotomicPol(N) dim = length(cycpol)-1 diff --git a/src/Rings/AbelianClosure.jl b/src/Rings/AbelianClosure.jl index 76d38f6b6e4d..598eb93f9aa6 100644 --- a/src/Rings/AbelianClosure.jl +++ b/src/Rings/AbelianClosure.jl @@ -503,7 +503,11 @@ conductor(a::AbsSimpleNumFieldElem) = conductor(parent(minimize(CyclotomicField, function conductor(k::AbsSimpleNumField) f, c = Hecke.is_cyclotomic_type(k) f || error("field is not of cyclotomic type") - return c + if is_conductor(c) + return c + else + return div(c, 2) + end end conductor(a::QQAbFieldElem) = conductor(data(a))