Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to FreeAssAlgIdeal #4035

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9e88254
some changes
Sequenzer Aug 14, 2024
c41b886
Merge branch 'oscar-system:master' into feature/ncgroebner-termorder
Sequenzer Aug 21, 2024
0e42541
added ordering support, as well as minor changes, plus some tests
Sequenzer Aug 21, 2024
217c9d2
in to is_subset
Sequenzer Aug 21, 2024
fd814e7
fixed the slowdown caused by is_groebner_basis (The function is still…
Sequenzer Aug 21, 2024
aa4d7c7
improving codecov
Sequenzer Aug 21, 2024
5fa7b08
even more tests for coverage
Sequenzer Aug 22, 2024
ed1643f
added Lars suggestion for hash function
Sequenzer Aug 27, 2024
5532ef1
`FreeAssAlgIdeal` -> `FreeAssociativeAlgebraIdeal`
Sequenzer Aug 29, 2024
b02dec0
update AbstractAlgebra and rename FreeAssAlgebra
Sequenzer Aug 29, 2024
f95b0b2
`FreeAssAlgElem` -> `FreeAssociativeAlgebraElem`
Sequenzer Aug 29, 2024
1f14480
upgrade script
Sequenzer Aug 29, 2024
87b9206
fix of kwarg change in FreeAssAlgebraIdeal
Sequenzer Aug 29, 2024
52bd763
removed the check_groebner_basis functionality
Sequenzer Aug 29, 2024
af5a479
switched out assert, added a base_ring check to is_subset
Sequenzer Aug 29, 2024
8990f92
refactored upgrade types + fix upgrade script for dicts + test
antonydellavecchia Aug 29, 2024
2709e35
fix tests
antonydellavecchia Aug 30, 2024
473d8f1
fixes backwards compatibility test
antonydellavecchia Sep 2, 2024
3255695
Merge branch 'master' into feature/ncgroebner-termorder
lgoettgens Sep 4, 2024
80387d3
Merge branch 'master' into feature/ncgroebner-termorder
lgoettgens Sep 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 78 additions & 25 deletions src/Rings/FreeAssAlgIdeal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,24 @@ 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
deg_bound::Int
function FreeAssAlgIdeal(g::IdealGens{T}, check_groebner_basis::Bool=false) where T <: FreeAssAlgElem
Sequenzer marked this conversation as resolved.
Show resolved Hide resolved
r = new{T}()
r.gens = IdealGens(R, g)
r.gens = g
@req !check_groebner_basis || base_ring(base_ring(g)) isa Field "Groebner basis check requires a field base ring"
if check_groebner_basis && base_ring(base_ring(g)) isa Field && is_groebner_basis(g)
Sequenzer marked this conversation as resolved.
Show resolved Hide resolved
r.gb = g
r.deg_bound = -1
end
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)]...)
Base.show(io::IO, a::FreeAssAlgIdeal) = print(io, "Ideal of ", base_ring(a), " with ", number_of_generators(a), " generators")

function Base.:(==)(I2::FreeAssAlgIdeal, I1::FreeAssAlgIdeal)
lgoettgens marked this conversation as resolved.
Show resolved Hide resolved
return is_subset(I1,I2) && is_subset(I2,I1)
Sequenzer marked this conversation as resolved.
Show resolved Hide resolved
end
@enable_all_show_via_expressify FreeAssAlgIdeal

@doc raw"""
ideal(R::FreeAssAlgebra, g::Vector{<:FreeAssAlgElem})
Expand All @@ -37,16 +43,20 @@ 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)
return FreeAssAlgIdeal(IdealGens(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 ideal(g::IdealGens{T}, check_groebner_basis::Bool=false) where T <: FreeAssAlgElem
return FreeAssAlgIdeal(g, check_groebner_basis)
Sequenzer marked this conversation as resolved.
Show resolved Hide resolved
end


function base_ring(I::FreeAssAlgIdeal{T}) where T
return I.gens.Ox::parent_type(T)
end
Expand Down Expand Up @@ -79,6 +89,9 @@ function Base.:*(a::FreeAssAlgIdeal{T}, b::FreeAssAlgIdeal{T}) where T
return ideal(R, [i*j for i in gens(a) for j in gens(b)])
end

AbstractAlgebra.normal_form(f::FreeAssAlgElem, I::FreeAssAlgIdeal) = normal_form(f, gens(I))
AbstractAlgebra.normal_form(f::FreeAssAlgElem, I::IdealGens{<:FreeAssAlgElem}) = normal_form(f, collect(I))

@doc raw"""
ideal_membership(a::FreeAssAlgElem, I::FreeAssAlgIdeal, deg_bound::Int)

Expand All @@ -98,24 +111,26 @@ true
```
"""
function ideal_membership(a::FreeAssAlgElem, I::FreeAssAlgIdeal, deg_bound::Int=-1)
return ideal_membership(a, IdealGens(gens(I)), deg_bound)
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::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)
gb = groebner_basis(I, deg_bound)
return iszero(normal_form(a, gb))
end

lp_I = Singular.Ideal(lpring, lpring.(gb))
return iszero(reduce(lpring(a), lp_I))
function Base.in(a::FreeAssAlgElem, I::FreeAssAlgIdeal)
return ideal_membership(a, I)
end

function Base.in(a::FreeAssAlgElem, I::FreeAssAlgIdeal, deg_bound::Int)
return ideal_membership(a, I, deg_bound)
function is_subset(I1::FreeAssAlgIdeal, I2::FreeAssAlgIdeal)
return all(x -> in(x, I2), gens(I1))
Sequenzer marked this conversation as resolved.
Show resolved Hide resolved
end

function (R::Singular.LPRing)(a::FreeAssAlgElem)
Expand All @@ -134,7 +149,7 @@ function (A::FreeAssAlgebra)(a::Singular.slpalg)
return finish(B)
end

_to_lpring(a::FreeAssAlgebra, deg_bound::Int) = Singular.FreeAlgebra(base_ring(a), String.(symbols(a)), deg_bound)
_to_lpring(a::FreeAssAlgebra, deg_bound::Int; ordering::Symbol=:deglex) = Singular.FreeAlgebra(base_ring(a), String.(symbols(a)), deg_bound; ordering=ordering)

@doc raw"""
groebner_basis(I::FreeAssAlgIdeal, deg_bound::Int=-1; protocol::Bool=false)
Expand All @@ -161,25 +176,41 @@ Ideal generating system with elements
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)
function groebner_basis(I::FreeAssAlgIdeal,
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{<:FreeAssAlgElem}, deg_bound::Int=-1; protocol::Bool=false)
gb = groebner_basis(collect(g), deg_bound, protocol=protocol)
function groebner_basis(g::IdealGens{<:FreeAssAlgElem},
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{<:FreeAssAlgElem}, deg_bound::Int=-1; protocol::Bool=false)
function groebner_basis(g::Vector{<:FreeAssAlgElem},
deg_bound::Int=-1;
ordering::Symbol=:deglex,
protocol::Bool=false,
interreduce::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"
@assert ordering == :deglex || deg_bound > 0 "only :deglex ordering is supported for no degree bound"

if deg_bound == -1
return AbstractAlgebra.groebner_basis(g)
gb = AbstractAlgebra.groebner_basis(g)
interreduce && return interreduce!(gb)
return gb
end

lpring, _ = _to_lpring(R, deg_bound)
lpring, _ = _to_lpring(R, deg_bound; ordering=ordering)
lp_I_gens = lpring.(g)

I = Singular.Ideal(lpring, lp_I_gens)
Expand All @@ -188,5 +219,27 @@ function groebner_basis(g::Vector{<:FreeAssAlgElem}, deg_bound::Int=-1; protocol
Singular.with_prot(protocol) do;
gb = gens(Singular.std(I))
end
return R.(gb)
gb = R.(gb)
interreduce && return interreduce!(gb)
return gb
end

is_groebner_basis(g::IdealGens{<:FreeAssAlgElem}) = is_groebner_basis(collect(g))

function is_groebner_basis(gb::Vector{<:FreeAssAlgElem})
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::FreeAssAlgIdeal)
I.gb = interreduce!(I.gb)
return I
end
function interreduce!(gb::IdealGens{<:FreeAssAlgElem})
return IdealGens(interreduce!(collect(gb)))
end
1 change: 1 addition & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@ export inradical
export integral_basis
export integrate
export interior_lattice_points
export interreduce!
export intersect
export intersection_form
export intersection_multiplicity
Expand Down
1 change: 1 addition & 0 deletions src/imports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import AbstractAlgebra:
gen,
Generic,
Generic.finish,
Generic.interreduce!,
Generic.MPolyBuildCtx,
Generic.MPolyCoeffs,
Generic.MPolyExponentVectors,
Expand Down
66 changes: 59 additions & 7 deletions test/Rings/FreeAssAlgIdeal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,26 @@ 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
@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 !isdefined(I2, :gb)
gb = groebner_basis(I2, 3; protocol=true)
@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 "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])
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)),FreeAssAlgElem{QQFieldElem})
Expand All @@ -56,9 +59,58 @@ end
f2 = x^2 + y^2
I = ideal([f1, f2])

gb = groebner_basis(I, 3; protocol=true)
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 "FreeAssAlgIdeal.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),true)
@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
Loading