Skip to content

Commit

Permalink
More lattice tests (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
thofma authored Jan 26, 2021
1 parent 6f703f8 commit b4dce59
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/Hecke.jl
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ end
function test_module(x, new::Bool = true)
julia_exe = Base.julia_cmd()
# On Windows, we also allow bla/blub"
x = _adjust_path
x = _adjust_path(x)
if x == "all"
test_file = joinpath(pkgdir, "test", "runtests.jl")
else
Expand Down
5 changes: 5 additions & 0 deletions src/QuadForm/Herm/Genus.jl
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,11 @@ function genus(::Type{HermLat}, E::S, p::T, data::Vector{Tuple{Int, Int, Int}};
z.isdyadic = isdyadic(p)
z.isramified = isramified(maximal_order(E), p)
@assert !(isramified(z) && isdyadic(z))

if type !== :det && type !== :disc
throw(error("type :$type must be :disc or :det"))
end

if !z.isramified || type === :det
z.data = copy(data)
else
Expand Down
9 changes: 8 additions & 1 deletion src/QuadForm/Lattices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export *, +, absolute_basis, absolute_basis_matrix, ambient_space, bad_primes,
local_basis_matrix, norm, pseudo_matrix, quadratic_lattice, rank,
rational_span, rescale, scale, volume, witt_invariant, lattice,
Zlattice, automorphism_group_generators, automorphism_group_order,
isisometric, islocal_norm, normic_defect
isisometric, islocal_norm, normic_defect, issublattice, issublattice_with_relations

export HermLat, QuadLat

Expand Down Expand Up @@ -162,6 +162,13 @@ function degree(L::AbsLat)
return dim(ambient_space(L))
end

@doc Markdown.doc"""
issublattice(L::AbsLat, M::AbsLat) -> Bool
Returns whether $M$ is a sublattice of $L$.
"""
issublattice(L::AbsLat, M::AbsLat)

################################################################################
#
# Gram matrix
Expand Down
41 changes: 41 additions & 0 deletions src/QuadForm/Quad/ZLattices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,47 @@ function isisometric(L::ZLat, M::ZLat; ambient_representation::Bool = true)
end
end

################################################################################
#
# Is sublattice?
#
################################################################################

function issublattice(M::ZLat, N::ZLat)
if ambient_space(M) != ambient_space(N)
return false
end

hassol, _rels = can_solve_with_solution(basis_matrix(M), basis_matrix(N), side=:left)

if !hassol || !isone(denominator(_rels))
return false
end

return true
end

@doc Markdown.doc"""
issublattice_with_relations(M::ZLat, N::ZLat) -> Bool, fmpq_mat
Returns whether $N$ is a sublattice of $N$. In this case, the second return
value is a matrix $B$ such that $B B_M = B_N$, where $B_M$ and $B_N$ are the
basis matrices of $M$ and $N$ respectively.
"""
function issublattice_with_relations(M::ZLat, N::ZLat)
if ambient_space(M) != ambient_space(N)
return false, basis_matrix(M)
end

hassol, _rels = can_solve_with_solution(basis_matrix(M), basis_matrix(N), side=:left)

if !hassol || !isone(denominator(_rels))
return false, basis_matrix(M)
end

return true, _rels
end

################################################################################
#
# Root lattice
Expand Down
116 changes: 93 additions & 23 deletions src/QuadForm/Torsion.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export discriminant_group

# Torsion QuadraticForm
#
# Example:
Expand Down Expand Up @@ -42,6 +44,8 @@ mutable struct TorQuadMod
TorQuadMod() = new()
end

ngens(T::TorQuadMod) = length(gens(T))

################################################################################
#
# Construction
Expand All @@ -51,8 +55,8 @@ end
# compute the torsion quadratic module M/N
function torsion_quadratic_module(M::ZLat, N::ZLat; modulus = fmpq(0))
@req ambient_space(M) === ambient_space(N) "Lattices must have same ambient space"
hassol, _rels = can_solve_with_solution(basis_matrix(M), basis_matrix(N), side=:left)
@req isone(denominator(_rels)) && hassol "Second lattice must be a submodule of first lattice"
fl, _rels = issublattice_with_relations(M, N)
@req fl "Second lattice must be a sublattice of first lattice"
rels = change_base_ring(FlintZZ, _rels)
A = abelian_group(rels)
S, mS = snf(A)
Expand Down Expand Up @@ -183,19 +187,64 @@ mutable struct TorQuadModElem
TorQuadModElem(T::TorQuadMod, a::GrpAbFinGenElem) = new(a, T)
end

# TODO: Check the parents ...
(T::TorQuadMod)(a::GrpAbFinGenElem) = TorQuadModElem(T, a)
################################################################################
#
# Creation
#
################################################################################

function (T::TorQuadMod)(a::GrpAbFinGenElem)
@req abelian_group(T) === parent(a) "Parents do not match"
return TorQuadModElem(T, a)
end

# Coerces an element of the ambient space of cover(T) to T

function (T::TorQuadMod)(v::Vector)
@req length(v) == dim(ambient_space(cover(T))) "Vector of wrong length"
vv = map(FlintQQ, v)
if eltype(vv) != fmpq
error("Cannot coerce elements to the rationals")
end
return T(vv::Vector{fmpq})
end

function (T::TorQuadMod)(v::Vector{fmpq})
@req length(v) == dim(ambient_space(cover(T))) "Vector of wrong length"
vv = change_base_ring(FlintZZ, matrix(FlintQQ, 1, length(v), v) * inv(basis_matrix(cover(T))))
return T(abelian_group(T)(vv * T.proj))
end

################################################################################
#
# Printing
#
################################################################################

function Base.show(io::IO, a::TorQuadModElem)
v = a.a.coeff
print(io, "[")
for i in 1:length(v)
if i == length(v)
print(io, v[i])
else
print(io, v[i], ", ")
end
end
print(io, "]")
end

################################################################################
#
# Generators
#
################################################################################

function gens(T::TorQuadMod)
if isdefined(T, :gens)
return T.gens
return T.gens::Vector{TorQuadModElem}
else
_gens = [T(g) for g in gens(abelian_group(T))]
_gens = TorQuadModElem[T(g) for g in gens(abelian_group(T))]
T.gens = _gens
return _gens
end
Expand All @@ -204,7 +253,10 @@ end
parent(a::TorQuadModElem) = a.parent

# Check the parent
(A::GrpAbFinGen)(a::TorQuadModElem) = a.a
function (A::GrpAbFinGen)(a::TorQuadModElem)
@req A === abelian_group(parent(a)) "Parents do not match"
return a.a
end

################################################################################
#
Expand Down Expand Up @@ -244,36 +296,55 @@ function lift(a::TorQuadModElem)
return fmpq[z[1, i] for i in 1:ncols(z)]
end

mutable struct TorQuadModMor
domain::TorQuadMod
codomain::TorQuadMod
################################################################################
#
# Maps between torsion quadratic modules
#
################################################################################

mutable struct TorQuadModMor <: Map{TorQuadMod, TorQuadMod, HeckeMap, TorQuadModMor}
header::MapHeader{TorQuadMod, TorQuadMod}
map_ab::GrpAbFinGenMap

function TorQuadModMor(T::TorQuadMod, S::TorQuadMod, m::GrpAbFinGenMap)
z = new()
z.header = MapHeader(T, S)
z.map_ab = m
return z
end
end

################################################################################
#
# User constructors
#
################################################################################

function hom(T::TorQuadMod, S::TorQuadMod, M::fmpz_mat)
f = hom(abelian_group(T), abelian_group(S), M)
return TorQuadModMor(T, S, map_ab)
end

function hom(T::TorQuadMod, S::TorQuadMod, img::Vector{TorQuadModElem})
_img = GrpAbFinGenElem[]
@req length(img) == ngens(T) "Wrong number of elements"
for g in img
@req parent(g) === S "Elements have the wrong parent"
push!(_img, abelian_group(S)(g))
end
map_ab = hom(abelian_group(T), abelian_group(S), _img)
return TorQuadModMor(T, S, map_ab)
end

domain(f::TorQuadModMor) = f.domain

codomain(f::TorQuadModMor) = f.codomain

function (f::TorQuadModMor)(a::TorQuadModElem)
function image(f::TorQuadModMor, a::TorQuadModElem)
A = abelian_group(domain(f))
return codomain(f)(f.map_ab(A(a)))
end


function preimage(f::TorQuadModMor, a::TorQuadModElem)
A = abelian_group(domain(f))
return domain(f)(f.map_ab\(A(a)))
end

################################################################################
#
Expand All @@ -283,22 +354,21 @@ end


@doc Markdown.doc"""
submodule(T::TorQuadMod, generators::Vector{TorQuadModElem})-> TorQuadMod, Map
sub(T::TorQuadMod, generators::Vector{TorQuadModElem})-> TorQuadMod, Map
Return the submodule of `T` defined by `generators` and the inclusion morphism.
"""
function submodule(T::TorQuadMod, generators::Vector{TorQuadModElem})
function sub(T::TorQuadMod, gens::Vector{TorQuadModElem})
V = ambient_space(T.cover)
generators = matrix(QQ, [lift(g) for g in generators])
gens_new = [basis_matrix(T.rels); generators]
cover = lattice(V, gens_new, isbasis=false)
_gens = matrix(QQ, [lift(g) for g in gens])
gens_new = [basis_matrix(T.rels); _gens]
cover = lattice(V, gens_new, isbasis = false)
S = torsion_quadratic_module(cover, T.rels)
imgs = [T(lift(g)) for g in gens(S)]
imgs = [T(lift(g)) for g in Hecke.gens(S)]
inclusion = hom(S, T, imgs)
return S, inclusion
end


function TorQuadMod(q::fmpq_mat)
@req issquare(q) "Matrix must be a square matrix"
@req issymmetric(q) "Matrix must be symmetric"
Expand Down
2 changes: 2 additions & 0 deletions test/QuadForm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
@time include("QuadForm/MassHerm.jl")
@time include("QuadForm/Quad.jl")
@time include("QuadForm/QuadBin.jl")
@time include("QuadForm/Herm.jl")
@time include("QuadForm/Torsion.jl")
end
4 changes: 4 additions & 0 deletions test/QuadForm/Herm.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@testset "Herm" begin
include("Herm/Spaces.jl")
include("Herm/Genus.jl")
end
40 changes: 40 additions & 0 deletions test/QuadForm/Herm/Genus.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@testset "Genus" begin
Qx, x = QQ["x"]
K, a = NumberField(x^2 - 2, "a")
OK = maximal_order(K)
Kt, t = K["t"]

E1, b1 = NumberField(t^2 - a, "b1") # ramified at 2
E2, b2 = NumberField(t^2 - 5, "b2") # unramified at 2

p = prime_decomposition(OK, 2)[1][1]
# ramified & dyadic
g = genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1)], type = :det)
@test sprint(show, "text/plain", g) isa String
@test sprint(show, g) isa String
g = genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1)], type = :disc)
@test sprint(show, "text/plain", g) isa String
@test sprint(show, g) isa String
@test_throws ErrorException genus(HermLat, E1, p, [(0, 1, 1, 1), (2, 2, -1, 0)], type = :det)
@test_throws ErrorException genus(HermLat, E1, p, [(0, 1, 1, 1), (2, 2, -1, 0)], type = :disc)
@test_throws ErrorException genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1)], type = :bla)

# unramified & dyadic
g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :det)
@test sprint(show, "text/plain", g) isa String
@test sprint(show, g) isa String
g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :disc)
@test sprint(show, "text/plain", g) isa String
@test sprint(show, g) isa String
@test_throws ErrorException genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :bla)

# ramified & non-dyadic
p = prime_decomposition(OK, 5)[1][1]
g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :det)
@test sprint(show, "text/plain", g) isa String
@test sprint(show, g) isa String
g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :disc)
@test sprint(show, "text/plain", g) isa String
@test sprint(show, g) isa String
@test_throws ErrorException genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :bla)
end
18 changes: 18 additions & 0 deletions test/QuadForm/Herm/Spaces.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@testset "Spaces" begin
Qx, x = PolynomialRing(FlintQQ, "x")
K, a = NumberField(x^2 - 2, "a1")
Kt, t = K["t"]

E, b = NumberField(t^2 + 3)

F = GF(3)

Hecke.change_base_ring(::Hecke.NfRel, ::Hecke.gfp_mat) = error("asd")
@test_throws ErrorException hermitian_space(E, F[1 2; 2 1])

Hecke.change_base_ring(::Hecke.NfRel, x::Hecke.gfp_mat) = x
@test_throws ErrorException hermitian_space(E, F[1 2; 2 1])

V = @inferred hermitian_space(E, FlintQQ[1 2; 2 1])
@test V isa Hecke.HermSpace
end
7 changes: 7 additions & 0 deletions test/QuadForm/Quad/Spaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
K2, a2 = NumberField(x^3 - 2, "a2")

K1t, t = PolynomialRing(K1, "t")
F = GF(3)

Hecke.change_base_ring(::FlintRationalField, ::Hecke.gfp_mat) = error("asd")
@test_throws ErrorException quadratic_space(FlintQQ, F[1 2; 2 1])

Hecke.change_base_ring(::FlintRationalField, x::Hecke.gfp_mat) = x
@test_throws ErrorException quadratic_space(FlintQQ, F[1 2; 2 1])

L, b = NumberField(t^2 + a1)

Expand Down
10 changes: 10 additions & 0 deletions test/QuadForm/Quad/ZLattices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ end

@test (@inferred base_ring(Lr0)) isa FlintIntegerRing

@test !(@inferred issublattice(Lr2, Lr1))
M = Zlattice(;gram = FlintQQ[2 2; 2 2])
@test !(@inferred issublattice(Lr0, M))
@test issublattice(Lr2, Lr0)
@test issublattice(Lr1, lattice(V, QQ[2 0;]))

fl, rels = @inferred issublattice_with_relations(Lr1, lattice(V, QQ[2 0;]))
@test fl
@test rels == QQ[2;]

# lattices of rank 0

B = matrix(QQ, 0, 2, [])
Expand Down
Loading

0 comments on commit b4dce59

Please sign in to comment.