diff --git a/src/AlgAss/AbsAlgAss.jl b/src/AlgAss/AbsAlgAss.jl index 0d52993b4d..db0fa23e6e 100644 --- a/src/AlgAss/AbsAlgAss.jl +++ b/src/AlgAss/AbsAlgAss.jl @@ -1473,7 +1473,8 @@ end # Given epimorphism h : A -> B, transport the refined wedderburn decomposition # of A to B -function _transport_refined_wedderburn_decomposition_forward(h::AbsAlgAssMor) +function _transport_refined_wedderburn_decomposition_forward(h::AbsAlgAssMor; is_anti::Bool = false) + # is_anti = h is anti-morphism A = domain(h) B = codomain(h) @@ -1510,7 +1511,15 @@ function _transport_refined_wedderburn_decomposition_forward(h::AbsAlgAssMor) CtoBc = hom(C, Bc, M, inv(M)) if isdefined(C, :isomorphic_full_matrix_algebra) CM, CtoCM = C.isomorphic_full_matrix_algebra + #bmat = basis_matrix([CM(transpose(matrix(x)), check = false) for x in basis(CM)]) + #ff = hom(CM, CM, bmat, inv(bmat)) f = AbsAlgAssMorGen(Bc, CM, inv(CtoBc).mat * CtoCM.M, CtoCM.Minv * CtoBc.mat) + if is_anti + BB = matrix([coefficients(CM(transpose(matrix(f(b))), check = false)) for b in basis(Bc)]) + BBinv = matrix([coefficients(preimage(CtoCM, CM(transpose(matrix(b)), check = false))) for b in _absolute_basis(CM)]) + #BBinv = inv(BB) + f = AbsAlgAssMorGen(Bc, CM, BB, BBinv) + end Bc.isomorphic_full_matrix_algebra = CM, f end end diff --git a/src/AlgAss/AlgGrp.jl b/src/AlgAss/AlgGrp.jl index 0ca4f13f1b..1faebca5fd 100644 --- a/src/AlgAss/AlgGrp.jl +++ b/src/AlgAss/AlgGrp.jl @@ -126,12 +126,21 @@ end # ################################################################################ +function show(io::IO, ::MIME"text/plain", A::AlgGrp) + io = pretty(io) + println(io, "Group algebra") + print(io, Indent()) + println(io, "of ", Lowercase(), group(A)) + print(io, "over ", Lowercase(), base_ring(A)) + print(io, Dedent()) +end + function show(io::IO, A::AlgGrp) - compact = get(io, :compact, false) - if compact + if get(io, :supercompact, false) print(io, "Group algebra of dimension ", dim(A), " over ", base_ring(A)) else - print(io, "Group algebra of group\n", group(A), "\nover\n", base_ring(A)) + print(io, "Group algebra of group of order ", order(group(A)), " over ") + print(IOContext(io, :supercompact => true), base_ring(A)) end end @@ -491,121 +500,121 @@ end automorphism_map(f::NfToAlgGrpMor) = f.mG -function galois_module(K::AnticNumberField, aut::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) - G = domain(aut) - A = FlintQQ[G] - return _galois_module(K, A, aut, normal_basis_generator = normal_basis_generator) -end - -function _galois_module(K::AnticNumberField, A, aut::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) - G = domain(aut) - alpha = normal_basis_generator - - basis_alpha = Vector{elem_type(K)}(undef, dim(A)) - for (i, g) in enumerate(G) - f = aut(g) - basis_alpha[A.group_to_base[g]] = f(alpha) - end - - M = zero_matrix(base_field(K), degree(K), degree(K)) - for i = 1:degree(K) - a = basis_alpha[i] - for j = 1:degree(K) - M[i, j] = coeff(a, j - 1) - end - end - - invM = inv(M) - - z = NfToAlgGrpMor{QQFieldElem, GrpGen, GrpGenElem}() - z.K = K - z.mG = aut - z.A = A - z.M = M - z.Minv = invM - - return A, z -end - -function galois_module(K::AnticNumberField, A::AlgGrp; normal_basis_generator = normal_basis(K)) - G = group(A) - Au, mAu = automorphism_group(K) - fl, f = is_isomorphic_with_map(G, Au) - @assert fl - aut = Vector{NfToNfMor}(undef, order(G)) - for g in G - aut[g[]] = mAu(f(g)) - end - h = GrpGenToNfMorSet(G, aut, K) - - return _galois_module(K, A, h, normal_basis_generator = normal_basis(K)) -end - -domain(f::NfToAlgGrpMor) = f.K - -codomain(f::NfToAlgGrpMor) = f.A - -function image(f::NfToAlgGrpMor, x::nf_elem) - K = domain(f) - @assert parent(x) === K - A = codomain(f) - - t = zero_matrix(base_field(K), 1, degree(K)) - for i = 1:degree(K) - t[1, i] = coeff(x, i - 1) - end - y = t*f.Minv - return A([ y[1, i] for i = 1:degree(K) ]) -end - -function preimage(f::NfToAlgGrpMor, x::AlgGrpElem) - K = domain(f) - t = matrix(base_field(K), 1, degree(K), coefficients(x)) - y = t*f.M - v = QQFieldElem[ y[1, i] for i = 1:degree(K) ] - return K(v) -end - -# Returns the group algebra Q[G] where G = Gal(K/Q) and a Q-linear map from K -# to Q[G] and one from Q[G] to K -function _galois_module(K::AnticNumberField, to_automorphisms::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) - G = domain(to_automorphisms) - A = FlintQQ[G] - alpha = normal_basis_generator - - basis_alpha = Vector{elem_type(K)}(undef, dim(A)) - for (i, g) in enumerate(G) - f = to_automorphisms(g) - basis_alpha[A.group_to_base[g]] = f(alpha) - end - - M = zero_matrix(base_field(K), degree(K), degree(K)) - for i = 1:degree(K) - a = basis_alpha[i] - for j = 1:degree(K) - M[i, j] = coeff(a, j - 1) - end - end - - invM = inv(M) - - function KtoA(x::nf_elem) - t = zero_matrix(base_field(K), 1, degree(K)) - for i = 1:degree(K) - t[1, i] = coeff(x, i - 1) - end - y = t*invM - return A([ y[1, i] for i = 1:degree(K) ]) - end - - function AtoK(x::AlgGrpElem) - t = matrix(base_field(K), 1, degree(K), coefficients(x)) - y = t*M - return K(parent(K.pol)([ y[1, i] for i = 1:degree(K) ])) - end - - return A, KtoA, AtoK -end +#function galois_module(K::AnticNumberField, aut::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) +# G = domain(aut) +# A = FlintQQ[G] +# return _galois_module(K, A, aut, normal_basis_generator = normal_basis_generator) +#end +# +#function _galois_module(K::AnticNumberField, A, aut::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) +# G = domain(aut) +# alpha = normal_basis_generator +# +# basis_alpha = Vector{elem_type(K)}(undef, dim(A)) +# for (i, g) in enumerate(G) +# f = aut(g) +# basis_alpha[A.group_to_base[g]] = f(alpha) +# end +# +# M = zero_matrix(base_field(K), degree(K), degree(K)) +# for i = 1:degree(K) +# a = basis_alpha[i] +# for j = 1:degree(K) +# M[i, j] = coeff(a, j - 1) +# end +# end +# +# invM = inv(M) +# +# z = NfToAlgGrpMor{QQFieldElem, GrpGen, GrpGenElem}() +# z.K = K +# z.mG = aut +# z.A = A +# z.M = M +# z.Minv = invM +# +# return A, z +#end +# +#function galois_module(K::AnticNumberField, A::AlgGrp; normal_basis_generator = normal_basis(K)) +# G = group(A) +# Au, mAu = automorphism_group(K) +# fl, f = is_isomorphic_with_map(G, Au) +# @assert fl +# aut = Vector{NfToNfMor}(undef, order(G)) +# for g in G +# aut[g[]] = mAu(f(g)) +# end +# h = GrpGenToNfMorSet(G, aut, K) +# +# return _galois_module(K, A, h, normal_basis_generator = normal_basis(K)) +#end +# +#domain(f::NfToAlgGrpMor) = f.K +# +#codomain(f::NfToAlgGrpMor) = f.A +# +#function image(f::NfToAlgGrpMor, x::nf_elem) +# K = domain(f) +# @assert parent(x) === K +# A = codomain(f) +# +# t = zero_matrix(base_field(K), 1, degree(K)) +# for i = 1:degree(K) +# t[1, i] = coeff(x, i - 1) +# end +# y = t*f.Minv +# return A([ y[1, i] for i = 1:degree(K) ]) +#end +# +#function preimage(f::NfToAlgGrpMor, x::AlgGrpElem) +# K = domain(f) +# t = matrix(base_field(K), 1, degree(K), coefficients(x)) +# y = t*f.M +# v = QQFieldElem[ y[1, i] for i = 1:degree(K) ] +# return K(v) +#end +# +## Returns the group algebra Q[G] where G = Gal(K/Q) and a Q-linear map from K +## to Q[G] and one from Q[G] to K +#function _galois_module(K::AnticNumberField, to_automorphisms::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) +# G = domain(to_automorphisms) +# A = FlintQQ[G] +# alpha = normal_basis_generator +# +# basis_alpha = Vector{elem_type(K)}(undef, dim(A)) +# for (i, g) in enumerate(G) +# f = to_automorphisms(g) +# basis_alpha[A.group_to_base[g]] = f(alpha) +# end +# +# M = zero_matrix(base_field(K), degree(K), degree(K)) +# for i = 1:degree(K) +# a = basis_alpha[i] +# for j = 1:degree(K) +# M[i, j] = coeff(a, j - 1) +# end +# end +# +# invM = inv(M) +# +# function KtoA(x::nf_elem) +# t = zero_matrix(base_field(K), 1, degree(K)) +# for i = 1:degree(K) +# t[1, i] = coeff(x, i - 1) +# end +# y = t*invM +# return A([ y[1, i] for i = 1:degree(K) ]) +# end +# +# function AtoK(x::AlgGrpElem) +# t = matrix(base_field(K), 1, degree(K), coefficients(x)) +# y = t*M +# return K(parent(K.pol)([ y[1, i] for i = 1:degree(K) ])) +# end +# +# return A, KtoA, AtoK +#end const _reps = [(i=24,j=12,n=5,dims=(1,1,2,3,3), reps=Vector{Vector{Rational{BigInt}}}[[[1],[1],[1],[1]], @@ -928,13 +937,13 @@ function _absolute_basis(A) n = degree(K) B = Vector{elem_type(A)}() bK = basis(K) - for i in 1:n - for j in 1:m + for i in 1:m + for j in 1:n v = Vector{elem_type(K)}(undef, m) for k in 1:m - v[i] = zero(K) + v[k] = zero(K) end - v[j] = bK[j] + v[i] = bK[j] push!(B, A(v)) end end @@ -1107,7 +1116,7 @@ function is_almost_maximally_ramified(K::AnticNumberField, p::ZZRingElem) return true end -function hom(KG::AlgGrp, KH::AlgGrp, m::GrpGenToGrpGenMor) +function hom(KG::AlgGrp, KH::AlgGrp, m::Map) @assert base_ring(KG) === base_ring(KH) K = base_ring(KG) M = zero_matrix(K, dim(KG), dim(KH)) diff --git a/src/AlgAss/AlgMat.jl b/src/AlgAss/AlgMat.jl index a9b60c5f3f..4547ecefbb 100644 --- a/src/AlgAss/AlgMat.jl +++ b/src/AlgAss/AlgMat.jl @@ -221,6 +221,22 @@ function multiplication_table(A::AlgMat; copy::Bool = true) end end +function denominator_of_multiplication_table(A::AlgMat) + get_attribute!(A, :denominator_of_multiplication_table) do + den = one(ZZ) + mt = multiplication_table(A) + d = degree(A) + for i in 1:d + for j in 1:d + for k in 1:d + den = lcm!(den, den, denominator(mt[i, j, k])) + end + end + end + return den + end::ZZRingElem +end + ################################################################################ # # Construction diff --git a/src/AlgAss/AlgMatElem.jl b/src/AlgAss/AlgMatElem.jl index 83f1799214..88ac632bc4 100644 --- a/src/AlgAss/AlgMatElem.jl +++ b/src/AlgAss/AlgMatElem.jl @@ -79,7 +79,7 @@ end function +(a::AlgMatElem{T, S, V}, b::AlgMatElem{T, S, V}) where {T, S, V} parent(a) != parent(b) && error("Parents don't match.") - c = parent(a)(matrix(a, copy = false) + matrix(b, copy = false)) + c = parent(a)(matrix(a, copy = false) + matrix(b, copy = false), check = false) if a.has_coeffs && b.has_coeffs c.coeffs = [ coefficients(a, copy = false)[i] + coefficients(b, copy = false)[i] for i = 1:dim(parent(a)) ] c.has_coeffs = true @@ -157,7 +157,7 @@ function *(a::AlgMatElem, b::T) where {T <: RingElem} if parent(b) == base_ring(A) b = coefficient_ring(A)(b) end - return A(matrix(a, copy = false)*b)::elem_type(A) + return A(matrix(a, copy = false)*b, check = false)::elem_type(A) end function *(b::T, a::AlgMatElem) where {T <: RingElem} @@ -165,17 +165,17 @@ function *(b::T, a::AlgMatElem) where {T <: RingElem} if parent(b) == base_ring(A) b = coefficient_ring(A)(b) end - return A(b*matrix(a, copy = false))::elem_type(A) + return A(b*matrix(a, copy = false), check = false)::elem_type(A) end function *(a::AlgMatElem{S, T, U}, b::U) where { S, T, U <: MatElem } A = parent(a) - return A(matrix(a, copy = false)*b) + return A(matrix(a, copy = false)*b, check = false) end function *(b::U, a::AlgMatElem{S, T, U}) where { S, T, U <: MatElem } A = parent(a) - return A(b*matrix(a, copy = false)) + return A(b*matrix(a, copy = false), check = false) end ################################################################################ @@ -224,7 +224,7 @@ end function (A::AlgMat)() n = degree(A) - return A(zero_matrix(coefficient_ring(A), n, n)) + return A(zero_matrix(coefficient_ring(A), n, n), check = false) end function (A::AlgMat{T, S})(M::S; check::Bool = true) where {T, S} @@ -262,7 +262,7 @@ function (A::AlgMat{T, S})(v::Vector{T}; copy::Bool = true) where { T, S } #M = add!(M, M, matrix(basis(A)[i], copy = false)*v[i]) M += matrix(B[i], copy = false)*R(v[i]) end - a = A(M) + a = A(M; check = false) if copy a.coeffs = Base.copy(v) else diff --git a/src/AlgAss/AlgQuat.jl b/src/AlgAss/AlgQuat.jl index b663cc134a..2bd820beef 100644 --- a/src/AlgAss/AlgQuat.jl +++ b/src/AlgAss/AlgQuat.jl @@ -160,8 +160,15 @@ function is_quaternion_algebra(A::AlgAss) K = base_ring(A) G = zero_matrix(K, 4, 4) B = copy(basis(A)) - @assert dot(B[1].coeffs, one(A).coeffs) != 0 - B[1] = one(A) + # Make one(A) the first element of B + for i in 1:4 + if dot(B[i].coeffs, one(A).coeffs) != 0 + B[i] = one(A) + B[1], B[i] = B[i], B[1] + break + end + end + @assert B[1] == one(A) for i in 1:4 for j in 1:4 G[i, j] = trred(B[i] * f(B[j]))//2 diff --git a/src/AlgAss/Map.jl b/src/AlgAss/Map.jl index 2908178815..3851a23f81 100644 --- a/src/AlgAss/Map.jl +++ b/src/AlgAss/Map.jl @@ -204,11 +204,17 @@ function compose_and_squash(f::AbsAlgAssMor{R, U, T}, g::AbsAlgAssMor{S, R, T}) end end -function hom(A::R, B::S, M::T) where {R <: AbsAlgAss, S <: AbsAlgAss, T} +function hom(A::R, B::S, imgs::Vector) where {R <: AbsAlgAss, S <: AbsAlgAss} + @assert length(imgs) == dim(A) + bmat = basis_matrix(imgs) + return hom(A, B, bmat) +end + +function hom(A::R, B::S, M::T) where {R <: AbsAlgAss, S <: AbsAlgAss, T <: MatElem} return AbsAlgAssMor{R, S, T}(A, B, M) end -function hom(A::R, B::S, M::T, N::T) where {R <: AbsAlgAss, S <: AbsAlgAss, T} +function hom(A::R, B::S, M::T, N::T) where {R <: AbsAlgAss, S <: AbsAlgAss, T <: MatElem} return AbsAlgAssMor{R, S, T}(A, B, M, N) end diff --git a/src/AlgAssAbsOrd/LocallyFreeClassGroup.jl b/src/AlgAssAbsOrd/LocallyFreeClassGroup.jl index 980628d4b8..abaab0f9c0 100644 --- a/src/AlgAssAbsOrd/LocallyFreeClassGroup.jl +++ b/src/AlgAssAbsOrd/LocallyFreeClassGroup.jl @@ -448,7 +448,22 @@ mutable struct DiscLogLocallyFreeClassGroup{S, T} <: Map{S, T, HeckeMap, DiscLog end end -#function (f::DiscLogLocallyFreeClassGroup)(A::AlgAssAbsOrdIdl) +function image(m::DiscLogLocallyFreeClassGroup, I::ModAlgAssLat) + V = I.V + O = I.base_ring + @req order(domain(m)) === O "Order of lattice and locally free class group must be identical" + A = algebra(V) + Areg = regular_module(A) + AregToA = x -> A(coordinates(x)) + fl, VToAreg = is_isomorphic_with_isomorphism(V, Areg) + if !fl + error("Ambient module of lattice must be free of rank one") + end + Ibmat = basis_matrix(I) + II = ideal_from_lattice_gens(A, O, [AregToA(VToAreg(V(_eltseq(Ibmat[i, :])))) for i in 1:nrows(Ibmat)]) + return m(II) +end + function image(m::DiscLogLocallyFreeClassGroup, I::AlgAssAbsOrdIdl) O = order(I) A = algebra(O) @@ -466,8 +481,12 @@ function image(m::DiscLogLocallyFreeClassGroup, I::AlgAssAbsOrdIdl) @assert order(I) === order(domain(m)) # Bley, Wilson: "Computations in relative algebraic K-groups" + + # The ideal must be principal, so let's do this + d = denominator(I, O) + I = d * I + n = norm(I) - @assert isone(denominator(n)) "Ideal is not integral" primes = collect(keys(factor(numerator(n)).fac)) C = codomain(RtoC) c = id(C) @@ -535,6 +554,8 @@ function image(m::DiscLogLocallyFreeClassGroup, I::AlgAssAbsOrdIdl) push!(exps, v) end b = FacElem(bases, exps) + # simplify! makes this work with the ray class group + simplify!(b) elts_in_R[j] = mR.groups_in_number_fields[j][2]\b end diff --git a/src/AlgAssAbsOrd/Order.jl b/src/AlgAssAbsOrd/Order.jl index 3bfd3b6f04..8e5d8e3101 100644 --- a/src/AlgAssAbsOrd/Order.jl +++ b/src/AlgAssAbsOrd/Order.jl @@ -1,4 +1,4 @@ -export algebra +export algebra, integral_group_ring add_assertion_scope(:AlgAssOrd) add_verbosity_scope(:AlgAssOrd) @@ -149,6 +149,16 @@ function _equation_order(A::AbsAlgAss{QQFieldElem}) return Order(A, b) end +################################################################################ +# +# Integral group ring +# +################################################################################ + +function integral_group_ring(A::AlgGrp{QQFieldElem}) + return Order(A, basis(A)) +end + ################################################################################ # # Index diff --git a/src/AlgAssAbsOrd/PIP.jl b/src/AlgAssAbsOrd/PIP.jl index 84fe12c8f4..7119521397 100644 --- a/src/AlgAssAbsOrd/PIP.jl +++ b/src/AlgAssAbsOrd/PIP.jl @@ -592,7 +592,7 @@ function _solve_norm_equation_over_center_quaternion(M, x) end end #@show nrm - V = _short_vectors_gram(G, nrm) + V = _short_vectors_gram(Vector, G, nrm) for i in 1:length(V) if V[i][2] == nrm y = sum(V[i][1][j] * B[j] for j in 1:4) @@ -725,7 +725,7 @@ function _lift_norm_one_unit_quaternion(x, F) #@show normred(elem_in_algebra(x)) # TODO: Replace this by short_vectors_gram(M, nrr) once it works - V = _short_vectors_gram(G, ZZRingElem(1)) + V = _short_vectors_gram(Vector, G, ZZRingElem(1)) for i in 1:length(V) y = sum(V[i][1][j] * B[j] for j in 1:4) @assert normred(y) == 1 diff --git a/src/Deprecations.jl b/src/Deprecations.jl index 1559abce94..6e6fa136a1 100644 --- a/src/Deprecations.jl +++ b/src/Deprecations.jl @@ -175,3 +175,7 @@ else return _fmpq_simplest_between(a, d, b, d) end end + +# Deprecated during 0.18.* + +@deprecate abelian_fields abelian_extensions diff --git a/src/FieldFactory/ab_exts.jl b/src/FieldFactory/ab_exts.jl index 6b56f72560..23cdbcc3cf 100644 --- a/src/FieldFactory/ab_exts.jl +++ b/src/FieldFactory/ab_exts.jl @@ -4,7 +4,7 @@ add_assertion_scope(:AbExt) add_verbosity_scope(:MaxAbExt) -export abelian_fields, abelian_normal_extensions, abelian_extensions +export abelian_extensions, abelian_normal_extensions, abelian_extensions ############################################################################### # @@ -12,7 +12,7 @@ export abelian_fields, abelian_normal_extensions, abelian_extensions # ############################################################################### -function abelian_fields(O::Union{ZZRing, QQField}, +function abelian_extensions(O::Union{ZZRing, QQField}, gtype::Vector{Int}, discriminant_bound::ZZRingElem; only_real::Bool = false, tame::Bool = false) @@ -20,13 +20,13 @@ function abelian_fields(O::Union{ZZRing, QQField}, Qx, x = polynomial_ring(FlintQQ, "x", cached = false) K, _ = number_field(x - 1, "a", cached = false) OK = maximal_order(K) - l = abelian_fields(OK, gtype, discriminant_bound, + l = abelian_extensions(OK, gtype, discriminant_bound, only_real = only_real, tame = tame) return l end -function abelian_fields(gtype::Vector{Int}, conds::Vector{Int}, absolute_discriminant_bound::ZZRingElem; only_real::Bool = false) +function abelian_extensions(gtype::Vector{Int}, conds::Vector{Int}, absolute_discriminant_bound::ZZRingElem; only_real::Bool = false) K = rationals_as_number_field()[1] O = maximal_order(K) gtype = map(Int, snf(abelian_group(gtype))[1].snf) @@ -59,7 +59,7 @@ function abelian_fields(gtype::Vector{Int}, conds::Vector{Int}, absolute_discrim return fields end -function abelian_fields(O::NfOrd, gtype::Vector{Int}, absolute_discriminant_bound::ZZRingElem; only_real::Bool = false, only_complex::Bool = false, tame::Bool = false) +function abelian_extensions(O::NfOrd, gtype::Vector{Int}, absolute_discriminant_bound::ZZRingElem; only_real::Bool = false, only_complex::Bool = false, tame::Bool = false) K = nf(O) @assert degree(K)==1 gtype = map(Int, snf(abelian_group(gtype))[1].snf) @@ -125,7 +125,7 @@ function abelian_normal_extensions(K::AnticNumberField, gtype::Vector{Int}, abso O = maximal_order(K) d = degree(K) if d == 1 - return abelian_fields(O, gtype, absolute_discriminant_bound, only_real = only_real, tame = tame) + return abelian_extensions(O, gtype, absolute_discriminant_bound, only_real = only_real, tame = tame) end gtype = map(Int, snf(abelian_group(gtype))[1].snf) n = prod(gtype) diff --git a/src/GrpAb/GrpAbFinGen.jl b/src/GrpAb/GrpAbFinGen.jl index 7a877a42c9..e0caabaf86 100644 --- a/src/GrpAb/GrpAbFinGen.jl +++ b/src/GrpAb/GrpAbFinGen.jl @@ -124,8 +124,13 @@ end Creates the direct product of the cyclic groups $\mathbf{Z}/m_i$, where $m_i$ is the $i$th entry of `M`. """ -function abelian_group(M::AbstractVector{<:IntegerUnion}; name::String = "") - return abelian_group(GrpAbFinGen, M, name=name) +function abelian_group(M::AbstractVector{<:Union{Any, IntegerUnion}}; name::String = "") + if eltype(M) === Any + _M = convert(Vector{ZZRingElem}, (ZZ.(M)))::Vector{ZZRingElem} + return abelian_group(GrpAbFinGen, _M, name=name) + else + return abelian_group(GrpAbFinGen, M, name=name) + end end function abelian_group(::Type{GrpAbFinGen}, M::AbstractVector{<:IntegerUnion}; name::String = "") diff --git a/src/ModAlgAss/Lattices.jl b/src/ModAlgAss/Lattices.jl index 684c40da68..ae9e624a85 100644 --- a/src/ModAlgAss/Lattices.jl +++ b/src/ModAlgAss/Lattices.jl @@ -280,3 +280,4 @@ function sublattices(L::ModAlgAssLat, p::Int, level = inf) end return res end + diff --git a/src/ModAlgAss/Lattices/Basics.jl b/src/ModAlgAss/Lattices/Basics.jl index e69a1d1e6e..ed4ed68409 100644 --- a/src/ModAlgAss/Lattices/Basics.jl +++ b/src/ModAlgAss/Lattices/Basics.jl @@ -31,6 +31,34 @@ function lattice(V::ModAlgAss{QQField}, O::AlgAssAbsOrd, B::MatElem; check::Bool end end +@doc raw""" + lattice(V::ModAlgAss, O::AlgAssAbsOrd, B::Vector) -> ModAlgAssLat + +Given a module with matrix action over a $\mathbf{Q}$-algebra $A$, a +$\mathbf{Z}$-order of $A$, return the $O$-lattice generated by $B$. +""" +function lattice(V::ModAlgAss{QQField}, O::AlgAssAbsOrd, B::Vector; check::Bool = true) + return lattice(V, O, matrix(QQ, coordinates.(B)); check) +end + +function lattice(V::ModAlgAss{QQField}, O::AlgAssAbsOrd, B::Vector{<:ModAlgAssElem}) + @req all(x -> parent(x) === V, B) "Elements must be contained in module" + BB = eltype(B)[] + BO = basis(O) + for b in BO + for bb in B + push!(BB, bb * elem_in_algebra(b)) + end + end + M = matrix(coordinates.(BB)) + MM = QQMatrix(hnf!(FakeFmpqMat(M), :upperright)) + r = nrows(MM) + while is_zero_row(MM, r) + r = r - 1 + end + return lattice(V, O, MM[1:r, :]) +end + # internal function to construct lattice function _lattice(V::ModAlgAss{QQField}, O::AlgAssAbsOrd, B::QQMatrix; check::Bool = true, is_hnf::Bool = false) if check @@ -42,6 +70,7 @@ function _lattice(V::ModAlgAss{QQField}, O::AlgAssAbsOrd, B::QQMatrix; check::Bo if !is_hnf BB = QQMatrix(hnf!(FakeFmpqMat(B), :upperright)) + # must strip zero rows if this is not a basis matrix else BB = B end @@ -153,7 +182,7 @@ function intersect(L::T, M::T) where {T <: ModAlgAssLat} end function Base.:(==)(L::T, M::T) where {T <: ModAlgAssLat} - return L.V === M.V && basis_matrix(M) == basis_matrix(N) + return L.V === M.V && basis_matrix(L) == basis_matrix(M) end ################################################################################ @@ -222,3 +251,85 @@ function index(L::T, M::T) where {T <: ModAlgAssLat} @req !isone(denominator(t)) "First lattice not contained in second lattice" return abs(det(t)) end + +################################################################################ +# +# Sublattice of free lattice +# +################################################################################ + +function lattice(O::AlgAssAbsOrd, v::Vector{<:Vector}) + A = algebra(O) + V = Amodule(A, map(x -> elem_in_algebra.(x), v)) + L = lattice(V, O, identity_matrix(ZZ, dim(V))) + # I want to try to compute Wedderburn decomposition of the endomorphism algebra + idm = central_primitive_idempotents(A) + ids = [i for i in 1:length(idm) if !is_zero(action(V, idm[i]))] + C, p = product_of_components_with_projection(A, ids) + W = regular_module(A, p) + fl, WtoV = is_isomorphic_with_isomorphism(W, V) + VtoW = inv(WtoV) + @assert fl + EV, EVmap = endomorphism_algebra(V) + EW, EWmap = endomorphism_algebra(W) + imgs = elem_type(EV)[] + for b in basis(EW) + push!(imgs, preimage(EVmap, VtoW * EWmap(b) * WtoV)) + end + h = hom(EW, EV, basis_matrix(imgs)) + for b in basis(EW) + for bb in basis(EW) + @assert h(b * bb) == h(b) * h(bb) + end + end + _transport_refined_wedderburn_decomposition_forward(h) + return L +end + +################################################################################ +# +# Free lattice +# +################################################################################ + +function free_lattice(O::AlgAssAbsOrd, r::Int) + B = basis(O) + @assert r == 1 + return lattice(O, [[b] for b in basis(O)]) +end + +################################################################################ +# +# Twists +# +################################################################################ + +function _twists(L::ModAlgAssLat) + V = L.V + A = algebra(V) + @req A isa AlgGrp "Algebra of the order must be group algebra" + Ts = _twists(V) + # the twists of V are the "same" vector space, so we can just push L to the + # twists + res = typeof(L)[] + for T in Ts + push!(res, lattice(T, L.base_ring, L.basis)) + end + return res +end + +################################################################################ +# +# Change base ring +# +################################################################################ + +function change_base_ring(f#=::AbsAlgAssMor=#, O::AlgAssAbsOrd, L::ModAlgAssLat) + B = codomain(f) + @assert algebra(L.base_ring) === B + V = L.V + A = domain(f) + BA = basis(A) + W = Amodule(A, [action(V, f(g)) for g in basis(A)]) + return lattice(W, O, basis_matrix(L)) +end diff --git a/src/ModAlgAss/Lattices/Morphisms.jl b/src/ModAlgAss/Lattices/Morphisms.jl index b7440841f5..6823072465 100644 --- a/src/ModAlgAss/Lattices/Morphisms.jl +++ b/src/ModAlgAss/Lattices/Morphisms.jl @@ -124,7 +124,9 @@ function is_locally_isomorphic(L::ModAlgAssLat, M::ModAlgAssLat, p::IntegerUnion fl = _is_loc_iso_abs_irred(L, M, p, Val{false}) else fl = _is_loc_iso_gen(L, M, p, Val{false}) - @assert _is_loc_iso_gen(L, M, p, Val{false}) == _is_loc_iso_abs_irred(L, M, p, Val{false}) + if is_absolutely_irreducible_known(L.V) && is_absolutely_irreducible(L.V) + @assert _is_loc_iso_gen(L, M, p, Val{false}) == _is_loc_iso_abs_irred(L, M, p, Val{false}) + end end return fl end @@ -182,3 +184,150 @@ function _is_loc_iso_abs_irred(L::ModAlgAssLat, return iszero(valuation(det(T), p)) end end + +################################################################################ +# +# Isomorphism +# +################################################################################ + +function _is_isomorphic_with_isomorphism_same_ambient_module(L::ModAlgAssLat, M::ModAlgAssLat) + E, f, O, I = _hom_space_as_ideal(L, M) + fl, beta = __isprincipal(O, I, :right) + if !fl + return false, zero_map(L.V, M.V) + else + isom = f(beta) + # test something + @assert isom(L) == M + return true, isom + end +end + +function is_isomorphic_with_isomorphism(L::ModAlgAssLat, M::ModAlgAssLat) + # the hom_space function wants L and M sitting inside the same ambient space + # there is some choice we can make + # we try to choose the order, where we already computed the endomorphism + # algebra + + if get_attribute(L.V, :endomorphism_algebra) !== nothing && isdefined(domain(get_attribute(L.V, :endomorphism_algebra)), :decomposition) + fl, iso = is_isomorphic_with_isomorphism(M.V, L.V) + if !fl + return false, zero_map(L.V, M.V) + end + MM = iso(M) + fl, LtoMM = _is_isomorphic_with_isomorphism_same_ambient_module(L, MM) + if fl + _iso = LtoMM * inv(iso) + @assert _iso(L) == M + return true, _iso + else + return false, zero_map(L.V, M.V) + end + else + fl, iso = is_isomorphic_with_isomorphism(L.V, M.V) + if !fl + return false, zero_map(L.V, M.V) + end + LL = iso(L) + fl, LLtoM = _is_isomorphic_with_isomorphism_same_ambient_module(LL, M) + if fl + _iso = iso * LLtoM + @assert _iso(L) == M + return true, _iso + else + return false, zero_map(L.V, M.V) + end + end +end + +function is_isomorphic(L::ModAlgAssLat, M::ModAlgAssLat) + return is_isomorphic_with_isomorphism(L, M)[1] +end + +################################################################################ +# +# Freeness test +# +################################################################################ + +function is_free(L::ModAlgAssLat) + O = L.base_ring + if !is_free(L.V) + return false + end + @assert L.V.free_rank == 1 + return is_isomorphic(L, free_lattice(O, 1)) +end + +function is_free_with_basis(L::ModAlgAssLat) + if !is_free(L.V) + return false, elem_type(L.V)[] + end + d = L.V.free_rank + @assert d != -1 + @assert d == 1 + O = L.base_ring + A = algebra(O) + M = free_lattice(O, d) + V = M.V + fl, iso = is_isomorphic_with_isomorphism(L, M) + if fl + return true, [preimage(iso, _element_of_standard_free_module(V, [elem_in_algebra(one(M.base_ring)) for i in 1:d]))] + else + return false, elem_type(L.V)[] + end +end + +function is_locally_free(L::ModAlgAssLat, p::IntegerUnion) + if !is_free(L.V) + return false + end + d = L.V.free_rank + @assert d != -1 + O = L.base_ring + M = free_lattice(O, d) + fl, LL, MM = _can_transport_into_same_ambient_module(L, M) + if !fl + return false + else + return is_locally_isomorphic(LL, MM, p)[1] + end +end + +function _can_transport_into_same_ambient_module(L, M) + if L.V === M.V + return true, L, M + end + fl, iso = is_isomorphic_with_isomorphism(M.V, L.V) + if !fl + return false, L, M + end + MM = iso(M) + return true, L, MM +end + +################################################################################ +# +# Testing Aut(G)-isomorphism +# +################################################################################ + +function is_aut_isomorphic(L::ModAlgAssLat, M::ModAlgAssLat) + for T in _twists(M) + if is_isomorphic(L, T) + return true + end + end + return false +end + + +function _make_compatible(L::ModAlgAssLat, M::ModAlgAssLat) + G = group(algebra(L.base_ring)) + H = group(algebra(M.base_ring)) + @assert is_isomorphic(G, H) + i = isomorphism(G, H) + h = hom(algebra(L.base_ring), algebra(M.base_ring), i) + return change_base_ring(h, L.base_ring, M) +end diff --git a/src/ModAlgAss/ModAlgAss.jl b/src/ModAlgAss/ModAlgAss.jl index 861e1c2c25..b281fb3df4 100644 --- a/src/ModAlgAss/ModAlgAss.jl +++ b/src/ModAlgAss/ModAlgAss.jl @@ -71,10 +71,87 @@ add_assertion_scope(:ModLattice) end end -function Base.show(io::IO, V::ModAlgAss) - print(io, "Amodule over field of dimension ", V.dim) +struct ModAlgAssElem{P, T} + parent::P + coordinates::Vector{T} +end + +parent(x::ModAlgAssElem) = x.parent + +function (V::ModAlgAss)(x::Vector) + if parent(x[1]) === V.base_ring + return ModAlgAssElem(V, x) + else + return ModAlgAssElem(V, convert(Vector{elem_type(V.base_ring)}, map(V.base_ring, x))) + end +end + +function Base.show(io::IO, v::ModAlgAssElem) + io = pretty(io) + print(io, "[") + join(io, coordinates(v), ", ") + print(io, "]") +end + +function Base.show(io::IO, ::MIME"text/plain", v::ModAlgAssElem) + io = pretty(io) + println(io, "Amodule element with") + print(io, Indent()) + println(IOContext(io, :compact => true), "parent ", parent(v)) + print(io, "and coordinates [") + join(io, coordinates(v), ", ") + print(io, "]") + print(io, Dedent()) +end + +zero(V::ModAlgAss) = V([zero(V.base_ring) for i in 1:dim(V)]) + +coordinates(x::ModAlgAssElem) = x.coordinates + +function Base.:(+)(x::ModAlgAssElem, y::ModAlgAssElem) + return parent(x)(coordinates(x) + coordinates(y)) +end + +function Base.:(*)(x::ModAlgAssElem, y::AbsAlgAssElem) + @assert parent(y) === parent(x).algebra + return parent(x)(coordinates(x) * action(parent(x), y)) +end + +function Base.:(*)(x::FieldElem, y::ModAlgAssElem) + return parent(y)(x * coordinates(y)) +end + +function Base.:(==)(x::ModAlgAssElem{P, T}, y::ModAlgAssElem{P, T}) where {P, T} + return parent(x) === parent(y) && coordinates(x) == coordinates(y) +end + +function Base.show(io::IO, ::MIME"text/plain", V::ModAlgAss) + io = pretty(io) + println(io, LowercaseOff(), "Amodule of dimension ", V.dim) + print(io, Indent(), "over ") if has_algebra(V) - print(io, " (with algebra defined))") + print(IOContext(io, :compact => true), Lowercase(), algebra(V)) + else + print("given by matrix action") + end + print(io, Dedent()) +end + +function Base.show(io::IO, V::ModAlgAss) + io = pretty(io) + if get(io, :supercompact, false) + # supercompact + print(io, LowercaseOff(), "Amodule of dimension ", dim(V)) + else + print(io, LowercaseOff(), "Amodule of dimension ", dim(V)) + print(io, Indent()) + if !has_algebra(V) + print(io, "given by action matrices") + else + print(io, "over ") + print(IOContext(io, :compact => true), Lowercase(), algebra(V)) + end + print(io, Dedent()) end end @@ -343,6 +420,14 @@ stub_basis_hom_space(a, b) = error("Load Oscar (or GAP) and try again") # ################################################################################ +function trivial_module(A::AbsAlgAss) + K = base_ring(A) + B = basis(A) + action_of_basis = [identity_matrix(K, 1) for b in B] + V = Amodule(A, action_of_basis) + return V +end + function regular_module(A::AbsAlgAss) K = base_ring(A) n = dim(A) @@ -353,3 +438,258 @@ function regular_module(A::AbsAlgAss) M.free_rank = 1 return M end + +function regular_module(A::AbsAlgAss, p)#::AbsAlgAssMor) + K = base_ring(A) + action_of_basis = [representation_matrix(p(b), :right) for b in basis(A)] + M = Amodule(A, action_of_basis) + # We do some more, because we know the endomorphism algbera + B, f = endomorphism_algebra(M) + C = codomain(p) + @assert dim(B) == dim(C) + imgs = elem_type(B)[] + for c in basis(C) + d = preimage(f, hom(M, M, representation_matrix(c, :left))) + push!(imgs, d) + end + # This is a bit of a hack + Cop, CtoCop = opposite_algebra(C) + h = hom(Cop, B, basis_matrix(imgs)) + for c in basis(Cop) + for cc in basis(Cop) + @assert h(c * cc) == h(c) * h(cc) + end + end + _assert_has_refined_wedderburn_decomposition(A) + _transport_refined_wedderburn_decomposition_forward(CtoCop, is_anti = true) + _transport_refined_wedderburn_decomposition_forward(p) + _transport_refined_wedderburn_decomposition_forward(h) + return M +end + +function free_module(A::AbsAlgAss, r::Int) + K = base_ring(A) + n = dim(A) + B = basis(A) + action_of_basis = Vector{dense_matrix_type(K)}() + for b in B + rm = representation_matrix(b, :right) + push!(action_of_basis, block_diagonal_matrix([rm for i in 1:r])) + end + M = Amodule(A, action_of_basis) + M.isfree = 1 + M.free_rank = r + return M +end + +function _element_of_standard_free_module(V::ModAlgAss, v::Vector) + #@assert V.isfree == 1 && V.free_rank == length(v) + @assert all(x -> parent(x) === algebra(V), v) + return V(reduce(vcat, coefficients.(v))) +end + +# submodule of A^k generated by B +function Amodule(A::AbsAlgAss, B::Vector{<:Vector}; is_basis::Bool = true) + @assert is_basis + # I first need to flatten those vectors to elements of K^n + K = base_ring(A) + BB = Vector{Vector{elem_type(K)}}() + for b in B + push!(BB, reduce(vcat, (coefficients(x) for x in b))) + end + bmat = matrix(BB) + basA = basis(A) + BBB = Vector{Vector{elem_type(K)}}() + action_matrices = dense_matrix_type(elem_type(K))[] + for b in basA + empty!(BBB) + for bb in B + push!(BBB, reduce(vcat, [coefficients(b * bb[i]) for i in 1:length(bb)])) + end + fl, X = can_solve_with_solution(bmat, matrix(BBB), side = :left) + @assert fl + push!(action_matrices, transpose(X)) + end + return Amodule(A, action_matrices) +end + +################################################################################ +# +# Galois module +# +################################################################################ + +# Type to represent a Q[Gal(K)]-linear map K -> V +mutable struct NfToModAlgAssMor{S, T, U} <: Map{AnticNumberField, ModAlgAss{S, T, U}, HeckeMap, NfToModAlgAssMor} + K::AnticNumberField + mG::GrpGenToNfMorSet{NfToNfMor, AnticNumberField} + V::ModAlgAss{S, T, U} + M::QQMatrix + Minv::QQMatrix + + function NfToModAlgAssMor{S, T, U}() where {S, T, U} + return new{S, T, U}() + end +end + +function Base.show(io::IO, M::NfToModAlgAssMor) + if get(io, :supercompact, false) + # no nested printing + print(io, "Galois module map") + else + # nested printing allowed, preferably supercompact + io = pretty(io) + print(io, "Galois module map: ") + print(IOContext(io, :supercompact => true), Lowercase(), domain(M), " -> ") + print(IOContext(io, :supercompact => true), Lowercase(), codomain(M)) + end +end + +function (f::NfToModAlgAssMor)(O::Union{NfAbsOrd, NfAbsOrdIdl}) + V = codomain(f) + B = basis(O) + A = algebra(V) + G = group(A) + ZG = Order(A, collect(G)) + return lattice(V, ZG, [f(elem_in_nf(b)) for b in B]) +end + +automorphism_map(f::NfToModAlgAssMor) = f.mG + +function galois_module(K::AnticNumberField, aut::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) + G = domain(aut) + A = FlintQQ[G] + return _galois_module(K, A, aut, normal_basis_generator = normal_basis_generator) +end + +function _galois_module(K::AnticNumberField, A, aut::Map = automorphism_group(K)[2]; normal_basis_generator = normal_basis(K)) + G = domain(aut) + alpha = normal_basis_generator + + basis_alpha = Vector{elem_type(K)}(undef, dim(A)) + for (i, g) in enumerate(G) + f = aut(g) + basis_alpha[A.group_to_base[g]] = f(alpha) + end + + M = zero_matrix(base_field(K), degree(K), degree(K)) + for i = 1:degree(K) + a = basis_alpha[i] + for j = 1:degree(K) + M[i, j] = coeff(a, j - 1) + end + end + + invM = inv(M) + + z = NfToModAlgAssMor{QQField, QQMatrix, typeof(A)}() + V = regular_module(A) + z.K = K + z.mG = aut + z.V = V + z.M = M + z.Minv = invM + V.isfree = 1 + V.free_rank = 1 + + return V, z +end + +domain(f::NfToModAlgAssMor) = f.K + +codomain(f::NfToModAlgAssMor) = f.V + +function image(f::NfToModAlgAssMor, x::nf_elem) + K = domain(f) + @assert parent(x) === K + V = codomain(f) + + t = zero_matrix(base_field(K), 1, degree(K)) + for i = 1:degree(K) + t[1, i] = coeff(x, i - 1) + end + y = t*f.Minv + return V([ y[1, i] for i = 1:degree(K) ]) +end + +function preimage(f::NfToModAlgAssMor, x::ModAlgAssElem) + K = domain(f) + y = coordinates(x)*f.M + return K(y) +end + +################################################################################ +# +# Isomorphism +# +################################################################################ + +# Implement V \cong W <=> tr(V) == tr(W) if V and W are semisimple +# (e.g. if A is semisimple), Lam, "Introduction to noncommutative algebra", +# +#function is_isomorphic(V::ModAlgAss, W::ModAlgAss) +# @req algebra(V) === algebra(W) "Modules must be defined over same algebra" +# A = algebra(V) +#end + +function is_isomorphic_with_isomorphism(V::ModAlgAss, W::ModAlgAss) + B = _basis_of_commutator_algebra(W.action_of_basis, V.action_of_basis) + l = length(B) + for i in 1:50 + c = sum(c * b for (c, b) in zip(rand(-1:1, l), B)) + if !iszero(det(c)) + return true, hom(V, W, c) + end + end + return false, hom(V, W, basis(W)) +end + +function is_free(V::ModAlgAss) + dV = dim(V) + A = algebra(V) + dA = dim(A) + if !is_divisible_by(dV, dA) + return false + end + d = div(dV, dA) + fl = is_isomorphic_with_isomorphism(V, free_module(algebra(V), d))[1] + if !fl + return false + end + V.isfree = 1 + V.free_rank = d + return true +end + +################################################################################ +# +# Twists +# +################################################################################ + +function _twists(V::ModAlgAss) + A = algebra(V) + @req A isa AlgGrp "Algebra must be a group algebra" + G = group(A) + A = outer_automorphisms(G) + res = typeof(V)[] + for a in A + push!(res, _twist(V, a)) + end + return res +end + +function _twist(V::ModAlgAss, a::Map) + A = algebra(V) + @req A isa AlgGrp "Algebra must be a group algebra" + G = group(A) + @req domain(a) == G == codomain(G) "Map must be an endomorphism of the group" + B = basis(A) + rep2 = QQMatrix[] + for i in 1:length(B) + g = A.base_to_group[i] + @assert A(g) == B[i] + push!(rep2, action(V, A(a(g)))) + end + W = Amodule(A, rep2) +end diff --git a/src/ModAlgAss/Morphisms.jl b/src/ModAlgAss/Morphisms.jl index ddf91ede4e..625a458b2e 100644 --- a/src/ModAlgAss/Morphisms.jl +++ b/src/ModAlgAss/Morphisms.jl @@ -22,7 +22,7 @@ codomain(f::ModAlgHom) = f.codomain matrix(f::ModAlgHom) = f.matrix -function morphism(V::T, W::T, M::MatrixElem; check = true) where {T <: ModAlgAss} +function hom(V::T, W::T, M::MatrixElem; check = true) where {T <: ModAlgAss} @req has_algebra(V) == has_algebra(W) "Both modules must have underlying algebra or not" if has_algebra(V) @req algebra(V) === algebra(W) "Modules must have same underlying algbera" @@ -40,6 +40,35 @@ function Base.show(io::IO, f::ModAlgHom) print(io, "Module morphism") end +function Base.:(==)(V::T, W::T) where {T <: ModAlgHom} + return domain(V) === domain(W) && + codomain(V) === codomain(W) && + matrix(V) == matrix(W) +end + +function inv(b::ModAlgHom) + return hom(codomain(b), domain(b), inv(matrix(b))) +end + +function image(f::ModAlgHom, a::ModAlgAssElem) + @assert parent(a) === domain(f) + return codomain(f)(coordinates(a) * matrix(f)) +end + +function preimage(f::ModAlgHom, a::ModAlgAssElem) + @assert parent(a) === codomain(f) + fl, b = can_solve_with_solution(matrix(f), matrix([coordinates(a)]), side = :left) + if !fl + error("No preimage") + else + return domain(f)(_eltseq(b)) + end +end + +function zero_map(V::ModAlgAss, W::ModAlgAss) + return hom(V, W, zero_matrix(base_ring(algebra(V)), dim(V), dim(W))) +end + ################################################################################# # # Endomorphism algebra map @@ -66,6 +95,23 @@ function image(f::EndAlgMap, a::AbsAlgAssElem) return ModAlgHom(f.V, f.V, matrix(a)) end +function preimage(f::EndAlgMap, b::ModAlgHom) + #@req parent(b) === codomain(f) "Element must be in the codomain of the map" + A = domain(f) + B = basis(A) + M = matrix([Hecke._eltseq(matrix(f(b))) for b in B]) + fl, v = can_solve_with_solution(M, matrix(base_ring(A), 1, ncols(M), Hecke._eltseq(matrix(b))), side = :left) + @assert fl + a = sum(v[i]*B[i] for i in 1:length(B)) + @assert f(a) == b + return a +end + +function compose(f::ModAlgHom, g::ModAlgHom) + @assert codomain(f) === domain(g) + return hom(domain(f), codomain(g), matrix(f) * matrix(g)) +end + ################################################################################ # # Basis of homomorphism spaces @@ -84,14 +130,21 @@ end ################################################################################ function endomorphism_algebra(V::ModAlgAss) - B = _basis_of_hom(V, V) - x, _ = consistent_action(V, V) - for b in B - for i in 1:length(x) - @assert x[i] * b == b * x[i] + f = get_attribute(V, :endomorphism_algebra) + if f !== nothing + return domain(f), f + else + B = _basis_of_hom(V, V) + x, _ = consistent_action(V, V) + for b in B + for i in 1:length(x) + @assert x[i] * b == b * x[i] + end end + A = matrix_algebra(coefficient_ring(V), B, isbasis = true) + f = EndAlgMap(A, V) + set_attribute!(V, :endomorphism_algebra => f) + return A, f end - A = matrix_algebra(coefficient_ring(V), B, isbasis = true) - return A, EndAlgMap(A, V) end diff --git a/src/NumField/NfAbs/NormRelation/Setup.jl b/src/NumField/NfAbs/NormRelation/Setup.jl index bdd8e59940..d9bf643b35 100644 --- a/src/NumField/NfAbs/NormRelation/Setup.jl +++ b/src/NumField/NfAbs/NormRelation/Setup.jl @@ -997,7 +997,7 @@ function _smallest_scalar_norm_relation_coprime(G::GrpGen, m::ZZRingElem) reverse!(all_non_trivial_subs) - QG = AlgGrp(FlintQQ, G) + QG = AlgGrp(FlintQQ, G, cached = false) norms_rev = Dict{elem_type(QG), Int}() norms = Vector{elem_type(QG)}(undef, length(all_non_trivial_subs)) for i in 1:length(all_non_trivial_subs) diff --git a/test/AlgAss.jl b/test/AlgAss.jl index 939b7e8ebc..ead04e6fe6 100644 --- a/test/AlgAss.jl +++ b/test/AlgAss.jl @@ -6,4 +6,5 @@ include("AlgAss/Elem.jl") include("AlgAss/Ideal.jl") include("AlgAss/Ramification.jl") + include("AlgAss/AlgQuat.jl") end diff --git a/test/AlgAss/AlgQuat.jl b/test/AlgAss/AlgQuat.jl new file mode 100644 index 0000000000..46b026dc5b --- /dev/null +++ b/test/AlgAss/AlgQuat.jl @@ -0,0 +1,14 @@ +@testset "Quaternion algebras" begin + M = Array{QQFieldElem, 3}(undef, 4, 4, 4) + M[:, :, 1] = [-2 4 -2 0; 0 0 -1 -3; 1 1 0 3; -3 3 -3 0] + M[:, :, 2] = [ -4 0 -1 -3; 2 0 2 0; -3 2 -5//2 -3//2; -3 0 -3//2 -9//2] + M[:, :, 3] = [-4 0 -2 -6; 4 -4 5 3; -4 4 -7//2 -9//2; 0 0 -3//2 -9//2] + M[:, :, 4] = [4//3 -8//3 8//3 0; 4//3 4//3 -1//3 3; -4//3 -4//3 5//6 -3//2; 0 0 3//2 -3//2] + A = AlgAss(QQ, M) + B, f = Hecke.is_quaternion_algebra(A) + for b in basis(B) + for bb in basis(B) + @test f(b) * f(bb) == f(b * bb) + end + end +end diff --git a/test/AlgAssAbsOrd/LocallyFreeClassGroup.jl b/test/AlgAssAbsOrd/LocallyFreeClassGroup.jl index 7731779a9c..f33c3fbdd9 100644 --- a/test/AlgAssAbsOrd/LocallyFreeClassGroup.jl +++ b/test/AlgAssAbsOrd/LocallyFreeClassGroup.jl @@ -65,21 +65,24 @@ end K, a = number_field(f, "a") # Gal(K/Q) = C2 x C6 (aka 12T2 aka small_group(12, 5)) OK = maximal_order(K) G, mG = automorphism_group(K) - A, KtoA = galois_module(K, mG, normal_basis_generator = a) - basisOK = [ KtoA(b.elem_in_nf) for b in basis(OK) ] - d = lcm([ denominator(b) for b in basisOK ]) - ZG = Order(A, basis(A)) - I = Hecke.ideal_from_lattice_gens(A, ZG, [ d*b for b in basisOK ]) + V, KtoV = galois_module(K, mG, normal_basis_generator = a) + A = algebra(V) + ZG = integral_group_ring(A) + I = KtoV(lll(OK)) S, mS = locally_free_class_group_with_disc_log(ZG) @test S.snf == ZZRingElem[ 2, 2 ] @test iszero(mS(I)) # Check whether one can also call it with AlgAss B, BtoA = AlgAss(A) - basisOK2 = [ BtoA\b for b in basisOK ] + Areg = Hecke.regular_module(A) + AregToA = x -> A(coordinates(x)) + fl, VToAreg = Hecke.is_isomorphic_with_isomorphism(V, Areg) + + basisOK2 = [ BtoA\(AregToA(VToAreg(KtoV(K(b))))) for b in basis(lll(OK)) ] d2 = lcm([ denominator(b) for b in basisOK2 ]) ZG = Order(B, basis(B)) - I = Hecke.ideal_from_lattice_gens(B, ZG, [ d*b for b in basisOK2 ]) + I = Hecke.ideal_from_lattice_gens(B, ZG, [ d2*b for b in basisOK2 ]) S, mS = locally_free_class_group_with_disc_log(ZG, check = false) @test S.snf == ZZRingElem[ 2, 2 ] @test iszero(mS(I)) @@ -88,13 +91,11 @@ end K, a = number_field(f, "a") # Gal(K/Q) = Q8 (aka 8T5 aka small_group(8, 4)) OK = maximal_order(K) G, mG = automorphism_group(K) - A, KtoA = galois_module(K, mG, normal_basis_generator = a) - basisOK = [ KtoA(b.elem_in_nf) for b in basis(OK) ] - d = lcm([ denominator(b) for b in basisOK ]) - ZG = Order(A, basis(A)) - I = Hecke.ideal_from_lattice_gens(A, ZG, [ d*b for b in basisOK ]) + V, KtoV = galois_module(K, mG, normal_basis_generator = a) + A = algebra(V) + ZG = integral_group_ring(A) S, mS = locally_free_class_group_with_disc_log(ZG) + I = KtoV(lll(OK)) @test S.snf == ZZRingElem[ 2 ] @test mS(I) == S[1] - @test iszero(mS(I^2)) end diff --git a/test/ModAlgAss.jl b/test/ModAlgAss.jl index ba15cbc4ae..e0250a4556 100644 --- a/test/ModAlgAss.jl +++ b/test/ModAlgAss.jl @@ -1,3 +1,4 @@ @testset "ModAlgAss" begin include("ModAlgAss/Lattices.jl") + include("ModAlgAss/ModAlgAss.jl") end diff --git a/test/ModAlgAss/ModAlgAss.jl b/test/ModAlgAss/ModAlgAss.jl new file mode 100644 index 0000000000..eca1b5807e --- /dev/null +++ b/test/ModAlgAss/ModAlgAss.jl @@ -0,0 +1,20 @@ +@testset "ModAlgAss" begin + K1, a = cyclotomic_field(7, "a") # C6 + Qx, x = QQ["x"] + K2, = number_field(x^8 + 12*x^6 + 36*x^4 + 36*x^2 + 9, "a") # Q8 + for K in [K1, K2] + G, mG = automorphism_group(K) + V, f = galois_module(K, mG) + QG = algebra(V) + for i in 1:10 + b = rand(K, -1:1) + c = rand(K, -1:1) + o = rand(QQ, -10:10) + @test f(o * b + c) == o * f(b) + f(c) + @test preimage(f, f(b)) == b + g = G[rand(1:order(G))] + @test f(mG(g)(b)) == f(b) * QG(g) + end + end +end + diff --git a/test/RCF/conductor_sieve.jl b/test/RCF/conductor_sieve.jl index 2319362355..8affef4217 100644 --- a/test/RCF/conductor_sieve.jl +++ b/test/RCF/conductor_sieve.jl @@ -8,7 +8,7 @@ @test length(l)==47 l1 = collect(Hecke.C22_extensions(10^4)) @test length(l1)==47 - @test length(abelian_fields(FlintQQ, [3], ZZRingElem(10)^3)) == 5 + @test length(abelian_extensions(FlintQQ, [3], ZZRingElem(10)^3)) == 5 K, a = number_field(x^2+1, "a") auts = small_generating_set(automorphism_list(K, copy = false)) diff --git a/test/RCF/rcf.jl b/test/RCF/rcf.jl index 061e429724..2df0ec7ca7 100644 --- a/test/RCF/rcf.jl +++ b/test/RCF/rcf.jl @@ -330,11 +330,10 @@ end end @testset "Conductor fix" begin - flds = abelian_fields(QQ, [2, 2], ZZ(4225), only_real = true) + flds = abelian_extensions(QQ, [2, 2], ZZ(4225), only_real = true) @test length(flds) == 4 end - @testset "Kaiser-Lorenz" begin Qx, x = QQ["x"] f = x^6-x^5+x^4-2*x^3+x^2+1