Skip to content

Commit

Permalink
Extend _order(K, gens) for non-commutative algebras (#1252)
Browse files Browse the repository at this point in the history
  • Loading branch information
joschmitt authored Oct 19, 2023
1 parent f1f10ef commit e7306f5
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 76 deletions.
73 changes: 7 additions & 66 deletions src/AlgAssAbsOrd/Order.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ is_commutative(O::AlgAssAbsOrd) = is_commutative(algebra(O))

is_maximal_known(O::AlgAssAbsOrd) = O.is_maximal != 0

@inline is_maximal_known_and_maximal(O::AlgAssAbsOrd) = isone(O.is_maximal)

@doc raw"""
is_maximal(O::AlgAssAbsOrd) -> Bool
Expand Down Expand Up @@ -136,72 +138,6 @@ function Order(A::S, M::FakeFmpqMat; check::Bool = true, cached::Bool = true) wh
end
end

function _order(A::S, gens::Vector{T}; cached::Bool = true, check::Bool = true) where {S <: AbsAlgAss, T <: AbsAlgAssElem}
if one(A) in gens
cur = gens
else
cur = append!([one(A)], gens)
end
local Bmat::FakeFmpqMat
Bmat = basis_matrix(cur, FakeFmpqMat)
if length(cur) > dim(A)
# There are too many generators, so we replace them by
# a basis for the Z-span
Bmat = hnf!(Bmat)
# We need to cut it off :(
# This is an upper-right HNF
nn = 1
while is_zero_row(Bmat, nn)
nn += 1
end

if nn != 1
Bmat = FakeFmpqMat(Bmat.num[nn:nrows(Bmat), 1:ncols(Bmat)], Bmat.den)
end
cur_bas = [elem_from_mat_row(A, Bmat.num, i, Bmat.den) for i in 1:nrows(Bmat)]
gens = cur_bas
else
cur_bas = [elem_from_mat_row(A, Bmat.num, i, Bmat.den) for i in 1:nrows(Bmat)]
end

prods = Vector{elem_type(A)}(undef, 2 * length(cur_bas) * length(gens))

while true
k = length(cur_bas)
resize!(prods, 2*k*length(gens))
l = 1
for i = 1:k
for j in 1:length(gens)
if isdefined(prods, l)
mul!(prods[l], cur_bas[i], gens[j])
else
prods[l] = cur_bas[i] * gens[j]
end
l +=1
if isdefined(prods, l)
mul!(prods[l], gens[j], cur_bas[i])
else
prods[l] = gens[j] * cur_bas[i]
end
l += 1
end
end
Ml = hnf(basis_matrix(prods, FakeFmpqMat))
r = findfirst(i -> !is_zero_row(Ml.num, i), 1:nrows(Ml))
nBmat = sub(Ml, r:nrows(Ml), 1:ncols(Ml))
if nrows(nBmat) == nrows(Bmat) && Bmat == nBmat
break
end
Bmat = nBmat
cur_bas = elem_type(A)[elem_from_mat_row(A, Bmat.num, i, Bmat.den) for i in 1:nrows(Bmat)]
end
if nrows(Bmat) != dim(A)
error("Elements do not generate an order")
end

return Order(A, Bmat, cached = cached, check = check)
end

function _equation_order(A::AbsAlgAss{QQFieldElem})
@assert is_commutative(A)
a = primitive_element_via_number_fields(A)
Expand Down Expand Up @@ -317,6 +253,11 @@ function basis_alg(O::AlgAssAbsOrd{S, T}; copy::Bool = true) where {S, T}
end
end

function basis(O::AlgAssAbsOrd{S, T}, A::S; copy::Bool = true) where {S, T}
@req algebra(O) === A "Algebras do not match"
return basis_alg(O, copy = copy)
end

################################################################################
#
# (Inverse) basis matrix
Expand Down
55 changes: 45 additions & 10 deletions src/NumFieldOrd/NfOrd/NfOrd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -997,13 +997,14 @@ equation_order(M::NfAbsOrd) = equation_order(nf(M))
# and whether the constructed order is actually an order.
# Via extends one may supply an order which will then be extended by the elements
# in elt.
function _order(K::S, elt::Vector{T}; cached::Bool = true, check::Bool = true, extends = nothing) where {S <: NumField{QQFieldElem}, T}
function _order(K::S, elt::Vector{T}; cached::Bool = true, check::Bool = true, extends = nothing) where {S <: Union{NumField{QQFieldElem}, AbsAlgAss{QQFieldElem}}, T}
elt = unique(elt)
n = degree(K)
n = dim(K)
is_comm = is_commutative(K)

if extends !== nothing
extended_order::order_type(K) = extends
@assert K === nf(extended_order)
@assert K === _algebra(extended_order)

if is_maximal_known_and_maximal(extended_order) || length(elt) == 0
return extended_order
Expand All @@ -1019,6 +1020,7 @@ function _order(K::S, elt::Vector{T}; cached::Bool = true, check::Bool = true, e
bas = elem_type(K)[one(K)]
B = basis_matrix(bas, FakeFmpqMat) # trivially in lower-left HNF
full_rank = false
m = FlintQQ()
end

dummy_vector = elem_type(K)[K()]
Expand All @@ -1036,7 +1038,8 @@ function _order(K::S, elt::Vector{T}; cached::Bool = true, check::Bool = true, e
# the previous elements of elt
in_span_of_B(e) && continue

# Multiply powers of e to the existing basis elements
# Add products of elements of bas and e; in the commutative case this just
# means multiplying by powers of e, for the non-commutative case see below
if check
f = minpoly(e)
isone(denominator(f)) || error("The elements do not define an order: $e is non-integral")
Expand All @@ -1046,18 +1049,50 @@ function _order(K::S, elt::Vector{T}; cached::Bool = true, check::Bool = true, e
end

start = 1
old_length = length(bas)
# Commutative case:
# We only multiply the elements of index start:length(bas) by e .
# Example: bas = [a_1, ..., a_k] with a_1 = 1. Then
# new_bas := [e, e*a_2, ..., e*a_k] and we append this to bas and set
# start := k + 1. In the next iteration, we then have
# new_bas := [e^2, e^2*a_2, ..., e^2*a_k] (assuming that there was no
# reduction of the basis in between).
for i in 1:df

powers_of_e = 1 # count the iterations, so the power of e by which we multiply
# (only relevant in the commutative case)
while true
# In the commutative case, this behaves like a "for _ in 1:df"-loop
if is_comm && powers_of_e == df + 1
break
end
powers_of_e += 1
new_bas = elem_type(K)[]
for j in start:length(bas)
t = e*bas[j]
in_span_of_B(t) && continue
push!(new_bas, t)
e_bj = e*bas[j]
if !in_span_of_B(e_bj)
if is_comm
push!(new_bas, e_bj)
else
# Non-commutative case:
# If bas = [a_1, ..., a_k], so that the Z-module generated by the a_i
# contains 1_K, we need to build all the possible words of the form
# a_?*e*a_?*e*a_?*...*e*a_?. (Because there is a linear combination
# of the a_i giving 1, we don't need to multiply by powers of e.)
# In the first iteration we build
# new_bas := [a_1*e*a_1, a_2*e*a_1, ..., a_k*e*a_1, a_1*e*a_2, ..., a_k*e*a_k]
# This gets appended to bas and we set start := k + 1.
# In the next iteration, we get
# new_bas := [a_1*e*a_1*e*a_1, a_2*e*a_1*e*a_1, ..., a_k*e*a_1*e*a_1,
# a_1*e*a_2*e*a_1, a_2*e*a_2*e*a_1, ...
# ...
# ]
# (assuming there is no reduction in between) and so on.
for k in 1:old_length
t = bas[k]*e_bj
!in_span_of_B(t) && push!(new_bas, t)
end
end
end
end
isempty(new_bas) && break
start = length(bas) + 1
Expand Down Expand Up @@ -1090,9 +1125,9 @@ function _order(K::S, elt::Vector{T}; cached::Bool = true, check::Bool = true, e
m = _det_triangular(numerator(B, copy = false))//denominator(B, copy = false)
end
end
bas = elem_type(K)[ elem_from_mat_row(K, numerator(B, copy = false), i, denominator(B, copy = false)) for i = 1:nrows(B) ]
bas = elem_type(K)[ elem_from_mat_row(K, numerator(B, copy = false), i, denominator(B, copy = false)) for i in 1:nrows(B) ]
start = 1
if check
if check && K isa NumField
@assert isone(bas[1])
end
end
Expand Down
42 changes: 42 additions & 0 deletions test/AlgAssAbsOrd/Order.jl
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,46 @@
O = maximal_order(A)
@test discriminant(O) == 1
end

@testset "order by generators" begin
Qx, x = QQ["x"]
A = associative_algebra(x^4 - 10*x^2 + 1) # number field QQ(sqrt(2), sqrt(3))
a = basis(A)[2]
b = 1//2*a^3 - 9//2*a # sqrt(2)
c = 1//2*a^3 - 11//2*a # sqrt(3)

O = Order(A, [ a, b, a*b ])
@test discriminant(O) == 9216
@test O == Order(A, [ a, b ])
@test O == Order(A, [ a, b ], check = false)

d = 1//4*a^3 + 1//4*a^2 + 3//4*a + 3//4
OO = Hecke._order(A, [d], extends = O)
@test is_maximal(OO)
@test_throws ErrorException Order(A, [ b ])

# Example where the non-commutative stuff happens:
# One needs to multiply "from both sides" and the "while true" loop in
# _order is necessary
# This is basically the example above but as 3x3 matrices
K, a = number_field(x^4 - 10*x^2 + 1, "a")
b = 1//2*a^3 - 9//2*a # sqrt(2)
c = 1//2*a^3 - 11//2*a # sqrt(3)
A = matrix_algebra(K, 3)
B, BtoA = Hecke.restrict_scalars(A, QQ)
g = map(z -> preimage(BtoA, z), [
A(matrix(K, [ b 0 0; 0 0 0; 0 0 0 ])),
A(matrix(K, [ c 0 0; 0 0 0; 0 0 0 ])),
A(matrix(K, [ 0 1 0; 0 0 0; 0 0 0 ])),
A(matrix(K, [ 0 0 0; 0 0 1; 0 0 0 ])),
A(matrix(K, [ 0 0 0; 1 0 0; 0 0 0 ])),
A(matrix(K, [ 0 0 0; 0 0 0; 0 1 0 ]))
])
O = Order(B, g)
@test discriminant(O) == ZZ(9216)^9
@test O == Order(B, g, check = false)
d = 1//4*a^3 + 1//4*a^2 + 3//4*a + 3//4
OO = Hecke._order(B, [BtoA\A(matrix(K, [ d 0 0; 0 0 0; 0 0 0 ]))], extends = O)
@test discriminant(OO) == ZZ(2304)^9
end
end

0 comments on commit e7306f5

Please sign in to comment.