Skip to content

Commit

Permalink
feat: improve radical code (#1416)
Browse files Browse the repository at this point in the history
- move code to radical.jl and disentangle
- avoid using FqField in the prime field case
  • Loading branch information
thofma authored Feb 24, 2024
1 parent 67a5f48 commit eb7d58a
Show file tree
Hide file tree
Showing 8 changed files with 467 additions and 190 deletions.
1 change: 1 addition & 0 deletions src/AlgAss.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ include("AlgAss/Map.jl")
include("AlgAss/AbsAlgAss.jl")
include("AlgAss/AlgQuat.jl")
include("AlgAss/AlgAss.jl")
include("AlgAss/radical.jl")
include("AlgAss/ChangeRing.jl")
include("AlgAss/AlgGrp.jl")
include("AlgAss/AlgMat.jl")
Expand Down
179 changes: 1 addition & 178 deletions src/AlgAss/AbsAlgAss.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,7 @@ Returns a matrix $M$ over the base ring of $A$ such that
$M_{i, j} = \mathrm{tr}(b_i \cdot b_j)$, where $b_1, \dots, b_n$ is the
basis of $A$.
"""
function trace_matrix(A::AbstractAssociativeAlgebra)
function trace_matrix(A::AbstractAssociativeAlgebra, tr = tr)
F = base_ring(A)
n = dim(A)
M = zero_matrix(F, n, n)
Expand Down Expand Up @@ -1203,183 +1203,6 @@ function restrict_scalars(A::AbstractAssociativeAlgebra{T}, K::Field) where {T}
return C, AtoC, CtoA
end

################################################################################
#
# Radical
#
################################################################################

@doc raw"""
radical(A::AbstractAssociativeAlgebra) -> AbsAlgAssIdl
Returns the Jacobson radical of $A$.
"""
function radical(A::AbstractAssociativeAlgebra{T}) where { T } #<: Union{ fpFieldElem, EuclideanRingResidueFieldElem{ZZRingElem}, fqPolyRepFieldElem, FqPolyRepFieldElem, QQFieldElem, AbsSimpleNumFieldElem } }
return ideal_from_gens(A, _radical(A), :twosided)
end

# Section 2.3.2 in W. Eberly: Computations for Algebras and Group Representations
# TODO: Fix the type
function _radical_prime_field(A::AbstractAssociativeAlgebra{T}) where { T } #<: Union{ fpFieldElem, EuclideanRingResidueFieldElem{ZZRingElem} } }
F = base_ring(A)
p = characteristic(F)
k = flog(ZZRingElem(dim(A)), p)

MF = trace_matrix(A)
d, B = nullspace(MF)
if d == 0
return elem_type(A)[]
end

C = transpose(B)
# Now, iterate: we need to find the kernel of tr((xy)^(p^i))/p^i mod p
# on the subspace generated by C
# Hard to believe, but this is linear!!!!
MZ = zero_matrix(FlintZZ, dim(A), dim(A))
pl = ZZRingElem(1)
a = A()
for l = 1:k
pl = p*pl
M = zero_matrix(F, dim(A), nrows(C))
for i = 1:nrows(C)
c = elem_from_mat_row(A, C, i)
for j = 1:dim(A)
a = mul!(a, c, A[j])
MF = representation_matrix(a)
for m = 1:nrows(MF)
for n = 1:ncols(MF)
MZ[m, n] = lift(ZZ, MF[m, n])
end
end
t = tr(MZ^Int(pl))
@assert iszero(mod(t, pl))
M[j, i] = F(divexact(t, pl))
end
end
d, B = nullspace(M)
if d == 0
return elem_type(A)[]
end
C = transpose(B)*C
end

return elem_type(A)[ elem_from_mat_row(A, C, i) for i = 1:nrows(C) ]
end

function _radical(A::AbstractAssociativeAlgebra{T}) where { T <: Union{ fpFieldElem, FpFieldElem } }
return _radical_prime_field(A)
end

function _radical(A::AbstractAssociativeAlgebra{T}) where { T <: Union{ fqPolyRepFieldElem, FqPolyRepFieldElem, FqFieldElem } }
F = base_ring(A)

p = characteristic(F)
if T <: fqPolyRepFieldElem
Fp = Native.GF(Int(p))
elseif T === FqFieldElem
Fp = GF(p)
else
Fp = Native.GF(p)
end

A2, A2toA = restrict_scalars(A, Fp)
if T === FqFieldElem
n = absolute_degree(F)
else
n = degree(F)
end

if n == 1
J = _radical_prime_field(A2)
return elem_type(A)[ A2toA(b) for b in J ]
end

if T === FqFieldElem
absgenF = Nemo._gen(F)
else
absgenF = gen(F)
end

k = flog(ZZRingElem(dim(A)), p)
Qx, x = polynomial_ring(FlintQQ, "x", cached = false)
f = Qx(push!(QQFieldElem[ -QQFieldElem(T === FqFieldElem ? Nemo._coeff(absgenF^n, i) : coeff(absgenF^n, i)) for i = 0:(n - 1) ], QQFieldElem(1)))
K, a = number_field(f, "a")

MF = trace_matrix(A2)
d, B = nullspace(MF)
if d == 0
return elem_type(A)[]
end

C = transpose(B)
pl = ZZRingElem(1)
MK = zero_matrix(K, dim(A), dim(A))
MQx = zero_matrix(Qx, dim(A), dim(A))
a = A()
for l = 1:k
pl = p*pl
M = zero_matrix(Fp, dim(A2), nrows(C))
for i = 1:nrows(C)
c = A2toA(elem_from_mat_row(A2, C, i))
for j = 1:dim(A)
a = mul!(a, c, A[j])
MF = representation_matrix(a)
MK = _lift_fq_mat!(MF, MK, MQx)
t = tr(MK^Int(pl))
@assert all([ iszero(mod(coeff(t, s), pl)) for s = 0:(n - 1) ])
jn = (j - 1)*n
for s = 1:n
M[jn + s, i] = Fp(divexact(numerator(coeff(t, s - 1)), pl))
end
end
end
d, B = nullspace(M)
if d == 0
return elem_type(A)[]
end
C = transpose(B)*C
end

return elem_type(A)[ A2toA(elem_from_mat_row(A2, C, i)) for i = 1:nrows(C) ]
end

function _lift_fq_mat!(M1::MatElem{T}, M2::MatElem{AbsSimpleNumFieldElem}, M3::MatElem{QQPolyRingElem}) where { T <: Union{ fqPolyRepFieldElem, FqPolyRepFieldElem, FqFieldElem } }
@assert ncols(M1) == ncols(M2) && ncols(M1) == ncols(M3)
@assert nrows(M1) == nrows(M2) && nrows(M1) == nrows(M3)
n = degree(base_ring(M1))
K = base_ring(M2)
R = base_ring(M3)
for i = 1:nrows(M1)
for j = 1:ncols(M1)
# Sadly, there is no setcoeff! for AbsSimpleNumFieldElem...
for k = 0:(n - 1)
if T === FqFieldElem
M3[i, j] = setcoeff!(M3[i, j], k, QQFieldElem(Nemo._coeff(M1[i, j], k)))
else
M3[i, j] = setcoeff!(M3[i, j], k, QQFieldElem(coeff(M1[i, j], k)))
end
end
ccall((:nf_elem_set_fmpq_poly, libantic), Nothing, (Ref{AbsSimpleNumFieldElem}, Ref{QQPolyRingElem}, Ref{AbsSimpleNumField}), M2[i, j], M3[i, j], K)
end
end
return M2
end

function _radical(A::AbstractAssociativeAlgebra{T}) where { T <: Union{ QQFieldElem, NumFieldElem } }
M = trace_matrix(A)
n, N = nullspace(M)
b = Vector{elem_type(A)}(undef, n)
t = zeros(base_ring(A), dim(A))
# the construct A(t) will make a copy (hopefully :))
for i = 1:n
for j = 1:dim(A)
t[j] = N[j, i]
end
b[i] = A(t)
end
return b
end

################################################################################
#
# is_simple
Expand Down
Loading

0 comments on commit eb7d58a

Please sign in to comment.