From 49673447ced5d47f7d3c77fcb04f696f08454f7c Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Thu, 29 Aug 2024 10:40:34 +0200 Subject: [PATCH] feat: bring algebras into shape - documentation - tests - renaming --- docs/src/.vitepress/config.mts | 8 + src/AlgAss.jl | 4 +- src/AlgAss/AbsAlgAss.jl | 679 ++++------- src/AlgAss/AlgGrp.jl | 7 +- src/AlgAss/AlgMat.jl | 5 - src/AlgAss/Conversions.jl | 615 ++++++++++ src/AlgAss/Decompose.jl | 457 +++++++ src/AlgAss/Elem.jl | 29 +- ...{AlgAss.jl => StructureConstantAlgebra.jl} | 1048 ++++------------- src/AlgAssAbsOrd/Elem.jl | 10 + src/AlgAssAbsOrd/Ideal.jl | 2 +- src/exports.jl | 7 + test/AlgAss.jl | 2 +- test/AlgAss/AbsAlgAss.jl | 2 +- test/AlgAss/Elem.jl | 18 + ...{AlgAss.jl => StructureConstantAlgebra.jl} | 45 +- 16 files changed, 1623 insertions(+), 1315 deletions(-) create mode 100644 src/AlgAss/Conversions.jl create mode 100644 src/AlgAss/Decompose.jl rename src/AlgAss/{AlgAss.jl => StructureConstantAlgebra.jl} (53%) rename test/AlgAss/{AlgAss.jl => StructureConstantAlgebra.jl} (76%) diff --git a/docs/src/.vitepress/config.mts b/docs/src/.vitepress/config.mts index f2d3143574..910aecd99d 100644 --- a/docs/src/.vitepress/config.mts +++ b/docs/src/.vitepress/config.mts @@ -88,6 +88,14 @@ export default defineConfig({ { text: 'Discriminant groups', link: 'manual/quad_forms/discriminant_group'}, ] }, + { text: 'Algebras', + collapsed: true, + items: [ + { text: 'Introduction', link: 'manual/algebras/intro'}, + { text: 'Basics', link: 'manual/algebras/basics'}, + { text: 'Structure constant algebras', link: 'manual/algebras/structureconstant'}, + ] + }, { text: 'Elliptic curves', collapsed: true, diff --git a/src/AlgAss.jl b/src/AlgAss.jl index c5c2fae399..12797bf171 100644 --- a/src/AlgAss.jl +++ b/src/AlgAss.jl @@ -1,7 +1,9 @@ include("AlgAss/Map.jl") include("AlgAss/AbsAlgAss.jl") +include("AlgAss/Decompose.jl") include("AlgAss/AlgQuat.jl") -include("AlgAss/AlgAss.jl") +include("AlgAss/StructureConstantAlgebra.jl") +include("AlgAss/Conversions.jl") include("AlgAss/radical.jl") include("AlgAss/ChangeRing.jl") include("AlgAss/AlgGrp.jl") diff --git a/src/AlgAss/AbsAlgAss.jl b/src/AlgAss/AbsAlgAss.jl index 2f7aecc1ae..128a430b1e 100644 --- a/src/AlgAss/AbsAlgAss.jl +++ b/src/AlgAss/AbsAlgAss.jl @@ -1,5 +1,91 @@ _base_ring(A::AbstractAssociativeAlgebra) = base_ring(A) +@doc raw""" + base_ring(A::AbstractAssociativeAlgebra) -> Field + +Given a $K$-algebra $A$, return $K$. +""" +base_ring(A::AbstractAssociativeAlgebra) + +################################################################################ +# +# Zero algebra +# +################################################################################ + +@doc raw""" + zero_algebra([T, ] K::Field) -> AbstractAssociativeAlgebra + +Return the zero ring as an algebra over the field $K$. + +The optional first argument determines the type of the algebra, and can be +`StructureConstantAlgebra` (default) or `MatrixAlgebra`. + +# Examples + +```jldoctest +julia> A = zero_algebra(QQ) +Structure constant algebra of dimension 0 over QQ +``` +""" +zero_algebra(K::Field) + +@doc raw""" + is_zero(A::AbstractAssociativeAlgebra) -> Bool + +Return whether $A$ is the zero algebra. +""" +is_zero(A::AbstractAssociativeAlgebra) = dim(A) == 0 + +@doc raw""" + has_one(A::AbstractAssociativeAlgebra) -> Bool + +Return whether $A$ has a one. +""" +has_one(A::AbstractAssociativeAlgebra) + +@doc raw""" + is_commutative(A::AbstractAssociativeAlgebra) -> Bool + +Return whether $A$ is commutative. + +# Examples + +```jldoctest +julia> A = matrix_algebra(QQ, 2); + +julia> is_commutative(A) +false +``` +""" +is_commutative(::AbstractAssociativeAlgebra) + +################################################################################ +# +# Center +# +################################################################################ + +@doc raw""" + center(A::AbstractAssociativeAlgebra) + -> StructureConstantAlgebra, Map + +Returns the center $C$ of $A$ and the inclusion $C \to A$. Note that $C$ itself +is an algebra. + +# Examples + +```jldoctest +julia> A = matrix_algebra(QQ, 2); + +julia> C, CtoA = center(A); + +julia> C +Structure constant algebra of dimension 1 over QQ +``` +""" +center(A::AbstractAssociativeAlgebra) + ################################################################################ # # Morphism types @@ -31,9 +117,11 @@ morphism_type(A::Type{T}) where {T <: AbstractAssociativeAlgebra} = morphism_typ ################################################################################ @doc raw""" - basis(A::AbstractAssociativeAlgebra) -> Vector{AbstractAssociativeAlgebraElem} + basis(A::AbstractAssociativeAlgebra) -> Vector -Returns the basis of $A$. +Given a $K$-algebra $A$ return the $K$-basis of $A$. See also +[`coordinates`](@ref) to get the the coordinates of an element with respect to +the bases. """ function basis(A::AbstractAssociativeAlgebra) if isdefined(A, :basis) @@ -92,12 +180,41 @@ end # ################################################################################ +@doc raw""" + dimension_of_center(A::AbstractAssociativeAlgebra) -> Int + +Given a $K$-algebra, return the $K$-dimension of the center of $A$. + +# Examples + +```jldoctest +julia> A = matrix_algebra(QQ, 2); + +julia> dimension_of_center(A) +1 +``` +""" @attr Int function dimension_of_center(A::AbstractAssociativeAlgebra) C, _ = center(A) return dim(C) end +@doc raw""" + dimension_over_center(A::AbstractAssociativeAlgebra) -> Int + +Given a simple $K$-algebra with center $C$, return the $C$-dimension $A$. + +# Examples + +```jldoctest +julia> A = matrix_algebra(QQ, 2); + +julia> dimension_of_center(A) +4 +``` +""" @attr Int function dimension_over_center(A::AbstractAssociativeAlgebra) + @req is_simple(A) "Algebra must be simple" return divexact(dim(A), dimension_of_center(A)) end @@ -105,6 +222,12 @@ end return isqrt(dimension_over_center(A)) end +@doc raw""" + is_central(A::AbstractAssociativeAlgebra) + +Return whether the $K$-algebra $A$ is central, that is, whether $K$ is the +center of $A$. +""" @attr Bool is_central(A::AbstractAssociativeAlgebra) = dimension_of_center(A) == 1 ################################################################################ @@ -124,10 +247,10 @@ $e \cdot A$ (if `action == :left`) respectively $A \cdot e$ (if `action == :righ and a map from this algebra to $A$. If `idempotent` is `true`, it is assumed that $e$ is idempotent in $A$. """ -function subalgebra(A::AbstractAssociativeAlgebra{T}, e::AbstractAssociativeAlgebraElem{T}, idempotent::Bool = false, action::Symbol = :left) where {T} +function _subalgebra(A::AbstractAssociativeAlgebra{T}, e::AbstractAssociativeAlgebraElem{T}, idempotent::Bool = false, action::Symbol = :left) where {T} @assert parent(e) == A B, mB = StructureConstantAlgebra(A) - C, mC = subalgebra(B, mB\e, idempotent, action) + C, mC = _subalgebra(B, mB\e, idempotent, action) mD = compose_and_squash(mB, mC) @assert domain(mD) == C return C, mD @@ -140,472 +263,15 @@ end Returns the subalgebra $A$ generated by the elements in `basis` and a map from this algebra to $A$. """ -function subalgebra(A::AbstractAssociativeAlgebra{T}, basis::Vector{ <: AbstractAssociativeAlgebraElem{T} }) where T +function _subalgebra(A::AbstractAssociativeAlgebra{T}, basis::Vector{ <: AbstractAssociativeAlgebraElem{T} }) where T B, mB = StructureConstantAlgebra(A) basis_pre = elem_type(B)[mB\(basis[i]) for i in 1:length(basis)] - C, mC = subalgebra(B, basis_pre) + C, mC = _subalgebra(B, basis_pre) mD = compose_and_squash(mB, mC) @assert domain(mD) == C return C, mD end -################################################################################ -# -# Decomposition -# -################################################################################ - -# Assume that A is a commutative algebra over a finite field of cardinality q. -# This functions computes a basis for ker(x -> x^q). -function kernel_of_frobenius(A::AbstractAssociativeAlgebra) - F = base_ring(A) - q = order(F) - - b = A() - B = zero_matrix(F, dim(A), dim(A)) - for i = 1:dim(A) - b.coeffs[i] = one(F) - if i > 1 - b.coeffs[i - 1] = zero(F) - end - c = b^q - b - for j = 1:dim(A) - B[j, i] = c.coeffs[j] - end - end - - V = kernel(B, side = :right) - return [ A(V[:, i]) for i in 1:ncols(V) ] -end - -@doc raw""" - decompose(A::AbstractAssociativeAlgebra) -> Array{Tuple{StructureConstantAlgebra, AbsAlgAssMor}} - -Given a semisimple algebra $A$ over a field, this function returns a -decomposition of $A$ as a direct sum of simple algebras and maps from these -components to $A$. -""" -function decompose(A::StructureConstantAlgebra{T}) where {T} - if isdefined(A, :decomposition) - return A.decomposition::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} - end - - if is_simple_known(A) && A.is_simple == 1 - B, mB = StructureConstantAlgebra(A) - return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] - end - - res = _decompose(A) - A.decomposition = res - return res -end - -# Generic function for everything besides StructureConstantAlgebra -function decompose(A::AbstractAssociativeAlgebra{T}) where T - return __decompose(A) -end - -function __decompose(A::AbstractAssociativeAlgebra{T}) where {T} - if isdefined(A, :decomposition) - return A.decomposition::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} - end - - B, mB = StructureConstantAlgebra(A) - - if is_simple_known(A) && A.is_simple == 1 - return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[ (B, mB) ] - end - - D = _decompose(B)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, StructureConstantAlgebra{T})}} - res = Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[] - for (S, mS) in D - mD = compose_and_squash(mB, mS) - push!(res, (S, mD)) - end - A.decomposition = res - Z, ZtoB = center(B) - if dim(Z) != dim(B) - if isdefined(A, :center) - @assert A.center[1] === Z - end - A.center = (Z, compose_and_squash(mB, ZtoB)) - end - return res -end - -function _decompose(A::AbstractAssociativeAlgebra{T}) where {T} - @assert _issemisimple(A) != 2 "Algebra is not semisimple" - if is_commutative(A) - return _dec_com(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} - else - return _dec_via_center(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} - end -end - -function _dec_via_center(A::S) where {T, S <: AbstractAssociativeAlgebra{T}} - ZA, mZA = center(A) - Algs = _dec_com(ZA) - ZA.decomposition = Algs - res = Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, S)}[ subalgebra(A, mZA(BtoZA(one(B))), true) for (B, BtoZA) in Algs] - for i in 1:length(res) - res[i][1].is_simple = 1 - B, BtoZA = Algs[i] # B is the centre of res[i][1] - # Build a map from B to res[i][1] via B -> ZA -> A -> res[i][1] - M = zero_matrix(base_ring(A), dim(B), dim(res[i][1])) - for j = 1:dim(B) - t = mZA(BtoZA(B[j])) - s = res[i][2]\t - elem_to_mat_row!(M, j, s) - end - if dim(res[i][1]) != dim(B) - res[i][1].center = (B, hom(B, res[i][1], M)) - else - # res[i][1] is commutative, so we do not cache the centre - iM = inv(M) - BtoA = hom(B, A, M*res[i][2].mat, res[i][2].imat*iM) - res[i] = (B, BtoA) - end - end - A.decomposition = res - return res -end - -function _dec_com(A::AbstractAssociativeAlgebra{T}) where {T} - v = get_attribute(A, :central_idempotents) - if v !== nothing - w = v::Vector{elem_type(A)} - return _dec_com_given_idempotents(A, w) - end - - if characteristic(base_ring(A)) > 0 - return _dec_com_finite(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} - else - return _dec_com_gen(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} - end -end - -function _dec_com_given_idempotents(A::AbstractAssociativeAlgebra{T}, v::Vector) where {T} - dec = Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[] - for idem in v - S, StoA = subalgebra(A, idem, true) - push!(dec, (S, StoA)) - end - return dec -end - -function _dec_com_gen(A::AbstractAssociativeAlgebra{T}) where {T <: FieldElem} - if dim(A) == 0 - # The zero-dimensional algebra is the zero ring, which is semisimple, but not simple - # It has *no* simple components. - A.is_simple = -1 - return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[] - end - - if dim(A) == 1 - A.is_simple = 1 - B, mB = StructureConstantAlgebra(A) - return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] - end - - F = base_ring(A) - - k = dim(A) - - V = elem_type(A)[A[i] for i in 1:k] - - while true - c = elem_type(F)[ rand(F, -10:10) for i = 1:k ] - a = dot(c, V) - f = minpoly(a) - - if degree(f) < 2 - continue - end - if is_irreducible(f) - if degree(f) == dim(A) - A.is_simple = 1 - B, mB = StructureConstantAlgebra(A) - return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] - end - continue - end - - @assert is_squarefree(f) - - fac = factor(f) - R = parent(f) - factors = Vector{elem_type(R)}() - for ff in keys(fac.fac) - push!(factors, ff) - end - sols = Vector{elem_type(R)}() - right_side = elem_type(R)[ R() for i = 1:length(factors) ] - max_deg = 0 - for i = 1:length(factors) - right_side[i] = R(1) - if i != 1 - right_side[i - 1] = R(0) - end - s = crt(right_side, factors) - push!(sols, s) - max_deg = max(max_deg, degree(s)) - end - x = one(A) - powers = Vector{elem_type(A)}() - for i = 1:max_deg + 1 - push!(powers, x) - x *= a - end - idems = Vector{elem_type(A)}() - for s in sols - idem = A() - for i = 0:degree(s) - idem += coeff(s, i)*powers[i + 1] - end - push!(idems, idem) - end - - A.is_simple = 2 - - res = Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}}() - for idem in idems - S, StoA = subalgebra(A, idem, true) - decS = _dec_com_gen(S)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(S))}} - for (B, BtoS) in decS - BtoA = compose_and_squash(StoA, BtoS) - push!(res, (B, BtoA)) - end - end - return res - end -end - -function _dec_com_finite(A::AbstractAssociativeAlgebra{T}) where T - if dim(A) == 1 - A.is_simple = 1 - B, mB = StructureConstantAlgebra(A) - return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] - end - - F = base_ring(A) - @assert !iszero(characteristic(F)) - V = kernel_of_frobenius(A) - k = length(V) - - if k == 1 - A.is_simple = 1 - B, mB = StructureConstantAlgebra(A) - return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] - end - - A.is_simple = 2 - c = elem_type(F)[ rand(F) for i = 1:k ] - M = zero_matrix(F, dim(A), dim(A)) - a = dot(c, V) - representation_matrix!(a, M) - f = minpoly(M) - while degree(f) < 2 - for i = 1:length(c) - c[i] = rand(F) - end - a = dot(c, V) - zero!(M) - representation_matrix!(a, M) - f = minpoly(M) - end - - #@assert is_squarefree(f) - fac = factor(f) - R = parent(f) - factorss = collect(keys(fac.fac)) - sols = Vector{typeof(f)}(undef, length(factorss)) - right_side = typeof(f)[ zero(R) for i = 1:length(factorss) ] - max_deg = 0 - for i = 1:length(factorss) - right_side[i] = one(R) - if 1 != i - right_side[i - 1] = zero(R) - end - sols[i] = crt(right_side, factorss) - max_deg = max(max_deg, degree(sols[i])) - end - powers = Vector{elem_type(A)}(undef, max_deg+1) - powers[1] = one(A) - powers[2] = a - x = a - for i = 3:max_deg + 1 - x *= a - powers[i] = x - end - - idems = Vector{elem_type(A)}() - for s in sols - idem = A() - for i = 0:degree(s) - idem += coeff(s, i)*powers[i + 1] - end - push!(idems, idem) - end - - res = Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}}() - for idem in idems - S, StoA = subalgebra(A, idem, true) - decS = _dec_com_finite(S) - for (B, BtoS) in decS - BtoA = compose_and_squash(StoA, BtoS) - push!(res, (B, BtoA)) - end - end - return res - -end - -################################################################################ -# -# Decomposition as number fields -# -################################################################################ - -@doc raw""" - components(::Type{Field}, A::AbstractAssociativeAlgebra) - -> Vector{Tuple{Field, Morphism}} - -Given an étale algebra $A$, return the simple components of $A$ -as fields $K$ together with the projection $A \to K$. -""" -function components(::Type{Field}, A::AbstractAssociativeAlgebra) - @assert is_commutative(A) - return as_number_fields(A) -end - -@doc raw""" - component(::Type{Field}, A::AbstractAssociativeAlgebra, i::Int) - -> Vector{Tuple{Field, Morphism}} - -Given an étale algebra $A$ and index $i$, return the $i$-th simple components -of $A$ as a field $K$ together with the projection $A \to K$. -""" -function component(::Type{Field}, A::AbstractAssociativeAlgebra, i::Int) - nf = as_number_fields(A) - return nf[i] -end - -@doc raw""" - as_number_fields(A::AbstractAssociativeAlgebra{QQFieldElem}) - -> Vector{Tuple{AbsSimpleNumField, AbsAlgAssToNfAbsMor}} - -Given a commutative algebra $A$ over $\mathbb Q$, this function returns a -decomposition of $A$ as direct sum of number fields and maps from $A$ to -these fields. -""" -function as_number_fields(A::AbstractAssociativeAlgebra{T}) where {T} - return __as_number_fields(A) -end - -function __as_number_fields(A::AbstractAssociativeAlgebra{T}; use_maximal_order::Bool = true) where {T} - if isdefined(A, :maps_to_numberfields) - NF = A.maps_to_numberfields::Vector{Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(A)}} - return NF - end - - result = _as_number_fields(A, use_maximal_order = use_maximal_order) - @assert all(domain(AtoK) === A for (_, AtoK) in result) - A.maps_to_numberfields = result - return result -end - -_ext_type(::Type{QQFieldElem}) = AbsSimpleNumField - -_ext_type(::Type{AbsSimpleNumFieldElem}) = RelSimpleNumField{AbsSimpleNumFieldElem} - -function _as_number_fields(A::AbstractAssociativeAlgebra{T}; use_maximal_order::Bool = true) where {T} - d = dim(A) - - Adec = decompose(A) - - fields_not_cached = false - for i = 1:length(Adec) - if !isdefined(Adec[i][1], :maps_to_numberfields) - fields_not_cached = true - end - end - - if fields_not_cached && T === QQFieldElem && use_maximal_order - # Compute a LLL reduced basis of the maximal order of A to find "small" - # polynomials for the number fields. - OA = maximal_order(A) - L = lll(basis_matrix(FakeFmpqMat, OA, copy = false).num) - n = basis_matrix(FakeFmpqMat, OA, copy = false).den - basis_lll = elem_type(A)[ elem_from_mat_row(A, L, i, n) for i = 1:d ] - elseif fields_not_cached - basis_lll = basis(A) - end - - KK = base_ring(A) - - M = zero_matrix(KK, 0, d) - matrices = Vector{dense_matrix_type(T)}() - fields = Vector{_ext_type(T)}() - for i = 1:length(Adec) - # For each small algebra construct a number field and the isomorphism - B, BtoA = Adec[i] - dB = dim(B) - if !isdefined(B, :maps_to_numberfields) - local K, BtoK - found_field = false # Only for debugging - for j = 1:d - t = BtoA\basis_lll[j] - mint = minpoly(t) - if degree(mint) == dB - found_field = true - K, BtoK = _as_field_with_isomorphism(B, t, mint) - B.maps_to_numberfields = Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(B)}[(K, BtoK)] - push!(fields, K) - break - end - end - @assert found_field "This should not happen..." - else - K, BtoK = B.maps_to_numberfields[1]::Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(B)} - push!(fields, K) - end - - # TODO: We can do a short cut, but the map must be BtoK \circ inv(BtoA) - #if length(Adec) == 1 - # res = Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(A)}[(K, BtoK)] - # A.maps_to_numberfields = res - # return res - #end - - # Construct the map from K to A - N = zero_matrix(KK, degree(K), d) - for j = 1:degree(K) - t = BtoA(BtoK\basis(K)[j]) - elem_to_mat_row!(N, j, t) - end - push!(matrices, N) - M = vcat(M, N) - end - @assert nrows(M) == d - - invM = inv(M) - matrices2 = Vector{dense_matrix_type(T)}(undef, length(matrices)) - offset = 1 - for i = 1:length(matrices) - r = nrows(matrices[i]) - N = sub(invM, 1:d, offset:(offset + r - 1)) - matrices2[i] = N - offset += r - end - - result = Vector{Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(A)}}() - for i = 1:length(fields) - push!(result, (fields[i], AbsAlgAssToNfAbsMor(A, fields[i], matrices2[i], matrices[i]))) - end - - return result -end - ################################################################################ # # Random elements @@ -730,23 +396,65 @@ function _add_row_to_rref!(M::MatElem{T}, v::Vector{T}, pivot_rows::Vector{Int}, end @doc raw""" - gens(A::AbstractAssociativeAlgebra, return_full_basis::Val = Val(false); - thorough_search::Bool = false) where T - -> Vector{AbstractAssociativeAlgebraElem} - -Returns a subset of `basis(A)`, which generates $A$ as an algebra over -`base_ring(A)`. -If `return_full_basis` is set to `Val(true)`, the function also returns a -`Vector{AbstractAssociativeAlgebraElem}` containing a full basis consisting of monomials in -the generators and a `Vector{Vector{Tuple{Int, Int}}}` containing the -information on how these monomials are built. E. g.: If the function returns -`g`, `full_basis` and `v`, then we have -`full_basis[i] = prod( g[j]^k for (j, k) in v[i] )`. + gens(A::AbstractAssociativeAlgebra; thorough_search::Bool = false) -> Vector + +Given a $K$-algebra $A$, return a subset of `basis(A)`, which generates $A$ as +an algebra over $K$. + +If `thorough_search` is `true`, the number of returned generators is possibly +smaller. This will in general increase the runtime. It is not guaranteed that +the number of generators is minimal in any case. + +The [`gens_with_data`](@ref) function computes additional data for expressing a +basis as words in the generators. + +# Examples + +```jldoctest +julia> A = matrix_algebra(QQ, 3); + +julia> gens(A; thorough_search = true) +5-element Vector{MatAlgebraElem{QQFieldElem, QQMatrix}}: + [1 0 0; 0 0 0; 0 0 0] + [0 0 0; 1 0 0; 0 0 0] + [0 0 0; 0 0 0; 1 0 0] + [0 1 0; 0 0 0; 0 0 0] + [0 0 1; 0 0 0; 0 0 0] +``` +""" +function gens(A::AbstractAssociativeAlgebra; thorough_search::Bool = false) + return _gens(A, Val(false); thorough_search = thorough_search) +end + +@doc raw""" + gens_with_data(A::AbstractAssociativeAlgebra; thorough_search::Bool = false) + -> Vector, Vector, Vector + +Given a $K$-algebra $A$, return a triple $(G, B, w)$ consisting of +- a subset $G$ of `basis(A)`, which generates $A$ as an algebra over $K$, +- a (new) basis $B$ and a vector `w::Vector{Tuple{Int, Int}}`, such that + `B[i] = prod(G[j]^k for (j, k) in w[i]`. + If `thorough_search` is `true`, the number of returned generators is possibly smaller. This will in general increase the runtime. It is not guaranteed that the number of generators is minimal in any case. + +# Examples + +```jldoctest +julia> A = matrix_algebra(QQ, 3); + +julia> G, B, w = gens_with_data(A; thorough_search = true); + +julia> B[1] == prod(G[i]^j for (i, j) in w[1]) +true +``` """ -function gens(A::AbstractAssociativeAlgebra, ::Val{return_full_basis} = Val(false); thorough_search::Bool = false) where return_full_basis +function gens_with_data(A::AbstractAssociativeAlgebra; thorough_search::Bool = false) + return _gens(A, Val(true); thorough_search = thorough_search) +end + +function _gens(A::AbstractAssociativeAlgebra, ::Val{return_full_basis} = Val(false); thorough_search::Bool = false) where return_full_basis d = dim(A) if !return_full_basis if isdefined(A, :gens) @@ -1229,6 +937,20 @@ end is_simple_known(A::AbstractAssociativeAlgebra) = A.is_simple != 0 +@doc raw""" + is_simple(A::AbstractAssociativeAlgebra) -> Bool + +Return whether the algebra $A$ is simple. + +# Examples + +```jldoctest +julia> A = matrix_algebra(QQ, 2); + +julia> is_simple(A) +true +``` +""" function is_simple(A::AbstractAssociativeAlgebra) if A.is_simple != 0 return A.is_simple == 1 @@ -1256,6 +978,11 @@ end # ################################################################################ +@doc raw""" + is_etale(A::AbstractAssociativeAlgebra) -> Bool + +Return whether the algebra $A$ is étale, that is, commutative and semisimple. +""" function is_etale(A::AbstractAssociativeAlgebra) return is_commutative(A) && is_semisimple(A) end diff --git a/src/AlgAss/AlgGrp.jl b/src/AlgAss/AlgGrp.jl index c65edf8731..d7fc8c297f 100644 --- a/src/AlgAss/AlgGrp.jl +++ b/src/AlgAss/AlgGrp.jl @@ -188,11 +188,6 @@ end # ################################################################################ -@doc raw""" - center(A::GroupAlgebra) -> StructureConstantAlgebra, AbsAlgAssMor - -Returns the center $C$ of $A$ and the inclusion $C \to A$. -""" function center(A::GroupAlgebra{T}) where {T} if isdefined(A, :center) return A.center::Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))} @@ -754,7 +749,7 @@ function __decompose_abelian_group_algebra(A::GroupAlgebra) idems = _central_primitive_idempotents_abelian(A) res = Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}}() for idem in idems - S, StoA = subalgebra(A, idem, true) + S, StoA = _subalgebra(A, idem, true) S.is_simple = 1 push!(res, (S, StoA)) end diff --git a/src/AlgAss/AlgMat.jl b/src/AlgAss/AlgMat.jl index e3fe809d4e..2e55c08fe1 100644 --- a/src/AlgAss/AlgMat.jl +++ b/src/AlgAss/AlgMat.jl @@ -595,11 +595,6 @@ end # ################################################################################ -@doc raw""" - center(A::MatAlgebra) -> StructureConstantAlgebra, AbsAlgAssMor - -Returns the center $C$ of $A$ and the inclusion $C \to A$. -""" function center(A::MatAlgebra{T, S}) where {T, S} if isdefined(A, :center) return A.center::Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))} diff --git a/src/AlgAss/Conversions.jl b/src/AlgAss/Conversions.jl new file mode 100644 index 0000000000..5bc4a01868 --- /dev/null +++ b/src/AlgAss/Conversions.jl @@ -0,0 +1,615 @@ +# Reduces the rows of M in `rows` modulo N in place. +# Assumes that N is in lowerleft HNF. +function reduce_rows_mod_hnf!(M::ZZMatrix, N::ZZMatrix, rows::Vector{Int}) + for i in rows + for j = ncols(M):-1:1 + if iszero(M[i, j]) + continue + end + + t = fdiv(M[i, j], N[j, j]) + for k = 1:j + M[i, k] = M[i, k] - t*N[j, k] + end + end + end + return M +end + +@doc raw""" + quo(O::AbsNumFieldOrder, I::AbsNumFieldOrderIdeal, p::Union{ Int, ZZRingElem }) + quo(O::AlgAssAbsOrd, I::AlgAssAbsOrdIdl, p::Union{ Int, ZZRingElem }) + -> StructureConstantAlgebra, AbsOrdToAlgAssMor + +Given an ideal $I$ such that $p \cdot O \subseteq I \subseteq O$ this function +constructs $O/I$ as an algebra over $\mathbb F_p$ together with the projection +map $O \to O/I$. +It is assumed that $p$ is prime. +""" +quo(O::Union{AbsNumFieldOrder, AlgAssAbsOrd}, I::Union{AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl}, p::IntegerUnion) = StructureConstantAlgebra(O, I, p) + +function StructureConstantAlgebra(O::Union{AbsNumFieldOrder, AlgAssAbsOrd}, I::Union{AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl}, p::IntegerUnion) + @assert order(I) === O + + n = degree(O) + bmatI = integral_basis_matrix_wrt(I, O, copy = false) + + basis_elts = Vector{Int}() + for i = 1:n + if is_coprime(bmatI[i, i], p) + continue + end + + push!(basis_elts, i) + end + + r = length(basis_elts) + Fp = GF(p, cached = false) + + if r == 0 + A = zero_algebra(Fp) + + local _image_zero + + let A = A + function _image_zero(a::Union{ AbsNumFieldOrderElem, AlgAssAbsOrdElem }) + return A() + end + end + + local _preimage_zero + + let O = O + function _preimage_zero(a::AssociativeAlgebraElem) + return O() + end + end + + OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image_zero, _preimage_zero) + return A, OtoA + end + + BO = basis(O, copy = false) + mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) + for i = 1:r + M = representation_matrix_mod(BO[basis_elts[i]], ZZRingElem(p)) + if r != degree(O) + M = reduce_rows_mod_hnf!(M, bmatI, basis_elts) + end + for j = 1:r + for k = 1:r + mult_table[i, j, k] = Fp(M[basis_elts[j], basis_elts[k]]) + end + end + end + + if isone(BO[1]) + one = zeros(Fp, r) + one[1] = Fp(1) + A = StructureConstantAlgebra(Fp, mult_table, one) + else + A = StructureConstantAlgebra(Fp, mult_table) + end + if is_commutative(O) + A.is_commutative = 1 + end + + local _image + + let I = I, A = A, basis_elts = basis_elts, Fp = Fp + function _image(a::Union{AbsNumFieldOrderElem, AlgAssAbsOrdElem}) + c = coordinates(mod(a, I), copy = false) + return A([ Fp(c[i]) for i in basis_elts ]) + end + end + + local _preimage + + temp = zero(O) + + let BO = BO, basis_elts = basis_elts, r = r, temp = temp + function _preimage(a::AssociativeAlgebraElem) + z = zero(O)::eltype(BO) + ca = coefficients(a, copy = false) + for i in 1:r + l = lift(ZZ, ca[i]) + addmul!(z, l, BO[basis_elts[i]], temp) + end + return z + #return sum(lift(coefficients(a, copy = false)[i])*BO[basis_elts[i]] for i = 1:r) + end + end + + OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image, _preimage) + + return A, OtoA +end + +# Requires M to be in lower left HNF +function reduce_vector_mod_hnf(v::ZZMatrix, M::ZZMatrix) + @assert ncols(v) == nrows(M) && nrows(M) == ncols(M) + + w = Vector{ZZRingElem}(undef, length(v)) + t = ZZRingElem() + for i in length(v):-1:1 + t = fdiv(v[1, i], M[i, i]) + for j in 1:i + w[j] = v[1, j] - t*M[i, j] + end + end + return w +end + +@doc raw""" + quo(I::AbsNumFieldOrderIdeal, J::AbsNumFieldOrderIdeal, p::Union{ Int, ZZRingElem }) + quo(I::AlgAssAbsOrdIdl, J::AlgAssAbsOrdIdl, p::Union{ Int, ZZRingElem }) + -> StructureConstantAlgebra, AbsOrdToAlgAssMor + +Given an ideal $J$ such that $p \cdot I \subseteq J \subseteq I$ this function +constructs $I/J$ as an algebra over $\mathbb F_p$ together with the projection +map $I \to I/J$. +It is assumed that $p$ is prime. +""" +quo(I::Union{ AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl }, J::Union{ AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl }, p::Union{ Integer, ZZRingElem }) = StructureConstantAlgebra(I, J, p) + +function StructureConstantAlgebra(I::Union{ AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl }, J::Union{AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl}, p::IntegerUnion) + @assert order(I) === order(J) + + O = order(I) + Oalgebra = _algebra(O) + + n = degree(O) + BmatJinI = hnf(basis_matrix(J, copy = false)*basis_mat_inv(I, copy = false), :lowerleft) + @assert isone(BmatJinI.den) "J is not a subset of I" + BmatJinI = BmatJinI.num + basis_elts = Vector{Int}() + for i = 1:n + if valuation(BmatJinI[i, i], p) == 0 + continue + end + + push!(basis_elts, i) + end + + r = length(basis_elts) + Fp = GF(p, cached = false) + + if r == 0 + A = zero_algebra(Fp) + + local _image_zero + + let A = A + function _image_zero(a::Union{ AbsNumFieldOrderElem, AlgAssAbsOrdElem }) + return A() + end + end + + local _preimage_zero + + let O = O + function _preimage_zero(a::AssociativeAlgebraElem) + return O() + end + end + + OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image_zero, _preimage_zero) + return A, OtoA + end + + BI = basis(I, copy = false) + BmatI = basis_matrix(I, copy = false) + BmatIinv = inv(BmatI) + + mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) + for i = 1:r + M = FakeFmpqMat(representation_matrix(_elem_in_algebra(BI[basis_elts[i]], copy = false))) + M = mul!(M, BmatI, M) + M = mul!(M, M, BmatIinv) + @assert M.den == 1 + M = M.num # M is now the representation matrix in the basis of I + if r != degree(O) + M = reduce_rows_mod_hnf!(M, BmatJinI, basis_elts) + end + for j = 1:r + for k = 1:r + mult_table[i, j, k] = Fp(M[basis_elts[j], basis_elts[k]]) + end + end + end + + A = StructureConstantAlgebra(Fp, mult_table) + if is_commutative(O) + A.is_commutative = 1 + end + + t = FakeFmpqMat(zero_matrix(FlintZZ, 1, n)) + + local _image + + let BmatJinI = BmatJinI, I = I, r = r, A = A, t = t, Fp = Fp + function _image(a::Union{AbsNumFieldOrderElem, AlgAssAbsOrdElem}) + elem_to_mat_row!(t.num, 1, t.den, _elem_in_algebra(a, copy = false)) + t = mul!(t, t, basis_mat_inv(I, copy = false)) + @assert isone(t.den) "Not an element of the domain" + c = reduce_vector_mod_hnf(t.num, BmatJinI) + return A([ Fp(c[i]) for i in basis_elts ]) + end + end + + local _preimage + + temppp = zero(Oalgebra) + + let BI = BI, basis_elts = basis_elts, r = r, Oalgebra = Oalgebra, temppp = temppp + function _preimage(a::AssociativeAlgebraElem) + acoords = map(x -> QQ(lift(ZZ, x)), coefficients(a, copy = false)) + z = zero(Oalgebra) + for i in 1:r + if is_zero(acoords[i]) + continue + end + temppp = mul!(temppp, acoords[i], BI[basis_elts[i]]) + z = add!(z, z, temppp) + end + _zz = O(z) + return _zz + end + end + + OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image, _preimage) + + return A, OtoA +end + +#= +Qx, x = QQ["x"]; +f = x^2 + 12*x - 92; +K, a = number_field(f, "a"); +OK = maximal_order(K); +Ky, y = K["y"]; +g = y^2 - 54*y - 73; +L, b = number_field(g, "b"); +OL = maximal_order(L); +p = prime_decomposition(OK, 2)[1][1] +=# + +# Assume that O is relative order over OK, I is an ideal of O and p is a prime +# ideal of OK with pO \subseteq I. O/I is an OK/p-algebra. +# +# The idea is to compute pseudo-basis of O and I respectively, for which the +# coefficient ideals have zero p-adic valuation. Then we can think in the +# localization at p and do as in the case of principal ideal domains. +@doc raw""" + quo(O::RelNumFieldOrder, I::RelNumFieldOrderIdeal, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) + quo(O::AlgAssRelOrd, I::AlgAssRelOrdIdl, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) + -> StructureConstantAlgebra, RelOrdToAlgAssMor + +Given an ideal $I$ such that $p \cdot O \subseteq I \subseteq O$ this function +constructs $O/I$ as an algebra over the finite field $R/p$, where $R$ is the +order of $p$, together with the projection map $O \to O/I$. +It is assumed that `R == base_ring(O)` and that $p$ is prime. +""" +quo(O::Union{ RelNumFieldOrder{T, S}, AlgAssRelOrd{T, S} }, I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}) where {T, S} = StructureConstantAlgebra(O, I, p) + +function StructureConstantAlgebra(O::Union{ RelNumFieldOrder{T, S}, AlgAssRelOrd{T, S} }, I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}, mF = residue_field(order(p), p)[2]) where {T, S} + + K = _algebra(O) + + new_basisO, new_basisI, new_bmatO, new_bmatI = coprime_bases(O, I, p) + new_bmatinvO = inv(new_bmatO) + + Fp = codomain(mF) + mmF = extend(mF, _base_ring(K)) + invmmF = pseudo_inv(mmF) + + basis_elts = Int[] + reducers = Int[] + + for i in 1:degree(O) + v = valuation(new_bmatI[i, i], p) + @assert v >= 0 + if v == 0 + push!(reducers, i) + else + push!(basis_elts, i) + end + end + + r = length(basis_elts) + + if r == 0 + A = zero_algebra(Fp) + + local _image_zero + + let A = A + function _image_zero(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) + return A() + end + end + + local _preimage_zero + + let O = O + function _preimage_zero(a::AssociativeAlgebraElem) + return O() + end + end + + OtoA = RelOrdToAlgAssMor(O, A, _image_zero, _preimage_zero) + return A, OtoA + end + + reverse!(reducers) + + tmp_matrix = zero_matrix(_base_ring(K), 1, degree(O)) + + function _coeff(c) + cfcs = coefficients(c, copy = false) + for i = 1:degree(O) + tmp_matrix[1, i] = cfcs[i] + end + return tmp_matrix*new_bmatinvO + end + + mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) + for i in 1:r + for j in 1:r + c = new_basisO[basis_elts[i]][1]*new_basisO[basis_elts[j]][1] + coeffs_c = _coeff(c) + + for k in reducers + d = -coeffs_c[k]//new_bmatI[k, k] + c = c + d*new_basisI[k][1] + end + coeffs_c = _coeff(c) + for k in 1:degree(O) + if !(k in basis_elts) + @assert iszero(coeffs_c[k]) + end + end + for k in 1:r + mult_table[i, j, k] = mmF(coeffs_c[basis_elts[k]]) + end + end + end + + if isone(new_basisO[basis_elts[1]][1]) + one = zeros(Fp, length(basis_elts)) + one[1] = Fp(1) + A = StructureConstantAlgebra(Fp, mult_table, one) + else + A = StructureConstantAlgebra(Fp, mult_table) + end + if is_commutative(O) + A.is_commutative = 1 + end + + local _image + + let A = A, O = O + function _image(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) + c = _elem_in_algebra(a, copy = false) + coeffs_c = _coeff(c) + for k in reducers + d = -coeffs_c[k]//new_bmatI[k, k] + c = c + d*new_basisI[k][1] + end + coeffs_c = _coeff(c) + for k in 1:degree(O) + if !(k in basis_elts) + @assert iszero(coeffs_c[k]) + end + end + b = A() + for k in 1:r + b.coeffs[k] = mmF(coeffs_c[basis_elts[k]]) + end + return b + end + end + + lifted_basis_of_A = [] + + for i in basis_elts + c = coprime_to(new_basisO[i][2], p) + b = invmmF(inv(mmF(c)))*c*new_basisO[i][1] + @assert b in O + push!(lifted_basis_of_A, b) + end + + local _preimage + let lifted_basis_of_A = lifted_basis_of_A, O = O, invmmF = invmmF + function _preimage(v::AssociativeAlgebraElem) + return O(sum((invmmF(v.coeffs[i]))*lifted_basis_of_A[i] for i in 1:r)) + end + end + + OtoA = RelOrdToAlgAssMor(O, A, _image, _preimage) + + return A, OtoA +end + +@doc raw""" + quo(I::RelNumFieldOrderIdeal, J::RelNumFieldOrderIdeal, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) + quo(I::AlgAssRelOrdIdl, J::AlgAssRelOrdIdl, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) + -> StructureConstantAlgebra, RelOrdToAlgAssMor + +Given an ideal $J$ such that $p \cdot I \subseteq J \subseteq I$ this function +constructs $I/J$ as an algebra over the finite field $R/p$, where $R$ is the +order of $p$, together with the projection map $I \to I/J$. +It is assumed that `order(I) === order(J)` and in particular both should be +defined. Further, it should hold `R == base_ring(order(I))` and $p$ should be +prime. +""" +quo(I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, J::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}, mF = residue_field(order(p), p)[2]) where {T, S} = StructureConstantAlgebra(I, J, p, mF) + +function StructureConstantAlgebra(I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, J::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}, mF = residue_field(order(p), p)[2]) where {T, S} + @assert _algebra(I) === _algebra(J) + @assert order(I) === order(J) + + O = order(I) + K = _algebra(I) + new_basisI, new_basisJ, new_bmatI, new_bmatJinI = coprime_bases(I, J, p) + bmatinvI = inv(new_bmatI) + + Fp = codomain(mF) + mmF = extend(mF, _base_ring(K)) + invmmF = pseudo_inv(mmF) + + basis_elts = Int[] + reducers = Int[] + + for i in 1:degree(O) + v = valuation(new_bmatJinI[i, i], p) + @assert v >= 0 + if v == 0 + push!(reducers, i) + else + push!(basis_elts, i) + end + end + + r = length(basis_elts) + + if r == 0 + A = zero_algebra(Fp) + + local _image_zero + + let A = A + function _image_zero(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) + return A() + end + end + + local _preimage_zero + + let O = O + function _preimage_zero(a::AssociativeAlgebraElem) + return O() + end + end + + OtoA = RelOrdToAlgAssMor(O, A, _image_zero, _preimage_zero) + return A, OtoA + end + + reverse!(reducers) + + tmp_matrix = zero_matrix(_base_ring(K), 1, degree(O)) + + function _coeff(c) + for i = 1:degree(O) + tmp_matrix[1, i] = coefficients(c, copy = false)[i] + end + return tmp_matrix*bmatinvI + end + + mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) + + for i in 1:r + for j in 1:r + c = new_basisI[basis_elts[i]][1]*new_basisI[basis_elts[j]][1] + coeffs = _coeff(c) + + for k in reducers + d = -coeffs[k]//new_bmatJinI[k, k] + c = c + d * new_basisJ[k][1] + end + coeffs = _coeff(c) + for k in 1:degree(O) + if !(k in basis_elts) + @assert iszero(coeffs[k]) + end + end + for k in 1:r + mult_table[i, j, k] = mmF(coeffs[basis_elts[k]]) + end + end + end + + if isone(new_basisI[basis_elts[1]][1]) + one = zeros(Fp, length(basis_elts)) + one[1] = Fp(1) + A = StructureConstantAlgebra(Fp, mult_table, one) + else + A = StructureConstantAlgebra(Fp, mult_table) + end + if is_commutative(O) + A.is_commutative = 1 + end + + local _image + + let O = O, new_bmatJinI = new_bmatJinI, A = A + function _image(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) + c = _elem_in_algebra(a, copy = false) + coeffs = _coeff(c) + for k in reducers + d = -coeffs[k]//new_bmatJinI[k, k] + c = c + d*new_basisJ[k][1] + end + coeffs = _coeff(c) + for k in 1:degree(O) + if !(k in basis_elts) + @assert iszero(coeffs[k]) + end + end + b = A() + for k in 1:r + b.coeffs[k] = mmF(coeffs[basis_elts[k]]) + end + return b + end + end + + + lifted_basis_of_A = [] + + for i in basis_elts + c = coprime_to(new_basisI[i][2], p) + b = invmmF(inv(mmF(c)))*c*new_basisI[i][1] + @assert O(b) in I + push!(lifted_basis_of_A, b) + end + + local _preimage + + let O = O, invmmF = invmmF, lifted_basis_of_A = lifted_basis_of_A + function _preimage(v::AssociativeAlgebraElem) + return O(sum((invmmF(v.coeffs[i])) * lifted_basis_of_A[i] for i in 1:r)) + end + end + + OtoA = RelOrdToAlgAssMor(O, A, _image, _preimage) + + return A, OtoA +end + +function StructureConstantAlgebra(A::Generic.MatRing{T}) where { T <: FieldElem } + n = A.n + K = base_ring(A) + n2 = n^2 + # We use the matrices M_{ij} with a 1 at row i and column j and zeros everywhere else as the basis for A. + # We sort "column major", so A[i + (j - 1)*n] corresponds to the matrix M_{ij}. + # M_{ik}*M_{lj} = 0, if k != l, and M_{ik}*M_{kj} = M_{ij} + mult_table = zeros(K, n2, n2, n2) + oneK = one(K) + for j = 0:n:(n2 - n) + for k = 1:n + kn = (k - 1)*n + for i = 1:n + mult_table[i + kn, k + j, i + j] = oneK + end + end + end + oneA = zeros(K, n2) + for i = 1:n + oneA[i + (i - 1)*n] = oneK + end + A = StructureConstantAlgebra(K, mult_table, oneA) + A.is_commutative = ( n == 1 ? 1 : 2 ) + return A +end diff --git a/src/AlgAss/Decompose.jl b/src/AlgAss/Decompose.jl new file mode 100644 index 0000000000..c24f9479c5 --- /dev/null +++ b/src/AlgAss/Decompose.jl @@ -0,0 +1,457 @@ +################################################################################ +# +# Decomposition +# +################################################################################ + +# Assume that A is a commutative algebra over a finite field of cardinality q. +# This functions computes a basis for ker(x -> x^q). +function kernel_of_frobenius(A::AbstractAssociativeAlgebra) + F = base_ring(A) + q = order(F) + + b = A() + B = zero_matrix(F, dim(A), dim(A)) + for i = 1:dim(A) + b.coeffs[i] = one(F) + if i > 1 + b.coeffs[i - 1] = zero(F) + end + c = b^q - b + for j = 1:dim(A) + B[j, i] = c.coeffs[j] + end + end + + V = kernel(B, side = :right) + return [ A(V[:, i]) for i in 1:ncols(V) ] +end + +@doc raw""" + decompose(A::AbstractAssociativeAlgebra) -> Array{Tuple{StructureConstantAlgebra, AbsAlgAssMor}} + +Given a semisimple algebra $A$, return a decomposition of $A$ as a direct sum +of simple algebras and maps from these +components to $A$. +""" +function decompose(A::StructureConstantAlgebra{T}) where {T} + if isdefined(A, :decomposition) + return A.decomposition::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} + end + + if is_simple_known(A) && A.is_simple == 1 + B, mB = StructureConstantAlgebra(A) + return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] + end + + res = _decompose(A) + A.decomposition = res + return res +end + +# Generic function for everything besides StructureConstantAlgebra +function decompose(A::AbstractAssociativeAlgebra{T}) where T + return __decompose(A) +end + +function __decompose(A::AbstractAssociativeAlgebra{T}) where {T} + if isdefined(A, :decomposition) + return A.decomposition::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} + end + + B, mB = StructureConstantAlgebra(A) + + if is_simple_known(A) && A.is_simple == 1 + return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[ (B, mB) ] + end + + D = _decompose(B)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, StructureConstantAlgebra{T})}} + res = Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[] + for (S, mS) in D + mD = compose_and_squash(mB, mS) + push!(res, (S, mD)) + end + A.decomposition = res + Z, ZtoB = center(B) + if dim(Z) != dim(B) + if isdefined(A, :center) + @assert A.center[1] === Z + end + A.center = (Z, compose_and_squash(mB, ZtoB)) + end + return res +end + +function _decompose(A::AbstractAssociativeAlgebra{T}) where {T} + @assert _issemisimple(A) != 2 "Algebra is not semisimple" + if is_commutative(A) + return _dec_com(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} + else + return _dec_via_center(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} + end +end + +function _dec_via_center(A::S) where {T, S <: AbstractAssociativeAlgebra{T}} + ZA, mZA = center(A) + Algs = _dec_com(ZA) + ZA.decomposition = Algs + res = Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, S)}[ _subalgebra(A, mZA(BtoZA(one(B))), true) for (B, BtoZA) in Algs] + for i in 1:length(res) + res[i][1].is_simple = 1 + B, BtoZA = Algs[i] # B is the centre of res[i][1] + # Build a map from B to res[i][1] via B -> ZA -> A -> res[i][1] + M = zero_matrix(base_ring(A), dim(B), dim(res[i][1])) + for j = 1:dim(B) + t = mZA(BtoZA(B[j])) + s = res[i][2]\t + elem_to_mat_row!(M, j, s) + end + if dim(res[i][1]) != dim(B) + res[i][1].center = (B, hom(B, res[i][1], M)) + else + # res[i][1] is commutative, so we do not cache the centre + iM = inv(M) + BtoA = hom(B, A, M*res[i][2].mat, res[i][2].imat*iM) + res[i] = (B, BtoA) + end + end + A.decomposition = res + return res +end + +function _dec_com(A::AbstractAssociativeAlgebra{T}) where {T} + v = get_attribute(A, :central_idempotents) + if v !== nothing + w = v::Vector{elem_type(A)} + return _dec_com_given_idempotents(A, w) + end + + if characteristic(base_ring(A)) > 0 + return _dec_com_finite(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} + else + return _dec_com_gen(A)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}} + end +end + +function _dec_com_given_idempotents(A::AbstractAssociativeAlgebra{T}, v::Vector) where {T} + dec = Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[] + for idem in v + S, StoA = _subalgebra(A, idem, true) + push!(dec, (S, StoA)) + end + return dec +end + +function _dec_com_gen(A::AbstractAssociativeAlgebra{T}) where {T <: FieldElem} + if dim(A) == 0 + # The zero-dimensional algebra is the zero ring, which is semisimple, but not simple + # It has *no* simple components. + A.is_simple = -1 + return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[] + end + + if dim(A) == 1 + A.is_simple = 1 + B, mB = StructureConstantAlgebra(A) + return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] + end + + F = base_ring(A) + + k = dim(A) + + V = elem_type(A)[A[i] for i in 1:k] + + while true + c = elem_type(F)[ rand(F, -10:10) for i = 1:k ] + a = dot(c, V) + f = minpoly(a) + + if degree(f) < 2 + continue + end + if is_irreducible(f) + if degree(f) == dim(A) + A.is_simple = 1 + B, mB = StructureConstantAlgebra(A) + return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] + end + continue + end + + @assert is_squarefree(f) + + fac = factor(f) + R = parent(f) + factors = Vector{elem_type(R)}() + for ff in keys(fac.fac) + push!(factors, ff) + end + sols = Vector{elem_type(R)}() + right_side = elem_type(R)[ R() for i = 1:length(factors) ] + max_deg = 0 + for i = 1:length(factors) + right_side[i] = R(1) + if i != 1 + right_side[i - 1] = R(0) + end + s = crt(right_side, factors) + push!(sols, s) + max_deg = max(max_deg, degree(s)) + end + x = one(A) + powers = Vector{elem_type(A)}() + for i = 1:max_deg + 1 + push!(powers, x) + x *= a + end + idems = Vector{elem_type(A)}() + for s in sols + idem = A() + for i = 0:degree(s) + idem += coeff(s, i)*powers[i + 1] + end + push!(idems, idem) + end + + A.is_simple = 2 + + res = Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}}() + for idem in idems + S, StoA = _subalgebra(A, idem, true) + decS = _dec_com_gen(S)::Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(S))}} + for (B, BtoS) in decS + BtoA = compose_and_squash(StoA, BtoS) + push!(res, (B, BtoA)) + end + end + return res + end +end + +function _dec_com_finite(A::AbstractAssociativeAlgebra{T}) where T + if dim(A) == 1 + A.is_simple = 1 + B, mB = StructureConstantAlgebra(A) + return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] + end + + F = base_ring(A) + @assert !iszero(characteristic(F)) + V = kernel_of_frobenius(A) + k = length(V) + + if k == 1 + A.is_simple = 1 + B, mB = StructureConstantAlgebra(A) + return Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}[(B, mB)] + end + + A.is_simple = 2 + c = elem_type(F)[ rand(F) for i = 1:k ] + M = zero_matrix(F, dim(A), dim(A)) + a = dot(c, V) + representation_matrix!(a, M) + f = minpoly(M) + while degree(f) < 2 + for i = 1:length(c) + c[i] = rand(F) + end + a = dot(c, V) + zero!(M) + representation_matrix!(a, M) + f = minpoly(M) + end + + #@assert is_squarefree(f) + fac = factor(f) + R = parent(f) + factorss = collect(keys(fac.fac)) + sols = Vector{typeof(f)}(undef, length(factorss)) + right_side = typeof(f)[ zero(R) for i = 1:length(factorss) ] + max_deg = 0 + for i = 1:length(factorss) + right_side[i] = one(R) + if 1 != i + right_side[i - 1] = zero(R) + end + sols[i] = crt(right_side, factorss) + max_deg = max(max_deg, degree(sols[i])) + end + powers = Vector{elem_type(A)}(undef, max_deg+1) + powers[1] = one(A) + powers[2] = a + x = a + for i = 3:max_deg + 1 + x *= a + powers[i] = x + end + + idems = Vector{elem_type(A)}() + for s in sols + idem = A() + for i = 0:degree(s) + idem += coeff(s, i)*powers[i + 1] + end + push!(idems, idem) + end + + res = Vector{Tuple{StructureConstantAlgebra{T}, morphism_type(StructureConstantAlgebra{T}, typeof(A))}}() + for idem in idems + S, StoA = _subalgebra(A, idem, true) + decS = _dec_com_finite(S) + for (B, BtoS) in decS + BtoA = compose_and_squash(StoA, BtoS) + push!(res, (B, BtoA)) + end + end + return res +end + +################################################################################ +# +# Decomposition as number fields +# +################################################################################ + +@doc raw""" + components(::Type{Field}, A::AbstractAssociativeAlgebra) + -> Vector{Tuple{Field, Morphism}} + +Given an étale algebra $A$, return the simple components of $A$ +as fields $K$ together with the projection $A \to K$. +""" +function components(::Type{Field}, A::AbstractAssociativeAlgebra) + @assert is_commutative(A) + return as_number_fields(A) +end + +@doc raw""" + component(::Type{Field}, A::AbstractAssociativeAlgebra, i::Int) + -> Vector{Tuple{Field, Morphism}} + +Given an étale algebra $A$ and index $i$, return the $i$-th simple components +of $A$ as a field $K$ together with the projection $A \to K$. +""" +function component(::Type{Field}, A::AbstractAssociativeAlgebra, i::Int) + nf = as_number_fields(A) + return nf[i] +end + +@doc raw""" + as_number_fields(A::AbstractAssociativeAlgebra{QQFieldElem}) + -> Vector{Tuple{AbsSimpleNumField, AbsAlgAssToNfAbsMor}} + +Given a commutative algebra $A$ over $\mathbb Q$, this function returns a +decomposition of $A$ as direct sum of number fields and maps from $A$ to +these fields. +""" +function as_number_fields(A::AbstractAssociativeAlgebra{T}) where {T} + return __as_number_fields(A) +end + +function __as_number_fields(A::AbstractAssociativeAlgebra{T}; use_maximal_order::Bool = true) where {T} + if isdefined(A, :maps_to_numberfields) + NF = A.maps_to_numberfields::Vector{Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(A)}} + return NF + end + + result = _as_number_fields(A, use_maximal_order = use_maximal_order) + @assert all(domain(AtoK) === A for (_, AtoK) in result) + A.maps_to_numberfields = result + return result +end + +_ext_type(::Type{QQFieldElem}) = AbsSimpleNumField + +_ext_type(::Type{AbsSimpleNumFieldElem}) = RelSimpleNumField{AbsSimpleNumFieldElem} + +function _as_number_fields(A::AbstractAssociativeAlgebra{T}; use_maximal_order::Bool = true) where {T} + d = dim(A) + + Adec = decompose(A) + + fields_not_cached = false + for i = 1:length(Adec) + if !isdefined(Adec[i][1], :maps_to_numberfields) + fields_not_cached = true + end + end + + if fields_not_cached && T === QQFieldElem && use_maximal_order + # Compute a LLL reduced basis of the maximal order of A to find "small" + # polynomials for the number fields. + OA = maximal_order(A) + L = lll(basis_matrix(FakeFmpqMat, OA, copy = false).num) + n = basis_matrix(FakeFmpqMat, OA, copy = false).den + basis_lll = elem_type(A)[ elem_from_mat_row(A, L, i, n) for i = 1:d ] + elseif fields_not_cached + basis_lll = basis(A) + end + + KK = base_ring(A) + + M = zero_matrix(KK, 0, d) + matrices = Vector{dense_matrix_type(T)}() + fields = Vector{_ext_type(T)}() + for i = 1:length(Adec) + # For each small algebra construct a number field and the isomorphism + B, BtoA = Adec[i] + dB = dim(B) + if !isdefined(B, :maps_to_numberfields) + local K, BtoK + found_field = false # Only for debugging + for j = 1:d + t = BtoA\basis_lll[j] + mint = minpoly(t) + if degree(mint) == dB + found_field = true + K, BtoK = _as_field_with_isomorphism(B, t, mint) + B.maps_to_numberfields = Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(B)}[(K, BtoK)] + push!(fields, K) + break + end + end + @assert found_field "This should not happen..." + else + K, BtoK = B.maps_to_numberfields[1]::Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(B)} + push!(fields, K) + end + + # TODO: We can do a short cut, but the map must be BtoK \circ inv(BtoA) + #if length(Adec) == 1 + # res = Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(A)}[(K, BtoK)] + # A.maps_to_numberfields = res + # return res + #end + + # Construct the map from K to A + N = zero_matrix(KK, degree(K), d) + for j = 1:degree(K) + t = BtoA(BtoK\basis(K)[j]) + elem_to_mat_row!(N, j, t) + end + push!(matrices, N) + M = vcat(M, N) + end + @assert nrows(M) == d + + invM = inv(M) + matrices2 = Vector{dense_matrix_type(T)}(undef, length(matrices)) + offset = 1 + for i = 1:length(matrices) + r = nrows(matrices[i]) + N = sub(invM, 1:d, offset:(offset + r - 1)) + matrices2[i] = N + offset += r + end + + result = Vector{Tuple{_ext_type(T), _abs_alg_ass_to_nf_abs_mor_type(A)}}() + for i = 1:length(fields) + push!(result, (fields[i], AbsAlgAssToNfAbsMor(A, fields[i], matrices2[i], matrices[i]))) + end + + return result +end + + diff --git a/src/AlgAss/Elem.jl b/src/AlgAss/Elem.jl index 9f4a572fdb..d26f167d72 100644 --- a/src/AlgAss/Elem.jl +++ b/src/AlgAss/Elem.jl @@ -114,6 +114,24 @@ function is_integral(a::AbstractAssociativeAlgebraElem) return true end +################################################################################ +# +# Idempotent +# +################################################################################ + +function is_idempotent(a::AbstractAssociativeAlgebraElem) + return a^2 == a +end + +function is_central(a::AbstractAssociativeAlgebraElem) + return all(a * b == b * a for b in basis(parent(a))) +end + +function is_central_idempotent(a::AbstractAssociativeAlgebraElem) + return is_idempotent(a) && is_central(a) +end + ################################################################################ # # Unary operations @@ -593,12 +611,17 @@ end (A::GroupAlgebra{T, S, R})() where {T, S, R} = GroupAlgebraElem{T, typeof(A)}(A) -function (A::StructureConstantAlgebra{T})(c::Vector{T}; copy::Bool = true) where {T} +function (A::StructureConstantAlgebra{T})(c::Vector; copy::Bool = true) where {T} length(c) != dim(A) && error("Dimensions don't match.") + if eltype(c) === T + _c = c + else + _c = convert(Vector{T}, map(base_ring(A), c))::Vector{T} + end if copy - return AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}(A, deepcopy(c)) + return AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}(A, deepcopy(_c)) else - return AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}(A, c) + return AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}(A, _c) end end diff --git a/src/AlgAss/AlgAss.jl b/src/AlgAss/StructureConstantAlgebra.jl similarity index 53% rename from src/AlgAss/AlgAss.jl rename to src/AlgAss/StructureConstantAlgebra.jl index 81f8b27d94..9af7f8a4cd 100644 --- a/src/AlgAss/AlgAss.jl +++ b/src/AlgAss/StructureConstantAlgebra.jl @@ -2,160 +2,98 @@ add_assertion_scope(:StructureConstantAlgebra) ################################################################################ # -# Basic field access +# Derived types # ################################################################################ -function denominator_of_multiplication_table(A::StructureConstantAlgebra{QQFieldElem}) - get_attribute!(A, :denominator_of_multiplication_table) do - den = one(ZZ) - mt = multiplication_table(A) - d = degree(A) - for i in 1:d - for j in 1:d - for k in 1:d - den = lcm!(den, den, denominator(mt[i, j, k])) - end - end - end - return den - end::ZZRingElem -end - -base_ring(A::StructureConstantAlgebra{T}) where {T} = A.base_ring::parent_type(T) - -base_ring_type(::Type{StructureConstantAlgebra{T}}) where {T} = parent_type(T) - -has_one(A::StructureConstantAlgebra) = A.has_one - -iszero(A::StructureConstantAlgebra) = A.iszero - -function Generic.dim(A::StructureConstantAlgebra) - if iszero(A) - return 0 - end - return size(multiplication_table(A, copy = false), 1) -end - -degree(A::StructureConstantAlgebra) = dim(A) - elem_type(::Type{StructureConstantAlgebra{T}}) where {T} = AssociativeAlgebraElem{T, StructureConstantAlgebra{T}} -order_type(::StructureConstantAlgebra{QQFieldElem}) = AlgAssAbsOrd{StructureConstantAlgebra{QQFieldElem}, elem_type(StructureConstantAlgebra{QQFieldElem})} +# Definitions for orders order_type(::Type{StructureConstantAlgebra{QQFieldElem}}) = AlgAssAbsOrd{StructureConstantAlgebra{QQFieldElem}, elem_type(StructureConstantAlgebra{QQFieldElem})} +order_type(::Type{StructureConstantAlgebra{T}}) where {T <: NumFieldElem} = AlgAssRelOrd{T, fractional_ideal_type(order_type(parent_type(T)))} +order_type(A::StructureConstantAlgebra) = order_type(typeof(A)) -order_type(::StructureConstantAlgebra{T}) where { T <: NumFieldElem } = AlgAssRelOrd{T, fractional_ideal_type(order_type(parent_type(T)))} -order_type(::Type{StructureConstantAlgebra{T}}) where { T <: NumFieldElem } = AlgAssRelOrd{T, fractional_ideal_type(order_type(parent_type(T)))} +################################################################################ +# +# Constructors +# +################################################################################ @doc raw""" - multiplication_table(A::StructureConstantAlgebra; copy::Bool = true) -> Array{RingElem, 3} + structure_constant_algebra(R::Ring, sctable::Array{_, 3}; one::Vector = nothing, + check::Bool = true) -Given an algebra $A$ this function returns the multiplication table of $A$: -If the function returns $M$ and the basis of $A$ is $e_1,\dots, e_n$ then -it holds $e_i \cdot e_j = \sum_k M[i, j, k] \cdot e_k$. +Given an array with dimensions $(d, d, d)$ and a ring $R$, return the +$d$-dimensional structure constant algebra over $R$. The basis `e` of $R$ +satisfies `e[i] * e[j] = sum(sctable[i,j,k] * e[k] for k in 1:d)`. + +Unless `check = false`, this includes (time consuming) associativity and +distributivity checks. If `one` is given, record the element with the +supplied coordinate vector as the one element of the algebra. + +# Examples + +```jldoctest +julia> associative_algebra(QQ, reshape([1, 0, 0, 2, 0, 1, 1, 0], (2, 2, 2))) +Associative algebra of dimension 2 over Rational field +``` """ -function multiplication_table(A::StructureConstantAlgebra; copy::Bool = true) - if copy - return deepcopy(A.mult_table) - else - return A.mult_table - end +function structure_constant_algebra(R::Ring, sctable::Array{<:Any, 3}; one = nothing, + check::Bool = true) + return associative_algebra(R, sctable; one = one, check = check) end -################################################################################ -# -# Commutativity -# -################################################################################ - -is_commutative_known(A::StructureConstantAlgebra) = (A.is_commutative != 0) +structure_constant_algebra(R::Ring, mult_table::Array{T, 3}, one::Vector{T}; check::Bool = true) where T = StructureConstantAlgebra(R, mult_table, one; check) @doc raw""" - is_commutative(A::StructureConstantAlgebra) -> Bool + structure_constant_algebra(f::PolyRingElem) + +Given a polynomial $f$ over a commutative ring $R$, return the quotient ring +$R[X]/(f)$ as an algebra. + +# Examples -Returns `true` if $A$ is a commutative ring and `false` otherwise. +```jldoctest +julia> structure_constant_algebra(f) +Associative algebra of dimension 2 over Rational field +``` """ -function is_commutative(A::StructureConstantAlgebra) - if is_commutative_known(A) - return A.is_commutative == 1 - end - for i = 1:dim(A) - for j = i + 1:dim(A) - if multiplication_table(A, copy = false)[i, j, :] != multiplication_table(A, copy = false)[j, i, :] - A.is_commutative = 2 - return false - end - end - end - A.is_commutative = 1 - return true -end +structure_constant_algebra(f::PolyRingElem) = StructureConstantAlgebra(f) -################################################################################ -# -# Construction -# -################################################################################ +function associative_algebra(R::Ring, sctable::Array{<:Any, 3}; one = nothing, + check::Bool = true) + @req all(isequal(size(sctable, 1)), size(sctable)) "Multiplication must have dimensions have same length" + d = size(sctable, 1) + if one !== nothing + @req length(one) == size(sctable, 1) "Length ($(length(one))) of vector for one element must be $(d)" + end -# This only works if base_ring(A) is a field (probably) -# Returns (true, one) if there is a one and (false, something) if not. -function find_one(A::StructureConstantAlgebra) - if iszero(A) - return true, elem_type(base_ring(A))[] + if (d > 0 && parent(d[1, 1, 1]) === R) || + (d == 0 && eltype(sctable) === elem_type(R)) + _sctable = sctable::Array{elem_type(R), 3} + else + _sctable = convert(Array{elem_type(R), 3}, map(R, sctable))::Array{elem_type(R), 3} end - n = dim(A) - M = zero_matrix(base_ring(A), n^2, n) - c = zero_matrix(base_ring(A), n^2, 1) - for k = 1:n - kn = (k - 1)*n - c[kn + k, 1] = base_ring(A)(1) - for i = 1:n - for j = 1:n - M[i + kn, j] = deepcopy(multiplication_table(A, copy = false)[j, k, i]) + + if one isa Vector + if length(one) > 0 + if parent(one[1]) === R + _one = one + else + _one = map(R, one)::Vector{elem_type(R)} end + else + _one = elem_type(R)[]::Vector{elem_type(R)} end + return StructureConstantAlgebra(R, _sctable, _one; check) end - Mc = hcat(M, c) - rref!(Mc) - if iszero(Mc[n, n]) - return false, zeros(base_ring(A), n) - end - if n != 1 && !iszero(Mc[n + 1, n + 1]) - return false, zeros(base_ring(A), n) - end - cc = _solve_ut(sub(Mc, 1:n, 1:n), sub(Mc, 1:n, (n + 1):(n + 1))) - one = elem_type(base_ring(A))[ cc[i, 1] for i = 1:n ] - return true, one + @assert one isa Nothing + return StructureConstantAlgebra(R, _sctable; check) end -raw""" - zero_algebra(R::Ring) -> StructureConstantAlgebra - -Return the zero ring as an associative $R$-algebra. -""" -function zero_algebra(R::Ring) - A = StructureConstantAlgebra{elem_type(R)}(R) - A.iszero = true - A.is_commutative = 1 - A.has_one = true - A.one = elem_type(R)[] - A.mult_table = Array{elem_type(R), 3}(undef, 0, 0, 0) - return A -end - -raw""" - associative_algebra(R::Ring, mult_table::Array{T, 3}[, one::Vector{T}]; check::Bool = true) where T - - Associative Algebra over `R` with generators $e_1,\dots,e_d$ where `size(mult_table) == (d, d, d)` and $e_ie_j$ = `sum(mult_table[i,j,k]*e[k] for k in 1:d)`. - Unless `check = false`, this includes (time consuming) associativity and distributivity checks. - If `one` is given, record the element with the according coefficient vector as one element of the algebra. -""" -associative_algebra(R::Ring, mult_table::Array{<:Any, 3}; check::Bool = true) = StructureConstantAlgebra(R, mult_table; check) associative_algebra(R::Ring, mult_table::Array{T, 3}, one::Vector{T}; check::Bool = true) where T = StructureConstantAlgebra(R, mult_table, one; check) function StructureConstantAlgebra(R::Ring, mult_table::Array{T, 3}, one::Vector{T}; check::Bool = get_assertion_level(:StructureConstantAlgebra) > 0) where {T} - @req all(isequal(size(mult_table, 1)), size(mult_table)) "Multiplication must have dimensions have same length" - if size(mult_table, 1) == 0 return zero_algebra(R) end @@ -204,668 +142,204 @@ function StructureConstantAlgebra(R::Ring, d::Int, arr::Vector{T}) where {T} return StructureConstantAlgebra(R, mult_table) end -raw""" - associative_algebra(f::PolyRingElem) +@doc raw""" + structure_constant_algebra(K::SimpleNumField) -> StructureConstantAlgebra, Map + +Given a number field $L/K$, return $L$ as a $K$-algebra $A$ together with a +$K$-linear map $A \to L$. -Associative algebra $R[x]/f$. +# Examples + +```jldoctest +julia> L, = quadratic_field(2); + +julia> structure_constant_algebra(L) +(Associative algebra of dimension 2 over Rational field, Map: associative algebra of dimension 2 over QQ -> L) +``` """ -associative_algebra(f::PolyRingElem) = StructureConstantAlgebra(f) -function StructureConstantAlgebra(f::PolyRingElem) - R = base_ring(parent(f)) - n = degree(f) - Rx = parent(f) - x = gen(Rx) - B = Vector{elem_type(Rx)}(undef, 2*n - 1) - B[1] = Rx(1) - for i = 2:2*n - 1 - B[i] = mod(B[i - 1]*x, f) - end - mult_table = Array{elem_type(R), 3}(undef, n, n, n) - for i = 1:n - for j = i:n - for k = 1:n - mult_table[i, j, k] = coeff(B[i + j - 1], k - 1) - mult_table[j, i, k] = coeff(B[i + j - 1], k - 1) - end - end - end - one = map(R, zeros(Int, n)) - one[1] = R(1) - A = StructureConstantAlgebra(R, mult_table, one) - A.is_commutative = 1 - return A +function structure_constant_algebra(K::SimpleNumField) + StructureConstantAlgebra(K) end -function StructureConstantAlgebra(K::AbsSimpleNumField) - A = StructureConstantAlgebra(K.pol) - m = AbsAlgAssToNfAbsMor(A, K, identity_matrix(FlintQQ, dim(A)), identity_matrix(FlintQQ, dim(A))) +function StructureConstantAlgebra(K::SimpleNumField) + A = StructureConstantAlgebra(defining_polynomial(K)) + k = base_field(K) + m = AbsAlgAssToNfAbsMor(A, K, identity_matrix(k, dim(A)), identity_matrix(k, dim(A))) A.maps_to_numberfields = [ (K, m) ] return A, m end -# Reduces the rows of M in `rows` modulo N in place. -# Assumes that N is in lowerleft HNF. -function reduce_rows_mod_hnf!(M::ZZMatrix, N::ZZMatrix, rows::Vector{Int}) - for i in rows - for j = ncols(M):-1:1 - if iszero(M[i, j]) - continue - end - - t = fdiv(M[i, j], N[j, j]) - for k = 1:j - M[i, k] = M[i, k] - t*N[j, k] - end - end - end - return M -end - -function addmul!(a::AlgAssAbsOrdElem, b::ZZRingElem, c::AlgAssAbsOrdElem, d = parent(a)()) - mul!(d, b, c) - return add!(a, a, d) -end +################################################################################ +# +# Zero algebra +# +################################################################################ -function addmul!(a::AbsNumFieldOrderElem, b::ZZRingElem, c::AbsNumFieldOrderElem, d = parent(a)()) - mul!(d, b, c) - return add!(a, a, d) +function zero_algebra(R::Ring) + A = StructureConstantAlgebra{elem_type(R)}(R) + A.iszero = true + A.is_commutative = 1 + A.has_one = true + A.one = elem_type(R)[] + A.mult_table = Array{elem_type(R), 3}(undef, 0, 0, 0) + return A end -@doc raw""" - quo(O::AbsNumFieldOrder, I::AbsNumFieldOrderIdeal, p::Union{ Int, ZZRingElem }) - quo(O::AlgAssAbsOrd, I::AlgAssAbsOrdIdl, p::Union{ Int, ZZRingElem }) - -> StructureConstantAlgebra, AbsOrdToAlgAssMor - -Given an ideal $I$ such that $p \cdot O \subseteq I \subseteq O$ this function -constructs $O/I$ as an algebra over $\mathbb F_p$ together with the projection -map $O \to O/I$. -It is assumed that $p$ is prime. -""" -quo(O::Union{AbsNumFieldOrder, AlgAssAbsOrd}, I::Union{AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl}, p::IntegerUnion) = StructureConstantAlgebra(O, I, p) - -function StructureConstantAlgebra(O::Union{AbsNumFieldOrder, AlgAssAbsOrd}, I::Union{AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl}, p::IntegerUnion) - @assert order(I) === O - - n = degree(O) - bmatI = integral_basis_matrix_wrt(I, O, copy = false) - - basis_elts = Vector{Int}() - for i = 1:n - if is_coprime(bmatI[i, i], p) - continue - end - - push!(basis_elts, i) - end - - r = length(basis_elts) - Fp = GF(p, cached = false) - - if r == 0 - A = zero_algebra(Fp) - - local _image_zero - - let A = A - function _image_zero(a::Union{ AbsNumFieldOrderElem, AlgAssAbsOrdElem }) - return A() - end - end - - local _preimage_zero - - let O = O - function _preimage_zero(a::AssociativeAlgebraElem) - return O() - end - end - - OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image_zero, _preimage_zero) - return A, OtoA - end - - BO = basis(O, copy = false) - mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) - for i = 1:r - M = representation_matrix_mod(BO[basis_elts[i]], ZZRingElem(p)) - if r != degree(O) - M = reduce_rows_mod_hnf!(M, bmatI, basis_elts) - end - for j = 1:r - for k = 1:r - mult_table[i, j, k] = Fp(M[basis_elts[j], basis_elts[k]]) - end - end - end - - if isone(BO[1]) - one = zeros(Fp, r) - one[1] = Fp(1) - A = StructureConstantAlgebra(Fp, mult_table, one) - else - A = StructureConstantAlgebra(Fp, mult_table) - end - if is_commutative(O) - A.is_commutative = 1 - end +zero_algebra(::Type{StructureConstantAlgebra}, R::Ring) = return zero_algebra(R) - local _image - - let I = I, A = A, basis_elts = basis_elts, Fp = Fp - function _image(a::Union{AbsNumFieldOrderElem, AlgAssAbsOrdElem}) - c = coordinates(mod(a, I), copy = false) - return A([ Fp(c[i]) for i in basis_elts ]) - end - end - - local _preimage - - temp = zero(O) +################################################################################ +# +# Basic field access +# +################################################################################ - let BO = BO, basis_elts = basis_elts, r = r, temp = temp - function _preimage(a::AssociativeAlgebraElem) - z = zero(O)::eltype(BO) - ca = coefficients(a, copy = false) - for i in 1:r - l = lift(ZZ, ca[i]) - addmul!(z, l, BO[basis_elts[i]], temp) +function denominator_of_structure_constant_table(A::StructureConstantAlgebra{QQFieldElem}) + get_attribute!(A, :denominator_of_multiplication_table) do + den = one(ZZ) + mt = structure_constant_table(A) + d = dim(A) + for i in 1:d + for j in 1:d + for k in 1:d + den = lcm!(den, den, denominator(mt[i, j, k])) + end end - return z - #return sum(lift(coefficients(a, copy = false)[i])*BO[basis_elts[i]] for i = 1:r) end - end - - OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image, _preimage) - - return A, OtoA -end - -# Requires M to be in lower left HNF -function reduce_vector_mod_hnf(v::ZZMatrix, M::ZZMatrix) - @assert ncols(v) == nrows(M) && nrows(M) == ncols(M) - - w = Vector{ZZRingElem}(undef, length(v)) - t = ZZRingElem() - for i in length(v):-1:1 - t = fdiv(v[1, i], M[i, i]) - for j in 1:i - w[j] = v[1, j] - t*M[i, j] - end - end - return w + return den + end::ZZRingElem end -@doc raw""" - quo(I::AbsNumFieldOrderIdeal, J::AbsNumFieldOrderIdeal, p::Union{ Int, ZZRingElem }) - quo(I::AlgAssAbsOrdIdl, J::AlgAssAbsOrdIdl, p::Union{ Int, ZZRingElem }) - -> StructureConstantAlgebra, AbsOrdToAlgAssMor - -Given an ideal $J$ such that $p \cdot I \subseteq J \subseteq I$ this function -constructs $I/J$ as an algebra over $\mathbb F_p$ together with the projection -map $I \to I/J$. -It is assumed that $p$ is prime. -""" -quo(I::Union{ AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl }, J::Union{ AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl }, p::Union{ Integer, ZZRingElem }) = StructureConstantAlgebra(I, J, p) - -function StructureConstantAlgebra(I::Union{ AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl }, J::Union{AbsNumFieldOrderIdeal, AlgAssAbsOrdIdl}, p::IntegerUnion) - @assert order(I) === order(J) - - O = order(I) - Oalgebra = _algebra(O) - - n = degree(O) - BmatJinI = hnf(basis_matrix(J, copy = false)*basis_mat_inv(I, copy = false), :lowerleft) - @assert isone(BmatJinI.den) "J is not a subset of I" - BmatJinI = BmatJinI.num - basis_elts = Vector{Int}() - for i = 1:n - if valuation(BmatJinI[i, i], p) == 0 - continue - end - - push!(basis_elts, i) - end - - r = length(basis_elts) - Fp = GF(p, cached = false) - - if r == 0 - A = zero_algebra(Fp) - - local _image_zero - - let A = A - function _image_zero(a::Union{ AbsNumFieldOrderElem, AlgAssAbsOrdElem }) - return A() - end - end - - local _preimage_zero - - let O = O - function _preimage_zero(a::AssociativeAlgebraElem) - return O() - end - end - - OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image_zero, _preimage_zero) - return A, OtoA - end - - BI = basis(I, copy = false) - BmatI = basis_matrix(I, copy = false) - BmatIinv = inv(BmatI) - - mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) - for i = 1:r - M = FakeFmpqMat(representation_matrix(_elem_in_algebra(BI[basis_elts[i]], copy = false))) - M = mul!(M, BmatI, M) - M = mul!(M, M, BmatIinv) - @assert M.den == 1 - M = M.num # M is now the representation matrix in the basis of I - if r != degree(O) - M = reduce_rows_mod_hnf!(M, BmatJinI, basis_elts) - end - for j = 1:r - for k = 1:r - mult_table[i, j, k] = Fp(M[basis_elts[j], basis_elts[k]]) - end - end - end - - A = StructureConstantAlgebra(Fp, mult_table) - if is_commutative(O) - A.is_commutative = 1 - end - - t = FakeFmpqMat(zero_matrix(FlintZZ, 1, n)) - - local _image +# use abstract doc +base_ring(A::StructureConstantAlgebra{T}) where {T} = A.base_ring::parent_type(T) - let BmatJinI = BmatJinI, I = I, r = r, A = A, t = t, Fp = Fp - function _image(a::Union{AbsNumFieldOrderElem, AlgAssAbsOrdElem}) - elem_to_mat_row!(t.num, 1, t.den, _elem_in_algebra(a, copy = false)) - t = mul!(t, t, basis_mat_inv(I, copy = false)) - @assert isone(t.den) "Not an element of the domain" - c = reduce_vector_mod_hnf(t.num, BmatJinI) - return A([ Fp(c[i]) for i in basis_elts ]) - end - end +# use abstract doc +base_ring_type(::Type{StructureConstantAlgebra{T}}) where {T} = parent_type(T) - local _preimage +# use abstract doc +has_one(A::StructureConstantAlgebra) = A.has_one - temppp = zero(Oalgebra) +# use abstract doc +iszero(A::StructureConstantAlgebra) = A.iszero - let BI = BI, basis_elts = basis_elts, r = r, Oalgebra = Oalgebra, temppp = temppp - function _preimage(a::AssociativeAlgebraElem) - acoords = map(x -> QQ(lift(ZZ, x)), coefficients(a, copy = false)) - z = zero(Oalgebra) - for i in 1:r - if is_zero(acoords[i]) - continue - end - temppp = mul!(temppp, acoords[i], BI[basis_elts[i]]) - z = add!(z, z, temppp) - end - _zz = O(z) - return _zz - end +function Generic.dim(A::StructureConstantAlgebra) + if iszero(A) + return 0 end - - OtoA = AbsOrdToAlgAssMor{typeof(O), elem_type(Fp)}(O, A, _image, _preimage) - - return A, OtoA + return size(structure_constant_table(A, copy = false), 1) end -#= -Qx, x = QQ["x"]; -f = x^2 + 12*x - 92; -K, a = number_field(f, "a"); -OK = maximal_order(K); -Ky, y = K["y"]; -g = y^2 - 54*y - 73; -L, b = number_field(g, "b"); -OL = maximal_order(L); -p = prime_decomposition(OK, 2)[1][1] -=# - -# Assume that O is relative order over OK, I is an ideal of O and p is a prime -# ideal of OK with pO \subseteq I. O/I is an OK/p-algebra. -# -# The idea is to compute pseudo-basis of O and I respectively, for which the -# coefficient ideals have zero p-adic valuation. Then we can think in the -# localization at p and do as in the case of principal ideal domains. @doc raw""" - quo(O::RelNumFieldOrder, I::RelNumFieldOrderIdeal, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) - quo(O::AlgAssRelOrd, I::AlgAssRelOrdIdl, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) - -> StructureConstantAlgebra, RelOrdToAlgAssMor - -Given an ideal $I$ such that $p \cdot O \subseteq I \subseteq O$ this function -constructs $O/I$ as an algebra over the finite field $R/p$, where $R$ is the -order of $p$, together with the projection map $O \to O/I$. -It is assumed that `R == base_ring(O)` and that $p$ is prime. -""" -quo(O::Union{ RelNumFieldOrder{T, S}, AlgAssRelOrd{T, S} }, I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}) where {T, S} = StructureConstantAlgebra(O, I, p) - -function StructureConstantAlgebra(O::Union{ RelNumFieldOrder{T, S}, AlgAssRelOrd{T, S} }, I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}, mF = residue_field(order(p), p)[2]) where {T, S} - - K = _algebra(O) - - new_basisO, new_basisI, new_bmatO, new_bmatI = coprime_bases(O, I, p) - new_bmatinvO = inv(new_bmatO) - - Fp = codomain(mF) - mmF = extend(mF, _base_ring(K)) - invmmF = pseudo_inv(mmF) - - basis_elts = Int[] - reducers = Int[] - - for i in 1:degree(O) - v = valuation(new_bmatI[i, i], p) - @assert v >= 0 - if v == 0 - push!(reducers, i) - else - push!(basis_elts, i) - end - end - - r = length(basis_elts) - - if r == 0 - A = zero_algebra(Fp) - - local _image_zero - - let A = A - function _image_zero(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) - return A() - end - end - - local _preimage_zero - - let O = O - function _preimage_zero(a::AssociativeAlgebraElem) - return O() - end - end - - OtoA = RelOrdToAlgAssMor(O, A, _image_zero, _preimage_zero) - return A, OtoA - end + structure_constant_table(A::StructureConstantAlgebra; copy::Bool = true) -> Array{_, 3} - reverse!(reducers) +Given an algebra $A$, return the structure constant table of $A$. See +[`structure_constant_algebra`](@ref) for the defining property. - tmp_matrix = zero_matrix(_base_ring(K), 1, degree(O)) +# Examples - function _coeff(c) - cfcs = coefficients(c, copy = false) - for i = 1:degree(O) - tmp_matrix[1, i] = cfcs[i] - end - return tmp_matrix*new_bmatinvO - end +```jldoctest +julia> A = associative_algebra(QQ, reshape([1, 0, 0, 2, 0, 1, 1, 0], (2, 2, 2))); - mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) - for i in 1:r - for j in 1:r - c = new_basisO[basis_elts[i]][1]*new_basisO[basis_elts[j]][1] - coeffs_c = _coeff(c) +julia> structure_constant_table(A) +2×2×2 Array{QQFieldElem, 3}: +[:, :, 1] = + 1 0 + 0 2 - for k in reducers - d = -coeffs_c[k]//new_bmatI[k, k] - c = c + d*new_basisI[k][1] - end - coeffs_c = _coeff(c) - for k in 1:degree(O) - if !(k in basis_elts) - @assert iszero(coeffs_c[k]) - end - end - for k in 1:r - mult_table[i, j, k] = mmF(coeffs_c[basis_elts[k]]) - end - end - end +[:, :, 2] = + 0 1 + 1 0 +``` +""" +function structure_constant_table(A::StructureConstantAlgebra; copy::Bool = true) + return multiplication_table(A; copy = copy) +end - if isone(new_basisO[basis_elts[1]][1]) - one = zeros(Fp, length(basis_elts)) - one[1] = Fp(1) - A = StructureConstantAlgebra(Fp, mult_table, one) +function multiplication_table(A::StructureConstantAlgebra; copy::Bool = true) + if copy + return deepcopy(A.mult_table) else - A = StructureConstantAlgebra(Fp, mult_table) - end - if is_commutative(O) - A.is_commutative = 1 - end - - local _image - - let A = A, O = O - function _image(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) - c = _elem_in_algebra(a, copy = false) - coeffs_c = _coeff(c) - for k in reducers - d = -coeffs_c[k]//new_bmatI[k, k] - c = c + d*new_basisI[k][1] - end - coeffs_c = _coeff(c) - for k in 1:degree(O) - if !(k in basis_elts) - @assert iszero(coeffs_c[k]) - end - end - b = A() - for k in 1:r - b.coeffs[k] = mmF(coeffs_c[basis_elts[k]]) - end - return b - end - end - - lifted_basis_of_A = [] - - for i in basis_elts - c = coprime_to(new_basisO[i][2], p) - b = invmmF(inv(mmF(c)))*c*new_basisO[i][1] - @assert b in O - push!(lifted_basis_of_A, b) - end - - local _preimage - let lifted_basis_of_A = lifted_basis_of_A, O = O, invmmF = invmmF - function _preimage(v::AssociativeAlgebraElem) - return O(sum((invmmF(v.coeffs[i]))*lifted_basis_of_A[i] for i in 1:r)) - end + return A.mult_table end - - OtoA = RelOrdToAlgAssMor(O, A, _image, _preimage) - - return A, OtoA end -@doc raw""" - quo(I::RelNumFieldOrderIdeal, J::RelNumFieldOrderIdeal, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) - quo(I::AlgAssRelOrdIdl, J::AlgAssRelOrdIdl, p::Union{ AbsNumFieldOrderIdeal, RelNumFieldOrderIdeal }) - -> StructureConstantAlgebra, RelOrdToAlgAssMor - -Given an ideal $J$ such that $p \cdot I \subseteq J \subseteq I$ this function -constructs $I/J$ as an algebra over the finite field $R/p$, where $R$ is the -order of $p$, together with the projection map $I \to I/J$. -It is assumed that `order(I) === order(J)` and in particular both should be -defined. Further, it should hold `R == base_ring(order(I))` and $p$ should be -prime. -""" -quo(I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, J::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}, mF = residue_field(order(p), p)[2]) where {T, S} = StructureConstantAlgebra(I, J, p, mF) - -function StructureConstantAlgebra(I::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, J::Union{ RelNumFieldOrderIdeal{T, S}, AlgAssRelOrdIdl{T, S} }, p::Union{AbsNumFieldOrderIdeal{AbsSimpleNumField, AbsSimpleNumFieldElem}, RelNumFieldOrderIdeal}, mF = residue_field(order(p), p)[2]) where {T, S} - @assert _algebra(I) === _algebra(J) - @assert order(I) === order(J) - - O = order(I) - K = _algebra(I) - new_basisI, new_basisJ, new_bmatI, new_bmatJinI = coprime_bases(I, J, p) - bmatinvI = inv(new_bmatI) - - Fp = codomain(mF) - mmF = extend(mF, _base_ring(K)) - invmmF = pseudo_inv(mmF) +################################################################################ +# +# Commutativity +# +################################################################################ - basis_elts = Int[] - reducers = Int[] +is_commutative_known(A::StructureConstantAlgebra) = (A.is_commutative != 0) - for i in 1:degree(O) - v = valuation(new_bmatJinI[i, i], p) - @assert v >= 0 - if v == 0 - push!(reducers, i) - else - push!(basis_elts, i) - end +function is_commutative(A::StructureConstantAlgebra) + if is_commutative_known(A) + return A.is_commutative == 1 end - - r = length(basis_elts) - - if r == 0 - A = zero_algebra(Fp) - - local _image_zero - - let A = A - function _image_zero(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) - return A() - end - end - - local _preimage_zero - - let O = O - function _preimage_zero(a::AssociativeAlgebraElem) - return O() + for i = 1:dim(A) + for j = i + 1:dim(A) + if structure_constant_table(A, copy = false)[i, j, :] != structure_constant_table(A, copy = false)[j, i, :] + A.is_commutative = 2 + return false end end - - OtoA = RelOrdToAlgAssMor(O, A, _image_zero, _preimage_zero) - return A, OtoA end + A.is_commutative = 1 + return true +end - reverse!(reducers) - - tmp_matrix = zero_matrix(_base_ring(K), 1, degree(O)) - - function _coeff(c) - for i = 1:degree(O) - tmp_matrix[1, i] = coefficients(c, copy = false)[i] - end - return tmp_matrix*bmatinvI - end - - mult_table = Array{elem_type(Fp), 3}(undef, r, r, r) - - for i in 1:r - for j in 1:r - c = new_basisI[basis_elts[i]][1]*new_basisI[basis_elts[j]][1] - coeffs = _coeff(c) - - for k in reducers - d = -coeffs[k]//new_bmatJinI[k, k] - c = c + d * new_basisJ[k][1] - end - coeffs = _coeff(c) - for k in 1:degree(O) - if !(k in basis_elts) - @assert iszero(coeffs[k]) - end - end - for k in 1:r - mult_table[i, j, k] = mmF(coeffs[basis_elts[k]]) - end - end - end +################################################################################ +# +# Finding the one +# +################################################################################ - if isone(new_basisI[basis_elts[1]][1]) - one = zeros(Fp, length(basis_elts)) - one[1] = Fp(1) - A = StructureConstantAlgebra(Fp, mult_table, one) - else - A = StructureConstantAlgebra(Fp, mult_table) - end - if is_commutative(O) - A.is_commutative = 1 +# This only works if base_ring(A) is a field (probably) +# Returns (true, one) if there is a one and (false, something) if not. +function find_one(A::StructureConstantAlgebra) + if iszero(A) + return true, elem_type(base_ring(A))[] end - - local _image - - let O = O, new_bmatJinI = new_bmatJinI, A = A - function _image(a::Union{ RelNumFieldOrderElem, AlgAssRelOrdElem }) - c = _elem_in_algebra(a, copy = false) - coeffs = _coeff(c) - for k in reducers - d = -coeffs[k]//new_bmatJinI[k, k] - c = c + d*new_basisJ[k][1] - end - coeffs = _coeff(c) - for k in 1:degree(O) - if !(k in basis_elts) - @assert iszero(coeffs[k]) - end - end - b = A() - for k in 1:r - b.coeffs[k] = mmF(coeffs[basis_elts[k]]) + n = dim(A) + M = zero_matrix(base_ring(A), n^2, n) + c = zero_matrix(base_ring(A), n^2, 1) + for k = 1:n + kn = (k - 1)*n + c[kn + k, 1] = base_ring(A)(1) + for i = 1:n + for j = 1:n + M[i + kn, j] = deepcopy(structure_constant_table(A, copy = false)[j, k, i]) end - return b end end + fl, cc = can_solve_with_solution(M, c; side = :right) + one = elem_type(base_ring(A))[cc[i, 1] for i = 1:n] + return true, one +end +associative_algebra(f::PolyRingElem) = StructureConstantAlgebra(f) - lifted_basis_of_A = [] - - for i in basis_elts - c = coprime_to(new_basisI[i][2], p) - b = invmmF(inv(mmF(c)))*c*new_basisI[i][1] - @assert O(b) in I - push!(lifted_basis_of_A, b) - end - - local _preimage - - let O = O, invmmF = invmmF, lifted_basis_of_A = lifted_basis_of_A - function _preimage(v::AssociativeAlgebraElem) - return O(sum((invmmF(v.coeffs[i])) * lifted_basis_of_A[i] for i in 1:r)) - end +function StructureConstantAlgebra(f::PolyRingElem) + R = base_ring(parent(f)) + n = degree(f) + Rx = parent(f) + x = gen(Rx) + B = Vector{elem_type(Rx)}(undef, 2*n - 1) + B[1] = Rx(1) + for i = 2:2*n - 1 + B[i] = mod(B[i - 1]*x, f) end - - OtoA = RelOrdToAlgAssMor(O, A, _image, _preimage) - - return A, OtoA -end - -function StructureConstantAlgebra(A::Generic.MatRing{T}) where { T <: FieldElem } - n = A.n - K = base_ring(A) - n2 = n^2 - # We use the matrices M_{ij} with a 1 at row i and column j and zeros everywhere else as the basis for A. - # We sort "column major", so A[i + (j - 1)*n] corresponds to the matrix M_{ij}. - # M_{ik}*M_{lj} = 0, if k != l, and M_{ik}*M_{kj} = M_{ij} - mult_table = zeros(K, n2, n2, n2) - oneK = one(K) - for j = 0:n:(n2 - n) - for k = 1:n - kn = (k - 1)*n - for i = 1:n - mult_table[i + kn, k + j, i + j] = oneK + mult_table = Array{elem_type(R), 3}(undef, n, n, n) + for i = 1:n + for j = i:n + for k = 1:n + mult_table[i, j, k] = coeff(B[i + j - 1], k - 1) + mult_table[j, i, k] = coeff(B[i + j - 1], k - 1) end end end - oneA = zeros(K, n2) - for i = 1:n - oneA[i + (i - 1)*n] = oneK - end - A = StructureConstantAlgebra(K, mult_table, oneA) - A.is_commutative = ( n == 1 ? 1 : 2 ) + one = map(R, zeros(Int, n)) + one[1] = R(1) + A = StructureConstantAlgebra(R, mult_table, one) + A.is_commutative = 1 return A end @@ -882,24 +356,13 @@ end ################################################################################ function show(io::IO, A::StructureConstantAlgebra) - print(io, "Associative algebra of dimension ", dim(A), " over ", base_ring(A)) -end - -################################################################################ -# -# Deepcopy -# -################################################################################ - -function Base.deepcopy_internal(A::StructureConstantAlgebra{T}, dict::IdDict) where {T} - B = StructureConstantAlgebra{T}(base_ring(A)) - for x in fieldnames(typeof(A)) - if x != :base_ring && isdefined(A, x) - setfield!(B, x, Base.deepcopy_internal(getfield(A, x), dict)) - end + if is_terse(io) + print(io, "Structure constant algebra") + else + io = pretty(io) + print(io, "Structure constant algebra of dimension ", dim(A), " over ") + print(terse(io), Lowercase(), base_ring(A)) end - B.base_ring = A.base_ring - return B end ################################################################################ @@ -908,7 +371,7 @@ end # ################################################################################ -# Builds a multiplication table for the subalgebra of A with basis matrix B. +# Builds a multiplication table for the _subalgebra of A with basis matrix B. # We assume ncols(B) == dim(A). # A rref of B will be computed IN PLACE! If return_LU is Val{true}, a LU-factorization # of transpose(rref(B)) is returned. @@ -975,7 +438,7 @@ function _build_subalgebra_mult_table!(A::StructureConstantAlgebra{T}, B::MatEle end @doc raw""" - subalgebra(A::StructureConstantAlgebra, e::AssociativeAlgebraElem, idempotent::Bool = false, + _subalgebra(A::StructureConstantAlgebra, e::AssociativeAlgebraElem, idempotent::Bool = false, action::Symbol = :left) -> StructureConstantAlgebra, AbsAlgAssMor @@ -984,7 +447,7 @@ $e \cdot A$ (if `action == :left`) respectively $A \cdot e$ (if `action == :righ and a map from this algebra to $A$. If `idempotent` is `true`, it is assumed that $e$ is idempotent in $A$. """ -function subalgebra(A::StructureConstantAlgebra{T}, e::AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}, idempotent::Bool = false, action::Symbol = :left) where {T} +function _subalgebra(A::StructureConstantAlgebra{T}, e::AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}, idempotent::Bool = false, action::Symbol = :left) where {T} @assert parent(e) == A R = base_ring(A) n = dim(A) @@ -1003,6 +466,7 @@ function subalgebra(A::StructureConstantAlgebra{T}, e::AssociativeAlgebraElem{T, basis_mat_of_eA = sub(B1, 1:r, 1:n) if idempotent + #LLsolvectx = solve_init(LL) # c = A() # d = zero_matrix(R, n, 1) # for k = 1:n @@ -1051,12 +515,12 @@ function subalgebra(A::StructureConstantAlgebra{T}, e::AssociativeAlgebraElem{T, end @doc raw""" - subalgebra(A::StructureConstantAlgebra, basis::Vector{AssociativeAlgebraElem}) -> StructureConstantAlgebra, AbsAlgAssMor + _subalgebra(A::StructureConstantAlgebra, basis::Vector{AssociativeAlgebraElem}) -> StructureConstantAlgebra, AbsAlgAssMor -Returns the subalgebra of $A$ generated by the elements in `basis` and a map +Returns the _subalgebra of $A$ generated by the elements in `basis` and a map from this algebra to $A$. """ -function subalgebra(A::StructureConstantAlgebra{T}, basis::Vector{AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}}; is_commutative = false) where T +function _subalgebra(A::StructureConstantAlgebra{T}, basis::Vector{AssociativeAlgebraElem{T, StructureConstantAlgebra{T}}}; is_commutative = false) where T M = zero_matrix(base_ring(A), dim(A), dim(A)) for i = 1:length(basis) elem_to_mat_row!(M, i, basis[i]) @@ -1066,47 +530,6 @@ function subalgebra(A::StructureConstantAlgebra{T}, basis::Vector{AssociativeAlg return B, hom(B, A, sub(M, 1:length(basis), 1:dim(A))) end -############################################################################### -# -# Trace Matrix -# -############################################################################### - -function _assure_trace_basis(A::StructureConstantAlgebra{T}) where T - if !isdefined(A, :trace_basis_elem) - A.trace_basis_elem = Vector{T}(undef, dim(A)) - for i=1:length(A.trace_basis_elem) - A.trace_basis_elem[i]=sum(multiplication_table(A, copy = false)[i,j,j] for j= 1:dim(A)) - end - end - return nothing -end - -@doc raw""" - trace_matrix(A::StructureConstantAlgebra) -> MatElem - -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::StructureConstantAlgebra) - _assure_trace_basis(A) - F = base_ring(A) - n = dim(A) - M = zero_matrix(F, n, n) - for i = 1:n - M[i,i] = tr(A[i]^2) - end - for i = 1:n - for j = i+1:n - x = tr(A[i]*A[j]) - M[i,j] = x - M[j,i] = x - end - end - return M -end - ############################################################################### # # Center @@ -1115,7 +538,7 @@ end function _rep_for_center!(M::T, A::StructureConstantAlgebra) where T<: MatElem n = dim(A) - mt = multiplication_table(A, copy = false) + mt = structure_constant_table(A, copy = false) tt = zero(base_ring(A)) for i=1:n for j = 1:n @@ -1132,11 +555,6 @@ function _rep_for_center!(M::T, A::StructureConstantAlgebra) where T<: MatElem return nothing end -@doc raw""" - center(A::StructureConstantAlgebra) -> StructureConstantAlgebra, AbsAlgAssMor - -Returns the center $C$ of $A$ and the inclusion $C \to A$. -""" function center(A::StructureConstantAlgebra{T}) where {T} if is_commutative(A) B, mB = StructureConstantAlgebra(A) @@ -1156,7 +574,7 @@ function center(A::StructureConstantAlgebra{T}) where {T} for i=1:k res[i]= A(T[B[j,i] for j=1:n]) end - C, mC = subalgebra(A, res, is_commutative = true) + C, mC = _subalgebra(A, res, is_commutative = true) A.center = C, mC # Store the idempotents of A if known so that the Wedderburn decompositions @@ -1218,7 +636,7 @@ function _find_idempotent_via_non_squarefree_poly(A::StructureConstantAlgebra{T} end b = sf_part(a) # This is not really an algebra, only a right sided ideal - bA, bAtoA = subalgebra(A, b, false, :left) + bA, bAtoA = _subalgebra(A, b, false, :left) # Find an element e of bA such that e*x == x for all x in bA M = zero_matrix(base_ring(A), dim(bA), 0) @@ -1298,8 +716,8 @@ function _primitive_idempotents(A::StructureConstantAlgebra{T}) where { T } #<: idempotents = Vector{elem_type(A)}() - eA, m1 = subalgebra(A, e, true, :left) - eAe, m2 = subalgebra(eA, m1\e, true, :right) + eA, m1 = _subalgebra(A, e, true, :left) + eAe, m2 = _subalgebra(eA, m1\e, true, :right) if dim(eAe) == dim(A) push!(idempotents, e) else @@ -1308,8 +726,8 @@ function _primitive_idempotents(A::StructureConstantAlgebra{T}) where { T } #<: end f = (1 - e) - fA, n1 = subalgebra(A, f, true, :left) - fAf, n2 = subalgebra(fA, n1\f, true, :right) + fA, n1 = _subalgebra(A, f, true, :left) + fAf, n2 = _subalgebra(fA, n1\f, true, :right) if dim(fAf) == dim(A) push!(idempotents, f) @@ -1343,8 +761,8 @@ function _matrix_basis(A::StructureConstantAlgebra{T}, idempotents::Vector{S}) w for i = 2:k b = idempotents[i] e = a + b - eA, m1 = subalgebra(A, e, true, :left) - eAe, m2 = subalgebra(eA, m1\e, true, :right) + eA, m1 = _subalgebra(A, e, true, :left) + eAe, m2 = _subalgebra(eA, m1\e, true, :right) aa = m2\(m1\(a)) bb = m2\(m1\(b)) @@ -1451,7 +869,7 @@ function direct_product(a::StructureConstantAlgebra{T}, _algebras::StructureCons mt = zeros(base_ring(algebras[1]), d, d, d) offset = 0 for B in algebras - mtB = multiplication_table(B, copy = false) + mtB = structure_constant_table(B, copy = false) dd = dim(B) for i = 1:dd for j = 1:dd @@ -1531,8 +949,8 @@ end # ################################################################################ +# internal use only function quaternion_algebra2(K::Field, a::T, b::T) where { T <: FieldElem } - M = zeros(K, 4, 4, 4) M[1, 1, 1] = one(K) # 1*1=1 diff --git a/src/AlgAssAbsOrd/Elem.jl b/src/AlgAssAbsOrd/Elem.jl index 8af940474f..f5110fb4f2 100644 --- a/src/AlgAssAbsOrd/Elem.jl +++ b/src/AlgAssAbsOrd/Elem.jl @@ -335,6 +335,16 @@ end mul!(z::AlgAssAbsOrdElem, y::AlgAssAbsOrdElem, x::Union{ Int, ZZRingElem }) = mul!(z, x, y) +function addmul!(a::AlgAssAbsOrdElem, b::ZZRingElem, c::AlgAssAbsOrdElem, d = parent(a)()) + mul!(d, b, c) + return add!(a, a, d) +end + +function addmul!(a::AbsNumFieldOrderElem, b::ZZRingElem, c::AbsNumFieldOrderElem, d = parent(a)()) + mul!(d, b, c) + return add!(a, a, d) +end + ################################################################################ # # String I/O diff --git a/src/AlgAssAbsOrd/Ideal.jl b/src/AlgAssAbsOrd/Ideal.jl index f86b5dfce3..065dc110eb 100644 --- a/src/AlgAssAbsOrd/Ideal.jl +++ b/src/AlgAssAbsOrd/Ideal.jl @@ -534,7 +534,7 @@ function *(a::AlgAssAbsOrdIdl{S, T}, b::AlgAssAbsOrdIdl{S, T}) where {S, T} _, oned = integral_split(coefficients(one(A)), ZZ) if is_full_rank(a) && is_full_rank(b) - el = b.basis_matrix.den * a.eldiv_mul * a.basis_matrix.den * b.eldiv_mul * denominator_of_multiplication_table(A) * oned + el = b.basis_matrix.den * a.eldiv_mul * a.basis_matrix.den * b.eldiv_mul * denominator_of_structure_constant_table(A) * oned H = sub(hnf_modular_eldiv!(FakeFmpqMat(M), el), (d2 - d + 1):d2, 1:d) @hassert :AlgAssOrd 1 H == sub(hnf(FakeFmpqMat(M)), (d2 - d + 1):d2, 1:d) else diff --git a/src/exports.jl b/src/exports.jl index d84061c036..663026678f 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -285,6 +285,8 @@ export different_divisor export differential export dim export dimension +export dimension_of_center +export dimension_over_center export direct_product export direct_sum export disc_log @@ -360,6 +362,7 @@ export gcd_into! export gen_index export generators export generic_group +export gens_with_data export genus export genus_field export genus_representatives @@ -456,6 +459,7 @@ export is_automorphous export is_bass export is_bijective export is_central +export is_central_idempotent export is_chain_complex export is_characteristic export is_cochain_complex @@ -489,6 +493,7 @@ export is_fundamental_discriminant export is_genus export is_gorenstein export is_hermitian +export is_idempotent export is_indefinite export is_independent export is_index_divisor @@ -860,6 +865,8 @@ export stable_faltings_height export stable_subgroups export stable_submodules export strong_echelon_form +export structure_constant_algebra +export structure_constant_table export sub export subalgebra export subfields diff --git a/test/AlgAss.jl b/test/AlgAss.jl index 3199438ad3..7ed121234b 100644 --- a/test/AlgAss.jl +++ b/test/AlgAss.jl @@ -1,6 +1,6 @@ @testset "AlgAss" begin include("AlgAss/AbsAlgAss.jl") - include("AlgAss/AlgAss.jl") + include("AlgAss/StructureConstantAlgebra.jl") include("AlgAss/radical.jl") include("AlgAss/AlgGrp.jl") include("AlgAss/AlgMat.jl") diff --git a/test/AlgAss/AbsAlgAss.jl b/test/AlgAss/AbsAlgAss.jl index 7f095a5baa..abbd7fa066 100644 --- a/test/AlgAss/AbsAlgAss.jl +++ b/test/AlgAss/AbsAlgAss.jl @@ -84,7 +84,7 @@ @testset "Generators" begin Qx, x = FlintQQ["x"] A = StructureConstantAlgebra((x^2 + 1)*(x^2 + 3)) - g, full_basis, v = gens(A, Val(true)) + g, full_basis, v = gens_with_data(A) @test length(full_basis) == dim(A) diff --git a/test/AlgAss/Elem.jl b/test/AlgAss/Elem.jl index cd82c3a23e..3634e9ed05 100644 --- a/test/AlgAss/Elem.jl +++ b/test/AlgAss/Elem.jl @@ -16,8 +16,26 @@ @test Hecke.is_integral(QQFieldElem(1, 2)*C[1]) == false end + let + A = matrix_algebra(QQ, 2) + @test is_idempotent(A(QQ[1 0; 0 0])) + @test !is_central(A(QQ[1 0; 0 0])) + @test is_central(A(QQ[2 0; 0 2])) + @test !is_central_idempotent(A(QQ[1 0; 0 0])) + @test is_central_idempotent(A(QQ[1 0; 0 1])) + + Qx, x = FlintQQ["x"] + f = (x + 1)*(x - 1) + B = StructureConstantAlgebra(f) + e = B([1//2, 1//2]) + @test is_central_idempotent(e) + end + + @testset "Characteristic polynomial" begin + f = x^2 + 1 K, a = number_field(f, "a") + A = StructureConstantAlgebra(f) b = rand(K, -10:10) c = A(coefficients(b)) diff --git a/test/AlgAss/AlgAss.jl b/test/AlgAss/StructureConstantAlgebra.jl similarity index 76% rename from test/AlgAss/AlgAss.jl rename to test/AlgAss/StructureConstantAlgebra.jl index 34b41e1574..13d0859a40 100644 --- a/test/AlgAss/AlgAss.jl +++ b/test/AlgAss/StructureConstantAlgebra.jl @@ -48,7 +48,40 @@ end # creation - @test_throws ArgumentError associative_algebra(QQ, map(QQ, reshape([1 2 1 2; 1 2 1 2], 2, 2, 2))) + @test_throws ArgumentError structure_constant_algebra(QQ, map(QQ, reshape([1 2 1 2; 1 2 1 2], 2, 2, 2))) + @test_throws ArgumentError structure_constant_algebra(QQ, reshape([2], 1, 1, 1); one = QQFieldElem[2, 3]) + + A = structure_constant_algebra(QQ, reshape([1], 1, 1, 1); one = QQFieldElem[1]) + A = structure_constant_algebra(QQ, reshape([1], 1, 1, 1); one = Int[1]) + A = structure_constant_algebra(QQ, reshape([], 0, 0, 0); one = []) + A = StructureConstantAlgebra(QQ, 0, QQFieldElem[]) + A = zero_algebra(StructureConstantAlgebra, QQ) + fl, v = Hecke.find_one(A) + @test fl && v == QQFieldElem[] + + A = @inferred associative_algebra(QQ, reshape([1, 0, 0, 2, 0, 1, 1, 0], (2, 2, 2))) + @test dim(A) == 2 + @test base_ring(A) === QQ + @test structure_constant_table(A) == reshape([1, 0, 0, 2, 0, 1, 1, 0], (2, 2, 2)) + @test structure_constant_table(A; copy = false) == reshape([1, 0, 0, 2, 0, 1, 1, 0], (2, 2, 2)) + + K, = quadratic_field(2) + A, m = structure_constant_algebra(K) + @test base_ring(A) === QQ + @test dim(A) == 2 + @test m(one(A)) == one(K) + @test preimage(m, one(K)) == one(A) + @test m(zero(A)) == zero(K) + @test preimage(m, zero(K)) == zero(A) + Kt, t = K["t"] + L, b = number_field(t^2 + 3, "b") + A, m = structure_constant_algebra(L) + @test base_ring(A) === K + @test dim(A) == 2 + @test m(one(A)) == one(L) + @test preimage(m, one(L)) == one(A) + @test m(zero(A)) == zero(L) + @test preimage(m, zero(L)) == zero(A) @testset "Change of ring" begin @@ -106,7 +139,7 @@ end # Extend from F_p^m to F_p^n Fqx, x = Fq["x"] f = x^2 + 5x + 2 - A = associative_algebra(f) + A = structure_constant_algebra(f) B, BtoA = Hecke._as_algebra_over_center(A) @test characteristic(base_ring(B)) == characteristic(Fq) @test absolute_degree(base_ring(B)) == degree(f)*degree(Fq) @@ -124,7 +157,7 @@ end mt[2, 1, 2] = Fp(1) mt[2, 2, 1] = Fp(1) mt[2, 2, 2] = Fp(1) - A = associative_algebra(Fp, mt) + A = structure_constant_algebra(Fp, mt) B, BtoA = Hecke._as_algebra_over_center(A) @test characteristic(base_ring(B)) == characteristic(Fp) @test degree(base_ring(B)) == dim(A) @@ -134,9 +167,9 @@ end # zero algebra - A = associative_algebra(QQ, Array{QQFieldElem}(undef, 0, 0, 0)) + A = structure_constant_algebra(QQ, Array{QQFieldElem}(undef, 0, 0, 0)) @test dim(A) == 0 - A = associative_algebra(QQ, Array{QQFieldElem}(undef, 0, 0, 0), QQFieldElem[]) + A = structure_constant_algebra(QQ, Array{QQFieldElem}(undef, 0, 0, 0), QQFieldElem[]) @test dim(A) == 0 end @@ -226,7 +259,7 @@ end K, a = number_field(x^2 - 2, "a") HH = Hecke.quaternion_algebra2(2, 3) - A = associative_algebra(K, map(K, HH.mult_table)) + A = structure_constant_algebra(K, map(K, HH.mult_table)) Ps = real_places(K) @test is_split(A, Ps[1]) @test is_split(A, Ps[2])