Skip to content

Commit

Permalink
feat: multiplicative dependencies for matrices
Browse files Browse the repository at this point in the history
  • Loading branch information
thofma committed Nov 2, 2024
1 parent 7d52dcb commit 726c02c
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 0 deletions.
93 changes: 93 additions & 0 deletions src/AlgAss/AbsAlgAss.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1241,3 +1241,96 @@ function _skolem_noether(h::AbsAlgAssMor)
error("Not impelemented yet")
end
end

################################################################################
#
# Maximal separable subalgebra
#
################################################################################

# (Part of) Algorithm 5.5 of Lenstra-Silverberg, Algorithms for Commutative Algebras Over the Rational Numbers
# (they only state it for QQ, but should be valid in charcteristic zero)
function maximal_separable_subalgebra(A::AbstractAssociativeAlgebra)
@req is_commutative(A) "Algebra must be commutative"
@req is_zero(characteristic(base_ring(A))) "Characteristic of base ring must be zero"

B = basis(A)
BU = eltype(B)[]
for b in B
u, v = jordan_chevalley_decomposition(b)
push!(BU, u)
end
R = echelon_form(basis_matrix(BU); trim = true)
return _subalgebra(A, [elem_from_mat_row(A, R, i) for i in 1:nrows(R)])
end

################################################################################
#
# Multiplicative dependencies
#
################################################################################

function _multiplicative_dependencies(A::AbstractAssociativeAlgebra, S::Vector)
@req is_commutative(A) "Algebra must be commutative"
@req is_zero(characteristic(base_ring(A))) "Characteristic of base ring must be zero"

Check warning on line 1275 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1273-L1275

Added lines #L1273 - L1275 were not covered by tests

B, BtoA = maximal_separable_subalgebra(A)

Check warning on line 1277 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1277

Added line #L1277 was not covered by tests

nfs = _as_number_fields(B)

Check warning on line 1279 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1279

Added line #L1279 was not covered by tests

jcs = first.(jordan_chevalley_decomposition.(S))
jc = [has_preimage_with_preimage(BtoA, x)[2] for x in first.(jordan_chevalley_decomposition.(S))]

Check warning on line 1282 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1281-L1282

Added lines #L1281 - L1282 were not covered by tests

F = free_abelian_group(length(S))

Check warning on line 1284 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1284

Added line #L1284 was not covered by tests

kernels = ZZMatrix[]

Check warning on line 1286 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1286

Added line #L1286 was not covered by tests

for (K, BtoK) in nfs
eltsinK = BtoK.(jc)
if any(is_zero, eltsinK)
error("oopsie, elements not invertible")

Check warning on line 1291 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1288-L1291

Added lines #L1288 - L1291 were not covered by tests
end
# hack to get something
v = _mult_dep(K, eltsinK)
Hm = reduce(vcat, v)
push!(kernels, Hm)
end

Check warning on line 1297 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1294-L1297

Added lines #L1294 - L1297 were not covered by tests

J = radical(A)
m = dim(A) # TODO: compute the nilpotency index
logimgs = elem_type(A)[]
for (s, pis) in zip(S, jcs)
w = s*inv(pis)
v = 1 - w
logimg = -sum(v^i * QQ(1//i) for i in 1:m)
push!(logimgs, logimg)
end
Blog, = integral_split(basis_matrix(logimgs), ZZ)

Check warning on line 1308 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1299-L1308

Added lines #L1299 - L1308 were not covered by tests

push!(kernels, kernel(Blog, side = :left))

Check warning on line 1310 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1310

Added line #L1310 was not covered by tests

RtoF = reduce((x, y) -> intersect(x, y, false)[2], [sub(F, k, false)[2] for k in kernels])
return [_eltseq(x.coeff) for x in RtoF.(gens(domain(RtoF)))]

Check warning on line 1313 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1312-L1313

Added lines #L1312 - L1313 were not covered by tests
end

function _multiplicative_dependencies(v::Vector{<:MatElem})
A = matrix_algebra(QQ, v)
S = A.(v)
_multiplicative_dependencies(A, S)

Check warning on line 1319 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1316-L1319

Added lines #L1316 - L1319 were not covered by tests
end

# TODO: Use Ge
function _mult_dep(K::AbsSimpleNumField, S::Vector)
OK = maximal_order(K)
sup = reduce(vcat, [ support(s, OK) for s in S])

Check warning on line 1325 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1323-L1325

Added lines #L1323 - L1325 were not covered by tests
# I don't know why noone ever implemented this
if isempty(sup)
push!(sup, prime_ideals_over(OK, 2)[1])

Check warning on line 1328 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1327-L1328

Added lines #L1327 - L1328 were not covered by tests
end
U, mU = sunit_group(unique!(sup))
SinU = .\(mU, S)
F = free_abelian_group(length(SinU))
h = hom(F, U, SinU)
K, mK = kernel(h)
[x.coeff for x in mK.(gens(K))]

Check warning on line 1335 in src/AlgAss/AbsAlgAss.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/AbsAlgAss.jl#L1330-L1335

Added lines #L1330 - L1335 were not covered by tests
end
47 changes: 47 additions & 0 deletions src/AlgAss/Elem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1227,3 +1227,50 @@ end
function denominator(x::AbstractAssociativeAlgebraElem)
return lcm([ denominator(y) for y in coefficients(x, copy = false) ])
end

################################################################################
#
# Jordan-Chevalley decomposition
#
################################################################################

function _gcdx3(a, b, c)
g, u, v = gcdx(b, c)
g, s, t = gcdx(a, g)
@assert s * a + u * t * b + v * t * c == g
return g, s, u * t, v * t

Check warning on line 1241 in src/AlgAss/Elem.jl

View check run for this annotation

Codecov / codecov/patch

src/AlgAss/Elem.jl#L1237-L1241

Added lines #L1237 - L1241 were not covered by tests
end

# Algorithm 5.1 of Lenstra-Silverberg, Algorithms for Commutative Algebras Over the Rational Numbers
# (they only state it for QQ, but should be valid in charcteristic zero
function jordan_chevalley_decomposition(x::AbstractAssociativeAlgebraElem)
@req is_commutative(parent(x)) "Algebra must be commutative"
@req is_zero(characteristic(base_ring(parent(x)))) "Base field must be of characteristic zero"
g = minpoly(x)
gp = derivative(g)
ggpgcd = gcd(g, gp)
ghat = divexact(g, ggpgcd)
ghatp = derivative(ghat)
m = zero_matrix(base_ring(gp), degree(ggpgcd), degree(ggpgcd))
h = mod(ghatp, ggpgcd)
for l in 0:degree(h)
m[1, l + 1] = coeff(h, l)
end
for i in 1:(degree(ggpgcd)-1)
h = mod(i * shift_left(ghat, i - 1) + shift_left(ghatp, i), ggpgcd)
for l in 0:degree(h)
m[i + 1, l + 1] = coeff(h, l)
end
end
v = [zero(QQ) for k in 1:degree(ggpgcd)]
if length(v) > 0
v[1] = 1
end
fl, _q = can_solve_with_solution(m, v; side = :left)
@assert fl
q = parent(g)(_q)
v = q(x)*ghat(x)
u = x - v
return u, v
end

1 change: 1 addition & 0 deletions src/GrpAb/stable_sub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ Given a ZpnGModule $M$, the function returns all the submodules of $M$.
"""
function submodules(M::ZpnGModule; typequo=Int[-1], typesub=Int[-1], ord=-1)
@show typequo

if typequo!=[-1]
return submodules_with_quo_struct(M,typequo)
Expand Down
11 changes: 11 additions & 0 deletions test/AlgAss/AbsAlgAss.jl
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,15 @@
h = hom(A, A, inv(X) .* basis(A) .* X)
a = Hecke._skolem_noether(h)
@test all(h(b) == inv(a) * b * a for b in basis(A))

let
# maximal separable subalgebra
Qx, x = QQ[:x]
A = associative_algebra((x^2 + 1)^2)
B, BtoA = Hecke.maximal_separable_subalgebra(A)
@test domain(BtoA) === B
@test codomain(BtoA) == A
@test dim(B) == 2
@test is_simple(B)
end
end
11 changes: 11 additions & 0 deletions test/AlgAss/Elem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,15 @@
QG = Hecke._group_algebra(QQ, G; sparse = true);
@test QG(Dict(a => 2, zero(G) => 1)) == 2 * QG(a) + 1 * QG(zero(G))
@test QG(a => ZZ(2), zero(G) => QQ(1)) == 2 * QG(a) + 1 * QG(zero(G))

let
# Jordan-Chevalley
Qx, x = QQ[:x]
A = associative_algebra((x^2 + 1)^2)
alpha = basis(A)[2]
u, v = Hecke.jordan_chevalley_decomposition(alpha)
@test u == A(QQ.([0, 3//2, 0, 1//2]))
@test v == A(QQ.([0, -1//2, 0, -1//2]))
@test u + v == alpha
end
end

0 comments on commit 726c02c

Please sign in to comment.