From 415930fda32ed7f94698e1a4f7fdeb864ca21cba Mon Sep 17 00:00:00 2001 From: Claus Fieker Date: Mon, 16 Sep 2024 14:23:01 +0200 Subject: [PATCH] feat: residue fields of orders in global function fields --- src/AlgAss/AbsAlgAss.jl | 11 ++++++ src/FunField/DegreeLocalization.jl | 2 + src/GenOrd/GenOrd.jl | 2 +- src/GenOrd/Ideal.jl | 56 ++++++++++++++++++++++++--- src/GenOrd/Types.jl | 4 ++ src/HeckeTypes.jl | 17 ++++---- src/NumFieldOrd/NfOrd/ResidueField.jl | 35 ++++++++++------- test/GenOrd/Ideal.jl | 40 +++++++++++++++++++ 8 files changed, 140 insertions(+), 27 deletions(-) diff --git a/src/AlgAss/AbsAlgAss.jl b/src/AlgAss/AbsAlgAss.jl index 9764b52efd..550354f0b0 100644 --- a/src/AlgAss/AbsAlgAss.jl +++ b/src/AlgAss/AbsAlgAss.jl @@ -608,6 +608,17 @@ function _primitive_element(A::AbstractAssociativeAlgebra{QQFieldElem}) return a, minpoly(a) end +function __primitive_element(A::AbstractAssociativeAlgebra{QQFieldElem}) + return _primitive_element(A) +end + +function __primitive_element(A::AbstractAssociativeAlgebra{<:NumFieldElem}) + B, BtoA = restrict_scalars(A, QQ) + a, f = __primitive_element(B) + b = BtoA(a) + return b, minpoly(b) +end + function __primitive_element(A::S) where {T <: FinFieldElem, S <: AbstractAssociativeAlgebra{T}} #<: Union{zzModRingElem, FqPolyRepFieldElem, fqPolyRepFieldElem, EuclideanRingResidueRingElem{ZZRingElem}, QQFieldElem, EuclideanRingResidueFieldElem{ZZRingElem}, fpFieldElem} d = dim(A) a = rand(A) diff --git a/src/FunField/DegreeLocalization.jl b/src/FunField/DegreeLocalization.jl index 643628c07d..8e5fa2c27c 100644 --- a/src/FunField/DegreeLocalization.jl +++ b/src/FunField/DegreeLocalization.jl @@ -299,6 +299,8 @@ function divrem(a::KInftyElem{T}, b::KInftyElem{T}, check::Bool=true) where T <: end end +Base.rem(a::KInftyElem{T}, b::KInftyElem{T}, checked::Bool=true) where T <: FieldElement = mod(a, b, checked) + ############################################################################### # # GCD diff --git a/src/GenOrd/GenOrd.jl b/src/GenOrd/GenOrd.jl index bcda105c8b..9cf13a6790 100644 --- a/src/GenOrd/GenOrd.jl +++ b/src/GenOrd/GenOrd.jl @@ -96,7 +96,7 @@ basis_matrix_inverse(O::GenOrd{S}) where {S} = O.itrans::dense_matrix_type(elem_ # ################################################################################ -function basis(O::GenOrd) +function basis(O::GenOrd; copy::Bool = true) get_attribute!(O, :basis) do if _is_standard(O) return map(O, basis(field(O))) diff --git a/src/GenOrd/Ideal.jl b/src/GenOrd/Ideal.jl index e5cb2b364e..55d7497f13 100644 --- a/src/GenOrd/Ideal.jl +++ b/src/GenOrd/Ideal.jl @@ -730,7 +730,12 @@ function _decomposition(O::GenOrd, I::GenOrdIdl, Ip::GenOrdIdl, T::GenOrdIdl, p: return ideals end -function Hecke.StructureConstantAlgebra(O::GenOrd, I::GenOrdIdl, p::RingElem) +function StructureConstantAlgebra(O::GenOrd, I::GenOrdIdl, p::RingElem) + FQ, phi = residue_field(base_ring(O), p) + StructureConstantAlgebra(O, I, p, phi) +end + +function StructureConstantAlgebra(O::GenOrd, I::GenOrdIdl, p::RingElem, phi) @assert order(I) === O n = degree(O) @@ -746,7 +751,7 @@ function Hecke.StructureConstantAlgebra(O::GenOrd, I::GenOrdIdl, p::RingElem) end r = length(basis_elts) - FQ, phi = residue_field(O.R,p) + FQ = codomain(phi) if r == 0 A = zero_algebra(FQ) @@ -999,7 +1004,7 @@ end # ################################################################################ -mutable struct GenOrdToAlgAssMor{S, T} <: Map{S, StructureConstantAlgebra{T}, Hecke.HeckeMap, GenOrdToAlgAssMor} +mutable struct GenOrdToAlgAssMor{S, T} <: Map{S, StructureConstantAlgebra{T}, Hecke.HeckeMap, Any}#GenOrdToAlgAssMor} header::Hecke.MapHeader function GenOrdToAlgAssMor{S, T}(O::S, A::StructureConstantAlgebra{T}, _image::Function, _preimage::Function) where {S <: GenOrd, T} @@ -1010,10 +1015,9 @@ mutable struct GenOrdToAlgAssMor{S, T} <: Map{S, StructureConstantAlgebra{T}, He end function GenOrdToAlgAssMor(O::GenOrd, A::StructureConstantAlgebra{T}, _image, _preimage) where {T} - return AbsOrdToAlgAssMor{typeof(O), T}(O, A, _image, _preimage) + return GenOrdToAlgAssMor{typeof(O), T}(O, A, _image, _preimage) end - ################################################################################ # # Misc. @@ -1033,3 +1037,45 @@ function Hecke.characteristic(R::EuclideanRingResidueField{Hecke.GenOrdElem{Gene return characteristic(function_field(base_ring(R))) end +mutable struct GenOrdToFqField{S, T} <: Map{S, T, Hecke.HeckeMap, Any}#GenOrdToFqField} + header::Hecke.MapHeader + + function GenOrdToFqField{S, T}(O::S, A::T, _image::Function, _preimage::Function) where {S <: GenOrd, T} + z = new{S, T}() + z.header = Hecke.MapHeader(O, A, _image, _preimage) + return z + end +end + +function Nemo._residue_field(f::PolyRingElem{<:NumFieldElem}) + Kt = parent(f) + L, b = number_field(f, "a"; cached = false) + return L, MapFromFunc(Kt, L, x -> L(x), y -> Kt(y)) +end + +function residue_field(O::GenOrd, P::GenOrdIdl, check::Bool = true) + a, g, b, phi = get_residue_field_data(P)::Tuple{elem_type(O), + dense_poly_type(base_ring(O)), + Vector{dense_matrix_type(base_ring(O))}, + Any} # not optimal + gg = map_coefficients(phi, g) + bb = map_entries.(phi, b) + K = base_ring(parent(gg)) + F, KtoF = Nemo._residue_field(gg) + d = degree(O) + imgofbas = [KtoF(parent(gg)(bb[i][1, :])) for i in 1:d] + powersofa = powers(a, degree(g)) + + _image = function(x::GenOrdElem) + @assert parent(x) === O + c = base_ring(O).(coordinates(x)) + return sum(phi(c[i]) * imgofbas[i] for i in 1:d) + end + + _preimage = function(x) + @assert parent(x) === F + return sum(preimage(phi, coeff(x, i - 1)) * powersofa[i] for i in 1:degree(g)) + end + + return F, GenOrdToFqField{typeof(O), typeof(F)}(O, F, _image, _preimage) +end diff --git a/src/GenOrd/Types.jl b/src/GenOrd/Types.jl index a0e5bc7c2e..59321311f4 100644 --- a/src/GenOrd/Types.jl +++ b/src/GenOrd/Types.jl @@ -91,6 +91,10 @@ end gen_two::GenOrdElem princ_gen::GenOrdElem + prim_elem::GenOrdElem + min_poly_prim_elem + basis_in_prim + phi splitting_type::Tuple{Int, Int} #ordered as ramification index, inertia degree diff --git a/src/HeckeTypes.jl b/src/HeckeTypes.jl index ea7256311d..8fc72d10a2 100644 --- a/src/HeckeTypes.jl +++ b/src/HeckeTypes.jl @@ -1,3 +1,11 @@ +################################################################################ +# +# Maps +# +################################################################################ + +include("Map/MapType.jl") + ################################################################################ # # Abstract types for number fields @@ -959,6 +967,7 @@ const AbsNumFieldOrderIdealSetID = AbstractAlgebra.CacheDictType{AbsNumFieldOrde prim_elem::AbsNumFieldOrderElem{S, T} min_poly_prim_elem::ZZPolyRingElem # minimal polynomial modulo P basis_in_prim::Vector{ZZMatrix} # + phi::MapFromFunc{ZZRing, FqField} function AbsNumFieldOrderIdeal{S, T}(O::AbsNumFieldOrder{S, T}) where {S, T} # populate the bits types (Bool, Int) with default values @@ -1859,14 +1868,6 @@ function QuadBin(R, a, b, c) return z end -################################################################################ -# -# Maps -# -################################################################################ - -include("Map/MapType.jl") - ################################################################################ # # NoElements diff --git a/src/NumFieldOrd/NfOrd/ResidueField.jl b/src/NumFieldOrd/NfOrd/ResidueField.jl index 0ed470ae27..fd59b32795 100644 --- a/src/NumFieldOrd/NfOrd/ResidueField.jl +++ b/src/NumFieldOrd/NfOrd/ResidueField.jl @@ -11,42 +11,50 @@ # pi(a) # - a vector of ZZMatrix B, such that # pi(basis(O)[i]) = sum_j B[i][1, j] * pi(a)^j -function compute_residue_field_data(m) +function compute_residue_field_data(m, phi) O = domain(m) basisO = basis(O, copy = false) B = codomain(m) primB, minprimB, getcoordpowerbasis = _as_field(B) f = degree(minprimB) + F = domain(phi) + @assert F === base_ring(O) prim_elem = m\(primB) - min_poly_prim_elem = ZZPolyRingElem(ZZRingElem[lift(ZZ, coeff(minprimB, i)) for i in 0:degree(minprimB)]) - min_poly_prim_elem.parent = ZZPolyRing(FlintZZ, :$, false) - basis_in_prim = Vector{ZZMatrix}(undef, degree(O)) + min_poly_prim_elem = (polynomial_ring(F, :$, cached = false)[1])(elem_type(F)[preimage(phi, coeff(minprimB, i)) for i in 0:degree(minprimB)]) + basis_in_prim = Vector{dense_matrix_type(F)}(undef, degree(O)) for i in 1:degree(O) - basis_in_prim[i] = zero_matrix(FlintZZ, 1, f) + basis_in_prim[i] = zero_matrix(F, 1, f) t = getcoordpowerbasis(m(basisO[i])) for j in 1:f - basis_in_prim[i][1, j] = lift(ZZ, t[1, j]) + basis_in_prim[i][1, j] = preimage(phi, t[1, j]) end end - return prim_elem, min_poly_prim_elem, basis_in_prim + return prim_elem, min_poly_prim_elem, basis_in_prim, phi end # Compute the residue field data and store it in the prime P given the map m -function compute_residue_field_data!(P, m) - P.prim_elem, P.min_poly_prim_elem, P.basis_in_prim = compute_residue_field_data(m) +function compute_residue_field_data!(P::AbsNumFieldOrderIdeal, m) + F = base_ring(codomain(m)) + phi = MapFromFunc(ZZ, F, x -> F(x), y -> lift(ZZ, y)) + return compute_residue_field_data!(P, m, phi) +end + +function compute_residue_field_data!(P, m, phi) + P.prim_elem, P.min_poly_prim_elem, P.basis_in_prim, P.phi = compute_residue_field_data(m, phi) return nothing end # Compute the residue field data given the prime P function compute_residue_field_data!(P) p = minimum(P) - if fits(Int, p) + if isa(p, IntegerUnion) && fits(Int, p) smallp = Int(p) A, m = StructureConstantAlgebra(order(P), P, smallp) compute_residue_field_data!(P, m) else - AA, mm = StructureConstantAlgebra(order(P), P, p) - compute_residue_field_data!(P, mm) + _, phi = residue_field(base_ring(order(P)), p) + AA, mm = StructureConstantAlgebra(order(P), P, p, phi) + compute_residue_field_data!(P, mm, phi) end return nothing end @@ -55,7 +63,7 @@ end # data is often cached. function get_residue_field_data(P) if isdefined(P, :prim_elem) - return P.prim_elem, P.min_poly_prim_elem, P.basis_in_prim + return P.prim_elem, P.min_poly_prim_elem, P.basis_in_prim, P.phi else compute_residue_field_data!(P) get_residue_field_data(P) @@ -184,6 +192,7 @@ end # High level functions # ################################################################################ + @doc raw""" residue_field(O::AbsSimpleNumFieldOrder, P::AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, check::Bool = true) -> Field, Map diff --git a/test/GenOrd/Ideal.jl b/test/GenOrd/Ideal.jl index 9d8e7e2db9..37dded3327 100644 --- a/test/GenOrd/Ideal.jl +++ b/test/GenOrd/Ideal.jl @@ -36,4 +36,44 @@ h = O.R(x) L = prime_decomposition(O, h) @test prod([f[1]^f[2] for f in L]) == Hecke.GenOrdIdl(O, h) + + for (P, _) in L + F, OtoF = residue_field(O, P) + for i in 1:10 + a = dot([rand(base_ring(O), 1:5, 1:5) for i in 1:degree(O)], basis(O)) + b = dot([rand(base_ring(O), 1:5, 1:5) for i in 1:degree(O)], basis(O)) + @test OtoF(a) * OtoF(b) == OtoF(a * b) + c = OtoF(a) + @test OtoF(OtoF\c) == c + end + end + + k = GF(5) + kt, t = rational_function_field(k, "t") + ktx, x = kt["x"] + F, a = function_field(x^5+x+3*t+1) + OF = Hecke.finite_maximal_order(F) + OI = Hecke.infinite_maximal_order(F) + lp = prime_decomposition(OF, numerator(t-1)) + for (P, _) in lp + K, OFtoK = residue_field(OF, P) + for i in 1:10 + a = dot([rand(base_ring(OF), 1:5) for i in 1:degree(OF)], basis(OF)) + b = dot([rand(base_ring(OF), 1:5) for i in 1:degree(OF)], basis(OF)) + @test OFtoK(a) * OFtoK(b) == OFtoK(a * b) + c = rand(K) + @test OFtoK(OFtoK\c) == c + end + end + lp = prime_decomposition(OI, base_ring(OI)(1//t)) + for (P, _) in lp + K, OItoK = residue_field(OI, P) + for i in 1:10 + a = OI(numerator(rand(kt, 1:5))(1//t)) + b = OI(numerator(rand(kt, 1:5))(1//t)) + @test OItoK(a) * OItoK(b) == OItoK(a * b) + c = rand(K) + @test OItoK(OItoK\c) == c + end + end end