Skip to content

Commit

Permalink
QuadFormWithIsom Patch 2: towards better time in CI (#2825)
Browse files Browse the repository at this point in the history
  • Loading branch information
StevellM committed Sep 20, 2023
1 parent f4ce9c0 commit 93ddb13
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 267 deletions.
344 changes: 166 additions & 178 deletions experimental/QuadFormAndIsom/src/embeddings.jl

Large diffs are not rendered by default.

74 changes: 38 additions & 36 deletions experimental/QuadFormAndIsom/src/enumeration.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##################################################################################
#################################################################################
#
# This is an import to Oscar of the methods written following the paper [BH23] on
# "Finite subgroups of automorphisms of K3 surfaces".
Expand Down Expand Up @@ -101,7 +101,7 @@ julia> is_admissible_triple(genus(F), genus(C), genus(Lf), 5)
true
```
"""
function is_admissible_triple(A::ZZGenus, B::ZZGenus, C::ZZGenus, p::Integer)
function is_admissible_triple(A::ZZGenus, B::ZZGenus, C::ZZGenus, p::IntegerUnion)
zg = genus(integer_lattice(; gram = matrix(QQ, 0, 0, [])))
AperpB = direct_sum(A, B)
(signature_tuple(AperpB) == signature_tuple(C)) || (return false)
Expand All @@ -122,7 +122,7 @@ function is_admissible_triple(A::ZZGenus, B::ZZGenus, C::ZZGenus, p::Integer)
end

# A+B and C must agree locally at every primes except p
for q in filter(qq -> qq != p, union!([2], primes(AperpB), primes(C)))
for q in filter(qq -> qq != p, union!(ZZRingElem[2], primes(AperpB), primes(C)))
if local_symbol(AperpB, q) != local_symbol(C, q)
return false
end
Expand Down Expand Up @@ -223,18 +223,14 @@ function is_admissible_triple(A::ZZGenus, B::ZZGenus, C::ZZGenus, p::Integer)
return false
end

qA, qB, qC = discriminant_group.([A, B, C])
qC = discriminant_group(C)
spec = (p == 2) && (_is_free(qA, p, l+1)) && (_is_free(qB, p, l+1)) && (_is_even(qC, p, l))
rA = _rho_functor(qA, p, l+1)
rB = _rho_functor(qB, p, l+1)
if spec
return is_anti_isometric_with_anti_isometry(rA, rB)[1]
else
return _anti_isometry_bilinear(rA, rB)[1]
end
rA = _rho_functor(qA, p, l+1; quad = spec)
rB = _rho_functor(qB, p, l+1; quad = spec)
return is_anti_isometric_with_anti_isometry(rA, rB)[1]
end

function is_admissible_triple(A::T, B::T, C::T, p::Integer) where T <: Union{ZZLat, ZZLatWithIsom}
function is_admissible_triple(A::T, B::T, C::T, p::IntegerUnion) where T <: Union{ZZLat, ZZLatWithIsom}
return is_admissible_triple(genus(A), genus(B), genus(C), p)
end

Expand Down Expand Up @@ -273,7 +269,7 @@ julia> admissible_triples(g, 2)
(Genus symbol: II_(0, 0), Genus symbol: II_(5, 0) 2^-1_3 3^1)
```
"""
function admissible_triples(G::ZZGenus, p::Integer; pA::Int = -1, pB::Int = -1)
function admissible_triples(G::ZZGenus, p::IntegerUnion; pA::Int = -1, pB::Int = -1)
@req is_prime(p) "p must be a prime number"
@req is_integral(G) "G must be a genus of integral lattices"
rG = rank(G)
Expand Down Expand Up @@ -319,7 +315,7 @@ function admissible_triples(G::ZZGenus, p::Integer; pA::Int = -1, pB::Int = -1)
return L
end

admissible_triples(L::T, p::Integer; pA::Int = -1, pB::Int = -1) where T <: Union{ZZLat, ZZLatWithIsom} = admissible_triples(genus(L), p; pA, pB)
admissible_triples(L::T, p::IntegerUnion; pA::Int = -1, pB::Int = -1) where T <: Union{ZZLat, ZZLatWithIsom} = admissible_triples(genus(L), p; pA, pB)

##################################################################################
#
Expand All @@ -330,12 +326,13 @@ admissible_triples(L::T, p::Integer; pA::Int = -1, pB::Int = -1) where T <: Unio
# we compute ideals of E/K whose absolute norm is equal to d

function _ideals_of_norm(E::Field, d::QQFieldElem)
OE = maximal_order(E)
if denominator(d) == 1
return _ideals_of_norm(E, numerator(d))
elseif numerator(d) == 1
return [inv(I) for I in _ideals_of_norm(E, denominator(d))]
return Hecke.fractional_ideal_type(OE)[inv(I) for I in _ideals_of_norm(E, denominator(d))]
else
return [I*inv(J) for (I, J) in Hecke.cartesian_product_iterator([_ideals_of_norm(E, numerator(d)), _ideals_of_norm(E, denominator(d))]; inplace=false)]
return Hecke.fractional_ideal_type(OE)[I*inv(J) for (I, J) in Hecke.cartesian_product_iterator(Vector{Hecke.fractional_ideal_type(OE)}[_ideals_of_norm(E, numerator(d)), _ideals_of_norm(E, denominator(d))]; inplace=false)]
end
end

Expand Down Expand Up @@ -498,19 +495,19 @@ function representatives_of_hermitian_type(Lf::ZZLatWithIsom, m::Int = 1)
@vprintln :ZZLatWithIsom 1 "$H"
M, fM = trace_lattice_with_isometry(H)
det(M) == d || continue
M = integer_lattice_with_isometry(M, fM)
@hassert :ZZLatWithIsom 1 is_of_hermitian_type(M)
@hassert :ZZLatWithIsom 1 order_of_isometry(M) == n*m
MfM = integer_lattice_with_isometry(M, fM; check = false)
@hassert :ZZLatWithIsom 1 is_of_hermitian_type(MfM)
@hassert :ZZLatWithIsom 1 order_of_isometry(MfM) == n*m
if is_even(M) != is_even(Lf)
continue
end
if !is_of_same_type(M^m, Lf)
if !is_of_same_type(MfM^m, Lf)
continue
end
gr = genus_representatives(H)
for HH in gr
M, fM = trace_lattice_with_isometry(HH)
push!(reps, integer_lattice_with_isometry(M, fM))
push!(reps, integer_lattice_with_isometry(M, fM; check = false))
end
end
return reps
Expand Down Expand Up @@ -552,7 +549,7 @@ julia> is_of_same_type(Lf, reps[2]^2)
true
```
"""
function splitting_of_hermitian_prime_power(Lf::ZZLatWithIsom, p::Int; pA::Int = -1, pB::Int = -1)
function splitting_of_hermitian_prime_power(Lf::ZZLatWithIsom, p::IntegerUnion; pA::Int = -1, pB::Int = -1)
rank(Lf) == 0 && return ZZLatWithIsom[Lf]

@req is_prime(p) "p must be a prime number"
Expand Down Expand Up @@ -615,7 +612,7 @@ julia> splitting_of_prime_power(Lf, 3, 1)
Integer lattice with isometry of finite order 3
```
"""
function splitting_of_prime_power(Lf::ZZLatWithIsom, p::Int, b::Int = 0)
function splitting_of_prime_power(Lf::ZZLatWithIsom, p::IntegerUnion, b::Int = 0)
if rank(Lf) == 0
(b == 0) && return ZZLatWithIsom[Lf]
return ZZLatWithIsom[]
Expand Down Expand Up @@ -668,29 +665,32 @@ of $(L, f)$.
Note that `e` can be 0, while `d` has to be positive.
"""
function splitting_of_pure_mixed_prime_power(Lf::ZZLatWithIsom, p::Int)
function splitting_of_pure_mixed_prime_power(Lf::ZZLatWithIsom, _p::IntegerUnion)
rank(Lf) == 0 && return ZZLatWithIsom[Lf]

n = order_of_isometry(Lf)

@req is_finite(n) "Isometry must be of finite order"

p = typeof(n)(_p)
@req is_prime(p) "p must be a prime number"
@req is_finite(order_of_isometry(Lf)) "Isometry must be of finite order"

n = order_of_isometry(Lf)
pd = prime_divisors(n)

@req 1 <= length(pd) <= 2 && p in pd "Order must be divisible by p and have at most 2 prime divisors"

if length(pd) == 2
q = pd[1] == p ? pd[2] : pd[1]
d = valuation(n, p)
e = valuation(n, q)
d = valuation(n, p)::Int # Weird ? Valuation is type stable and returns Int but it bugs here
e = valuation(n, q)::Int
else
q = 1
d = valuation(n, p)
e = 0
q = one(n)
d = valuation(n, p)::Int
e = zero(n)
end

phi = minimal_polynomial(Lf)
chi = prod([cyclotomic_polynomial(p^d*q^i, parent(phi)) for i=0:e])
chi = prod(cyclotomic_polynomial(p^d*q^i, parent(phi)) for i=0:e; init = zero(phi))

@req is_divisible_by(chi, phi) "Minimal polynomial is not of the correct form"

Expand Down Expand Up @@ -763,7 +763,7 @@ julia> all(LL -> is_of_same_type(Lf, LL^2), reps)
true
```
"""
function splitting_of_mixed_prime_power(Lf::ZZLatWithIsom, p::Int, b::Int = 1)
function splitting_of_mixed_prime_power(Lf::ZZLatWithIsom, p::IntegerUnion, b::Int = 1)
if rank(Lf) == 0
b == 0 && return ZZLatWithIsom[Lf]
return ZZLatWithIsom[]
Expand All @@ -788,7 +788,7 @@ function splitting_of_mixed_prime_power(Lf::ZZLatWithIsom, p::Int, b::Int = 1)

x = gen(parent(minimal_polynomial(Lf)))
B0 = kernel_lattice(Lf, x^(divexact(n, p)) - 1)
A0 = kernel_lattice(Lf, prod([cyclotomic_polynomial(p^d*q^i) for i in 0:e]))
A0 = kernel_lattice(Lf, prod(cyclotomic_polynomial(p^d*q^i) for i in 0:e))
A = splitting_of_pure_mixed_prime_power(A0, p)
isempty(A) && return reps
B = splitting_of_mixed_prime_power(B0, p, 0)
Expand Down Expand Up @@ -817,13 +817,15 @@ Note that currently we support only orders which admit at most 2 prime divisors.
function enumerate_classes_of_lattices_with_isometry(L::ZZLat, order::IntegerUnion)
@req is_finite(order) && order >= 1 "order must be positive and finite"
if order == 1
return representatives_of_hermitian_type(integer_lattice_with_isometry(L))
reps = representatives_of_hermitian_type(integer_lattice_with_isometry(L))
return reps
end
pd = prime_divisors(order)
@req length(pd) in [1,2] "order must have at most two prime divisors"
if length(pd) == 1
v = valuation(order, pd[1])
return _enumerate_prime_power(L, pd[1], v)
reps = _enumerate_prime_power(L, pd[1], v)
return reps
end
p, q = sort!(pd)
vp = valuation(order, p)
Expand Down
51 changes: 28 additions & 23 deletions experimental/QuadFormAndIsom/src/hermitian_miranda_morrison.jl
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ function _get_product_quotient(E::Hecke.NfRel, Fac::Vector{Tuple{NfOrdIdl, Int}}

if length(Fac) == 0
A = abelian_group()
function dlog_0(x::Vector); return id(A); end;
function dlog_0(x::Vector{<:Hecke.NfRelElem}); return id(A); end;
function exp_0(x::GrpAbFinGenElem); return one(E); end;
return A, dlog_0, exp_0
end
Expand All @@ -230,25 +230,25 @@ function _get_product_quotient(E::Hecke.NfRel, Fac::Vector{Tuple{NfOrdIdl, Int}}

G, proj, inj = biproduct(groups...)

function dlog(x::Vector)
function dlog(x::Vector{<:Hecke.NfRelElem})
if length(x) == 1
return sum([inj[i](dlogs[i](x[1])) for i in 1:length(Fac)])
return sum(inj[i](dlogs[i](x[1])) for i in 1:length(Fac))
else
@hassert :ZZLatWithIsom 1 length(x) == length(Fac)
return sum([inj[i](dlogs[i](x[i])) for i in 1:length(Fac)])
return sum(inj[i](dlogs[i](x[i])) for i in 1:length(Fac))
end
end

function exp(x::GrpAbFinGenElem)
v = Hecke.NfRelElem[exps[i](proj[i](x)) for i in 1:length(Fac)]
v = elem_type(E)[exps[i](proj[i](x)) for i in 1:length(Fac)]
@hassert :ZZLatWithIsom 1 dlog(v) == x
return v
end

for i in 1:10
a = rand(G)
@hassert :ZZLatWithIsom 1 dlog(exp(a)) == a
end
@v_do :ZZLatWithIsom 1 for i in 1:10
a = rand(G)
@hassert :ZZLatWithIsom 1 dlog(exp(a)) == a
end

return G, dlog, exp
end
Expand Down Expand Up @@ -314,14 +314,15 @@ function _local_determinants_morphism(Lf::ZZLatWithIsom)
qL, fqL = discriminant_group(Lf)
OqL = orthogonal_group(qL)
if rank(Lf) != degree(Lf)
Lf2 = integer_lattice_with_isometry(integer_lattice(gram = gram_matrix(Lf)), isometry(Lf); ambient_representation = false)
Lf2 = integer_lattice_with_isometry(integer_lattice(gram = gram_matrix(Lf)), isometry(Lf); ambient_representation = false, check = false)
qL2, fqL2 = discriminant_group(Lf2)
OqL2 = orthogonal_group(qL2)
ok, phi12 = is_isometric_with_isometry(qL, qL2)
@hassert :ZZLatWithIsom 1 ok
ok, g0 = is_conjugate_with_data(OqL, fqL, OqL(compose(phi12, compose(hom(fqL2), inv(phi12)))))
ok, g0 = is_conjugate_with_data(OqL, OqL(compose(phi12, compose(hom(fqL2), inv(phi12))); check = false), fqL)
@hassert :ZZLatWithIsom 1 ok
phi12 = compose(hom(OqL(g0)), phi12)
#@hassert :ZZLatWithIsom 1 matrix(compose(hom(fqL), phi12)) == matrix(compose(phi12, hom(fqL2)))
@hassert :ZZLatWithIsom 1 is_isometry(phi12)
else
Lf2 = Lf
Expand All @@ -333,8 +334,11 @@ function _local_determinants_morphism(Lf::ZZLatWithIsom)
# fqL, G is the group where we want to compute the image of O(L, f). This
# group G corresponds to U(D_L) in the notation of BH23.
G2, _ = centralizer(OqL2, fqL2)
G, _ = sub(OqL, elem_type(OqL)[OqL(compose(phi12, compose(hom(g), inv(phi12)))) for g in gens(G2)])
GtoG2 = hom(G, G2, gens(G), gens(G2))
gensG2 = gens(G2)
gensG = elem_type(OqL)[OqL(compose(phi12, compose(hom(g), inv(phi12))); check = false) for g in gensG2]
G, GinOqL = sub(OqL, gensG)
@hassert :ZZLatWithIsom 1 fqL in G
GtoG2 = hom(G, G2, gensG, gensG2; check = false)

# This is the associated hermitian O_E-lattice to (L, f): we want to make qL
# (aka D_L) correspond to the quotient D^{-1}H^#/H by the trace construction,
Expand Down Expand Up @@ -407,7 +411,7 @@ function _local_determinants_morphism(Lf::ZZLatWithIsom)
RmodF, Flog, _ = _get_product_quotient(E, Fdata)

A = elem_type(RmodF)[Flog(Fsharpexp(g)) for g in gens(RmodFsharp)]
f = hom(RmodFsharp, RmodF, A)
f = hom(RmodFsharp, RmodF, A; check = false)
FmodFsharp, j = kernel(f)

# Now according to Theorem 6.15 of BH23, it remains to quotient out the image
Expand All @@ -418,10 +422,10 @@ function _local_determinants_morphism(Lf::ZZLatWithIsom)
OK = base_ring(OE)
UOK, mUOK = unit_group(OK)

fU = hom(UOEabs, UOK, elem_type(UOK)[mUOK\norm(OE(mUOK(m))) for m in gens(UOK)])
fU = hom(UOEabs, UOK, elem_type(UOK)[mUOK\norm(OE(mUOK(m))) for m in gens(UOK)]; check = false)
KU, jU = kernel(fU)

gene_norm_one = Hecke.NfRelElem[EabstoE(Eabs(mUOEabs(jU(k)))) for k in gens(KU)]
gene_norm_one = elem_type(E)[EabstoE(Eabs(mUOEabs(jU(k)))) for k in gens(KU)]

# Now according to Theorem 6.15 of BH23, it remains to quotient out
FOEmodFsharp, m = sub(RmodFsharp, elem_type(RmodFsharp)[Fsharplog(typeof(x)[x for i in 1:length(S)]) for x in gene_norm_one])
Expand All @@ -445,7 +449,7 @@ function _local_determinants_morphism(Lf::ZZLatWithIsom)
# consider up to now, and then map the corresponding determinant adeles inside
# Q. Since our matrices were approximate lifts of the generators of G, we can
# create the map we wanted from those data.
for g in gens(G2)
for g in gensG2
ds = elem_type(E)[]
for p in S
if !_is_special(H, p)
Expand All @@ -464,10 +468,11 @@ function _local_determinants_morphism(Lf::ZZLatWithIsom)
end

GSQ, SQtoGSQ, _ = Oscar._isomorphic_gap_group(SQ)
f2 = hom(G2, GSQ, gens(G2), SQtoGSQ.(imgs); check = false)
f2 = hom(G2, GSQ, gensG2, SQtoGSQ.(imgs); check = false)
f = compose(GtoG2, f2)

return f
@hassert :ZZLatWithIsom 1 isone(f(fqL))
return f, GinOqL # Needs the second map to map the kernel of f into OqL
end

# We check whether for the prime ideal p E_O(L_p) != F(L_p).
Expand Down Expand Up @@ -506,7 +511,7 @@ function _is_special(L::HermLat, p::NfOrdIdl)
u = elem_in_nf(uniformizer(P))
s = involution(L)
su = s(u)
H = block_diagonal_matrix([matrix(E, 2, 2, [0 u^(S[i]); su^(S[i]) 0]) for i in 1:length(S)])
H = block_diagonal_matrix(dense_matrix_type(E)[matrix(E, 2, 2, [0 u^(S[i]); su^(S[i]) 0]) for i in 1:length(S)])
return is_locally_isometric(L, hermitian_lattice(E; gram = H), p)
end

Expand Down Expand Up @@ -677,7 +682,7 @@ function _approximate_isometry(H::HermLat, H2::HermLat, g::AutomorphismGroupElem
Bps = _local_basis_modular_submodules(H2, minimum(P), a, res)
Bp = reduce(vcat, Bps)
Gp = Bp*gram_matrix(ambient_space(H))*map_entries(involution(E), transpose(Bp))
Fp = block_diagonal_matrix([_transfer_discriminant_isometry(res, g, Bps[i], P, BHp_inv) for i in 1:length(Bps)])
Fp = block_diagonal_matrix(typeof(Gp)[_transfer_discriminant_isometry(res, g, Bps[i], P, BHp_inv) for i in 1:length(Bps)])
# This is the local defect. By default, it should have scale P-valuations -a
# and norm P-valuation e-1-a
Rp = Gp - Fp*Gp*map_entries(involution(E), transpose(Fp))
Expand Down Expand Up @@ -717,7 +722,7 @@ function _local_basis_modular_submodules(H::HermLat, p::NfOrdIdl, a::Int, res::A
L2 = restrict_scalars(H2, res)
L2 = intersect(L, L2)
B2 = basis_matrix(L2)
gene = [res(vec(collect(B2[i, :]))) for i in 1:nrows(B2)]
gene = Vector{elem_type(base_field(H))}[res(vec(collect(B2[i, :]))) for i in 1:nrows(B2)]
H2 = lattice(ambient_space(H), gene)
b = local_basis_matrix(H2, p; type = :submodule)
end
Expand All @@ -736,7 +741,7 @@ end
# exactly what we look for (a unit at P with trace 1);
# - p is ramified, and then we cook up a good element. The actual code from
# that part is taken from the Sage implementation of Simon Brandhorst
function _find_rho(P::Hecke.NfRelOrdIdl, e)
function _find_rho(P::Hecke.NfRelOrdIdl, e::Int)
OE = order(P)
E = nf(OE)
lp = prime_decomposition(OE, minimum(P))
Expand Down
Loading

0 comments on commit 93ddb13

Please sign in to comment.