From 35c0cce4336d8083845d17e9694fb5acfee93a45 Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Sat, 2 Nov 2024 23:57:28 +0100 Subject: [PATCH] feat: multiplicative dependencies for matrices --- src/AlgAss/AbsAlgAss.jl | 93 ++++++++++++++++++++++++++++++++++++++++ src/AlgAss/Elem.jl | 47 ++++++++++++++++++++ src/GrpAb/stable_sub.jl | 1 + test/AlgAss/AbsAlgAss.jl | 22 ++++++++++ test/AlgAss/Elem.jl | 11 +++++ 5 files changed, 174 insertions(+) diff --git a/src/AlgAss/AbsAlgAss.jl b/src/AlgAss/AbsAlgAss.jl index bdf064a76f..c2ad2e79e7 100644 --- a/src/AlgAss/AbsAlgAss.jl +++ b/src/AlgAss/AbsAlgAss.jl @@ -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" + + B, BtoA = maximal_separable_subalgebra(A) + + nfs = _as_number_fields(B) + + jcs = first.(jordan_chevalley_decomposition.(S)) + jc = [has_preimage_with_preimage(BtoA, x)[2] for x in first.(jordan_chevalley_decomposition.(S))] + + F = free_abelian_group(length(S)) + + kernels = ZZMatrix[] + + for (K, BtoK) in nfs + eltsinK = BtoK.(jc) + if any(is_zero, eltsinK) + error("oopsie, elements not invertible") + end + # hack to get something + v = _mult_dep(K, eltsinK) + Hm = reduce(vcat, v) + push!(kernels, Hm) + end + + 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) + + push!(kernels, kernel(Blog, side = :left)) + + 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)))] +end + +function _multiplicative_dependencies(v::Vector{<:MatElem}) + A = matrix_algebra(QQ, v) + S = A.(v) + _multiplicative_dependencies(A, S) +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]) + # I don't know why noone ever implemented this + if isempty(sup) + push!(sup, prime_ideals_over(OK, 2)[1]) + 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))] +end diff --git a/src/AlgAss/Elem.jl b/src/AlgAss/Elem.jl index 908d3cd02a..a7b164db1f 100644 --- a/src/AlgAss/Elem.jl +++ b/src/AlgAss/Elem.jl @@ -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 +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 + diff --git a/src/GrpAb/stable_sub.jl b/src/GrpAb/stable_sub.jl index 42f63ab63e..882ec357b2 100644 --- a/src/GrpAb/stable_sub.jl +++ b/src/GrpAb/stable_sub.jl @@ -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) diff --git a/test/AlgAss/AbsAlgAss.jl b/test/AlgAss/AbsAlgAss.jl index 1051cffd86..8dff9477a6 100644 --- a/test/AlgAss/AbsAlgAss.jl +++ b/test/AlgAss/AbsAlgAss.jl @@ -247,4 +247,26 @@ 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 + + let + # multiplicative depdendencies + a = QQ[1 2; 3 4] + c = QQ[-3 2; 3 0] + v = [a, a^2, c, c*a] + m = Hecke._multiplicative_dependencies([a, a^2, c, c*a]) + for w in m + @test isone(prod(v[i]^Int(w[i]) for i in 1:length(v))) + end + end end diff --git a/test/AlgAss/Elem.jl b/test/AlgAss/Elem.jl index 4a7150a8b5..99a1de452e 100644 --- a/test/AlgAss/Elem.jl +++ b/test/AlgAss/Elem.jl @@ -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