Skip to content

Commit 0904b0b

Browse files
authored
feat: multiplicative dependencies for matrices (#1671)
1 parent 8564bb0 commit 0904b0b

File tree

5 files changed

+174
-0
lines changed

5 files changed

+174
-0
lines changed

src/AlgAss/AbsAlgAss.jl

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,3 +1241,96 @@ function _skolem_noether(h::AbsAlgAssMor)
12411241
error("Not impelemented yet")
12421242
end
12431243
end
1244+
1245+
################################################################################
1246+
#
1247+
# Maximal separable subalgebra
1248+
#
1249+
################################################################################
1250+
1251+
# (Part of) Algorithm 5.5 of Lenstra-Silverberg, Algorithms for Commutative Algebras Over the Rational Numbers
1252+
# (they only state it for QQ, but should be valid in charcteristic zero)
1253+
function maximal_separable_subalgebra(A::AbstractAssociativeAlgebra)
1254+
@req is_commutative(A) "Algebra must be commutative"
1255+
@req is_zero(characteristic(base_ring(A))) "Characteristic of base ring must be zero"
1256+
1257+
B = basis(A)
1258+
BU = eltype(B)[]
1259+
for b in B
1260+
u, v = jordan_chevalley_decomposition(b)
1261+
push!(BU, u)
1262+
end
1263+
R = echelon_form(basis_matrix(BU); trim = true)
1264+
return _subalgebra(A, [elem_from_mat_row(A, R, i) for i in 1:nrows(R)])
1265+
end
1266+
1267+
################################################################################
1268+
#
1269+
# Multiplicative dependencies
1270+
#
1271+
################################################################################
1272+
1273+
function _multiplicative_dependencies(A::AbstractAssociativeAlgebra, S::Vector)
1274+
@req is_commutative(A) "Algebra must be commutative"
1275+
@req is_zero(characteristic(base_ring(A))) "Characteristic of base ring must be zero"
1276+
1277+
B, BtoA = maximal_separable_subalgebra(A)
1278+
1279+
nfs = _as_number_fields(B)
1280+
1281+
jcs = first.(jordan_chevalley_decomposition.(S))
1282+
jc = [has_preimage_with_preimage(BtoA, x)[2] for x in first.(jordan_chevalley_decomposition.(S))]
1283+
1284+
F = free_abelian_group(length(S))
1285+
1286+
kernels = ZZMatrix[]
1287+
1288+
for (K, BtoK) in nfs
1289+
eltsinK = BtoK.(jc)
1290+
if any(is_zero, eltsinK)
1291+
error("oopsie, elements not invertible")
1292+
end
1293+
# hack to get something
1294+
v = _mult_dep(K, eltsinK)
1295+
Hm = reduce(vcat, v)
1296+
push!(kernels, Hm)
1297+
end
1298+
1299+
J = radical(A)
1300+
m = dim(A) # TODO: compute the nilpotency index
1301+
logimgs = elem_type(A)[]
1302+
for (s, pis) in zip(S, jcs)
1303+
w = s*inv(pis)
1304+
v = 1 - w
1305+
logimg = -sum(v^i * QQ(1//i) for i in 1:m)
1306+
push!(logimgs, logimg)
1307+
end
1308+
Blog, = integral_split(basis_matrix(logimgs), ZZ)
1309+
1310+
push!(kernels, kernel(Blog, side = :left))
1311+
1312+
RtoF = reduce((x, y) -> intersect(x, y, false)[2], [sub(F, k, false)[2] for k in kernels])
1313+
return [_eltseq(x.coeff) for x in RtoF.(gens(domain(RtoF)))]
1314+
end
1315+
1316+
function _multiplicative_dependencies(v::Vector{<:MatElem})
1317+
A = matrix_algebra(QQ, v)
1318+
S = A.(v)
1319+
_multiplicative_dependencies(A, S)
1320+
end
1321+
1322+
# TODO: Use Ge
1323+
function _mult_dep(K::AbsSimpleNumField, S::Vector)
1324+
OK = maximal_order(K)
1325+
sup = reduce(vcat, [ support(s, OK) for s in S])
1326+
# I don't know why noone ever implemented this
1327+
if isempty(sup)
1328+
push!(sup, prime_ideals_over(OK, 2)[1])
1329+
end
1330+
U, mU = sunit_group(unique!(sup))
1331+
SinU = .\(mU, S)
1332+
F = free_abelian_group(length(SinU))
1333+
h = hom(F, U, SinU)
1334+
K, mK = kernel(h)
1335+
[x.coeff for x in mK.(gens(K))]
1336+
end

src/AlgAss/Elem.jl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,3 +1227,50 @@ end
12271227
function denominator(x::AbstractAssociativeAlgebraElem)
12281228
return lcm([ denominator(y) for y in coefficients(x, copy = false) ])
12291229
end
1230+
1231+
################################################################################
1232+
#
1233+
# Jordan-Chevalley decomposition
1234+
#
1235+
################################################################################
1236+
1237+
function _gcdx3(a, b, c)
1238+
g, u, v = gcdx(b, c)
1239+
g, s, t = gcdx(a, g)
1240+
@assert s * a + u * t * b + v * t * c == g
1241+
return g, s, u * t, v * t
1242+
end
1243+
1244+
# Algorithm 5.1 of Lenstra-Silverberg, Algorithms for Commutative Algebras Over the Rational Numbers
1245+
# (they only state it for QQ, but should be valid in charcteristic zero
1246+
function jordan_chevalley_decomposition(x::AbstractAssociativeAlgebraElem)
1247+
@req is_commutative(parent(x)) "Algebra must be commutative"
1248+
@req is_zero(characteristic(base_ring(parent(x)))) "Base field must be of characteristic zero"
1249+
g = minpoly(x)
1250+
gp = derivative(g)
1251+
ggpgcd = gcd(g, gp)
1252+
ghat = divexact(g, ggpgcd)
1253+
ghatp = derivative(ghat)
1254+
m = zero_matrix(base_ring(gp), degree(ggpgcd), degree(ggpgcd))
1255+
h = mod(ghatp, ggpgcd)
1256+
for l in 0:degree(h)
1257+
m[1, l + 1] = coeff(h, l)
1258+
end
1259+
for i in 1:(degree(ggpgcd)-1)
1260+
h = mod(i * shift_left(ghat, i - 1) + shift_left(ghatp, i), ggpgcd)
1261+
for l in 0:degree(h)
1262+
m[i + 1, l + 1] = coeff(h, l)
1263+
end
1264+
end
1265+
v = [zero(QQ) for k in 1:degree(ggpgcd)]
1266+
if length(v) > 0
1267+
v[1] = 1
1268+
end
1269+
fl, _q = can_solve_with_solution(m, v; side = :left)
1270+
@assert fl
1271+
q = parent(g)(_q)
1272+
v = q(x)*ghat(x)
1273+
u = x - v
1274+
return u, v
1275+
end
1276+

src/GrpAb/stable_sub.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ Given a ZpnGModule $M$, the function returns all the submodules of $M$.
432432
433433
"""
434434
function submodules(M::ZpnGModule; typequo=Int[-1], typesub=Int[-1], ord=-1)
435+
@show typequo
435436

436437
if typequo!=[-1]
437438
return submodules_with_quo_struct(M,typequo)

test/AlgAss/AbsAlgAss.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,26 @@
247247
h = hom(A, A, inv(X) .* basis(A) .* X)
248248
a = Hecke._skolem_noether(h)
249249
@test all(h(b) == inv(a) * b * a for b in basis(A))
250+
251+
let
252+
# maximal separable subalgebra
253+
Qx, x = QQ[:x]
254+
A = associative_algebra((x^2 + 1)^2)
255+
B, BtoA = Hecke.maximal_separable_subalgebra(A)
256+
@test domain(BtoA) === B
257+
@test codomain(BtoA) == A
258+
@test dim(B) == 2
259+
@test is_simple(B)
260+
end
261+
262+
let
263+
# multiplicative depdendencies
264+
a = QQ[1 2; 3 4]
265+
c = QQ[-3 2; 3 0]
266+
v = [a, a^2, c, c*a]
267+
m = Hecke._multiplicative_dependencies([a, a^2, c, c*a])
268+
for w in m
269+
@test isone(prod(v[i]^Int(w[i]) for i in 1:length(v)))
270+
end
271+
end
250272
end

test/AlgAss/Elem.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,15 @@
9494
QG = Hecke._group_algebra(QQ, G; sparse = true);
9595
@test QG(Dict(a => 2, zero(G) => 1)) == 2 * QG(a) + 1 * QG(zero(G))
9696
@test QG(a => ZZ(2), zero(G) => QQ(1)) == 2 * QG(a) + 1 * QG(zero(G))
97+
98+
let
99+
# Jordan-Chevalley
100+
Qx, x = QQ[:x]
101+
A = associative_algebra((x^2 + 1)^2)
102+
alpha = basis(A)[2]
103+
u, v = Hecke.jordan_chevalley_decomposition(alpha)
104+
@test u == A(QQ.([0, 3//2, 0, 1//2]))
105+
@test v == A(QQ.([0, -1//2, 0, -1//2]))
106+
@test u + v == alpha
107+
end
97108
end

0 commit comments

Comments
 (0)