Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various: factoring, ideal, relative #1729

Merged
merged 5 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/src/Experimental/galois.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ galois_group(f::PolyElem{<:FieldElem})
```

Over the rational function field, we can also compute the monodromy group:
```@meta
DocTestFilters = r"Group\(.*\]\)"
```
```jldoctest galqt; setup = :(using Oscar, Random ; Random.seed!(1))
julia> Qt, t = RationalFunctionField(QQ, "t");

Expand All @@ -107,10 +110,10 @@ julia> subfields(F)
(Function Field over Rational Field with defining polynomial a^3 - 108*t^2 - 108*t - 27, -_a^2)

julia> galois_group(F)
(Group([ (), (1,5)(2,3)(4,6), (1,3,4)(2,5,6) ]), Galois Context for s^6 + 108*t^2 + 540*t + 675)
(Group([ (1,3,4)(2,5,6), (1,2)(3,6)(4,5) ]), Galois Context for s^6 + 108*t^2 + 540*t + 675)

julia> G, C, k = galois_group(F, overC = true)
(Group([ (1,3,4)(2,5,6) ]), Galois Context for s^6 + 108*t^2 + 540*t + 675, Number field over Rational Field with defining polynomial x^2 + 12*x + 24336)
(Group([ (1,4,3)(2,6,5) ]), Galois Context for s^6 + 108*t^2 + 540*t + 675, Number field over Rational Field with defining polynomial x^2 + 12*x + 24336)

```
So, while the splitting field over `Q(t)` has degree `6`, the galois group there
Expand All @@ -119,6 +122,9 @@ over `C(t)` is only of degree `3`. Here the group collapses to a cyclic group
of degree `3`, the algebraic closure of `Q` in the splitting field is the
quadratic field returned last. It can be seen to be isomorphic to a cyclotomic field:

```@meta
DocTestFilters = nothing
```
```jldoctest galqt
julia> is_isomorphic(k, cyclotomic_field(3)[1])
true
Expand Down
85 changes: 74 additions & 11 deletions experimental/GaloisGrp/GaloisGrp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ module GaloisGrp

using Oscar, Markdown
import Base: ^, +, -, *, ==
import Oscar: Hecke, AbstractAlgebra, GAP
import Oscar: Hecke, AbstractAlgebra, GAP, extension_field
using Oscar: SLPolyRing, SLPoly, SLPolynomialRing, CycleType

export galois_group, slpoly_ring, elementary_symmetric, galois_quotient,
power_sum, to_elementary_symmetric, cauchy_ideal, galois_ideal, fixed_field
power_sum, to_elementary_symmetric, cauchy_ideal, galois_ideal,
fixed_field, valuation_of_roots

import Hecke: orbit, fixed_field, extension_field


function __init__()
GAP.Packages.load("ferret"; install=true)

Expand Down Expand Up @@ -2259,14 +2259,14 @@ function galois_quotient(C::GaloisCtx, Q::PermGroup)
return res
end

"""
@doc Markdown.doc"""
galois_quotient(C::GaloisCtx, d::Int)

Finds all(?) subfields (up to isomorphism) of the splitting field of degree d
with galois group isomorphic to the original one.

# Examples
```jldoctest
```jldoctest; filter = r"Group\(.*\]\)"
julia> Qx, x = QQ["x"];

julia> G, C = galois_group(x^3-2);
Expand All @@ -2276,7 +2276,7 @@ julia> galois_quotient(C, 6)
Number field over Rational Field with defining polynomial x^6 + 324*x^4 - 4*x^3 + 34992*x^2 + 1296*x + 1259716

julia> galois_group(ans[1])
(Group([ (), (1,5)(2,4)(3,6), (1,2,3)(4,5,6) ]), Galois Context for x^6 + 324*x^4 - 4*x^3 + 34992*x^2 + 1296*x + 1259716 and prime 13)
(Group([ (1,2,3)(4,5,6), (1,4)(2,6)(3,5) ]), Galois Context for x^6 + 324*x^4 - 4*x^3 + 34992*x^2 + 1296*x + 1259716 and prime 13)

julia> is_isomorphic(ans[1], G)
true
Expand Down Expand Up @@ -2376,7 +2376,7 @@ functions and the coefficients of the polynomial.
julia> Qx, x = QQ["x"];

julia> i = galois_ideal(galois_group(x^4-2)[2])
ideal(x4^4 - 2, x3^3 + x3^2*x4 + x3*x4^2 + x4^3, x2^2 + x2*x3 + x2*x4 + x3^2 + x3*x4 + x4^2, x1 + x2 + x3 + x4, -x1*x2 - x1*x3 - x2*x4 - x3*x4)
ideal(x4^4 - 2, x3^3 + x3^2*x4 + x3*x4^2 + x4^3, x2^2 + x2*x3 + x2*x4 + x3^2 + x3*x4 + x4^2, x1 + x2 + x3 + x4, x1*x4 + x2*x3, x1^2*x4^2 + x2^2*x3^2 - 4, x1^4 - 2, x2^4 - 2, x3^4 - 2, x4^4 - 2)

julia> k, _ = number_field(i);

Expand All @@ -2386,6 +2386,7 @@ julia> length(roots(x^4-2, k))
```
"""
function galois_ideal(C::GaloisCtx, extra::Int = 5)
#TODO if group is small, then factoring will be better...
f = C.f
id = gens(cauchy_ideal(f))
R = parent(id[1])
Expand All @@ -2396,10 +2397,70 @@ function galois_ideal(C::GaloisCtx, extra::Int = 5)
#or the 1st group in the descent chain (C.chn)...
#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
_, g = slpoly_ring(ZZ, n)
for bs = C.start[2]
W = isomorphic_perm_group(wreath_product(symmetric_group(length(bs[1])), symmetric_group(length(bs))))[1]
W = W^symmetric_group(n)(vcat(bs...))
G = intersect(G, W)[1]
#each subfield causes, possibly, several invariants...
# (prod(g[b]) for b = bs)
# are the conjugates of a primitive element, need possibly a shift
# prod(g[b] .+ i) (acording to Klueners)
# so the elem. symm (or the power sums) of the above need to be in Z
# furthermore, all roots of this poly are in K, so beta = g(alpha)
# this gives more invariants...
# all of them together basically prove the subfield, so they should
# be enough for the galois_ideal
# TODO: if the subfield has a smaller group, then this group
# could also be incorporated here...(use the galois_ideal of the
# subfield and evaluate at the PE below)
# in general, if G is large, then there are few descents, hence this
# might work well
# if G is small, then iterated factoring (possibly using the SolveRadical
# stuff) is better. See galois_factor there
r = roots(C, 5)
k = 0
while true
pe = [prod(r[b] .+ k) for b = bs]
if length(Set(pe)) == length(bs)
break
end
k += 1
end
PE = [prod(g[b] .+ k) for b = bs]
B = upper_bound(C, power_sum, PE, length(PE))
rt = roots(C, bound_to_precision(C, B))
pe = [evaluate(I, rt) for I = PE]
po = pe
fl, v = isinteger(C, B, sum(pe))
push!(id, sum(evaluate(I, x) for I = PE) -v)
@assert fl
h = [QQ(v)]
while length(h) < length(PE)
po = po .* pe
fl, v = isinteger(C, B, sum(po))
@assert fl
push!(h, QQ(v))
push!(id, sum(evaluate(I^length(h), x) for I = PE) - v)
end
h = Hecke.power_sums_to_polynomial(h)
q = roots(h, number_field(C.f)[1])
@assert length(q) > 0
q = parent(defining_polynomial(parent(q[1])))(q[1])
#TODO: think h(q(y)) is probably boring, while q(y) == pe might be
# not....
for y = x
push!(id, h(q(y)))
end
end
end

if length(C.chn) == 0
c = maximal_subgroup_chain(symmetric_group(n), C.G)
c = maximal_subgroup_chain(G, C.G)
else
c = maximal_subgroup_chain(symmetric_group(n), C.chn[1][1])
c = maximal_subgroup_chain(G, C.chn[1][1])
end

r = roots(C, bound_to_precision(C, C.B))
Expand All @@ -2423,6 +2484,7 @@ function galois_ideal(C::GaloisCtx, extra::Int = 5)
end
end
end

for (_, I, ts, T) = C.chn
B = upper_bound(C, I, ts)
r = roots(C, bound_to_precision(C, B, extra))
Expand Down Expand Up @@ -2594,8 +2656,9 @@ end

using .GaloisGrp
export galois_group, slpoly_ring, elementary_symmetric, galois_quotient,
power_sum, to_elementary_symmetric, cauchy_ideal, galois_ideal, fixed_field,
maximal_subgroup_reps, extension_field
power_sum, to_elementary_symmetric, cauchy_ideal, galois_ideal,
fixed_field, maximal_subgroup_reps, extension_field, slope,
valuation_of_roots

#=
M12: 2-transitive, hence msum is a waste
Expand Down
41 changes: 41 additions & 0 deletions experimental/GaloisGrp/Qt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -641,5 +641,46 @@ end

Hecke.lines(P::Hecke.Polygon) = P.lines
slope(l::Hecke.Line) = l.slope
export slope

function valuation_of_roots(f::fmpz_poly, p::fmpz)
@assert is_prime(p)
x = gen(parent(f))
N = Hecke.newton_polygon(f, x, p)
return [(slope(l), length(l)) for l = Hecke.lines(N)]
end

function valuation_of_roots(f::fmpz_poly, p::Integer)
return valuation_of_roots(f, fmpz(p))
end

function valuation_of_roots(f::fmpq_poly, p)
return valuation_of_roots(numerator(f), p)
end

function Hecke.newton_polygon(f::T) where T <: Generic.Poly{S} where S <: Union{qadic, padic, Hecke.LocalFieldElem}
dev = collect(coefficients(f))
d = degree(base_ring(f))
a = Tuple{Int, Int}[]
#careful: valuation is q-valued, normalised for val(p) == 1
# lines have Int corredinated, so we scale by the degree of the field
# => the slopes are also multiplied by this!!!
for i = 0:length(dev) -1
if !iszero(dev[i+1])
push!(a, (i, Int(numerator(d*valuation(dev[i+1])))))
end
end
P = Hecke.lower_convex_hull(a)
p = prime(base_ring(f))
return NewtonPolygon(P, f, gen(parent(f)), p, [parent(f)(x) for x = dev])
end

function valuation_of_roots(f::T) where T <: Generic.Poly{S} where S <: Union{qadic, padic, Hecke.LocalFieldElem}
d = degree(base_ring(f))
return [(fmpq(slope(l)//d), length(l)) for l = Hecke.lines(Hecke.newton_polygon(f))]
end





43 changes: 41 additions & 2 deletions experimental/GaloisGrp/RelGalois.jl
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ function galois_group(K::Hecke.SimpleNumField{nf_elem}; prime::Any = 0)
else
G = symmetric_group(degree(K))
end
GC.G = G
return G, GC
C.G = G
return G, C
end

G, F, si = starting_group(C, K)
Expand Down Expand Up @@ -214,3 +214,42 @@ function bound_to_precision(C::GaloisCtx{Hecke.vanHoeijCtx}, y::BoundRingElem{fm
N = ceil(Int, degree(k)/2/log(norm(P))*(log(c1*c2) + 2*log(v)))
return N
end

function galois_group(f::PolyElem{nf_elem}, ::FlintRationalField)
@assert isirreducible(f)

g = f
k = 0

p = Hecke.p_start
F = GF(p)

Kx = parent(f)
K = base_ring(Kx)

Zx = Hecke.Globals.Zx
local N
@vtime :PolyFactor Np = Hecke.norm_mod(g, p, Zx)
while is_constant(Np) || !is_squarefree(map_coefficients(F, Np))
k = k + 1
g = compose(f, gen(Kx) - k*gen(K))
@vtime :PolyFactor 2 Np = Hecke.norm_mod(g, p, Zx)
end

@vprint :PolyFactor 2 "need to shift by $k, now the norm\n"
if any(x -> denominator(x) > 1, coefficients(g)) ||
!Hecke.is_defining_polynomial_nice(K)
@vtime :PolyFactor 2 N = Hecke.Globals.Qx(norm(g))
else
@vtime :PolyFactor 2 N = Hecke.norm_mod(g, Zx)
@hassert :PolyFactor 1 N == Zx(norm(g))
end

while is_constant(N) || !is_squarefree(N)
error("should not happen")
end

global last_N = N
@show N
return galois_group(N)
end
44 changes: 41 additions & 3 deletions experimental/GaloisGrp/Solve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,16 @@ function recognise(C::GaloisCtx, S::SubField, I::SLPoly)
end

function recognise(C::GaloisCtx, S::SubField, J::Vector{<:SLPoly})
B = dual_basis_bound(S) * length(S.conj) *
maximum(I->Oscar.GaloisGrp.upper_bound(C, I, S.ts), J)
if isdefined(S, :ts)
B = dual_basis_bound(S) * length(S.conj) *
maximum(I->Oscar.GaloisGrp.upper_bound(C, I, S.ts), J)
else
B = dual_basis_bound(S) * length(S.conj) *
maximum(I->Oscar.GaloisGrp.upper_bound(C, I), J)
end
pr = Oscar.GaloisGrp.bound_to_precision(C, B)
r = roots(C, pr)
if S.ts != gen(parent(S.ts))
if isdefined(S, :ts) && S.ts != gen(parent(S.ts))
r = map(S.ts, r)
end
b = basis_abs(S)
Expand Down Expand Up @@ -600,6 +605,39 @@ function basis_abs(S::SubField)
return [i*j for j = d for i = b]
end

function factor_degree(G::PermGroup)
@assert is_transitive(G)
n = degree(G)
S = [stabilizer(G, i)[1] for i=1:n]
deg = Int[]
U = G
for i=1:n
V = intersect(U, S[i])[1]
push!(deg, index(U, V))
U = V
end
return deg
end

function galois_factor(C::GaloisCtx)
G = C.G
ld = factor_degree(G)
n = degree(G)
_, x = slpoly_ring(ZZ, n)
Gi = [stabilizer(G, 1)[1]]
J = [1]
I = [x[1]]
for i = 2:n
if ld[i] > 1
push!(J, i)
push!(I, x[i])
push!(Gi, stabilizer(G, J, on_tuples)[1])
end
end
z = _fixed_field(C, Gi, invar = I)
return recognise(C, z, x)
end

end # SolveRadical

import .SolveRadical