diff --git a/Project.toml b/Project.toml index 16c43a8860e5..1929b36eaf8d 100644 --- a/Project.toml +++ b/Project.toml @@ -25,7 +25,7 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" cohomCalg_jll = "5558cf25-a90e-53b0-b813-cadaa3ae7ade" [compat] -AbstractAlgebra = "0.42.0" +AbstractAlgebra = "0.42.3" AlgebraicSolving = "0.5.1" Distributed = "1.6" GAP = "0.10.2" diff --git a/docs/src/NoncommutativeAlgebra/free_associative_algebra.md b/docs/src/NoncommutativeAlgebra/free_associative_algebra.md index 36b32886a245..9f3b8dc80179 100644 --- a/docs/src/NoncommutativeAlgebra/free_associative_algebra.md +++ b/docs/src/NoncommutativeAlgebra/free_associative_algebra.md @@ -9,13 +9,13 @@ CurrentModule = Oscar ### Types The OSCAR type for two-sided ideals in a free associative algebra is -`FreeAssAlgIdeal{T}`, where `T` is the element type of the algebra. +`FreeAssociativeAlgebraIdeal{T}`, where `T` is the element type of the algebra. ### Constructors ```julia -ideal(R::FreeAssAlgebra, g::Vector{T}) where T <: FreeAssAlgElem -ideal(g::Vector{T}) where T <: FreeAssAlgElem +ideal(R::FreeAssociativeAlgebra, g::Vector{T}) where T <: FreeAssociativeAlgebraElem +ideal(g::Vector{T}) where T <: FreeAssociativeAlgebraElem ``` ### Ideal Membership @@ -25,11 +25,11 @@ Non-commutative polynomial rings are not Noetherian. Hence, in general, Groebne Setting the parameter `deg_bound` to a positive value yields the truncation of the Groebner bases to a fixed degree. Such a truncation is always finite. ```@docs -groebner_basis(I::FreeAssAlgIdeal, deg_bound::Int=-1; protocol::Bool=false) +groebner_basis(I::FreeAssociativeAlgebraIdeal, deg_bound::Int=-1; protocol::Bool=false) ``` If a finite Gröbner basis exists, it solves the ideal membership problem. ```@docs -ideal_membership(a::FreeAssAlgElem, I::FreeAssAlgIdeal, deg_bound::Int) +ideal_membership(a::FreeAssociativeAlgebraElem, I::FreeAssociativeAlgebraIdeal, deg_bound::Int) ``` diff --git a/src/Groups/action.jl b/src/Groups/action.jl index 0165be9256e2..37037e5d606e 100644 --- a/src/Groups/action.jl +++ b/src/Groups/action.jl @@ -256,12 +256,12 @@ end @doc raw""" on_indeterminates(f::GapObj, p::PermGroupElem) on_indeterminates(f::MPolyRingElem, p::PermGroupElem) - on_indeterminates(f::FreeAssAlgElem, p::PermGroupElem) + on_indeterminates(f::FreeAssociativeAlgebraElem, p::PermGroupElem) on_indeterminates(f::MPolyIdeal, p::PermGroupElem) Return the image of `f` under `p` where `p` acts via permuting the indeterminates. -For `MPolyRingElem`, `FreeAssAlgElem`, and `MPolyIdeal` objects, +For `MPolyRingElem`, `FreeAssociativeAlgebraElem`, and `MPolyIdeal` objects, one can also call `^` instead of `on_indeterminates`. # Examples @@ -303,7 +303,7 @@ function on_indeterminates(f::MPolyRingElem, s::PermGroupElem) return finish(g) end -function on_indeterminates(f::FreeAssAlgElem{T}, s::PermGroupElem) where T +function on_indeterminates(f::FreeAssociativeAlgebraElem{T}, s::PermGroupElem) where T G = parent(s) S = parent(f) @assert ngens(S) == degree(G) @@ -374,7 +374,7 @@ end ^(f::MPolyRingElem, p::PermGroupElem) = on_indeterminates(f, p) -^(f::FreeAssAlgElem, p::PermGroupElem) = on_indeterminates(f, p) +^(f::FreeAssociativeAlgebraElem, p::PermGroupElem) = on_indeterminates(f, p) ^(f::MPolyRingElem{T}, p::MatrixGroupElem{T, S}) where T where S = on_indeterminates(f, p) diff --git a/src/Rings/FreeAssAlgIdeal.jl b/src/Rings/FreeAssAlgIdeal.jl deleted file mode 100644 index 44c27f5a3f8d..000000000000 --- a/src/Rings/FreeAssAlgIdeal.jl +++ /dev/null @@ -1,192 +0,0 @@ -############################################################################### -# -# Ideals of a free associative algebra -# -############################################################################### - -# Currently ideal membership relies entirely on Singular, where a degree bound -# is imposed and a inconclusive answer may be returned. We can later add the -# Groebner machinery operating purely on the Oscar types, and hence not -# necessarily be confined to a degree bound. - -@doc raw""" - mutable struct FreeAssAlgIdeal{T} - -Two-sided ideal of a free associative algebra with elements of type `T`. -""" -mutable struct FreeAssAlgIdeal{T} <: Ideal{T} - gens::IdealGens{T} - gb::IdealGens{T} - - function FreeAssAlgIdeal(R::FreeAssAlgebra, g::Vector{T}) where T <: FreeAssAlgElem - r = new{T}() - r.gens = IdealGens(R, g) - return r - end -end - -function AbstractAlgebra.expressify(a::FreeAssAlgIdeal; context = nothing) - return Expr(:call, :ideal, [expressify(g, context = context) for g in collect(a.gens)]...) -end -@enable_all_show_via_expressify FreeAssAlgIdeal - -@doc raw""" - ideal(R::FreeAssAlgebra, g::Vector{<:FreeAssAlgElem}) - -Return the two-sided ideal of $R$ generated by $g$. -""" -function ideal(R::FreeAssAlgebra, g::Vector{<:FreeAssAlgElem}) - @assert all(x -> parent(x) == R, g) "parent mismatch" - return FreeAssAlgIdeal(R, g) -end - -function ideal(g::Vector{<:FreeAssAlgElem}) - @assert length(g) > 0 "cannot infer base ring" - algebra = parent(g[1]) - return ideal(algebra, g) - -end - -function base_ring(I::FreeAssAlgIdeal{T}) where T - return I.gens.Ox::parent_type(T) -end - -function base_ring_type(::Type{<:FreeAssAlgIdeal{T}}) where T - return parent_type(T) -end - -function number_of_generators(a::FreeAssAlgIdeal) - return length(a.gens) -end - -function gen(a::FreeAssAlgIdeal{T}, i::Int) where T - return a.gens[Val(:O), i] -end - -function gens(a::FreeAssAlgIdeal{T}) where T - return T[gen(a,i) for i in 1:ngens(a)] -end - -function Base.:+(a::FreeAssAlgIdeal{T}, b::FreeAssAlgIdeal{T}) where T - R = base_ring(a) - @assert R == base_ring(b) "parent mismatch" - return ideal(R, vcat(gens(a), gens(b))) -end - -function Base.:*(a::FreeAssAlgIdeal{T}, b::FreeAssAlgIdeal{T}) where T - R = base_ring(a) - @assert R == base_ring(b) "parent mismatch" - return ideal(R, [i*j for i in gens(a) for j in gens(b)]) -end - -@doc raw""" - ideal_membership(a::FreeAssAlgElem, I::FreeAssAlgIdeal, deg_bound::Int) - -Returns `true` if intermediate degree calculations bounded by `deg_bound` prove that $a$ is in $I$. -Otherwise, returning `false` indicates an inconclusive answer, but larger `deg_bound`s give more confidence in a negative answer. -If `deg_bound` is not specified, the default value is `-1`, which means that no degree bound is imposed, -resulting in a calculation using a much slower algorithm that may not terminate, but will return a full Groebner basis if it does. -```jldoctest -julia> free, (x,y,z) = free_associative_algebra(QQ, ["x", "y", "z"]); - -julia> f1 = x*y + y*z; - -julia> I = ideal([f1]); - -julia> ideal_membership(f1, I, 4) -true -``` -""" -function ideal_membership(a::FreeAssAlgElem, I::FreeAssAlgIdeal, deg_bound::Int=-1) - return ideal_membership(a, IdealGens(gens(I)), deg_bound) -end -function ideal_membership(a::FreeAssAlgElem, I::IdealGens{<:FreeAssAlgElem}, deg_bound::Int=-1) - return ideal_membership(a, collect(I), deg_bound) -end -function ideal_membership(a::FreeAssAlgElem, I::Vector{<:FreeAssAlgElem}, deg_bound::Int=-1) - R = parent(a) - @assert all(x -> parent(x) == R, I) "parent mismatch" - gb = groebner_basis(I, deg_bound; protocol=false) - deg_bound = max(maximum(total_degree.(gb)),total_degree(a)) - lpring, _ = _to_lpring(R, deg_bound) - - lp_I = Singular.Ideal(lpring, lpring.(gb)) - return iszero(reduce(lpring(a), lp_I)) -end - -function Base.in(a::FreeAssAlgElem, I::FreeAssAlgIdeal, deg_bound::Int) - return ideal_membership(a, I, deg_bound) -end - -function (R::Singular.LPRing)(a::FreeAssAlgElem) - B = MPolyBuildCtx(R) - for (c, e) in zip(coefficients(a), exponent_words(a)) - push_term!(B, base_ring(R)(c), e) - end - return finish(B) -end - -function (A::FreeAssAlgebra)(a::Singular.slpalg) - B = MPolyBuildCtx(A) - for (c,e) in zip(Oscar.coefficients(a), Singular.exponent_words(a)) - push_term!(B, base_ring(A)(c), e) - end - return finish(B) -end - -_to_lpring(a::FreeAssAlgebra, deg_bound::Int) = Singular.FreeAlgebra(base_ring(a), String.(symbols(a)), deg_bound) - -@doc raw""" - groebner_basis(I::FreeAssAlgIdeal, deg_bound::Int=-1; protocol::Bool=false) - -Return the Groebner basis of `I` with respect to the degree bound `deg_bound`. -If `protocol` is `true`, the protocol of the computation is also returned. -The default value of `deg_bound` is `-1`, which means that no degree bound is -imposed, resulting in a computation that is usually slower, but will return a -full Groebner basis if there exists a finite one. -```jldoctest -julia> free, (x,y,z) = free_associative_algebra(QQ, ["x", "y", "z"]); - -julia> f1 = x*y + y*z; - -julia> f2 = x^2 + y^2; - -julia> I = ideal([f1, f2]); - -julia> gb = groebner_basis(I, 3; protocol=false) -Ideal generating system with elements - 1 -> x*y + y*z - 2 -> x^2 + y^2 - 3 -> y^3 + y*z^2 - 4 -> y^2*x + y*z*y -``` -""" -function groebner_basis(I::FreeAssAlgIdeal, deg_bound::Int=-1; protocol::Bool=false) - isdefined(I, :gb) && return I.gb - I.gb = groebner_basis(IdealGens(gens(I)), deg_bound, protocol=protocol) - return I.gb -end -function groebner_basis(g::IdealGens{<:FreeAssAlgElem}, deg_bound::Int=-1; protocol::Bool=false) - gb = groebner_basis(collect(g), deg_bound, protocol=protocol) - return IdealGens(gb) -end -function groebner_basis(g::Vector{<:FreeAssAlgElem}, deg_bound::Int=-1; protocol::Bool=false) - R = parent(g[1]) - @assert all(x -> parent(x) == R, g) "parent mismatch" - @assert deg_bound >= 0 || !protocol "computing with a protocol requires a degree bound" - - if deg_bound == -1 - return AbstractAlgebra.groebner_basis(g) - end - - lpring, _ = _to_lpring(R, deg_bound) - lp_I_gens = lpring.(g) - - I = Singular.Ideal(lpring, lp_I_gens) - gb = nothing - - Singular.with_prot(protocol) do; - gb = gens(Singular.std(I)) - end - return R.(gb) -end diff --git a/src/Rings/FreeAssociativeAlgebraIdeal.jl b/src/Rings/FreeAssociativeAlgebraIdeal.jl new file mode 100644 index 000000000000..7e34f422c963 --- /dev/null +++ b/src/Rings/FreeAssociativeAlgebraIdeal.jl @@ -0,0 +1,248 @@ +############################################################################### +# +# Ideals of a free associative algebra +# +############################################################################### + +# Currently ideal membership relies entirely on Singular, where a degree bound +# is imposed and a inconclusive answer may be returned. We can later add the +# Groebner machinery operating purely on the Oscar types, and hence not +# necessarily be confined to a degree bound. + +@doc raw""" + mutable struct FreeAssociativeAlgebraIdeal{T} + +Two-sided ideal of a free associative algebra with elements of type `T`. +""" +mutable struct FreeAssociativeAlgebraIdeal{T} <: Ideal{T} + gens::IdealGens{T} + gb::IdealGens{T} + deg_bound::Int + function FreeAssociativeAlgebraIdeal(g::IdealGens{T}) where T <: FreeAssociativeAlgebraElem + r = new{T}() + r.gens = g + return r + end +end + +Base.show(io::IO, a::FreeAssociativeAlgebraIdeal) = print(io, "Ideal of ", base_ring(a), " with ", number_of_generators(a), " generators") + +function Base.:(==)(I2::FreeAssociativeAlgebraIdeal, I1::FreeAssociativeAlgebraIdeal) + return is_subset(I1,I2) && is_subset(I2,I1) +end + +function Base.hash(a::FreeAssociativeAlgebraIdeal, h::UInt) + return hash(base_ring(a), h) +end + +@doc raw""" + ideal(R::FreeAssociativeAlgebra, g::Vector{<:FreeAssociativeAlgebraElem}) + +Return the two-sided ideal of $R$ generated by $g$. +""" +function ideal(R::FreeAssociativeAlgebra, g::Vector{<:FreeAssociativeAlgebraElem}) + @req all(x -> parent(x) == R, g) "parent mismatch" + return FreeAssociativeAlgebraIdeal(IdealGens(R, g)) +end + +function ideal(g::Vector{<:FreeAssociativeAlgebraElem}) + @req length(g) > 0 "cannot infer base ring" + algebra = parent(g[1]) + return ideal(algebra, g) +end + +function ideal(g::IdealGens{T}) where T <: FreeAssociativeAlgebraElem + return FreeAssociativeAlgebraIdeal(g) +end + + +function base_ring(I::FreeAssociativeAlgebraIdeal{T}) where T + return I.gens.Ox::parent_type(T) +end + +function base_ring_type(::Type{<:FreeAssociativeAlgebraIdeal{T}}) where T + return parent_type(T) +end + +function number_of_generators(a::FreeAssociativeAlgebraIdeal) + return length(a.gens) +end + +function gen(a::FreeAssociativeAlgebraIdeal{T}, i::Int) where T + return a.gens[Val(:O), i] +end + +function gens(a::FreeAssociativeAlgebraIdeal{T}) where T + return T[gen(a,i) for i in 1:ngens(a)] +end + +function Base.:+(a::FreeAssociativeAlgebraIdeal{T}, b::FreeAssociativeAlgebraIdeal{T}) where T + R = base_ring(a) + @req R == base_ring(b) "parent mismatch" + return ideal(R, vcat(gens(a), gens(b))) +end + +function Base.:*(a::FreeAssociativeAlgebraIdeal{T}, b::FreeAssociativeAlgebraIdeal{T}) where T + R = base_ring(a) + @req R == base_ring(b) "parent mismatch" + return ideal(R, [i*j for i in gens(a) for j in gens(b)]) +end + +AbstractAlgebra.normal_form(f::FreeAssociativeAlgebraElem, I::FreeAssociativeAlgebraIdeal) = normal_form(f, gens(I)) +AbstractAlgebra.normal_form(f::FreeAssociativeAlgebraElem, I::IdealGens{<:FreeAssociativeAlgebraElem}) = normal_form(f, collect(I)) + +@doc raw""" + ideal_membership(a::FreeAssociativeAlgebraElem, I::FreeAssociativeAlgebraIdeal, deg_bound::Int) + +Returns `true` if intermediate degree calculations bounded by `deg_bound` prove that $a$ is in $I$. +Otherwise, returning `false` indicates an inconclusive answer, but larger `deg_bound`s give more confidence in a negative answer. +If `deg_bound` is not specified, the default value is `-1`, which means that no degree bound is imposed, +resulting in a calculation using a much slower algorithm that may not terminate, but will return a full Groebner basis if it does. +```jldoctest +julia> free, (x,y,z) = free_associative_algebra(QQ, ["x", "y", "z"]); + +julia> f1 = x*y + y*z; + +julia> I = ideal([f1]); + +julia> ideal_membership(f1, I, 4) +true +``` +""" +function ideal_membership(a::FreeAssociativeAlgebraElem, I::FreeAssociativeAlgebraIdeal, deg_bound::Int=-1) + isdefined(I, :gb) && (I.deg_bound == -1 || I.deg_bound >= deg_bound) && return iszero(normal_form(a, I.gb)) + groebner_basis(I, deg_bound) + return ideal_membership(a, I, deg_bound) +end +function ideal_membership(a::FreeAssociativeAlgebraElem, I::IdealGens{<:FreeAssociativeAlgebraElem}, deg_bound::Int=-1) + return ideal_membership(a, collect(I), deg_bound) +end +function ideal_membership(a::FreeAssociativeAlgebraElem, I::Vector{<:FreeAssociativeAlgebraElem}, deg_bound::Int=-1) + R = parent(a) + @req all(x -> parent(x) == R, I) "parent mismatch" + gb = groebner_basis(I, deg_bound) + return iszero(normal_form(a, gb)) +end + +function Base.in(a::FreeAssociativeAlgebraElem, I::FreeAssociativeAlgebraIdeal) + return ideal_membership(a, I) +end + +function is_subset(I1::FreeAssociativeAlgebraIdeal, I2::FreeAssociativeAlgebraIdeal) + @req base_ring(I1) === base_ring(I2) "parent mismatch" + I1 === I2 && return true + gens(I1) == gens(I2) && return true + isdefined(I1, :gb) && isdefined(I2, :gb) && I1.gb == I2.gb && return true + return all(x -> in(x, I2), gens(I1)) +end + +function (R::Singular.LPRing)(a::FreeAssociativeAlgebraElem) + B = MPolyBuildCtx(R) + for (c, e) in zip(coefficients(a), exponent_words(a)) + push_term!(B, base_ring(R)(c), e) + end + return finish(B) +end + +function (A::FreeAssociativeAlgebra)(a::Singular.slpalg) + B = MPolyBuildCtx(A) + for (c,e) in zip(Oscar.coefficients(a), Singular.exponent_words(a)) + push_term!(B, base_ring(A)(c), e) + end + return finish(B) +end + +_to_lpring(a::FreeAssociativeAlgebra, deg_bound::Int; ordering::Symbol=:deglex) = Singular.FreeAlgebra(base_ring(a), String.(symbols(a)), deg_bound; ordering=ordering) + +@doc raw""" + groebner_basis(I::FreeAssociativeAlgebraIdeal, deg_bound::Int=-1; protocol::Bool=false) + +Return the Groebner basis of `I` with respect to the degree bound `deg_bound`. +If `protocol` is `true`, the protocol of the computation is also returned. +The default value of `deg_bound` is `-1`, which means that no degree bound is +imposed, resulting in a computation that is usually slower, but will return a +full Groebner basis if there exists a finite one. +```jldoctest +julia> free, (x,y,z) = free_associative_algebra(QQ, ["x", "y", "z"]); + +julia> f1 = x*y + y*z; + +julia> f2 = x^2 + y^2; + +julia> I = ideal([f1, f2]); + +julia> gb = groebner_basis(I, 3; protocol=false) +Ideal generating system with elements + 1 -> x*y + y*z + 2 -> x^2 + y^2 + 3 -> y^3 + y*z^2 + 4 -> y^2*x + y*z*y +``` +""" +function groebner_basis(I::FreeAssociativeAlgebraIdeal, + deg_bound::Int=-1; + protocol::Bool=false, + interreduce::Bool=false) + isdefined(I, :gb) && (I.deg_bound == -1 || I.deg_bound >= deg_bound) && return I.gb + I.gb = groebner_basis(IdealGens(gens(I)), deg_bound; ordering=:deglex, protocol=protocol, interreduce=interreduce) + I.deg_bound = deg_bound + return I.gb +end +function groebner_basis(g::IdealGens{<:FreeAssociativeAlgebraElem}, + deg_bound::Int=-1; + ordering::Symbol=:deglex, + protocol::Bool=false, + interreduce::Bool=false) + gb = groebner_basis(collect(g), deg_bound; ordering=ordering, protocol=protocol, interreduce=interreduce) + return IdealGens(gb) +end +function groebner_basis(g::Vector{<:FreeAssociativeAlgebraElem}, + deg_bound::Int=-1; + ordering::Symbol=:deglex, + protocol::Bool=false, + interreduce::Bool=false) + + R = parent(g[1]) + @req all(x -> parent(x) == R, g) "parent mismatch" + @req deg_bound >= 0 || !protocol "computing with a protocol requires a degree bound" + @req ordering == :deglex || deg_bound > 0 "only :deglex ordering is supported for no degree bound" + + if deg_bound == -1 + gb = AbstractAlgebra.groebner_basis(g) + interreduce && return interreduce!(gb) + return gb + end + + lpring, _ = _to_lpring(R, deg_bound; ordering=ordering) + lp_I_gens = lpring.(g) + + I = Singular.Ideal(lpring, lp_I_gens) + gb = nothing + + Singular.with_prot(protocol) do; + gb = gens(Singular.std(I)) + end + gb = R.(gb) + interreduce && return interreduce!(gb) + return gb +end + +is_groebner_basis(g::IdealGens{<:FreeAssociativeAlgebraElem}) = is_groebner_basis(collect(g)) + +function is_groebner_basis(gb::Vector{<:FreeAssociativeAlgebraElem}) + obsructions = AbstractAlgebra.Generic.get_obstructions(gb) + while !isempty(obsructions) + f = AbstractAlgebra.Generic.s_polynomial(popfirst!(obsructions)[1]) + iszero(normal_form(f, gb)) || return false + end + return true +end + + +function interreduce!(I::FreeAssociativeAlgebraIdeal) + I.gb = interreduce!(I.gb) + return I +end +function interreduce!(gb::IdealGens{<:FreeAssociativeAlgebraElem}) + return IdealGens(interreduce!(collect(gb))) +end diff --git a/src/Rings/Rings.jl b/src/Rings/Rings.jl index 15838de1fe2f..7ec21f35cf30 100644 --- a/src/Rings/Rings.jl +++ b/src/Rings/Rings.jl @@ -34,7 +34,7 @@ include("binomial_ideals.jl") include("PBWAlgebra.jl") include("PBWAlgebraQuo.jl") -include("FreeAssAlgIdeal.jl") +include("FreeAssociativeAlgebraIdeal.jl") include("hilbert.jl") include("primary_decomposition_helpers.jl") include("resultant.jl") diff --git a/src/Serialization/Algebras.jl b/src/Serialization/Algebras.jl index b8f0b95d305e..a2b34ac2bcb9 100644 --- a/src/Serialization/Algebras.jl +++ b/src/Serialization/Algebras.jl @@ -1,28 +1,28 @@ ################################################################################ -# FreeAssAlgebra +# FreeAssociativeAlgebra # Free associative algebra serialization -@register_serialization_type FreeAssAlgebra uses_id +@register_serialization_type FreeAssociativeAlgebra uses_id -function save_object(s::SerializerState, A::FreeAssAlgebra) +function save_object(s::SerializerState, A::FreeAssociativeAlgebra) save_data_dict(s) do save_typed_object(s, base_ring(A), :base_ring), save_object(s, symbols(A), :symbols) end end -function load_object(s::DeserializerState, ::Type{<:FreeAssAlgebra}) +function load_object(s::DeserializerState, ::Type{<:FreeAssociativeAlgebra}) R = load_typed_object(s, :base_ring) gens = load_object(s, Vector{Symbol}, :symbols) return free_associative_algebra(R, gens)[1] end # Free associative algebra element serialization -@register_serialization_type FreeAssAlgElem uses_params +@register_serialization_type FreeAssociativeAlgebraElem uses_params # see save_type_params in Rings -function save_object(s::SerializerState, f::FreeAssAlgElem) +function save_object(s::SerializerState, f::FreeAssociativeAlgebraElem) save_data_array(s) do for term in terms(f) save_data_array(s) do @@ -33,7 +33,7 @@ function save_object(s::SerializerState, f::FreeAssAlgElem) end end -function load_object(s::DeserializerState, ::Type{<:FreeAssAlgElem}, parents::Vector) +function load_object(s::DeserializerState, ::Type{<:FreeAssociativeAlgebraElem}, parents::Vector) parent_algebra = parents[end] coeff_type = elem_type(base_ring(parent_algebra)) elem = MPolyBuildCtx(parent_algebra) @@ -53,4 +53,4 @@ function load_object(s::DeserializerState, ::Type{<:FreeAssAlgElem}, parents::Ve end # Ideals -@register_serialization_type FreeAssAlgIdeal uses_params +@register_serialization_type FreeAssociativeAlgebraIdeal uses_params diff --git a/src/Serialization/Rings.jl b/src/Serialization/Rings.jl index 7637c2804e15..dfc1415f5b98 100644 --- a/src/Serialization/Rings.jl +++ b/src/Serialization/Rings.jl @@ -2,10 +2,10 @@ # Common union types # this will need a better name at some point -const RingMatElemUnion = Union{RingElem, MatElem, FreeAssAlgElem, SMat} +const RingMatElemUnion = Union{RingElem, MatElem, FreeAssociativeAlgebraElem, SMat} # this union will also need a better name at some point -const RingMatSpaceUnion = Union{Ring, MatSpace, SMatSpace, FreeAssAlgebra} +const RingMatSpaceUnion = Union{Ring, MatSpace, SMatSpace, FreeAssociativeAlgebra} ################################################################################ # Utility functions for ring parent tree @@ -292,7 +292,7 @@ end # way to abstract saving params soon const IdealOrdUnionType = Union{MPolyIdeal, LaurentMPolyIdeal, - FreeAssAlgIdeal, + FreeAssociativeAlgebraIdeal, IdealGens, MonomialOrdering} diff --git a/src/Serialization/Upgrades/0.15.0.jl b/src/Serialization/Upgrades/0.15.0.jl index 10d8a2fb7c2a..eb4a6b8ead0a 100644 --- a/src/Serialization/Upgrades/0.15.0.jl +++ b/src/Serialization/Upgrades/0.15.0.jl @@ -5,9 +5,6 @@ push!(upgrade_scripts_set, UpgradeScript( v"0.15.0", function upgrade_0_15_0(s::UpgradeState, dict::Dict) - - upgraded_dict = dict - renamings = Dict{String,String}([ ("FlintPadicField", "PadicField"), ("padic", "PadicFieldElem"), @@ -31,24 +28,8 @@ push!(upgrade_scripts_set, UpgradeScript( ("EmbeddedElem", "EmbeddedNumFieldElem"), ]) - function upgrade_type(d::String) - return get(renamings, d, d) - end - function upgrade_type(d::Dict) - upg_d = d - upg_d[:name] = get(renamings, d[:name], d[:name]) - if d[:params] isa Dict && haskey(d[:params], :_type) - upg_d[:params][:_type] = upgrade_type(d[:params][:_type]) - elseif d[:params] isa Vector - upg_d[:params] = [upgrade_type(v) for v in d[:params]] - end - return upg_d - end - - if haskey(dict, :_type) - upgraded_dict[:_type] = upgrade_type(dict[:_type]) - end - + upgraded_dict = upgrade_types(dict, renamings) + if haskey(dict, :data) && dict[:data] isa Dict upgraded_dict[:data] = upgrade_0_15_0(s, dict[:data]) end diff --git a/src/Serialization/Upgrades/1.2.0.jl b/src/Serialization/Upgrades/1.2.0.jl new file mode 100644 index 000000000000..7f828705c15f --- /dev/null +++ b/src/Serialization/Upgrades/1.2.0.jl @@ -0,0 +1,32 @@ +################################################################################ +# Upgrade Summary +# +# `FreeAssAlgebra`: We renamed `FreeAssAlgebra` to `FreeAssociativeAlgebra`, +# as well as `FreeAssAlgElem` to `FreeAssociativeAlgebraElem` and this upgrade script takes care of this renaming. + +push!(upgrade_scripts_set, UpgradeScript( + v"1.2.0", + function upgrade_1_2_0(s::UpgradeState, dict::Dict) + renamings = Dict{String,String}([ + ("FreeAssAlgebra", "FreeAssociativeAlgebra"), + ("FreeAssAlgElem", "FreeAssociativeAlgebraElem"), + ("FreeAssAlgIdeal", "FreeAssociativeAlgebraIdeal"), + ]) + + upgraded_dict = upgrade_types(dict, renamings) + + if haskey(dict, :data) && dict[:data] isa Dict + upgraded_dict[:data] = upgrade_1_2_0(s, dict[:data]) + end + + if haskey(dict, :_refs) + upgraded_refs = Dict() + for (k, v) in dict[:_refs] + upgraded_refs[k] = upgrade_1_2_0(s, v) + end + upgraded_dict[:_refs] = upgraded_refs + end + + return upgraded_dict + end +)) diff --git a/src/Serialization/Upgrades/main.jl b/src/Serialization/Upgrades/main.jl index 8f16414cc99c..56345cf0ed17 100644 --- a/src/Serialization/Upgrades/main.jl +++ b/src/Serialization/Upgrades/main.jl @@ -87,12 +87,53 @@ function upgrade_data(upgrade::Function, s::UpgradeState, dict::Dict) return upgraded_dict end +# upgrades all types in dict based on renamings +function upgrade_types(dict::Dict, renamings::Dict{String, String}) + function upgrade_type(d::String) + return get(renamings, d, d) + end + + function upgrade_type(v::Vector) + return map(upgrade_type, v) + end + + function upgrade_type(d::Dict) + upg_d = d + + if haskey(d, :name) + upg_d[:name] = get(renamings, d[:name], d[:name]) + else + upg_d[:_type] = get(renamings, d[:_type], d[:_type]) + return upg_d + end + + if d[:params] isa Dict + if haskey(d[:params], :_type) + upg_d[:params][:_type] = upgrade_type(d[:params][:_type]) + else + for (k, v) in d[:params] + upg_d[:params][k] = upgrade_type(d[:params][k]) + end + end + elseif d[:params] isa Vector + upg_d[:params] = upgrade_type(d[:params]) + end + return upg_d + end + + if haskey(dict, :_type) + dict[:_type] = upgrade_type(dict[:_type]) + end + return dict +end + include("0.11.3.jl") include("0.12.0.jl") include("0.12.2.jl") include("0.13.0.jl") include("0.15.0.jl") include("1.1.0.jl") +include("1.2.0.jl") const upgrade_scripts = collect(upgrade_scripts_set) sort!(upgrade_scripts; by=version) diff --git a/src/deprecations.jl b/src/deprecations.jl index 2f577d91222a..fb2d27a26d9a 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -136,3 +136,6 @@ Base.@deprecate_binding MPolyRingElemLoc MPolyLocRingElem # deprecated for 1.2 Base.@deprecate_binding QQAbElem QQAbFieldElem + +Base.@deprecate_binding FreeAssAlgIdeal FreeAssociativeAlgebraIdeal + diff --git a/src/exports.jl b/src/exports.jl index 685e38a4f45a..b27a7f2553a0 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -706,6 +706,7 @@ export inradical export integral_basis export integrate export interior_lattice_points +export interreduce! export intersect export intersection_form export intersection_multiplicity diff --git a/src/imports.jl b/src/imports.jl index a0c09fbf2144..d7e8251a76f3 100644 --- a/src/imports.jl +++ b/src/imports.jl @@ -75,6 +75,7 @@ import AbstractAlgebra: gen, Generic, Generic.finish, + Generic.interreduce!, Generic.MPolyBuildCtx, Generic.MPolyCoeffs, Generic.MPolyExponentVectors, diff --git a/test/Rings/FreeAssAlgIdeal.jl b/test/Rings/FreeAssAlgIdeal.jl deleted file mode 100644 index c07a431e9490..000000000000 --- a/test/Rings/FreeAssAlgIdeal.jl +++ /dev/null @@ -1,64 +0,0 @@ -@testset "FreeAssAlgIdeal.basic" begin - Zt = polynomial_ring(ZZ, "t")[1] - R, (x, y, z) = free_associative_algebra(Zt, ["x", "y", "z", "w"]) - I = ideal(R, [x*y*x, y*z^2]) - @test base_ring(I) == R - for p in gens(R) - @test parent(p) == R - end -end - -@testset "FreeAssAlgIdeal.printing" begin - R, (x, y, z) = free_associative_algebra(GF(5), ["x", "y", "z", "w"]) - I = ideal(R, [x*y*x, y*z^2]) - @test length(string(I)) > 3 -end - -@testset "FreeAssAlgIdeal.membership" begin - R, (x, y, z) = free_associative_algebra(QQ, ["x", "y", "z"]) - I = ideal(R, [x*y - y*x, x*z - z*x]) - @test !in(x, I, 5) - @test !in(x, I, 10) - @test in(x*y*z - y*z*x, I, 9) # 9 should be enough - - f1 = x*y + y*z - I2 = ideal([f1]) - @test !ideal_membership(x*y, I2, 3) - @test ideal_membership(f1, I2, 4) - @test ideal_membership(f1, I2) - @test !isdefined(I2, :gb) - gb = groebner_basis(I2, 3; protocol=true) - @test isdefined(I2, :gb) -end - -@testset "FreeAssAlgIdeal.utils" begin - R, (x, y, z) = free_associative_algebra(QQ, ["x", "y", "z"]) - I = Oscar.ideal(R, [x*y - y*x, x*z - z*x]) - @test isa(ngens(I),Int) - @test isequal(ngens(I),2) - @test isa(gen(I,ngens(I)),FreeAssAlgElem{QQFieldElem}) - @test isa(gens(I),Vector) - - lpring, _ = Oscar._to_lpring(R, 3) - @test isa(lpring,NCRing) - - _, (x, y, z) = Singular.FreeAlgebra(QQ, ["x", "y","z"],6) - free, _ = free_associative_algebra(QQ, ["x", "y", "z"]) - f1 = x*y + y*z - - F1 = free(f1) - @test isa(F1,FreeAssAlgElem) -end - -@testset "FreeAssAlgIdeal.groebner_basis" begin - free, (x,y,z) = free_associative_algebra(QQ, ["x", "y", "z"]) - f1 = x*y + y*z - f2 = x^2 + y^2 - I = ideal([f1, f2]) - - gb = groebner_basis(I, 3; protocol=true) - @test maximum(total_degree.(gb))==3 - @test isdefined(I, :gb) - gb2 = groebner_basis([f1, f2], 5; protocol=false) - @test maximum(total_degree.(gb2))==5 -end diff --git a/test/Rings/FreeAssociativeAlgebraIdeal.jl b/test/Rings/FreeAssociativeAlgebraIdeal.jl new file mode 100644 index 000000000000..9697ea3eb56f --- /dev/null +++ b/test/Rings/FreeAssociativeAlgebraIdeal.jl @@ -0,0 +1,116 @@ +@testset "FreeAssociativeAlgebraIdeal.basic" begin + Zt = polynomial_ring(ZZ, "t")[1] + R, (x, y, z) = free_associative_algebra(Zt, ["x", "y", "z", "w"]) + I = ideal(R, [x*y*x, y*z^2]) + @test base_ring(I) == R + for p in gens(R) + @test parent(p) == R + end +end + +@testset "FreeAssociativeAlgebraIdeal.printing" begin + R, (x, y, z) = free_associative_algebra(GF(5), ["x", "y", "z", "w"]) + I = ideal(R, [x*y*x, y*z^2]) + @test length(string(I)) > 3 +end + +@testset "FreeAssociativeAlgebraIdeal.membership" begin + R, (x, y, z) = free_associative_algebra(QQ, ["x", "y", "z"]) + I = ideal(R, [x*y - y*x, x*z - z*x]) + @test !ideal_membership(x, I, 5) + @test !ideal_membership(x, I, 10) + @test ideal_membership(x*y*z - y*z*x, I, 9) # 9 should be enough + + f1 = x*y + y*z + I2 = ideal([f1]) + @test !ideal_membership(x*y, I2, 3) + @test ideal_membership(f1, I2, 4) + @test ideal_membership(f1, I2.gens, 4) + @test ideal_membership(f1, I2) + @test ideal_membership(f1, I2.gens) + gb = groebner_basis(I2, 3; protocol=false) + @test isdefined(I2, :gb) + @test length(gens(I * I2)) == 2 +end + +@testset "FreeAssociativeAlgebraIdeal.utils" begin + R, (x, y, z) = free_associative_algebra(QQ, ["x", "y", "z"]) + I = ideal(R, [x*y - y*x, x*z - z*x]) + @test base_ring(I) == R + @test isa(ngens(I),Int) + @test isequal(ngens(I),2) + @test isa(gen(I,ngens(I)),FreeAssociativeAlgebraElem{QQFieldElem}) + @test isa(gens(I),Vector) + + lpring, _ = Oscar._to_lpring(R, 3) + @test isa(lpring,NCRing) + + _, (x, y, z) = Singular.FreeAlgebra(QQ, ["x", "y","z"],6) + free, _ = free_associative_algebra(QQ, ["x", "y", "z"]) + f1 = x*y + y*z + + F1 = free(f1) + @test isa(F1,FreeAssociativeAlgebraElem) +end + +@testset "FreeAssociativeAlgebraIdeal.groebner_basis" begin + free, (x,y,z) = free_associative_algebra(QQ, ["x", "y", "z"]) + f1 = x*y + y*z + f2 = x^2 + y^2 + I = ideal([f1, f2]) + + gb = groebner_basis(I, 3; protocol=false) + @test !is_groebner_basis(gb) + @test maximum(total_degree.(gb))==3 + @test isdefined(I, :gb) + gb2 = groebner_basis([f1, f2], 5; protocol=false) + @test maximum(total_degree.(gb2))==5 + @test !is_groebner_basis(gb2) +end + +@testset "FreeAssociativeAlgebraIdeal.groebner_basis.quantum_automorphism_group" begin + M = uniform_matroid(3,4) + qAut1 = gens(quantum_automorphism_group(M)) + gb1 = groebner_basis(qAut1; interreduce=true) + gb2 = groebner_basis(qAut1, 4,ordering=:deglex,interreduce=true) + @test sort(leading_monomial.(gb2)) == sort(leading_monomial.(gb1)) + @test is_groebner_basis(gb1) + @test is_groebner_basis(gb2) + I1 = ideal(gb1); I2 = ideal(gb2) + @test I1 == I2 + + qAut2 = quantum_symmetric_group(4) + gena = gens(qAut2) + + gb3 = groebner_basis(gena; interreduce=false) + I1 = ideal(copy(gb3)) + groebner_basis(I1) + @test length(gb3) == 146 + interreduce!(gb3) + @test length(I1.gb) == 146 + interreduce!(I1) + @test length(I1.gb) == 78 + @test length(gb3) == 78 + + + gb4 = groebner_basis(gena, 4,ordering=:deglex,interreduce=true) + @test is_groebner_basis(gb3) + @test is_groebner_basis(gb4) + @test sort(leading_monomial.(gb3)) == sort(leading_monomial.(gb4)) + + I1 = ideal(gb3); I2 = ideal(gb4) + @test gb4[end] in I1 + @test normal_form(gb4[end],I1) == 0 + @test I1 == I2 + + x = base_ring(I1)[1]; y = base_ring(I1)[7] + @test !(x*y - y*x in I1) + + gb5 = groebner_basis(gens(quantum_symmetric_group(3)); interreduce=true) + A = parent(gb5[1]) + I3 = ideal(Oscar.IdealGens(A, gb5)) + @test is_groebner_basis(I3.gens) + + x = base_ring(ideal(gb5))[1]; y = base_ring(ideal(gb5))[7] + @test x*y - y*x in ideal(gb5) +end diff --git a/test/Serialization/upgrades/file_version_less_than_1.2.0.json b/test/Serialization/upgrades/file_version_less_than_1.2.0.json new file mode 100644 index 000000000000..6758721f2054 --- /dev/null +++ b/test/Serialization/upgrades/file_version_less_than_1.2.0.json @@ -0,0 +1,158 @@ +{ + "_ns": { + "Oscar": [ + "https://github.com/oscar-system/Oscar.jl", + "1.1.0-DEV-bd8e18b45854187743733b0ba1b9c56c27f7f73a" + ] + }, + "_type": { + "name": "Dict", + "params": { + "key_type": "String", + "revlex_basis_encoding": "String", + "Aut_bases_timed": "Float64", + "Aut_bases_lp": { + "name": "Vector", + "params": { + "name": "FreeAssAlgElem", + "params": "235311eb-8527-4a86-84b4-f146ec0a12ba" + } + }, + "Aut_bases": { + "name": "Vector", + "params": { + "name": "FreeAssAlgElem", + "params": "235311eb-8527-4a86-84b4-f146ec0a12ba" + } + }, + "Aut_bases_lp_timed": "Float64", + "Aut_bases_lp_prot": { + "name": "Vector", + "params": "UInt8" + }, + "Aut_bases_lp_degree_bound": "Base.Int" + } + }, + "data": { + "revlex_basis_encoding": "*", + "Aut_bases_timed": "0.073384228", + "Aut_bases_lp": [ + [ + [ + [ + "1" + ], + "1" + ], + [ + [], + "-1" + ] + ] + ], + "Aut_bases": [ + [ + [ + [ + "1", + "1" + ], + "1" + ], + [ + [ + "1" + ], + "-1" + ] + ], + [ + [ + [ + "1" + ], + "1" + ], + [ + [], + "-1" + ] + ], + [ + [ + [ + "1" + ], + "1" + ], + [ + [], + "-1" + ] + ] + ], + "Aut_bases_lp_timed": "0.030181131", + "Aut_bases_lp_prot": [ + "49", + "40", + "50", + "41", + "115", + "50", + "45", + "10", + "112", + "114", + "111", + "100", + "117", + "99", + "116", + "32", + "99", + "114", + "105", + "116", + "101", + "114", + "105", + "111", + "110", + "58", + "48", + "32", + "99", + "104", + "97", + "105", + "110", + "32", + "99", + "114", + "105", + "116", + "101", + "114", + "105", + "111", + "110", + "58", + "48", + "10" + ], + "Aut_bases_lp_degree_bound": "3" + }, + "_refs": { + "235311eb-8527-4a86-84b4-f146ec0a12ba": { + "_type": "FreeAssAlgebra", + "data": { + "base_ring": { + "_type": "QQField" + }, + "symbols": [ + "u[1,1]" + ] + } + } + } +} diff --git a/test/Serialization/upgrades/runtests.jl b/test/Serialization/upgrades/runtests.jl index a66641b0d2c3..01dad397d4d4 100644 --- a/test/Serialization/upgrades/runtests.jl +++ b/test/Serialization/upgrades/runtests.jl @@ -21,4 +21,10 @@ loaded_p = load(joinpath(@__DIR__, "file_version_less_than_0.12.0.json"); params=Rx); @test p == loaded_p end + + @testset "< 1.2.0 Upgrade" begin + # test loading + loaded = load(joinpath(@__DIR__, "file_version_less_than_1.2.0.json")); + @test loaded isa Dict + end end