From 24cb8c25e9475b2f4543ed7a60f51aaf342674ff Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 5 May 2022 15:28:40 +0200 Subject: [PATCH 01/80] add some Groebner machinery for FreeAssociativeAlgebra --- src/AbstractAlgebra.jl | 5 +- src/Generic.jl | 2 + src/generic/FreeAssAlgebraGroebner.jl | 459 ++++++++++++++++++++ test/NCRings-test.jl | 1 + test/generic/FreeAssAlgebraGroebner-test.jl | 18 + 5 files changed, 483 insertions(+), 2 deletions(-) create mode 100644 src/generic/FreeAssAlgebraGroebner.jl create mode 100644 test/generic/FreeAssAlgebraGroebner-test.jl diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index ec565be973..6ec3d9025f 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -424,10 +424,11 @@ import .Generic: abs_series, abs_series_type, downscale, enable_cache!, exp_gcd, exponent, exponent_vector, exponent_word, - finish, fit!, gcd, gcdx, + finish, fit!, + gcd, gcdx, groebner_basis, has_left_neighbor, has_bottom_neighbor, hash, hooklength, identity_map, - image_map, image_fn, intersection, + image_map, image_fn, interreduce!, intersection, inverse_fn, inverse_image_fn, inverse_mat, reverse_rows, reverse_rows!, inv!, invmod, diff --git a/src/Generic.jl b/src/Generic.jl index 4071137c7b..eee70a756e 100644 --- a/src/Generic.jl +++ b/src/Generic.jl @@ -142,6 +142,8 @@ include("generic/MapCache.jl") include("generic/Ideal.jl") +include("generic/FreeAssAlgebraGroebner.jl") + ############################################################################### # # Temporary miscellaneous files being moved from Hecke.jl diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl new file mode 100644 index 0000000000..fd92790e60 --- /dev/null +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -0,0 +1,459 @@ +############################################################################### +# +# FreeAssAlgebraGroebner.jl : free associative algebra R Groeber basis +# +############################################################################### + +export groebner_basis, interreduce! + +const groebner_debug_level = 0 + +# skip all of the extra length-checking +function _leading_word(a::FreeAssAlgElem{T}) where T + return a.exps[1] +end + +# normal form with leftmost word divisions +function normal_form( + f::FreeAssAlgElem{T}, + g::Vector{FreeAssAlgElem{T}} +) where T <: FieldElement + R = parent(f) + s = length(g) + rcoeffs = T[] + rexps = Vector{Int}[] + while length(f) > 0 + i = 1 + @label again + ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) + if !ok && i < s + i += 1 + @goto again + end + if ok + qi = divexact(f.coeffs[1], g[i].coeffs[1]) + f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation + else + push!(rcoeffs, f.coeffs[1]) + push!(rexps, f.exps[1]) + f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f)-1) + end + end + r = FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) + return r +end + +# weak normal form with leftmost word divisions +function normal_form_weak( + f::FreeAssAlgElem{T}, + g::Vector{FreeAssAlgElem{T}} +) where T <: FieldElement + R = parent(f) + s = length(g) + while length(f) > 0 + i = 1 + @label again + ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) + if !ok && i < s + i += 1 + @goto again + end + if ok + qi = divexact(f.coeffs[1], g[i].coeffs[1]) + f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation + else + break + end + end + return f +end + +function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T + i = 1 + while length(g) > 1 && length(g) >= i + r = normal_form(g[i], g[1:end .!= i]) + if iszero(r) + deleteat!(g, i) + elseif g[i] != r + g[i] = r + i = 1 + else + i += 1 + end + end + return g +end + +## checks whether there is an overlap between a and b at position i of b +# such that b[i:length(b)] = a[1:length(b)-i] +function check_left_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) + for j in 0:length(b)-i + if j >= length(a) + return false # this is a center overlap + end + if b[i+j] != a[j+1] + return false + end + end + return true +end + +### +# find all non-trivial left-obstructions of a and b +# i.e. all words w_1 and w_2^' s.t. w_1 a = b w_2^' +# where length(w_1) < length(b) and length(w_2^') < length(a) +# the return vector is of the form [(w_1, w_2^'), ...] +# if w_1 or w_2^' is empty, the corresponding obstruction is not returned +function left_obstructions(a::Vector{Int}, b::Vector{Int}) + v = Tuple{Vector{Int}, Vector{Int}}[]; + for i in 2:length(b) + if check_left_overlap(a, b, i) + if length(b)-i + 2 <= length(a) # w_2^' should not be empty! + push!(v, (b[1:i-1], a[length(b)-i + 2:length(a)])) + end + end + end + return v +end + + +### +# find all non-trivial right-obstructions of a and b +# i.e. all words w_2 and w_1^' s.t. a w_1^' = w_2 b +# where length(w_1^') < length(b) and length(w_2) < length(a) +# the return vector is of the form [(w_2, w_1^'), ...] +# if w_1^' or w_2 is empty, the corresponding obstruction is not returned +function right_obstructions(a::Vector{Int}, b::Vector{Int}) + return left_obstructions(b, a) +end + +### +# check, whether a is a true subword of b at index i +function check_center_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) + for j in 1:length(a) + if i+j -1 > length(b) + return false + end + if a[j] != b[i + j - 1] + return false + end + end + return true +end + +function center_obstructions_first_in_second(a::Vector{Int}, b::Vector{Int}) + v = Tuple{Vector{Int}, Vector{Int}}[] + for i in 1:length(b) + if check_center_overlap(a, b, i) + push!(v, (b[1:i-1], b[i + length(a): length(b)])) + end + end + return v +end + +## +# return all center obstructions of a and b, i.e. all (w_i, w_i^') +# such that either +# w_i a w_i^' = b +# or +# w_i b w_i^' = a +# either or both of w_i and w_i^' can be empty +function center_obstructions(a::Vector{Int}, b::Vector{Int}) + if length(a) > length(b) + return center_obstructions_first_in_second(b, a) + else + return center_obstructions_first_in_second(a, b) + end +end + +# all non-trivial ones +function obstructions(a::Vector{Int}, b::Vector{Int}) + one = Int[] # the empty word + res = Tuple{Vector{Int}, Vector{Int}, Vector{Int}, Vector{Int}}[] + for x in center_obstructions_first_in_second(b, a) + push!(res, (one, one, x[1], x[2])) + end + for x in center_obstructions_first_in_second(a, b) + push!(res, (x[1], x[2], one, one)) + end + for x in left_obstructions(a, b) + push!(res, (x[1], one, one, x[2])) + end + for x in left_obstructions(b, a) + push!(res, (one, x[2], x[1], one)) + end + for x in res + @assert vcat(x[1], a, x[2]) == vcat(x[3], b, x[4]) + end + return res +end + +# all non-trivial self obstructions +function obstructions(a::Vector{Int}) + one = Int[] # the empty word + res = Tuple{Vector{Int}, Vector{Int}, Vector{Int}, Vector{Int}}[] + for x in left_obstructions(a, a) + push!(res, (one, x[2], x[1], one)) + end + for x in res + @assert vcat(x[1], a, x[2]) == vcat(x[3], a, x[4]) + end + return res +end + + +# check whether w_2 = v w_1 for some word v +function is_subword_right(w_1::Vector{Int}, w_2::Vector{Int}) + if length(w_1) > length(w_2) + return false + end + for i in 0:length(w_1) - 1 + if w_1[length(w_1) - i] != w_2[length(w_2) - i] + return false + end + end + return true +end + + +# check whether w_2 = w_1 v for some word v +function is_subword_left(w_1::Vector{Int}, w_2::Vector{Int}) + if length(w_1) > length(w_2) + return false + end + for i in 1:length(w_1) + if w_1[i] != w_2[i] + return false + end + end + return true +end + + +### +# check if for obs1 = (w_i, w_i^'; u_j, u_j^') and obs2 = (w_k, w_k^'; v_l, v_l^') +# it holds that u_j == w v_l and u_j^' = v_l^' w^' for some w, w^' +# i.e. if obs2 is a subobstruction of obs1 +# both w and w^' might be empty +function is_subobstruction(obs1::NTuple{4, Vector{Int}}, obs2::NTuple{4, Vector{Int}}) +# if length(obs2[3]) > length(obs1[3]) || length(obs2[4]) > length(obs1[4]) +# return false +# end + if is_subword_right(obs2[3], obs1[3]) && is_subword_left(obs2[4], obs1[4]) + return true + else + return false + end +end + +# check whether there exists a w^'' such that +# w1 LM(g1) w2 = w1 LM(g1) w^'' LM(g2) u2 +function has_overlap(g1, g2, w1, w2, u1, u2) + lw1 = _leading_word(g1) + lw2 = _leading_word(g2) + concatenated_word = vcat(w1, lw1, w2) + for i in 1:length(w1) + c = popfirst!(concatenated_word) + @assert c == w1[i] + end + for i in 1:length(lw1) + c = popfirst!(concatenated_word) + @assert c == lw1[i] + end + for j in 0:length(u2)-1 + c = pop!(concatenated_word) + @assert c = u2[length(u2)-j] + end + if length(concatenated_word) < length(lw2) + return false + end + return is_subword_right(lw2, concatenated_word) # TODO maybe just comparing lengths should be sufficient +end + +function is_redundant( + obs::NTuple{4, Vector{Int}}, + obs_index::Int, + s::Int, + B::Matrix{Vector{NTuple{4, Vector{Int}}}} +) + # cases 4b + 4c + for j in 1:size(B)[1] + for k in 1:length(B[j, s]) + if is_subobstruction(obs, B[j, s][k]) + # case 4b + if length(obs[3]) - length(B[j, s][k][3]) + length(obs[4]) - length(B[j,s][k][4]) > 0 + return true + # case 4c + elseif obs_index > j + return true + elseif obs_index == j && + length(obs[3]) - length(B[j, s][k][3]) + length(obs[4]) - length(B[j,s][k][4]) == 0 && + word_gt(obs[1], B[j, s][k][1]) + return true + end + end + end + end + # case 4d + # size(B) should be (s, s) + # we want to iterate over all B[i, obs_index] with i < s + for i in 1:size(B)[1]-1 + for k in 1:length(B[i, obs_index]) + if is_subword_right(B[i, obs_index][k][3], obs[1]) && is_subword_left(B[i, obs_index][k][4], obs[2]) + if groebner_debug_level > 0 + show(obs) + show(B[i, obs_index][k]) + end + u1 = copy(obs[1]) + u2 = copy(obs[2]) + v1 = copy(B[i, obs_index][k][3]) + v2 = copy(B[i, obs_index][k][4]) + for i in 1:length(v1) + pop!(u1) + end + for j in 1:length(v2) + popfirst!(u2) + end + @assert word_cmp(vcat(u1, v1), obs[1]) == 0 + @assert word_cmp(vcat(v2, u2), obs[2]) == 0 + if !has_overlap(g[i], g[s], vcat(u1, B[i, obs_index][k][1]), vcat(B[i, obs_index][k][2], u2), obs[3], obs[4]) + return true + end + end + end + end + return false +end + +## s is the index of the newly added layer of obstructions in B +function remove_redundancies!( + B::Matrix{Vector{NTuple{4, Vector{Int}}}}, + s::Int, + g::Vector{FreeAssAlgElem{T}} +) where T + del_counter = 0 + for i in 1:s + k = 1 + while k <= length(B[i, s]) + if is_redundant(B[i, s][k], i, s, B) + deleteat!(B[i, s], k) + del_counter += 1 + else + k += 1 + end + end + end + # TODO case 4e from Thm 4.1 in Kreuzer Xiu +end + +function get_obstructions(g::Vector{FreeAssAlgElem{T}}, s::Int) where T + dummy_obstr = (Int[], Int[], Int[], Int[]) + empty_obstr_set = typeof(dummy_obstr)[] + new_B = typeof(empty_obstr_set)[ + if i > j + empty_obstr_set + elseif i == j + obstructions(_leading_word(g[i])) + else + obstructions(_leading_word(g[i]), _leading_word(g[j])) + end + for i in 1:s, j in 1:s] + obstr_count = 0 + for i in 1:s, j in 1:s + obstr_count += length(new_B[i,j]) + end + # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu + if groebner_debug_level > 0 + println("$obstr_count many obstructions") + end + return new_B +end + + +function add_obstructions( + g::Vector{FreeAssAlgElem{T}}, + B::Matrix{Vector{NTuple{4, Vector{Int64}}}}, + s::Int +) where T + dummy_obstr = (Int[], Int[], Int[], Int[]) + empty_obstr_set = typeof(dummy_obstr)[] + new_B = Vector{NTuple{4, Vector{Int64}}}[ + if i < s && j < s + # copy old entry + B[i, j] + elseif i > j + empty_obstr_set + elseif i == j + obstructions(_leading_word(g[i])) + else + obstructions(_leading_word(g[i]), _leading_word(g[j])) + end + for i in 1:s, j in 1:s] + remove_redundancies!(new_B, s, g) + return new_B +end + + +function groebner_basis_buchberger( + g::Vector{FreeAssAlgElem{T}}, + degbound = typemax(Int)::Int +) where T <: FieldElement + + g = copy(g) + R = parent(g[1]) + checked_obstructions = 0 + + # step 1 + s = length(g) + dummy_obstr = (Int[], Int[], Int[], Int[]) + empty_obstr_set = typeof(dummy_obstr)[] + + B = get_obstructions(g, s) + while true + @assert s == length(g) + i = j = 0 + o = dummy_obstr + # check all entries with i <= j + for jj in 1:s, ii in 1:jj + if !isempty(B[ii, jj]) + (i, j) = (ii, jj) + o = popfirst!(B[i, j]) + break + end + end + if !(i > 0 && j > 0) + # B is empty + return g + end + + # step3 + S = _sub_rest(mul_term(inv(leading_coefficient(g[i])), o[1], g[i], o[2]), + mul_term(inv(leading_coefficient(g[j])), o[3], g[j], o[4]), 1) + Sp = normal_form(S, g) # or normal_form_weak + checked_obstructions += 1 + if groebner_debug_level > 0 + if checked_obstructions % 5000 == 0 + println("checked $checked_obstructions obstructions") + end + end + if iszero(Sp) || total_degree(Sp) > degbound + continue + end + + # step4 + s += 1 + push!(g, Sp) + if groebner_debug_level > 0 + println("adding new obstructions! checked $checked_obstructions so far") + end + B = add_obstructions(g, B, s) + end +end + +function groebner_basis( + g::Vector{FreeAssAlgElem{T}}, + degbound = typemax(Int)::Int +) where T <: FieldElement + return groebner_basis_buchberger(g, degbound) +end + diff --git a/test/NCRings-test.jl b/test/NCRings-test.jl index 7a8382500b..b2f5e7a369 100644 --- a/test/NCRings-test.jl +++ b/test/NCRings-test.jl @@ -1,6 +1,7 @@ include("generic/MatrixAlgebra-test.jl") include("generic/NCPoly-test.jl") include("generic/FreeAssAlgebra-test.jl") +include("generic/FreeAssAlgebraGroebner-test.jl") @testset "NCRings.oftype" begin F = GF(3) diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl new file mode 100644 index 0000000000..23441e7607 --- /dev/null +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -0,0 +1,18 @@ +@testset "Generic.FreeAssAlgebra.groebner" begin + + R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) + g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, + (y*x)^3*t + (y*x)^2*t + t + s]) + @test length(g) >= 5 + + # Example 6.1 Kreuzer & Xiu + R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 5 + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 15 +end From d80fd56d86859878898aee76647564a2965a5d0b Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Wed, 11 May 2022 17:42:59 +0200 Subject: [PATCH 02/80] added an isless function for FreeAssAlgElems and changed the termination criterion for buchberger from a maxdegree to max nonzero reductions --- .gitignore | 1 + src/generic/FreeAssAlgebra.jl | 32 +++++++++++++++++++++++++++ src/generic/FreeAssAlgebraGroebner.jl | 16 +++++++++----- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 26419abff9..28ad349ca7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ doc/abstractalgebra.toc doc/build/assets doc/site temp +*~ diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 3d02d37d81..a75e45aa4d 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -339,6 +339,38 @@ function combine_like_terms!(z::FreeAssAlgElem{T}) where T return z end +function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T + if p == q + return false + end + l = 0 + if length(p.exps) < length(q.exps) + l = length(p.exps) + else + l = length(q.exps) + end + sort_terms!(p) + sort_terms!(q) + for i in 1:l + if word_gt(q.exps[i], p.exps[i]) + return true + elseif word_gt(p.exps[i], q.exps[i]) + return false + elseif p.coeffs[i] == q.coeffs[i] + return p.coeffs[i] < q.coeffs[i] + end + end + if length(p.exps) < length(q.exps) + return true + else + return false + end +end + +function <(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T + return isless(p, q) +end + ############################################################################### # # Arithmetic diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index fd92790e60..4768d7b790 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -3,7 +3,8 @@ # FreeAssAlgebraGroebner.jl : free associative algebra R Groeber basis # ############################################################################### - +include("../AbstractTypes.jl") +include("FreeAssAlgebra.jl") export groebner_basis, interreduce! const groebner_debug_level = 0 @@ -396,12 +397,13 @@ end function groebner_basis_buchberger( g::Vector{FreeAssAlgElem{T}}, - degbound = typemax(Int)::Int + reduction_bound = typemax(Int)::Int ) where T <: FieldElement g = copy(g) R = parent(g[1]) checked_obstructions = 0 + nonzero_reductions = 0 # step 1 s = length(g) @@ -436,9 +438,10 @@ function groebner_basis_buchberger( println("checked $checked_obstructions obstructions") end end - if iszero(Sp) || total_degree(Sp) > degbound + if iszero(Sp) continue end + nonzero_reductions += 1 # step4 s += 1 @@ -446,14 +449,17 @@ function groebner_basis_buchberger( if groebner_debug_level > 0 println("adding new obstructions! checked $checked_obstructions so far") end + if nonzero_reductions >= reduction_bound + return g + end B = add_obstructions(g, B, s) end end function groebner_basis( g::Vector{FreeAssAlgElem{T}}, - degbound = typemax(Int)::Int + reduction_bound = typemax(Int)::Int ) where T <: FieldElement - return groebner_basis_buchberger(g, degbound) + return groebner_basis_buchberger(g, reduction_bound) end From ebf71e3a78ff315ec851dd6391ec51ed6fe4226c Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 26 May 2022 21:21:01 +0200 Subject: [PATCH 03/80] replaced the divides_leftmost substring check with a kmp variant for asymptotic linear running time instead of quadratic and replaced the storage of obstructions by a priority queue to remove unnecessary running time there --- Project.toml | 4 + src/generic/FreeAssAlgebra.jl | 70 ++++++++--- src/generic/FreeAssAlgebraGroebner.jl | 174 ++++++++++++++------------ 3 files changed, 152 insertions(+), 96 deletions(-) diff --git a/Project.toml b/Project.toml index 15955d1aa9..a9882ec30e 100644 --- a/Project.toml +++ b/Project.toml @@ -3,11 +3,15 @@ uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" version = "0.26.0" [deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" +Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" +ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RandomExtensions = "fb686558-2515-59ef-acaa-46db3789a887" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index a75e45aa4d..87403932ce 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -508,24 +508,62 @@ function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) w end +function calc_suffix_match_vector(b::Vector{Int}) + pos = 1 + cnd = 0 + suffix_match_vector = Vector{Int}(undef, length(b) + 1) + suffix_match_vector[1] = -1 + while pos < length(b) + if b[pos + 1] == b[cnd + 1] + suffix_match_vector[pos + 1] = suffix_match_vector[cnd + 1] + else + suffix_match_vector[pos + 1] = cnd + while cnd >= 0 && b[pos+1] != b[cnd+1] + cnd = suffix_match_vector[cnd+1] + end + end + pos = pos + 1 + cnd = cnd + 1 + end + suffix_match_vector[pos + 1] = cnd + return suffix_match_vector +end + +function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}) + suffix_match_vector = calc_suffix_match_vector(b) + return word_divides_leftmost(a, b, suffix_match_vector) +end + # return (true, l, r) with a = l*b*r and length(l) minimal # or (false, junk, junk) if a is not two-sided divisible by b -function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}) - n = length(b) - for i in 0:length(a)-n - match = true - for j in 1:n - if b[j] != a[i+j] - match = false - break - end - end - if match - return (true, Int[a[k] for k in 1:i], - Int[a[k] for k in 1+i+n:length(a)]) - end - end - return (false, Int[], Int[]) +function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}, suffix_match_vector::Vector{Int}) + result_position = -1 + match = false + j = 0 + k = 0 + while j < length(a) + if b[k + 1] == a[j + 1] + j = j+1 + k = k+1 + if k == length(b) + result_position = j - k + match = true + break + end + else + k = suffix_match_vector[k + 1] + if k < 0 + j = j+1 + k = k+1 + end + end + + end + if match + return (true, Int[a[k] for k in 1:result_position], + Int[a[k] for k in 1+result_position+length(b):length(a)]) + end + return (false, Int[], Int[]) end # return (true, l, r) with a = l*b*r and length(r) minimal diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 4768d7b790..85dbccd327 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -1,13 +1,46 @@ ############################################################################### # -# FreeAssAlgebraGroebner.jl : free associative algebra R Groeber basis +# FreeAssAlgebraGroebner.jl : free associative algebra R Groebner basis # ############################################################################### -include("../AbstractTypes.jl") -include("FreeAssAlgebra.jl") +#include("../AbstractTypes.jl") +#include("FreeAssAlgebra.jl") export groebner_basis, interreduce! +using DataStructures + const groebner_debug_level = 0 +const Monomial = Vector{Int} + +abstract type Obstruction{T} end +""" +represents the overlap of the leading term of two polynomials +""" + +struct ObstructionTriple{T} <: Obstruction{T} + first_poly::FreeAssAlgElem{T} + second_poly::FreeAssAlgElem{T} + pre_and_suffixes::NTuple{4, Monomial} +end + +function FreeAssAlgElem{T}(R::FreeAssAlgebra{T}, mon::Monomial) where T + return FreeAssAlgElem{T}(R, [one(base_ring(R))], [mon], 1) +end + +""" +takes an obstruction triple (p, q, o) and returns the common multiple +of the leading terms of p and q defined by o +TODO documentation +""" +function common_multiple_leading_term(ot::ObstructionTriple{T}) where T + return FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * FreeAssAlgElem{T}(parent(ot.first_poly), _leading_word(ot.first_poly)) * FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) +end + +function s_polynomial(ot::ObstructionTriple{T}) where T + first_term = FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * ot.first_poly * FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) + second_term = FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[3]) * ot.second_poly * FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[4]) + return inv(leading_coefficient(ot.first_poly)) * first_term - inv(leading_coefficient(ot.second_poly)) * second_term +end # skip all of the extra length-checking function _leading_word(a::FreeAssAlgElem{T}) where T @@ -17,7 +50,8 @@ end # normal form with leftmost word divisions function normal_form( f::FreeAssAlgElem{T}, - g::Vector{FreeAssAlgElem{T}} + g::Vector{FreeAssAlgElem{T}}, + suffix_match_vectors::Vector{Vector{Int}} ) where T <: FieldElement R = parent(f) s = length(g) @@ -26,8 +60,8 @@ function normal_form( while length(f) > 0 i = 1 @label again - ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) - if !ok && i < s + ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1], suffix_match_vectors[i]) + if !ok && i < s i += 1 @goto again end @@ -271,7 +305,7 @@ function has_overlap(g1, g2, w1, w2, u1, u2) return is_subword_right(lw2, concatenated_word) # TODO maybe just comparing lengths should be sufficient end -function is_redundant( +function is_redundant(# TODO do we need g in the signature? obs::NTuple{4, Vector{Int}}, obs_index::Int, s::Int, @@ -347,51 +381,46 @@ function remove_redundancies!( # TODO case 4e from Thm 4.1 in Kreuzer Xiu end -function get_obstructions(g::Vector{FreeAssAlgElem{T}}, s::Int) where T - dummy_obstr = (Int[], Int[], Int[], Int[]) - empty_obstr_set = typeof(dummy_obstr)[] - new_B = typeof(empty_obstr_set)[ - if i > j - empty_obstr_set - elseif i == j - obstructions(_leading_word(g[i])) - else - obstructions(_leading_word(g[i]), _leading_word(g[j])) - end - for i in 1:s, j in 1:s] - obstr_count = 0 - for i in 1:s, j in 1:s - obstr_count += length(new_B[i,j]) - end - # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu - if groebner_debug_level > 0 - println("$obstr_count many obstructions") - end - return new_B +function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T + s = length(g) + result = PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}() + for i in 1:s, j in 1:i + if i == j + obs = obstructions(_leading_word(g[i])) + else + obs = obstructions(_leading_word(g[i]), _leading_word(g[j])) + end + for o in obs + triple = ObstructionTriple{T}(g[i], g[j], o) + enqueue!(result, triple, common_multiple_leading_term(triple)) + end + end + # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu + if groebner_debug_level > 0 + obstr_count = length(result) + println("$obstr_count many obstructions") + end + return result end -function add_obstructions( - g::Vector{FreeAssAlgElem{T}}, - B::Matrix{Vector{NTuple{4, Vector{Int64}}}}, - s::Int +function add_obstructions!( + obstruction_queue::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, + g::Vector{FreeAssAlgElem{T}} ) where T - dummy_obstr = (Int[], Int[], Int[], Int[]) - empty_obstr_set = typeof(dummy_obstr)[] - new_B = Vector{NTuple{4, Vector{Int64}}}[ - if i < s && j < s - # copy old entry - B[i, j] - elseif i > j - empty_obstr_set - elseif i == j - obstructions(_leading_word(g[i])) - else - obstructions(_leading_word(g[i]), _leading_word(g[j])) - end - for i in 1:s, j in 1:s] - remove_redundancies!(new_B, s, g) - return new_B + s = length(g) + for i in 1:s + if i == s + obs = obstructions(_leading_word(g[i])) + else + obs = obstructions(_leading_word(g[i]), _leading_word(g[s])) + end + for o in obs + triple = ObstructionTriple{T}(g[i], g[s], o) + enqueue!(obstruction_queue, triple, common_multiple_leading_term(triple)) + end + end + #remove_redundancies!(new_B, s, g) TODO match remove_redundancies to new types end @@ -401,40 +430,25 @@ function groebner_basis_buchberger( ) where T <: FieldElement g = copy(g) - R = parent(g[1]) checked_obstructions = 0 nonzero_reductions = 0 + # compute a vector of suffix matches for all elements of g + # to make normal form computation more efficient + suffix_match_vectors = Vector{Vector{Int}}(undef, length(g)) + for i in 1:length(g) + suffix_match_vectors[i] = calc_suffix_match_vector(g[i].exps[1]) + end # step 1 - s = length(g) - dummy_obstr = (Int[], Int[], Int[], Int[]) - empty_obstr_set = typeof(dummy_obstr)[] - - B = get_obstructions(g, s) - while true - @assert s == length(g) - i = j = 0 - o = dummy_obstr - # check all entries with i <= j - for jj in 1:s, ii in 1:jj - if !isempty(B[ii, jj]) - (i, j) = (ii, jj) - o = popfirst!(B[i, j]) - break - end - end - if !(i > 0 && j > 0) - # B is empty - return g - end - - # step3 - S = _sub_rest(mul_term(inv(leading_coefficient(g[i])), o[1], g[i], o[2]), - mul_term(inv(leading_coefficient(g[j])), o[3], g[j], o[4]), 1) - Sp = normal_form(S, g) # or normal_form_weak - checked_obstructions += 1 + obstruction_queue = get_obstructions(g) + while !isempty(obstruction_queue) + obstruction = dequeue!(obstruction_queue) + # step3 + S = s_polynomial(obstruction) + Sp = normal_form(S, g, suffix_match_vectors) # or normal_form_weak if groebner_debug_level > 0 - if checked_obstructions % 5000 == 0 + checked_obstructions += 1 + if checked_obstructions % 5000 == 0 println("checked $checked_obstructions obstructions") end end @@ -442,18 +456,18 @@ function groebner_basis_buchberger( continue end nonzero_reductions += 1 - # step4 - s += 1 push!(g, Sp) + push!(suffix_match_vectors, calc_suffix_match_vector(Sp.exps[1])) if groebner_debug_level > 0 println("adding new obstructions! checked $checked_obstructions so far") end if nonzero_reductions >= reduction_bound return g end - B = add_obstructions(g, B, s) + add_obstructions!(obstruction_queue, g) end + return g end function groebner_basis( From 2e851e1f41ce46176f6dce6f69b9d24fe16a55d1 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 9 Jun 2022 13:08:21 +0200 Subject: [PATCH 04/80] added an implementation of aho corasick --- src/generic/FreeAssAhoCorasick.jl | 112 ++++++++++++++++++++++++++ src/generic/FreeAssAlgebraGroebner.jl | 16 +++- 2 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 src/generic/FreeAssAhoCorasick.jl diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl new file mode 100644 index 0000000000..2ceca1605b --- /dev/null +++ b/src/generic/FreeAssAhoCorasick.jl @@ -0,0 +1,112 @@ +############################################################################### +# +# FreeAssAhoCorasick.jl : implement bulk divide check for leading terms of free associative Algebra elements +# for use e.g. in Groebner Basis computation +# +############################################################################### +using DataStructures + +const Word = Vector{Int} + +struct AhoCorasickAutomaton + goto::Vector{Dict{Int, Int}} + fail::Vector{Int} + output::Vector{Set{Word}} +end + +function AhoCorasickAutomaton(keywords::Vector{Word}) + automaton = AhoCorasickAutomaton([], [], []) + construct_goto!(automaton, keywords) + construct_fail!(automaton) + return automaton +end + +function lookup(automaton::AhoCorasickAutomaton, current_state::Int, next_letter::Int) + ret_value = get(automaton.goto[current_state], next_letter, nothing) + if current_state == 1 && isnothing(ret_value) + return 1 + end + return ret_value + +end + + +function Base.length(automaton::AhoCorasickAutomaton) + return length(automaton.goto) +end + + + +function new_state!(automaton) + push!(automaton.goto, Dict{Int, Int}()) + push!(automaton.output, Set{Word}()) + push!(automaton.fail, 1) + return length(automaton.goto) +end + +function enter!(automaton::AhoCorasickAutomaton, keyword::Word) + current_state = 1 + for c in keyword + new_state = get(automaton.goto[current_state], c, nothing) + if isnothing(new_state) + new_state = new_state!(automaton) + automaton.goto[current_state][c] = new_state + end + current_state = new_state + end + + push!(automaton.output[current_state], keyword) + +end + +function construct_goto!(automaton::AhoCorasickAutomaton, keywords::Vector{Word}) + new_state!(automaton) + for keyword in keywords + enter!(automaton, keyword) + end +end + +function construct_fail!(automaton::AhoCorasickAutomaton) + q = Queue{Int}() + for v in values(automaton.goto[1]) + enqueue!(q, v) + end + while !isempty(q) + current_state = dequeue!(q) + for k in keys(automaton.goto[current_state]) + new_state = lookup(automaton, current_state, k) + enqueue!(q, new_state) + state = automaton.fail[current_state] + while isnothing(lookup(automaton, state, k)) + state = automaton.fail[state] + end + automaton.fail[new_state] = lookup(automaton, state, k) + union!(automaton.output[new_state], automaton.output[automaton.fail[new_state]]) + end + end +end + +""" + +""" +function search(automaton::AhoCorasickAutomaton, word) + current_state = 1 + output_set = Set{Word}() + for i in 1:length(word) + c = word[i] + while true + next_state = lookup(automaton, current_state, c) + if !isnothing(next_state) + current_state = next_state + break + else + current_state = automaton.fail[current_state] + end + end + if !isempty(automaton.output[current_state]) + println(i) + end + union!(output_set, automaton.output[current_state]) + end + return output_set +end diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 85dbccd327..f1ae142955 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -5,6 +5,7 @@ ############################################################################### #include("../AbstractTypes.jl") #include("FreeAssAlgebra.jl") +include("FreeAssAhoCorasick.jl") export groebner_basis, interreduce! using DataStructures @@ -47,6 +48,14 @@ function _leading_word(a::FreeAssAlgElem{T}) where T return a.exps[1] end +function normal_form( + f::FreeAssAlgElem{T}, + g::Vector{FreeAssAlgElem{T}}, + aut::AhoCorasickAutomaton + ) where T + #TODO +end + # normal form with leftmost word divisions function normal_form( f::FreeAssAlgElem{T}, @@ -309,8 +318,9 @@ function is_redundant(# TODO do we need g in the signature? obs::NTuple{4, Vector{Int}}, obs_index::Int, s::Int, - B::Matrix{Vector{NTuple{4, Vector{Int}}}} -) + B::Matrix{Vector{NTuple{4, Vector{Int}}}}, + g::Vector{FreeAssAlgElem{T}} +) where T # cases 4b + 4c for j in 1:size(B)[1] for k in 1:length(B[j, s]) @@ -370,7 +380,7 @@ function remove_redundancies!( for i in 1:s k = 1 while k <= length(B[i, s]) - if is_redundant(B[i, s][k], i, s, B) + if is_redundant(B[i, s][k], i, s, B, g) deleteat!(B[i, s], k) del_counter += 1 else From 82104244d7b459492d8a05b03af696f5cae8340a Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 10 Jun 2022 10:31:20 +0200 Subject: [PATCH 05/80] started implementing normal form using aho corasick --- src/generic/FreeAssAhoCorasick.jl | 34 ++++++++++++++++----------- src/generic/FreeAssAlgebraGroebner.jl | 12 ++++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl index 2ceca1605b..ce0120cb2c 100644 --- a/src/generic/FreeAssAhoCorasick.jl +++ b/src/generic/FreeAssAhoCorasick.jl @@ -11,7 +11,7 @@ const Word = Vector{Int} struct AhoCorasickAutomaton goto::Vector{Dict{Int, Int}} fail::Vector{Int} - output::Vector{Set{Word}} + output::Vector{Tuple{Int, Word}} end function AhoCorasickAutomaton(keywords::Vector{Word}) @@ -39,14 +39,14 @@ end function new_state!(automaton) push!(automaton.goto, Dict{Int, Int}()) - push!(automaton.output, Set{Word}()) + push!(automaton.output, (typemax(Int), [])) push!(automaton.fail, 1) return length(automaton.goto) end -function enter!(automaton::AhoCorasickAutomaton, keyword::Word) +function enter!(automaton::AhoCorasickAutomaton, keyword::Word, current_index) current_state = 1 - for c in keyword + for c in keyword new_state = get(automaton.goto[current_state], c, nothing) if isnothing(new_state) new_state = new_state!(automaton) @@ -54,15 +54,17 @@ function enter!(automaton::AhoCorasickAutomaton, keyword::Word) end current_state = new_state end - - push!(automaton.output[current_state], keyword) - + if automaton.output[current_state][1] > current_index + automaton.output[current_state] = (current_index, keyword) + end end function construct_goto!(automaton::AhoCorasickAutomaton, keywords::Vector{Word}) new_state!(automaton) + current_index = 1 for keyword in keywords - enter!(automaton, keyword) + enter!(automaton, keyword, current_index) + current_index += 1 end end @@ -81,7 +83,10 @@ function construct_fail!(automaton::AhoCorasickAutomaton) state = automaton.fail[state] end automaton.fail[new_state] = lookup(automaton, state, k) - union!(automaton.output[new_state], automaton.output[automaton.fail[new_state]]) + if automaton.output[new_state][1] > automaton.output[automaton.fail[new_state]][1] + automaton.output[new_state] = automaton.output[automaton.fail[new_state]] # TODO check if this is the correct way to update output + end + end end end @@ -91,7 +96,8 @@ end """ function search(automaton::AhoCorasickAutomaton, word) current_state = 1 - output_set = Set{Word}() + output = [] +# Set{Tuple{Int, Tuple{Int, Word}}}() for i in 1:length(word) c = word[i] while true @@ -103,10 +109,10 @@ function search(automaton::AhoCorasickAutomaton, word) current_state = automaton.fail[current_state] end end - if !isempty(automaton.output[current_state]) - println(i) + if automaton.output[current_state][1] != typemax(Int) + push!(output, (i, automaton.output[current_state])) +# union!(output_set, (i, automaton.output[current_state])) end - union!(output_set, automaton.output[current_state]) end - return output_set + return output end diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index f1ae142955..b948a9ce56 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -48,14 +48,26 @@ function _leading_word(a::FreeAssAlgElem{T}) where T return a.exps[1] end +function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) + divisibles = search(aut, a) + output = divisibles[1] + for d in divisibles[2:length(divisibles)] +#TODO + end +end + +# implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel function normal_form( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton ) where T #TODO + while length(f) > 0 + end end + # normal form with leftmost word divisions function normal_form( f::FreeAssAlgElem{T}, From f3373109ce3458995e4d0346d817b23fa2190c88 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 10 Jun 2022 13:08:52 +0200 Subject: [PATCH 06/80] implemented a normal form computation using aho corasick --- src/generic/FreeAssAhoCorasick.jl | 7 +++---- src/generic/FreeAssAlgebraGroebner.jl | 22 +++++++++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl index ce0120cb2c..740e84b182 100644 --- a/src/generic/FreeAssAhoCorasick.jl +++ b/src/generic/FreeAssAhoCorasick.jl @@ -96,8 +96,6 @@ end """ function search(automaton::AhoCorasickAutomaton, word) current_state = 1 - output = [] -# Set{Tuple{Int, Tuple{Int, Word}}}() for i in 1:length(word) c = word[i] while true @@ -110,9 +108,10 @@ function search(automaton::AhoCorasickAutomaton, word) end end if automaton.output[current_state][1] != typemax(Int) - push!(output, (i, automaton.output[current_state])) + return (i, automaton.output[current_state]) + #push!(output, (i, automaton.output[current_state])) # union!(output_set, (i, automaton.output[current_state])) end end - return output + return nothing end diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index b948a9ce56..fe954aa037 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -49,11 +49,11 @@ function _leading_word(a::FreeAssAlgElem{T}) where T end function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) - divisibles = search(aut, a) - output = divisibles[1] - for d in divisibles[2:length(divisibles)] -#TODO + match = search(aut, a) + if isnothing(math) + return (false, [], []) end + return (true, a[1:match[1] - length(match[2])], a[match[1] + 1:length(a)], match[1]) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel @@ -62,9 +62,21 @@ function normal_form( g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton ) where T - #TODO + R = parent(f) + rexps = Vector{Int}[] + rcoeffs = T[] while length(f) > 0 + ok, left, right, match_index = gb_divides_leftmost_aho_corasick(f.exps[1], aut) + if ok + qi = divexact(f.coeffs[1], g[match_index].coeffs[1]) + f = _sub_rest(f, mul_term(qi, left, g[match_index], right), 1) + else + push!(rcoeffs, f.coeffs[1]) + push!(rexps, f.exps[1]) + f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f)-1) + end end + return FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) end From d8fa4d837084944fd93a6906b61f26a0fed5ce56 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 13 Jun 2022 12:03:26 +0200 Subject: [PATCH 07/80] implemented normal form using the more efficient substring matching algorithm --- src/generic/FreeAssAlgebraGroebner.jl | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index fe954aa037..70d0af9494 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -50,10 +50,10 @@ end function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) match = search(aut, a) - if isnothing(math) - return (false, [], []) + if isnothing(match) + return (false, [], [], -1) end - return (true, a[1:match[1] - length(match[2])], a[match[1] + 1:length(a)], match[1]) + return (true, a[1:match[1] - length(match[2])], a[match[1] + 1:length(a)], match[2][1]) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel @@ -466,12 +466,9 @@ function groebner_basis_buchberger( g = copy(g) checked_obstructions = 0 nonzero_reductions = 0 - # compute a vector of suffix matches for all elements of g + # compute the aho corasick automaton # to make normal form computation more efficient - suffix_match_vectors = Vector{Vector{Int}}(undef, length(g)) - for i in 1:length(g) - suffix_match_vectors[i] = calc_suffix_match_vector(g[i].exps[1]) - end + aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) # step 1 obstruction_queue = get_obstructions(g) @@ -479,7 +476,7 @@ function groebner_basis_buchberger( obstruction = dequeue!(obstruction_queue) # step3 S = s_polynomial(obstruction) - Sp = normal_form(S, g, suffix_match_vectors) # or normal_form_weak + Sp = normal_form(S, g, aut) # or normal_form_weak if groebner_debug_level > 0 checked_obstructions += 1 if checked_obstructions % 5000 == 0 @@ -492,7 +489,8 @@ function groebner_basis_buchberger( nonzero_reductions += 1 # step4 push!(g, Sp) - push!(suffix_match_vectors, calc_suffix_match_vector(Sp.exps[1])) + aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) + if groebner_debug_level > 0 println("adding new obstructions! checked $checked_obstructions so far") end From adfad26f000113498821724a96351a3e26b8722f Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 13 Jun 2022 12:24:04 +0200 Subject: [PATCH 08/80] added the previous version for benchmarking --- src/generic/FreeAssAlgebraGroebner.jl | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 70d0af9494..05a79be675 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -502,6 +502,63 @@ function groebner_basis_buchberger( return g end +""" +for benchmarking +""" +function groebner_basis_buchberger_slow( + g::Vector{FreeAssAlgElem{T}}, + reduction_bound = typemax(Int)::Int +) where T <: FieldElement + + g = copy(g) + checked_obstructions = 0 + nonzero_reductions = 0 + # compute a vector of suffix matches for all elements of g + # to make normal form computation more efficient + suffix_match_vectors = Vector{Vector{Int}}(undef, length(g)) + for i in 1:length(g) + suffix_match_vectors[i] = calc_suffix_match_vector(g[i].exps[1]) + end + + # step 1 + obstruction_queue = get_obstructions(g) + while !isempty(obstruction_queue) + obstruction = dequeue!(obstruction_queue) + # step3 + S = s_polynomial(obstruction) + Sp = normal_form(S, g, suffix_match_vectors) # or normal_form_weak + if groebner_debug_level > 0 + checked_obstructions += 1 + if checked_obstructions % 5000 == 0 + println("checked $checked_obstructions obstructions") + end + end + if iszero(Sp) + continue + end + nonzero_reductions += 1 + # step4 + push!(g, Sp) + push!(suffix_match_vectors, calc_suffix_match_vector(Sp.exps[1])) + if groebner_debug_level > 0 + println("adding new obstructions! checked $checked_obstructions so far") + end + if nonzero_reductions >= reduction_bound + return g + end + add_obstructions!(obstruction_queue, g) + end + return g +end + +function groebner_basis_slow( + g::Vector{FreeAssAlgElem{T}}, + reduction_bound = typemax(Int)::Int +) where T <: FieldElement + return groebner_basis_buchberger_slow(g, reduction_bound) +end + + function groebner_basis( g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int From 910674b44e48680720e7ea7bdd6047e2b792c8a3 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 13 Jun 2022 12:35:48 +0200 Subject: [PATCH 09/80] added manifest --- Manifest.toml | 705 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 705 insertions(+) create mode 100644 Manifest.toml diff --git a/Manifest.toml b/Manifest.toml new file mode 100644 index 0000000000..fef1aff2b8 --- /dev/null +++ b/Manifest.toml @@ -0,0 +1,705 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.7.2" +manifest_format = "2.0" + +[[deps.ATK_jll]] +deps = ["Artifacts", "Glib_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "58c36d8a1beeb12d63921bcfaa674baf30a1140e" +uuid = "7b86fcea-f67b-53e1-809c-8f1719c154e8" +version = "2.36.1+0" + +[[deps.AbstractTrees]] +git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.3.4" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.BenchmarkTools]] +deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] +git-tree-sha1 = "4c10eee4af024676200bc7752e536f858c6b8f93" +uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +version = "1.3.1" + +[[deps.Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.8+0" + +[[deps.Cairo]] +deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] +git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b" +uuid = "159f3aea-2a34-519c-b102-8c37f9878175" +version = "1.0.5" + +[[deps.Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.16.1+1" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "a985dc37e357a3b22b260a5def99f3530fb415d3" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.11.2" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] +git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.12.8" + +[[deps.Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "b153278a25dd42c65abbf4e62344f9d22e59191b" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.43.0" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[deps.DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "cc1a8e22627f33c789ab60b36a9132ac050bbf75" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.12" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.Dbus_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "97f1325c10bd02b1cc1882e9c2bf6407ba630ace" +uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" +version = "1.12.16+3" + +[[deps.DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[deps.Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.4.8+0" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "9267e5f50b0e12fdfd5a2455534345c4cf2c7f7a" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.14.0" + +[[deps.FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.4" + +[[deps.FlameGraphs]] +deps = ["AbstractTrees", "Colors", "FileIO", "FixedPointNumbers", "IndirectArrays", "LeftChildRightSiblingTrees", "Profile"] +git-tree-sha1 = "d9eee53657f6a13ee51120337f98684c9c702264" +uuid = "08572546-2f56-4bcf-ba4e-bab62c3a3f89" +version = "0.2.10" + +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.13.93+0" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.10.4+0" + +[[deps.FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.10+0" + +[[deps.GTK3_jll]] +deps = ["ATK_jll", "Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Libepoxy_jll", "Pango_jll", "Pkg", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXcomposite_jll", "Xorg_libXcursor_jll", "Xorg_libXdamage_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "Xorg_libXrender_jll", "at_spi2_atk_jll", "gdk_pixbuf_jll", "iso_codes_jll", "xkbcommon_jll"] +git-tree-sha1 = "b080a592525632d287aee4637a62682576b7f5e4" +uuid = "77ec8976-b24b-556a-a1bf-49a033a670a6" +version = "3.24.31+0" + +[[deps.Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + +[[deps.Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "a32d672ac2c967f3deb8a81d828afc739c838a06" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.68.3+2" + +[[deps.Graphics]] +deps = ["Colors", "LinearAlgebra", "NaNMath"] +git-tree-sha1 = "1c5a84319923bea76fa145d49e93aa4394c73fc2" +uuid = "a2bd30eb-e257-5431-a919-1863eab51364" +version = "1.1.1" + +[[deps.Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.14+0" + +[[deps.GroupsCore]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "9e1a5e9f3b81ad6a5c613d181664a0efc6fe6dd7" +uuid = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" +version = "0.4.0" + +[[deps.Gtk]] +deps = ["Cairo", "Cairo_jll", "Dates", "GTK3_jll", "Glib_jll", "Graphics", "JLLWrappers", "Libdl", "Librsvg_jll", "Pkg", "Reexport", "Serialization", "Test", "Xorg_xkeyboard_config_jll", "adwaita_icon_theme_jll", "gdk_pixbuf_jll", "hicolor_icon_theme_jll"] +git-tree-sha1 = "eb7302ec0e1690787f396bc882a7c681d430bfdb" +uuid = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44" +version = "1.2.1" + +[[deps.GtkObservables]] +deps = ["Cairo", "Colors", "Dates", "FixedPointNumbers", "Graphics", "Gtk", "IntervalSets", "LinearAlgebra", "Observables", "Reexport", "RoundingIntegers"] +git-tree-sha1 = "cf87f031fee932b90023ea37207c7a1de8caee6f" +uuid = "8710efd8-4ad6-11eb-33ea-2d5ceb25a41c" +version = "1.2.3" + +[[deps.HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] +git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "2.8.1+1" + +[[deps.IndirectArrays]] +git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" +uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" +version = "1.0.0" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.IntervalSets]] +deps = ["Dates", "Statistics"] +git-tree-sha1 = "ad841eddfb05f6d9be0bff1fa48dcae32f134a2d" +uuid = "8197267c-284f-5f27-9208-e0e47529a953" +version = "0.6.2" + +[[deps.JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.4.1" + +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.3" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "2.1.2+0" + +[[deps.LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "3.0.0+1" + +[[deps.LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.1+0" + +[[deps.LeftChildRightSiblingTrees]] +deps = ["AbstractTrees"] +git-tree-sha1 = "b864cb409e8e445688bc478ef87c0afe4f6d1f8d" +uuid = "1d6d02ad-be62-4b6b-8a6d-2f90e265016e" +version = "0.1.3" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[deps.LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.Libepoxy_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "18b65a0eff6b58546bec18065e73f8a04e83758d" +uuid = "42c93a91-0102-5b3f-8f9d-e41de60ac950" +version = "1.5.8+1" + +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+1" + +[[deps.Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.3.0+3" + +[[deps.Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.16.1+1" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.35.0+0" + +[[deps.Librsvg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pango_jll", "Pkg", "gdk_pixbuf_jll"] +git-tree-sha1 = "25d5e6b4eb3558613ace1c67d6a871420bfca527" +uuid = "925c91fb-5dd6-59dd-8e8c-345e74382d89" +version = "2.52.4+0" + +[[deps.Libtiff_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "c9551dd26e31ab17b86cbd00c2ede019c08758eb" +uuid = "89763e89-9b03-5906-acba-b20f662cd828" +version = "4.3.0+1" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.36.0+0" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.9" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[deps.MethodAnalysis]] +deps = ["AbstractTrees"] +git-tree-sha1 = "81e123ea81d6081fe8b733dbe79e1291d55cfb0f" +uuid = "85b6ec6f-f7df-4429-9514-a64bcd9ee824" +version = "0.4.6" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[deps.NaNMath]] +git-tree-sha1 = "b086b7ea07f8e38cf122f5016af580881ac914fe" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "0.3.7" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[deps.Observables]] +git-tree-sha1 = "dfd8d34871bc3ad08cd16026c1828e271d554db9" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.1" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" + +[[deps.OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[deps.PCRE_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488" +uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc" +version = "8.44.0+0" + +[[deps.Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3a121dfbba67c94a5bec9dde613c3d0cbcf3a12b" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.50.3+0" + +[[deps.Parsers]] +deps = ["Dates"] +git-tree-sha1 = "1285416549ccfcdf0c50d4997a94331e88d68413" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.3.1" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.40.1+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.3.0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.Profile]] +deps = ["Printf"] +uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" + +[[deps.ProfileView]] +deps = ["Cairo", "Colors", "FileIO", "FlameGraphs", "Graphics", "Gtk", "GtkObservables", "InteractiveUtils", "IntervalSets", "MethodAnalysis", "Preferences", "Profile", "UUIDs"] +git-tree-sha1 = "17c95da7223ca01bccedde748ae4ccfebb494106" +uuid = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7" +version = "1.5.1" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.RandomExtensions]] +deps = ["Random", "SparseArrays"] +git-tree-sha1 = "062986376ce6d394b23d5d90f01d81426113a3c9" +uuid = "fb686558-2515-59ef-acaa-46db3789a887" +version = "0.4.3" + +[[deps.Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + +[[deps.RoundingIntegers]] +git-tree-sha1 = "99acd97f396ea71a5be06ba6de5c9defe188a778" +uuid = "d5f540fe-1c90-5db3-b776-2e2f362d9394" +version = "1.1.0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.Wayland_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23" +uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" +version = "1.19.0+0" + +[[deps.Wayland_protocols_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" +uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" +version = "1.25.0+0" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.9.12+0" + +[[deps.XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.6.9+4" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.9+4" + +[[deps.Xorg_libXcomposite_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll"] +git-tree-sha1 = "7c688ca9c957837539bbe1c53629bb871025e423" +uuid = "3c9796d7-64a0-5134-86ad-79f8eb684845" +version = "0.4.5+4" + +[[deps.Xorg_libXcursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" +uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" +version = "1.2.0+4" + +[[deps.Xorg_libXdamage_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll"] +git-tree-sha1 = "fe4ffb2024ba3eddc862c6e1d70e2b070cd1c2bf" +uuid = "0aeada51-83db-5f97-b67e-184615cfc6f6" +version = "1.1.5+4" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.3+4" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "5.0.3+4" + +[[deps.Xorg_libXi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" +uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" +version = "1.7.10+4" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] +git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.4+4" + +[[deps.Xorg_libXrandr_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" +uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" +version = "1.5.2+4" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.10+4" + +[[deps.Xorg_libXtst_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXi_jll"] +git-tree-sha1 = "0c0a60851f44add2a64069ddf213e941c30ed93c" +uuid = "b6f176f1-7aea-5357-ad67-1d3e565ea1c6" +version = "1.2.3+4" + +[[deps.Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.0+3" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.13.0+3" + +[[deps.Xorg_libxkbfile_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2" +uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" +version = "1.1.0+4" + +[[deps.Xorg_xkbcomp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b" +uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" +version = "1.4.2+4" + +[[deps.Xorg_xkeyboard_config_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d" +uuid = "33bec58e-1273-512f-9401-5d533626f822" +version = "2.27.0+4" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.4.0+3" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[deps.Zstd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.2+0" + +[[deps.adwaita_icon_theme_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "hicolor_icon_theme_jll"] +git-tree-sha1 = "37c9a36ccb876e02876c8a654f1b2e8c1b443a78" +uuid = "b437f822-2cd6-5e08-a15c-8bac984d38ee" +version = "3.33.92+5" + +[[deps.at_spi2_atk_jll]] +deps = ["ATK_jll", "Artifacts", "JLLWrappers", "Libdl", "Pkg", "XML2_jll", "Xorg_libX11_jll", "at_spi2_core_jll"] +git-tree-sha1 = "f16ae690aca4761f33d2cb338ee9899e541f5eae" +uuid = "de012916-1e3f-58c2-8f29-df3ef51d412d" +version = "2.34.1+4" + +[[deps.at_spi2_core_jll]] +deps = ["Artifacts", "Dbus_jll", "Glib_jll", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXtst_jll"] +git-tree-sha1 = "d2d540cd145f2b2933614649c029d222fe125188" +uuid = "0fc3237b-ac94-5853-b45c-d43d59a06200" +version = "2.34.0+4" + +[[deps.gdk_pixbuf_jll]] +deps = ["Artifacts", "Glib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Xorg_libX11_jll", "libpng_jll"] +git-tree-sha1 = "c23323cd30d60941f8c68419a70905d9bdd92808" +uuid = "da03df04-f53b-5353-a52f-6a8b0620ced0" +version = "2.42.6+1" + +[[deps.hicolor_icon_theme_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b458a6f6fc2b1a8ca74ed63852e4eaf43fb9f5ea" +uuid = "059c91fe-1bad-52ad-bddd-f7b78713c282" +version = "0.17.0+3" + +[[deps.iso_codes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "5ee24c3ae30e006117ec2da5ea50f2ce457c019a" +uuid = "bf975903-5238-5d20-8243-bc370bc1e7e5" +version = "4.3.0+4" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.38+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" + +[[deps.xkbcommon_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] +git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6" +uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" +version = "0.9.1+5" From 8de2e0a28f9ca1308c4db0ffa27ec59f772c0857 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Wed, 15 Jun 2022 12:32:27 +0200 Subject: [PATCH 10/80] faster gb computation works now --- Manifest.toml | 42 ++++++++++++++++++++-- Project.toml | 1 + src/Generic.jl | 3 ++ src/generic/FreeAssAhoCorasick.jl | 25 +++++++++++--- src/generic/FreeAssAlgebra.jl | 2 +- src/generic/FreeAssAlgebraGroebner.jl | 50 +++++++++++++++++++++++---- 6 files changed, 110 insertions(+), 13 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index fef1aff2b8..c8c5056f15 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.7.2" +julia_version = "1.7.3" manifest_format = "2.0" [[deps.ATK_jll]] @@ -47,6 +47,12 @@ git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" version = "1.16.1+1" +[[deps.CodeTracking]] +deps = ["InteractiveUtils", "UUIDs"] +git-tree-sha1 = "6d4fa04343a7fc9f9cb9cff9558929f3d2752717" +uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" +version = "1.0.9" + [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] git-tree-sha1 = "a985dc37e357a3b22b260a5def99f3530fb415d3" @@ -69,6 +75,11 @@ version = "3.43.0" deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +[[deps.Crayons]] +git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.1.1" + [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] git-tree-sha1 = "cc1a8e22627f33c789ab60b36a9132ac050bbf75" @@ -85,6 +96,12 @@ git-tree-sha1 = "97f1325c10bd02b1cc1882e9c2bf6407ba630ace" uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" version = "1.12.16+3" +[[deps.Debugger]] +deps = ["CodeTracking", "Crayons", "Highlights", "InteractiveUtils", "JuliaInterpreter", "Markdown", "REPL"] +git-tree-sha1 = "2e1376ac793d255c8a312a99b961ef64ae60ffee" +uuid = "31a5f54b-26ea-5ae9-a837-f05ce5417438" +version = "0.7.6" + [[deps.DelimitedFiles]] deps = ["Mmap"] uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" @@ -93,8 +110,14 @@ uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +[[deps.DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.6" + [[deps.Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" [[deps.Expat_jll]] @@ -109,6 +132,9 @@ git-tree-sha1 = "9267e5f50b0e12fdfd5a2455534345c4cf2c7f7a" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" version = "1.14.0" +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + [[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" @@ -193,6 +219,12 @@ git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" version = "2.8.1+1" +[[deps.Highlights]] +deps = ["DocStringExtensions", "InteractiveUtils", "REPL"] +git-tree-sha1 = "d7e1d65e8599f2ee8df09c1461391e66ad9e2885" +uuid = "eafb193a-b7ab-5a9e-9068-77385905fa72" +version = "0.5.1" + [[deps.IndirectArrays]] git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" @@ -226,6 +258,12 @@ git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" version = "2.1.2+0" +[[deps.JuliaInterpreter]] +deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] +git-tree-sha1 = "52617c41d2761cc05ed81fe779804d3b7f14fff7" +uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" +version = "0.9.13" + [[deps.LERC_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" diff --git a/Project.toml b/Project.toml index a9882ec30e..c5e5b68ae1 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.26.0" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/src/Generic.jl b/src/Generic.jl index eee70a756e..c6d2372fc0 100644 --- a/src/Generic.jl +++ b/src/Generic.jl @@ -142,8 +142,11 @@ include("generic/MapCache.jl") include("generic/Ideal.jl") +include("generic/FreeAssAhoCorasick.jl") + include("generic/FreeAssAlgebraGroebner.jl") + ############################################################################### # # Temporary miscellaneous files being moved from Hecke.jl diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl index 740e84b182..168e6ac5fb 100644 --- a/src/generic/FreeAssAhoCorasick.jl +++ b/src/generic/FreeAssAhoCorasick.jl @@ -1,19 +1,32 @@ -############################################################################### # # FreeAssAhoCorasick.jl : implement bulk divide check for leading terms of free associative Algebra elements # for use e.g. in Groebner Basis computation # ############################################################################### +#TODO how to properly export this? +export search, AhoCorasickAutomaton, AhoCorasickMatch, Word + using DataStructures const Word = Vector{Int} +""" +Output stores for each node a tuple (i, k), where i is the index of the keyword k in +the original list of keywords. If several keywords would be the output of the node, only +the one with the smallest index is stored +""" struct AhoCorasickAutomaton goto::Vector{Dict{Int, Int}} fail::Vector{Int} output::Vector{Tuple{Int, Word}} end +struct AhoCorasickMatch + last_position::Int + keyword_index::Int + keyword::Word +end + function AhoCorasickAutomaton(keywords::Vector{Word}) automaton = AhoCorasickAutomaton([], [], []) construct_goto!(automaton, keywords) @@ -96,6 +109,7 @@ end """ function search(automaton::AhoCorasickAutomaton, word) current_state = 1 + result = AhoCorasickMatch(typemax(Int), typemax(Int), []) for i in 1:length(word) c = word[i] while true @@ -107,11 +121,14 @@ function search(automaton::AhoCorasickAutomaton, word) current_state = automaton.fail[current_state] end end - if automaton.output[current_state][1] != typemax(Int) - return (i, automaton.output[current_state]) + if automaton.output[current_state][1] < result.keyword_index + result = AhoCorasickMatch(i, automaton.output[current_state][1], automaton.output[current_state][2]) #push!(output, (i, automaton.output[current_state])) # union!(output_set, (i, automaton.output[current_state])) end end - return nothing + if result.keyword == [] + return nothing + end + return result end diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 87403932ce..67a17611a7 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -4,7 +4,7 @@ # ############################################################################### -export exponent_word, set_exponent_word +export exponent_word, set_exponent_word, calc_suffix_match_vector ############################################################################### # diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 05a79be675..f2e5bfdb05 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -5,8 +5,8 @@ ############################################################################### #include("../AbstractTypes.jl") #include("FreeAssAlgebra.jl") -include("FreeAssAhoCorasick.jl") -export groebner_basis, interreduce! +#include("FreeAssAhoCorasick.jl") +export groebner_basis, interreduce!, groebner_basis_slow, gb_divides_leftmost_aho_corasick, normal_form, get_obstructions, s_polynomial, add_obstructions! #TODO remove unnecessary exports using DataStructures @@ -53,7 +53,10 @@ function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) if isnothing(match) return (false, [], [], -1) end - return (true, a[1:match[1] - length(match[2])], a[match[1] + 1:length(a)], match[2][1]) +# println("a = $a") +# println("match = $match") +return (true, a[1:match.last_position - length(match.keyword)], a[match.last_position + 1:end], match.keyword_index) +#return (true, a[1:match[1] - length(match[2][2])], a[match[1] + 1:end], match[2][1]) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel @@ -81,6 +84,39 @@ end # normal form with leftmost word divisions +function normal_form( + f::FreeAssAlgElem{T}, + g::Vector{FreeAssAlgElem{T}}, +) where T <: FieldElement + R = parent(f) + s = length(g) + rcoeffs = T[] + rexps = Vector{Int}[] + while length(f) > 0 + i = 1 + @label again + ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) + if !ok && i < s + i += 1 + @goto again + end + if ok + qi = divexact(f.coeffs[1], g[i].coeffs[1]) + f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation + else + push!(rcoeffs, f.coeffs[1]) + push!(rexps, f.exps[1]) + f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f)-1) + end + end + r = FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) + return r +end + + + +# normal form with leftmost word divisions + function normal_form( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, @@ -432,7 +468,7 @@ function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu if groebner_debug_level > 0 obstr_count = length(result) - println("$obstr_count many obstructions") + #println("$obstr_count many obstructions") end return result end @@ -469,10 +505,10 @@ function groebner_basis_buchberger( # compute the aho corasick automaton # to make normal form computation more efficient aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - # step 1 obstruction_queue = get_obstructions(g) while !isempty(obstruction_queue) +# #println(length(obstruction_queue)) obstruction = dequeue!(obstruction_queue) # step3 S = s_polynomial(obstruction) @@ -480,12 +516,14 @@ function groebner_basis_buchberger( if groebner_debug_level > 0 checked_obstructions += 1 if checked_obstructions % 5000 == 0 - println("checked $checked_obstructions obstructions") + #println("checked $checked_obstructions obstructions") end end if iszero(Sp) + #println("is zero") continue end + #println(Sp) nonzero_reductions += 1 # step4 push!(g, Sp) From 46c8f38c6243765b9c0432c55e0db5828c7e98d0 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Wed, 22 Jun 2022 10:03:56 +0200 Subject: [PATCH 11/80] modified interreduce and made ahocorasick automata mutable --- src/generic/FreeAssAhoCorasick.jl | 10 ++- src/generic/FreeAssAlgebraGroebner.jl | 73 ++++++++++++---- test/generic/FreeAssAhoCorasick-test.jl | 14 ++++ test/generic/FreeAssAlgebraGroebner-test.jl | 93 +++++++++++++++++---- test/runtests_free_ass_groebner.jl | 9 ++ 5 files changed, 168 insertions(+), 31 deletions(-) create mode 100644 test/generic/FreeAssAhoCorasick-test.jl create mode 100644 test/runtests_free_ass_groebner.jl diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl index 168e6ac5fb..cb81168746 100644 --- a/src/generic/FreeAssAhoCorasick.jl +++ b/src/generic/FreeAssAhoCorasick.jl @@ -4,7 +4,7 @@ # ############################################################################### #TODO how to properly export this? -export search, AhoCorasickAutomaton, AhoCorasickMatch, Word +export search, AhoCorasickAutomaton, AhoCorasickMatch, Word, insert_keyword! using DataStructures @@ -15,7 +15,7 @@ Output stores for each node a tuple (i, k), where i is the index of the keyword the original list of keywords. If several keywords would be the output of the node, only the one with the smallest index is stored """ -struct AhoCorasickAutomaton +mutable struct AhoCorasickAutomaton goto::Vector{Dict{Int, Int}} fail::Vector{Int} output::Vector{Tuple{Int, Word}} @@ -104,6 +104,12 @@ function construct_fail!(automaton::AhoCorasickAutomaton) end end +function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) + enter!(aut, keyword, index) + aut.fail = ones(Int, length(aut.goto)) + construct_fail!(aut) +end + """ """ diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index f2e5bfdb05..e53287bee4 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -173,21 +173,42 @@ function normal_form_weak( end function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T - i = 1 - while length(g) > 1 && length(g) >= i - r = normal_form(g[i], g[1:end .!= i]) - if iszero(r) - deleteat!(g, i) - elseif g[i] != r - g[i] = r - i = 1 - else - i += 1 - end - end - return g + i = 1 + while length(g) > 1 && length(g) >= i + aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) + r = normal_form(g[i], g[1:end .!= i], aut) + if iszero(r) + deleteat!(g, i) + println(length(g)) + elseif g[i] != r + g[i] = r + #i = 1 + i += 1 + else + #println(i) + i += 1 + end + end + return g end + +#function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T +# i = 1 +# while length(g) > 1 && length(g) >= i +# r = normal_form(g[i], g[1:end .!= i]) +# if iszero(r) +# deleteat!(g, i) +# elseif g[i] != r +# g[i] = r +# i = 1 +# else +# i += 1 +# end +# end +# return g +#end + ## checks whether there is an overlap between a and b at position i of b # such that b[i:length(b)] = a[1:length(b)-i] function check_left_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) @@ -500,8 +521,13 @@ function groebner_basis_buchberger( ) where T <: FieldElement g = copy(g) + println(length(g)) + interreduce!(g) + println(length(g)) + checked_obstructions = 0 nonzero_reductions = 0 + interreduce_counter = 0 # compute the aho corasick automaton # to make normal form computation more efficient aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) @@ -527,14 +553,31 @@ function groebner_basis_buchberger( nonzero_reductions += 1 # step4 push!(g, Sp) - aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - + insert_keyword!(aut, Sp.exps[1], length(g)) + #aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) + + interreduce_counter += 1 + if interreduce_counter > 2000 + println("starting interreduce") + interreduce!(g) + println("done interreducing") + interreduce_counter = 0 + obstruction_queue = get_obstructions(g) + continue + end if groebner_debug_level > 0 println("adding new obstructions! checked $checked_obstructions so far") end if nonzero_reductions >= reduction_bound return g end + #debug + if nonzero_reductions > 2000 + println(length(obstruction_queue)) + println(length(g)) + println(nonzero_reductions) + end + #end debug add_obstructions!(obstruction_queue, g) end return g diff --git a/test/generic/FreeAssAhoCorasick-test.jl b/test/generic/FreeAssAhoCorasick-test.jl new file mode 100644 index 0000000000..297a928f86 --- /dev/null +++ b/test/generic/FreeAssAhoCorasick-test.jl @@ -0,0 +1,14 @@ +@testset "Generic.FreeAssAhoCorasick" begin + keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] + aut = AhoCorasickAutomaton(keywords) + @test search(aut, [10, 4, 1, 2, 3, 4]) == (6, (1, [1, 2, 3, 4])) + @test isnothing(search(aut, [])) + @test search(aut, [1, 5, 4, 1, 1, 1, 4, 4]) == (3, (2, [1, 5, 4])) + @test search(aut, [1, 2, 3, 1, 4, 1, 2, 1, 4, 1, 2]) == (7, (3, [4, 1, 2])) + @test search(aut, [2, 1, 2, 3, 1]) == (3, (4, [1, 2])) + @test isnothing(search(aut, [1, 3, 1, 5, 1, 4, 8])) + @test isnothing(search(aut, [8, 8, 7, 10, 456])) + @test search(aut, [4, 1, 5, 4]) == (4, (2, [1, 5, 4])) + @test isnothing(search(aut, [4, 1, 5, 10])) + @test !isnothing(AhoCorasickAutomaton(Vector{Int}[])) +end diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 23441e7607..2fb00de7bf 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -1,18 +1,83 @@ -@testset "Generic.FreeAssAlgebra.groebner" begin +#@testset "Generic.FreeAssAlgebra.groebner" begin +# +# R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) +# g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, +# (y*x)^3*t + (y*x)^2*t + t + s]) +# @test length(g) >= 5 +# +# # Example 6.1 Kreuzer & Xiu +# R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) +# +# g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) +# AbstractAlgebra.interreduce!(g) +# @test length(g) == 5 +# +# g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) +# AbstractAlgebra.interreduce!(g) +# @test length(g) == 15 +#end - R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) - g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, - (y*x)^3*t + (y*x)^2*t + t + s]) - @test length(g) >= 5 - - # Example 6.1 Kreuzer & Xiu - R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) +@testset "Generic.FreeAssociativeAlgebra.groebner.normal_form" begin + R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(QQ, ["x", "y", "u", "v", "t", "s"]) # x > y > ... > s + ideal_generators = [x*y, u*y*t, s*t - t*s, x*y*y + x*x*y - one(R)] + aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in ideal_generators]) + @test normal_form(x*y, ideal_generators, aut) == zero(R) + @test normal_form(u*y*t, ideal_generators, aut) == zero(R) + @test normal_form(s*t - t*s, ideal_generators, aut) == zero(R) + @test x*y*y + x*x*y - one(R) in ideal_generators + @test normal_form(x*y*t + y, ideal_generators, aut) == y + @test normal_form(one(R), ideal_generators, aut) == one(R) + @test normal_form(v*y, ideal_generators, aut) == v*y + @test normal_form(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators, aut) == zero(R) + @test gb_divides_leftmost_aho_corasick((x*y*u*v*t).exps[1], aut) == (true, [], [3, 4, 5], 1) + @test gb_divides_leftmost_aho_corasick((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) + @test gb_divides_leftmost_aho_corasick((x*s*t).exps[1], aut) == (false, [], [], -1) +end - g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) - AbstractAlgebra.interreduce!(g) - @test length(g) == 5 +include("/home/julien/uni/mathe/Promotion/ncmpoly/graph_to_ass_alg.jl") +using DataStructures - g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) - AbstractAlgebra.interreduce!(g) - @test length(g) == 15 +@testset "Generic.FreeAssociativeAlgebra.groebner_basis" begin +# M = [0 1 0 0 1; 1 0 1 1 1; 0 1 0 1 1; 0 1 1 0 1; 1 1 1 1 0] + M = [0 0 0 0 0 1 1; 0 0 0 0 0 1 0; 0 0 0 0 0 0 1; 0 0 0 0 0 0 1; 0 0 0 0 0 0 1; 1 1 0 0 0 0 0; 1 0 1 1 1 0 0] + A, u, r = free_alg_for_graph(M) + g = copy(r) + checked_obstructions = 0 + nonzero_reductions = 0 + reduction_bound = 5000 + # compute the aho corasick automaton + # to make normal form computation more efficient + aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) + + suffix_match_vectors = Vector{Vector{Int}}(undef, length(g)) + for i in 1:length(g) + suffix_match_vectors[i] = calc_suffix_match_vector(g[i].exps[1]) + end + # step 1 + obstruction_queue = get_obstructions(g) + while !isempty(obstruction_queue) + # #println(length(obstruction_queue)) + obstruction = dequeue!(obstruction_queue) + # step3 + S = s_polynomial(obstruction) + Sp = normal_form(S, g, aut) # or normal_form_weak + Sp2 = normal_form(S, g, suffix_match_vectors) + @test Sp == Sp2 + if iszero(Sp) + #println("is zero") + continue + end + #println(Sp) + nonzero_reductions += 1 + # step4 + push!(g, Sp) + aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) + push!(suffix_match_vectors, calc_suffix_match_vector(Sp2.exps[1])) + + if nonzero_reductions >= reduction_bound + return g + end + add_obstructions!(obstruction_queue, g) + end + return g end diff --git a/test/runtests_free_ass_groebner.jl b/test/runtests_free_ass_groebner.jl new file mode 100644 index 0000000000..8b3eb808d9 --- /dev/null +++ b/test/runtests_free_ass_groebner.jl @@ -0,0 +1,9 @@ +using Pkg +Pkg.activate("/home/julien/.julia/dev/AbstractAlgebra/") +using AbstractAlgebra +using AbstractAlgebra.Generic +using Test +include("generic/FreeAssAhoCorasick-test.jl") + + +include("generic/FreeAssAlgebraGroebner-test.jl") From e51bfa76cabb79445413f14535462a4797c52a06 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 24 Jun 2022 11:03:58 +0200 Subject: [PATCH 12/80] =?UTF-8?q?started=20adapting=20the=20gebauer=20m?= =?UTF-8?q?=C3=B6ller=20criteria=20to=20the=20new=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Manifest.toml | 7 +- src/generic/FreeAssAlgebraGroebner.jl | 277 ++++++++++++-------------- 2 files changed, 134 insertions(+), 150 deletions(-) diff --git a/Manifest.toml b/Manifest.toml index c8c5056f15..384617fd5f 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.7.3" +julia_version = "1.7.2" manifest_format = "2.0" [[deps.ATK_jll]] @@ -117,7 +117,7 @@ uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" version = "0.8.6" [[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" [[deps.Expat_jll]] @@ -132,9 +132,6 @@ git-tree-sha1 = "9267e5f50b0e12fdfd5a2455534345c4cf2c7f7a" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" version = "1.14.0" -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" - [[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index e53287bee4..02bb50585f 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -6,11 +6,11 @@ #include("../AbstractTypes.jl") #include("FreeAssAlgebra.jl") #include("FreeAssAhoCorasick.jl") -export groebner_basis, interreduce!, groebner_basis_slow, gb_divides_leftmost_aho_corasick, normal_form, get_obstructions, s_polynomial, add_obstructions! #TODO remove unnecessary exports +export groebner_basis, interreduce!, gb_divides_leftmost_aho_corasick, normal_form, get_obstructions, s_polynomial, add_obstructions!, remove_redundancies! #TODO remove unnecessary exports using DataStructures -const groebner_debug_level = 0 +const groebner_debug_level = 1 const Monomial = Vector{Int} abstract type Obstruction{T} end @@ -22,6 +22,8 @@ struct ObstructionTriple{T} <: Obstruction{T} first_poly::FreeAssAlgElem{T} second_poly::FreeAssAlgElem{T} pre_and_suffixes::NTuple{4, Monomial} + first_index::Int + second_index::Int end function FreeAssAlgElem{T}(R::FreeAssAlgebra{T}, mon::Monomial) where T @@ -174,18 +176,22 @@ end function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T i = 1 + counter = 0 while length(g) > 1 && length(g) >= i + counter += 1 + if counter % 5000 == 0 + println(length(g)) + println(i) + end aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) r = normal_form(g[i], g[1:end .!= i], aut) if iszero(r) deleteat!(g, i) - println(length(g)) elseif g[i] != r g[i] = r - #i = 1 - i += 1 + i = 1 + #i += 1 else - #println(i) i += 1 end end @@ -371,6 +377,21 @@ function is_subobstruction(obs1::NTuple{4, Vector{Int}}, obs2::NTuple{4, Vector{ end end +function is_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where T + return is_subobstruction(obs1.pre_and_suffixes, obs2.pre_and_suffixes) + +end + +""" +if obs2 is a subobstruction of obs1, i.e. obs1[3] = w obs2[3] and obs1[4] = obs2[4]w', +returns length(ww') +thus, if it returns 0 and obs2 is a subobstruction of obs1, they are equal (? is that true?) +if obs2 is not a subobstruction of obs1 the return value is useless +""" +function get_diff_length_for_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where T + return length(obs1.pre_and_suffixes[3]) - length(obs2.pre_and_suffixes[3]) + length(obs1.pre_and_suffixes[4]) - length(obs2.pre_and_suffixes[4]) +end + # check whether there exists a w^'' such that # w1 LM(g1) w2 = w1 LM(g1) w^'' LM(g2) u2 function has_overlap(g1, g2, w1, w2, u1, u2) @@ -396,80 +417,97 @@ function has_overlap(g1, g2, w1, w2, u1, u2) end function is_redundant(# TODO do we need g in the signature? - obs::NTuple{4, Vector{Int}}, - obs_index::Int, - s::Int, - B::Matrix{Vector{NTuple{4, Vector{Int}}}}, - g::Vector{FreeAssAlgElem{T}} + obs::ObstructionTriple{T}, + new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}} +# s::Int, +# B::Matrix{Vector{NTuple{4, Vector{Int}}}}, +# g::Vector{FreeAssAlgElem{T}} ) where T # cases 4b + 4c - for j in 1:size(B)[1] - for k in 1:length(B[j, s]) - if is_subobstruction(obs, B[j, s][k]) - # case 4b - if length(obs[3]) - length(B[j, s][k][3]) + length(obs[4]) - length(B[j,s][k][4]) > 0 - return true - # case 4c - elseif obs_index > j - return true - elseif obs_index == j && - length(obs[3]) - length(B[j, s][k][3]) + length(obs[4]) - length(B[j,s][k][4]) == 0 && - word_gt(obs[1], B[j, s][k][1]) - return true - end - end - end - end - # case 4d - # size(B) should be (s, s) - # we want to iterate over all B[i, obs_index] with i < s - for i in 1:size(B)[1]-1 - for k in 1:length(B[i, obs_index]) - if is_subword_right(B[i, obs_index][k][3], obs[1]) && is_subword_left(B[i, obs_index][k][4], obs[2]) - if groebner_debug_level > 0 - show(obs) - show(B[i, obs_index][k]) - end - u1 = copy(obs[1]) - u2 = copy(obs[2]) - v1 = copy(B[i, obs_index][k][3]) - v2 = copy(B[i, obs_index][k][4]) - for i in 1:length(v1) - pop!(u1) - end - for j in 1:length(v2) - popfirst!(u2) - end - @assert word_cmp(vcat(u1, v1), obs[1]) == 0 - @assert word_cmp(vcat(v2, u2), obs[2]) == 0 - if !has_overlap(g[i], g[s], vcat(u1, B[i, obs_index][k][1]), vcat(B[i, obs_index][k][2], u2), obs[3], obs[4]) - return true - end - end - end + for obstruction_pair in new_obstructions + o = obstruction_pair[1] + if o.second_index == obs.second_index + if is_subobstruction(obs, o) + # case 4b + if get_diff_length_for_subobstruction(obs, o) > 0 + return true + # case 4c + elseif obs.first_index > o.first_index + return true + elseif obs.first_index == o.first_index && get_diff_length_for_subobstruction(obs, o) == 0 && + word_gt(obs.pre_and_suffixes[1], o.pre_and_suffixes[1]) + return true + end + end + end end + #TODO maybe in a new function? +# # case 4d +# # size(B) should be (s, s) +# # we want to iterate over all B[i, obs_index] with i < s +# for i in 1:size(B)[1]-1 +# for k in 1:length(B[i, obs_index]) +# if is_subword_right(B[i, obs_index][k][3], obs[1]) && is_subword_left(B[i, obs_index][k][4], obs[2]) +# if groebner_debug_level > 0 +# show(obs) +# show(B[i, obs_index][k]) +# end +# u1 = copy(obs[1]) +# u2 = copy(obs[2]) +# v1 = copy(B[i, obs_index][k][3]) +# v2 = copy(B[i, obs_index][k][4]) +# for i in 1:length(v1) +# pop!(u1) +# end +# for j in 1:length(v2) +# popfirst!(u2) +# end +# @assert word_cmp(vcat(u1, v1), obs[1]) == 0 +# @assert word_cmp(vcat(v2, u2), obs[2]) == 0 +# if !has_overlap(g[i], g[s], vcat(u1, B[i, obs_index][k][1]), vcat(B[i, obs_index][k][2], u2), obs[3], obs[4]) +# return true +# end +# end +# end +# end return false end ## s is the index of the newly added layer of obstructions in B function remove_redundancies!( - B::Matrix{Vector{NTuple{4, Vector{Int}}}}, - s::Int, - g::Vector{FreeAssAlgElem{T}} + all_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, + newest_index::Int ) where T del_counter = 0 - for i in 1:s - k = 1 - while k <= length(B[i, s]) - if is_redundant(B[i, s][k], i, s, B, g) - deleteat!(B[i, s], k) - del_counter += 1 - else - k += 1 - end - end + new_obstructions = PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}() + for obstr_pair in all_obstructions + if obstr_pair[1].second_index == newest_index + new_obstructions[obstr_pair[1]] = obstr_pair[2] + end end + for obstr_pair in new_obstructions + if is_redundant(obstr_pair[1], new_obstructions) + del_counter += 1 + delete!(new_obstructions, obstr_pair[1]) + delete!(all_obstructions, obstr_pair[1]) + end + end + +# for i in 1:s +# k = 1 +# while k <= length(B[i, s]) +# if is_redundant(B[i, s][k], i, s, B, g) +# deleteat!(B[i, s], k) +# del_counter += 1 +# else +# k += 1 +# end +# end +# end # TODO case 4e from Thm 4.1 in Kreuzer Xiu + if del_counter > 0 + println("deleted obstructions: $del_counter") + end end function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T @@ -482,14 +520,14 @@ function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T obs = obstructions(_leading_word(g[i]), _leading_word(g[j])) end for o in obs - triple = ObstructionTriple{T}(g[i], g[j], o) + triple = ObstructionTriple{T}(g[i], g[j], o, i, j) enqueue!(result, triple, common_multiple_leading_term(triple)) end end # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu if groebner_debug_level > 0 obstr_count = length(result) - #println("$obstr_count many obstructions") + println("$obstr_count many obstructions") end return result end @@ -507,10 +545,11 @@ function add_obstructions!( obs = obstructions(_leading_word(g[i]), _leading_word(g[s])) end for o in obs - triple = ObstructionTriple{T}(g[i], g[s], o) + triple = ObstructionTriple{T}(g[i], g[s], o, i, s) enqueue!(obstruction_queue, triple, common_multiple_leading_term(triple)) end end + remove_redundancies!(obstruction_queue, s) #remove_redundancies!(new_B, s, g) TODO match remove_redundancies to new types end @@ -522,8 +561,9 @@ function groebner_basis_buchberger( g = copy(g) println(length(g)) - interreduce!(g) - println(length(g)) +# interreduce!(g) +# println("interreduced") +# println(length(g)) checked_obstructions = 0 nonzero_reductions = 0 @@ -533,6 +573,9 @@ function groebner_basis_buchberger( aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) # step 1 obstruction_queue = get_obstructions(g) + + println(length(obstruction_queue)) + while !isempty(obstruction_queue) # #println(length(obstruction_queue)) obstruction = dequeue!(obstruction_queue) @@ -542,7 +585,8 @@ function groebner_basis_buchberger( if groebner_debug_level > 0 checked_obstructions += 1 if checked_obstructions % 5000 == 0 - #println("checked $checked_obstructions obstructions") + println("checked $checked_obstructions obstructions") + println(length(obstruction_queue)) end end if iszero(Sp) @@ -557,25 +601,25 @@ function groebner_basis_buchberger( #aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) interreduce_counter += 1 - if interreduce_counter > 2000 - println("starting interreduce") - interreduce!(g) - println("done interreducing") - interreduce_counter = 0 - obstruction_queue = get_obstructions(g) - continue - end - if groebner_debug_level > 0 - println("adding new obstructions! checked $checked_obstructions so far") - end +# if interreduce_counter > 2000 +# println("starting interreduce") +# interreduce!(g) +# println("done interreducing") +# interreduce_counter = 0 +# obstruction_queue = get_obstructions(g) +# continue +# end +# if groebner_debug_level > 0 +# println("adding new obstructions! checked $checked_obstructions so far") +# end if nonzero_reductions >= reduction_bound return g end #debug if nonzero_reductions > 2000 - println(length(obstruction_queue)) - println(length(g)) - println(nonzero_reductions) + # println(length(obstruction_queue)) + # println(length(g)) + # println(nonzero_reductions) end #end debug add_obstructions!(obstruction_queue, g) @@ -583,63 +627,6 @@ function groebner_basis_buchberger( return g end -""" -for benchmarking -""" -function groebner_basis_buchberger_slow( - g::Vector{FreeAssAlgElem{T}}, - reduction_bound = typemax(Int)::Int -) where T <: FieldElement - - g = copy(g) - checked_obstructions = 0 - nonzero_reductions = 0 - # compute a vector of suffix matches for all elements of g - # to make normal form computation more efficient - suffix_match_vectors = Vector{Vector{Int}}(undef, length(g)) - for i in 1:length(g) - suffix_match_vectors[i] = calc_suffix_match_vector(g[i].exps[1]) - end - - # step 1 - obstruction_queue = get_obstructions(g) - while !isempty(obstruction_queue) - obstruction = dequeue!(obstruction_queue) - # step3 - S = s_polynomial(obstruction) - Sp = normal_form(S, g, suffix_match_vectors) # or normal_form_weak - if groebner_debug_level > 0 - checked_obstructions += 1 - if checked_obstructions % 5000 == 0 - println("checked $checked_obstructions obstructions") - end - end - if iszero(Sp) - continue - end - nonzero_reductions += 1 - # step4 - push!(g, Sp) - push!(suffix_match_vectors, calc_suffix_match_vector(Sp.exps[1])) - if groebner_debug_level > 0 - println("adding new obstructions! checked $checked_obstructions so far") - end - if nonzero_reductions >= reduction_bound - return g - end - add_obstructions!(obstruction_queue, g) - end - return g -end - -function groebner_basis_slow( - g::Vector{FreeAssAlgElem{T}}, - reduction_bound = typemax(Int)::Int -) where T <: FieldElement - return groebner_basis_buchberger_slow(g, reduction_bound) -end - - function groebner_basis( g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int From c665b347c80680b18e79c99594bdf387259bd0bf Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 5 Jul 2022 15:10:51 +0200 Subject: [PATCH 13/80] adapted remove redundancies to the new types and corrected has_overlap --- src/generic/FreeAssAlgebraGroebner.jl | 139 +++++++++++++++++++++----- 1 file changed, 114 insertions(+), 25 deletions(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 02bb50585f..1e802e7027 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -179,9 +179,9 @@ function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T counter = 0 while length(g) > 1 && length(g) >= i counter += 1 - if counter % 5000 == 0 + if counter % 500 == 0 println(length(g)) - println(i) +# println(i) end aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) r = normal_form(g[i], g[1:end .!= i], aut) @@ -395,25 +395,31 @@ end # check whether there exists a w^'' such that # w1 LM(g1) w2 = w1 LM(g1) w^'' LM(g2) u2 function has_overlap(g1, g2, w1, w2, u1, u2) - lw1 = _leading_word(g1) +# lw1 = _leading_word(g1) lw2 = _leading_word(g2) - concatenated_word = vcat(w1, lw1, w2) - for i in 1:length(w1) - c = popfirst!(concatenated_word) - @assert c == w1[i] - end - for i in 1:length(lw1) - c = popfirst!(concatenated_word) - @assert c == lw1[i] - end - for j in 0:length(u2)-1 - c = pop!(concatenated_word) - @assert c = u2[length(u2)-j] - end - if length(concatenated_word) < length(lw2) - return false - end - return is_subword_right(lw2, concatenated_word) # TODO maybe just comparing lengths should be sufficient + return length(w2) < length(lw2) + length(u2) +# +# concatenated_word = vcat(w1, lw1, w2) +# for i in 1:length(w1) +# c = popfirst!(concatenated_word) +# @assert c == w1[i] +# end +# for i in 1:length(lw1) +# c = popfirst!(concatenated_word) +# @assert c == lw1[i] +# end +# for j in 0:length(u2)-1 +# c = pop!(concatenated_word) +# @assert c = u2[length(u2)-j] +# end +# if length(concatenated_word) < length(lw2) +# return false +# end +# return is_subword_right(lw2, concatenated_word) # TODO maybe just comparing lengths should be sufficient +end + +function has_overlap(obs::ObstructionTriple{T}) where T + return has_overlap(obs.first_poly, obs.second_poly, obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], obs.pre_and_suffixes[3], obs.pre_and_suffixes[4]) end function is_redundant(# TODO do we need g in the signature? @@ -473,16 +479,89 @@ function is_redundant(# TODO do we need g in the signature? return false end +""" +check, whether obs1 is a proper multiple of obs2, i.e. they belong to the same polynomials and are of the form +obs1 = [w w_i, w_i' w'; w w_j, w_j' w'] and obs2 = [w_i, w_i'; w_j, w_j'] +""" +function is_proper_multiple(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where T + if obs1.first_poly != obs2.first_poly || obs1.second_poly != obs2.second_poly #TODO compare indices instead? + return false + end + if is_subword_right(obs2.pre_and_suffixes[1], obs1.pre_and_suffixes[1]) && is_subword_left(obs2.pre_and_suffixes[2], obs1.pre_and_suffixes[2]) + w = copy(obs1.pre_and_suffixes[1]) + w2 = copy(obs1.pre_and_suffixes[2]) + for _ in 1:length(obs2.pre_and_suffixes[1]) + pop!(w) + end + for _ in 1:length(obs2.pre_and_suffixes[2]) + popfirst!(w2) + end + if length(w) + length(w2) == 0 + return false + end + @assert obs1.pre_and_suffixes[1] == vcat(w, obs2.pre_and_suffixes[1]) + @assert obs1.pre_and_suffixes[2] == vcat(obs2.pre_and_suffixes[2], w2) + return obs1.pre_and_suffixes[3] == vcat(w, obs2.pre_and_suffixes[3]) && obs1.pre_and_suffixes[4] == vcat(obs2.pre_and_suffixes[4], w2) + end +end + +""" +check, whether obs is a proper multiple of any of the obstructions in the priority queue +""" +function is_proper_multiple(obs::ObstructionTriple{T}, obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}) where T + for obspair in obstructions + obs2 = obspair[1] + if is_proper_multiple(obs, obs2) + return true + end + end + return false +end + +function is_redundant(# TODO do we need g in the signature? + obs::ObstructionTriple{T}, + new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, + newest_element::FreeAssAlgElem{T}, + newest_index::Int + ) where T + println("is_redundant in use") + w1 = [] + w2 = [] + for i in 1:length(obs.second_poly.exps[1]) + word_to_check = vcat(obs.pre_and_suffixes[3], obs.second_poly.exps[1], obs.pre_and_suffixes[4]) + if check_center_overlap(newest_element.exps[1], word_to_check, i) + w1 = word_to_check[1:i-1] + w2 = word_to_check[i+length(newest_element.exps[1]):end] + break + end + end + if length(w1) + length(w2) == 0 + return false + end + #TODO check equality + # maybe w w' = [] yields false? + obs1 = ObstructionTriple{T}(obs.first_poly, newest_element, (obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], w1, w2), obs.first_index, newest_index) + obs2 = ObstructionTriple{T}(obs.second_poly, newest_element, (obs.pre_and_suffixes[3], obs.pre_and_suffixes[4], w1, w2), obs.second_index, newest_index) + o1_bool = !has_overlap(obs1) || is_proper_multiple(obs1, new_obstructions) # TODO maybe only call is_proper_multiple if both obs have no overlap for performance? + o2_bool = !has_overlap(obs2) || is_proper_multiple(obs2, new_obstructions) + return o1_bool && o2_bool +end + + ## s is the index of the newly added layer of obstructions in B function remove_redundancies!( all_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, - newest_index::Int + newest_index::Int, + newest_element::FreeAssAlgElem{T} ) where T del_counter = 0 new_obstructions = PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}() + old_obstructions = PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}() for obstr_pair in all_obstructions if obstr_pair[1].second_index == newest_index new_obstructions[obstr_pair[1]] = obstr_pair[2] + else + old_obstructions[obstr_pair[1]] = obstr_pair[2] end end for obstr_pair in new_obstructions @@ -492,6 +571,16 @@ function remove_redundancies!( delete!(all_obstructions, obstr_pair[1]) end end + if length(new_obstructions) == 0 + return nothing + end + + for obstr_pair in old_obstructions + if is_redundant(obstr_pair[1], new_obstructions, newest_element, newest_index) + del_counter += 1 + delete!(all_obstructions, obstr_pair[1]) + end + end # for i in 1:s # k = 1 @@ -527,7 +616,7 @@ function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu if groebner_debug_level > 0 obstr_count = length(result) - println("$obstr_count many obstructions") + #println("$obstr_count many obstructions") end return result end @@ -549,7 +638,7 @@ function add_obstructions!( enqueue!(obstruction_queue, triple, common_multiple_leading_term(triple)) end end - remove_redundancies!(obstruction_queue, s) + #remove_redundancies!(obstruction_queue, s, g[s]) #remove_redundancies!(new_B, s, g) TODO match remove_redundancies to new types end @@ -585,8 +674,8 @@ function groebner_basis_buchberger( if groebner_debug_level > 0 checked_obstructions += 1 if checked_obstructions % 5000 == 0 - println("checked $checked_obstructions obstructions") - println(length(obstruction_queue)) + #println("checked $checked_obstructions obstructions") + #println(length(obstruction_queue)) end end if iszero(Sp) From a53d92ebbff70075ddbe9ebcb20f26ef350bf331 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 18 Jul 2022 14:51:12 +0200 Subject: [PATCH 14/80] cleaned the code a bit --- src/generic/FreeAssAlgebraGroebner.jl | 177 ++------------------ test/generic/FreeAssAlgebraGroebner-test.jl | 84 ++-------- 2 files changed, 29 insertions(+), 232 deletions(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 1e802e7027..8d37a3ca73 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -6,8 +6,7 @@ #include("../AbstractTypes.jl") #include("FreeAssAlgebra.jl") #include("FreeAssAhoCorasick.jl") -export groebner_basis, interreduce!, gb_divides_leftmost_aho_corasick, normal_form, get_obstructions, s_polynomial, add_obstructions!, remove_redundancies! #TODO remove unnecessary exports - +export groebner_basis, interreduce!, gb_divides_leftmost_aho_corasick, normal_form using DataStructures const groebner_debug_level = 1 @@ -86,39 +85,6 @@ end # normal form with leftmost word divisions -function normal_form( - f::FreeAssAlgElem{T}, - g::Vector{FreeAssAlgElem{T}}, -) where T <: FieldElement - R = parent(f) - s = length(g) - rcoeffs = T[] - rexps = Vector{Int}[] - while length(f) > 0 - i = 1 - @label again - ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) - if !ok && i < s - i += 1 - @goto again - end - if ok - qi = divexact(f.coeffs[1], g[i].coeffs[1]) - f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation - else - push!(rcoeffs, f.coeffs[1]) - push!(rexps, f.exps[1]) - f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f)-1) - end - end - r = FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) - return r -end - - - -# normal form with leftmost word divisions - function normal_form( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, @@ -199,22 +165,6 @@ function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T end -#function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T -# i = 1 -# while length(g) > 1 && length(g) >= i -# r = normal_form(g[i], g[1:end .!= i]) -# if iszero(r) -# deleteat!(g, i) -# elseif g[i] != r -# g[i] = r -# i = 1 -# else -# i += 1 -# end -# end -# return g -#end - ## checks whether there is an overlap between a and b at position i of b # such that b[i:length(b)] = a[1:length(b)-i] function check_left_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) @@ -367,9 +317,6 @@ end # i.e. if obs2 is a subobstruction of obs1 # both w and w^' might be empty function is_subobstruction(obs1::NTuple{4, Vector{Int}}, obs2::NTuple{4, Vector{Int}}) -# if length(obs2[3]) > length(obs1[3]) || length(obs2[4]) > length(obs1[4]) -# return false -# end if is_subword_right(obs2[3], obs1[3]) && is_subword_left(obs2[4], obs1[4]) return true else @@ -392,42 +339,24 @@ function get_diff_length_for_subobstruction(obs1::ObstructionTriple{T}, obs2::Ob return length(obs1.pre_and_suffixes[3]) - length(obs2.pre_and_suffixes[3]) + length(obs1.pre_and_suffixes[4]) - length(obs2.pre_and_suffixes[4]) end -# check whether there exists a w^'' such that +# check whether there exists a (possibly empty) w^'' such that # w1 LM(g1) w2 = w1 LM(g1) w^'' LM(g2) u2 +# and if that is the case, i.e. there is no overlap, returns false +# assumes that (w1, w2; u1, u2) are an obstruction of g1 and g2 +# i.e. w1 LM(g1) w2 = u1 LM(g2) u2 function has_overlap(g1, g2, w1, w2, u1, u2) -# lw1 = _leading_word(g1) - lw2 = _leading_word(g2) - return length(w2) < length(lw2) + length(u2) -# -# concatenated_word = vcat(w1, lw1, w2) -# for i in 1:length(w1) -# c = popfirst!(concatenated_word) -# @assert c == w1[i] -# end -# for i in 1:length(lw1) -# c = popfirst!(concatenated_word) -# @assert c == lw1[i] -# end -# for j in 0:length(u2)-1 -# c = pop!(concatenated_word) -# @assert c = u2[length(u2)-j] -# end -# if length(concatenated_word) < length(lw2) -# return false -# end -# return is_subword_right(lw2, concatenated_word) # TODO maybe just comparing lengths should be sufficient + @assert vcat(w1, _leading_word(g1), w2) == vcat(u1, _leading_word(g2), u2) + lw2 = _leading_word(g2) + return length(w2) < length(lw2) + length(u2) end function has_overlap(obs::ObstructionTriple{T}) where T return has_overlap(obs.first_poly, obs.second_poly, obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], obs.pre_and_suffixes[3], obs.pre_and_suffixes[4]) end -function is_redundant(# TODO do we need g in the signature? +function is_redundant( obs::ObstructionTriple{T}, new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}} -# s::Int, -# B::Matrix{Vector{NTuple{4, Vector{Int}}}}, -# g::Vector{FreeAssAlgElem{T}} ) where T # cases 4b + 4c for obstruction_pair in new_obstructions @@ -447,35 +376,6 @@ function is_redundant(# TODO do we need g in the signature? end end end - #TODO maybe in a new function? -# # case 4d -# # size(B) should be (s, s) -# # we want to iterate over all B[i, obs_index] with i < s -# for i in 1:size(B)[1]-1 -# for k in 1:length(B[i, obs_index]) -# if is_subword_right(B[i, obs_index][k][3], obs[1]) && is_subword_left(B[i, obs_index][k][4], obs[2]) -# if groebner_debug_level > 0 -# show(obs) -# show(B[i, obs_index][k]) -# end -# u1 = copy(obs[1]) -# u2 = copy(obs[2]) -# v1 = copy(B[i, obs_index][k][3]) -# v2 = copy(B[i, obs_index][k][4]) -# for i in 1:length(v1) -# pop!(u1) -# end -# for j in 1:length(v2) -# popfirst!(u2) -# end -# @assert word_cmp(vcat(u1, v1), obs[1]) == 0 -# @assert word_cmp(vcat(v2, u2), obs[2]) == 0 -# if !has_overlap(g[i], g[s], vcat(u1, B[i, obs_index][k][1]), vcat(B[i, obs_index][k][2], u2), obs[3], obs[4]) -# return true -# end -# end -# end -# end return false end @@ -518,13 +418,12 @@ function is_proper_multiple(obs::ObstructionTriple{T}, obstructions::PriorityQue return false end -function is_redundant(# TODO do we need g in the signature? +function is_redundant( obs::ObstructionTriple{T}, new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, newest_element::FreeAssAlgElem{T}, newest_index::Int ) where T - println("is_redundant in use") w1 = [] w2 = [] for i in 1:length(obs.second_poly.exps[1]) @@ -538,8 +437,6 @@ function is_redundant(# TODO do we need g in the signature? if length(w1) + length(w2) == 0 return false end - #TODO check equality - # maybe w w' = [] yields false? obs1 = ObstructionTriple{T}(obs.first_poly, newest_element, (obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], w1, w2), obs.first_index, newest_index) obs2 = ObstructionTriple{T}(obs.second_poly, newest_element, (obs.pre_and_suffixes[3], obs.pre_and_suffixes[4], w1, w2), obs.second_index, newest_index) o1_bool = !has_overlap(obs1) || is_proper_multiple(obs1, new_obstructions) # TODO maybe only call is_proper_multiple if both obs have no overlap for performance? @@ -548,7 +445,6 @@ function is_redundant(# TODO do we need g in the signature? end -## s is the index of the newly added layer of obstructions in B function remove_redundancies!( all_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, newest_index::Int, @@ -582,21 +478,7 @@ function remove_redundancies!( end end -# for i in 1:s -# k = 1 -# while k <= length(B[i, s]) -# if is_redundant(B[i, s][k], i, s, B, g) -# deleteat!(B[i, s], k) -# del_counter += 1 -# else -# k += 1 -# end -# end -# end # TODO case 4e from Thm 4.1 in Kreuzer Xiu - if del_counter > 0 - println("deleted obstructions: $del_counter") - end end function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T @@ -614,10 +496,6 @@ function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T end end # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu - if groebner_debug_level > 0 - obstr_count = length(result) - #println("$obstr_count many obstructions") - end return result end @@ -638,8 +516,7 @@ function add_obstructions!( enqueue!(obstruction_queue, triple, common_multiple_leading_term(triple)) end end - #remove_redundancies!(obstruction_queue, s, g[s]) - #remove_redundancies!(new_B, s, g) TODO match remove_redundancies to new types + #remove_redundancies!(obstruction_queue, s, g[s]) #TODO too slow in practice end @@ -649,10 +526,6 @@ function groebner_basis_buchberger( ) where T <: FieldElement g = copy(g) - println(length(g)) -# interreduce!(g) -# println("interreduced") -# println(length(g)) checked_obstructions = 0 nonzero_reductions = 0 @@ -666,7 +539,6 @@ function groebner_basis_buchberger( println(length(obstruction_queue)) while !isempty(obstruction_queue) -# #println(length(obstruction_queue)) obstruction = dequeue!(obstruction_queue) # step3 S = s_polynomial(obstruction) @@ -674,43 +546,16 @@ function groebner_basis_buchberger( if groebner_debug_level > 0 checked_obstructions += 1 if checked_obstructions % 5000 == 0 - #println("checked $checked_obstructions obstructions") - #println(length(obstruction_queue)) end end - if iszero(Sp) - #println("is zero") - continue - end - #println(Sp) nonzero_reductions += 1 # step4 push!(g, Sp) insert_keyword!(aut, Sp.exps[1], length(g)) - #aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - interreduce_counter += 1 -# if interreduce_counter > 2000 -# println("starting interreduce") -# interreduce!(g) -# println("done interreducing") -# interreduce_counter = 0 -# obstruction_queue = get_obstructions(g) -# continue -# end -# if groebner_debug_level > 0 -# println("adding new obstructions! checked $checked_obstructions so far") -# end if nonzero_reductions >= reduction_bound return g end - #debug - if nonzero_reductions > 2000 - # println(length(obstruction_queue)) - # println(length(g)) - # println(nonzero_reductions) - end - #end debug add_obstructions!(obstruction_queue, g) end return g diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 2fb00de7bf..58dcc58be6 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -1,21 +1,21 @@ -#@testset "Generic.FreeAssAlgebra.groebner" begin -# -# R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) -# g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, -# (y*x)^3*t + (y*x)^2*t + t + s]) -# @test length(g) >= 5 -# -# # Example 6.1 Kreuzer & Xiu -# R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) -# -# g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) -# AbstractAlgebra.interreduce!(g) -# @test length(g) == 5 -# -# g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) -# AbstractAlgebra.interreduce!(g) -# @test length(g) == 15 -#end +@testset "Generic.FreeAssAlgebra.groebner" begin + + R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) + g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, + (y*x)^3*t + (y*x)^2*t + t + s]) + @test length(g) >= 5 + + # Example 6.1 Kreuzer & Xiu + R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 5 + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 15 +end @testset "Generic.FreeAssociativeAlgebra.groebner.normal_form" begin R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(QQ, ["x", "y", "u", "v", "t", "s"]) # x > y > ... > s @@ -33,51 +33,3 @@ @test gb_divides_leftmost_aho_corasick((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) @test gb_divides_leftmost_aho_corasick((x*s*t).exps[1], aut) == (false, [], [], -1) end - -include("/home/julien/uni/mathe/Promotion/ncmpoly/graph_to_ass_alg.jl") -using DataStructures - -@testset "Generic.FreeAssociativeAlgebra.groebner_basis" begin -# M = [0 1 0 0 1; 1 0 1 1 1; 0 1 0 1 1; 0 1 1 0 1; 1 1 1 1 0] - M = [0 0 0 0 0 1 1; 0 0 0 0 0 1 0; 0 0 0 0 0 0 1; 0 0 0 0 0 0 1; 0 0 0 0 0 0 1; 1 1 0 0 0 0 0; 1 0 1 1 1 0 0] - A, u, r = free_alg_for_graph(M) - g = copy(r) - checked_obstructions = 0 - nonzero_reductions = 0 - reduction_bound = 5000 - # compute the aho corasick automaton - # to make normal form computation more efficient - aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - - suffix_match_vectors = Vector{Vector{Int}}(undef, length(g)) - for i in 1:length(g) - suffix_match_vectors[i] = calc_suffix_match_vector(g[i].exps[1]) - end - # step 1 - obstruction_queue = get_obstructions(g) - while !isempty(obstruction_queue) - # #println(length(obstruction_queue)) - obstruction = dequeue!(obstruction_queue) - # step3 - S = s_polynomial(obstruction) - Sp = normal_form(S, g, aut) # or normal_form_weak - Sp2 = normal_form(S, g, suffix_match_vectors) - @test Sp == Sp2 - if iszero(Sp) - #println("is zero") - continue - end - #println(Sp) - nonzero_reductions += 1 - # step4 - push!(g, Sp) - aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - push!(suffix_match_vectors, calc_suffix_match_vector(Sp2.exps[1])) - - if nonzero_reductions >= reduction_bound - return g - end - add_obstructions!(obstruction_queue, g) - end - return g -end From 3a5cfded07d4680f0aeb757b6e23bc11739549c4 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 18 Jul 2022 14:54:05 +0200 Subject: [PATCH 15/80] removed more print statements --- src/generic/FreeAssAlgebraGroebner.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 8d37a3ca73..e8d07a2899 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -54,10 +54,7 @@ function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) if isnothing(match) return (false, [], [], -1) end -# println("a = $a") -# println("match = $match") -return (true, a[1:match.last_position - length(match.keyword)], a[match.last_position + 1:end], match.keyword_index) -#return (true, a[1:match[1] - length(match[2][2])], a[match[1] + 1:end], match[2][1]) + return (true, a[1:match.last_position - length(match.keyword)], a[match.last_position + 1:end], match.keyword_index) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel @@ -147,7 +144,6 @@ function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T counter += 1 if counter % 500 == 0 println(length(g)) -# println(i) end aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) r = normal_form(g[i], g[1:end .!= i], aut) From 022819c5b207b68409a6ae55eff40602d5d54890 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 19 Jul 2022 12:35:20 +0200 Subject: [PATCH 16/80] added some tests, cleaned code and added a hash function for AhoCorasickMatches --- src/generic/FreeAssAhoCorasick.jl | 5 + src/generic/FreeAssAlgebraGroebner.jl | 154 ++------------------ test/generic/FreeAssAhoCorasick-test.jl | 10 +- test/generic/FreeAssAlgebraGroebner-test.jl | 84 +++-------- 4 files changed, 43 insertions(+), 210 deletions(-) diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl index cb81168746..86984cf867 100644 --- a/src/generic/FreeAssAhoCorasick.jl +++ b/src/generic/FreeAssAhoCorasick.jl @@ -27,6 +27,11 @@ struct AhoCorasickMatch keyword::Word end +Base.hash(m::AhoCorasickMatch) = hash(m.last_position, hash(m.keyword_index, hash(m.keyword))) +function ==(m1::AhoCorasickMatch, m2::AhoCorasickMatch) + return m1.last_position == m2.last_position && m1.keyword_index == m2.keyword_index && m1.keyword == m2.keyword +end + function AhoCorasickAutomaton(keywords::Vector{Word}) automaton = AhoCorasickAutomaton([], [], []) construct_goto!(automaton, keywords) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 1e802e7027..61d33305e0 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -3,14 +3,11 @@ # FreeAssAlgebraGroebner.jl : free associative algebra R Groebner basis # ############################################################################### -#include("../AbstractTypes.jl") -#include("FreeAssAlgebra.jl") -#include("FreeAssAhoCorasick.jl") export groebner_basis, interreduce!, gb_divides_leftmost_aho_corasick, normal_form, get_obstructions, s_polynomial, add_obstructions!, remove_redundancies! #TODO remove unnecessary exports using DataStructures -const groebner_debug_level = 1 +const groebner_debug_level = 0 const Monomial = Vector{Int} abstract type Obstruction{T} end @@ -55,10 +52,7 @@ function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) if isnothing(match) return (false, [], [], -1) end -# println("a = $a") -# println("match = $match") return (true, a[1:match.last_position - length(match.keyword)], a[match.last_position + 1:end], match.keyword_index) -#return (true, a[1:match[1] - length(match[2][2])], a[match[1] + 1:end], match[2][1]) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel @@ -176,13 +170,7 @@ end function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T i = 1 - counter = 0 while length(g) > 1 && length(g) >= i - counter += 1 - if counter % 500 == 0 - println(length(g)) -# println(i) - end aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) r = normal_form(g[i], g[1:end .!= i], aut) if iszero(r) @@ -190,7 +178,6 @@ function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T elseif g[i] != r g[i] = r i = 1 - #i += 1 else i += 1 end @@ -198,23 +185,6 @@ function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T return g end - -#function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T -# i = 1 -# while length(g) > 1 && length(g) >= i -# r = normal_form(g[i], g[1:end .!= i]) -# if iszero(r) -# deleteat!(g, i) -# elseif g[i] != r -# g[i] = r -# i = 1 -# else -# i += 1 -# end -# end -# return g -#end - ## checks whether there is an overlap between a and b at position i of b # such that b[i:length(b)] = a[1:length(b)-i] function check_left_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) @@ -394,40 +364,24 @@ end # check whether there exists a w^'' such that # w1 LM(g1) w2 = w1 LM(g1) w^'' LM(g2) u2 +# only returns the correct value, if all the arguments come from an obstruction +function has_overlap(g2, w2, u2) + lw2 = _leading_word(g2) + return length(w2) < length(lw2) + length(u2) +end + function has_overlap(g1, g2, w1, w2, u1, u2) -# lw1 = _leading_word(g1) lw2 = _leading_word(g2) return length(w2) < length(lw2) + length(u2) -# -# concatenated_word = vcat(w1, lw1, w2) -# for i in 1:length(w1) -# c = popfirst!(concatenated_word) -# @assert c == w1[i] -# end -# for i in 1:length(lw1) -# c = popfirst!(concatenated_word) -# @assert c == lw1[i] -# end -# for j in 0:length(u2)-1 -# c = pop!(concatenated_word) -# @assert c = u2[length(u2)-j] -# end -# if length(concatenated_word) < length(lw2) -# return false -# end -# return is_subword_right(lw2, concatenated_word) # TODO maybe just comparing lengths should be sufficient end function has_overlap(obs::ObstructionTriple{T}) where T - return has_overlap(obs.first_poly, obs.second_poly, obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], obs.pre_and_suffixes[3], obs.pre_and_suffixes[4]) + return has_overlap(obs.second_poly, obs.pre_and_suffixes[2], obs.pre_and_suffixes[4]) end -function is_redundant(# TODO do we need g in the signature? +function is_redundant( obs::ObstructionTriple{T}, new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}} -# s::Int, -# B::Matrix{Vector{NTuple{4, Vector{Int}}}}, -# g::Vector{FreeAssAlgElem{T}} ) where T # cases 4b + 4c for obstruction_pair in new_obstructions @@ -447,35 +401,6 @@ function is_redundant(# TODO do we need g in the signature? end end end - #TODO maybe in a new function? -# # case 4d -# # size(B) should be (s, s) -# # we want to iterate over all B[i, obs_index] with i < s -# for i in 1:size(B)[1]-1 -# for k in 1:length(B[i, obs_index]) -# if is_subword_right(B[i, obs_index][k][3], obs[1]) && is_subword_left(B[i, obs_index][k][4], obs[2]) -# if groebner_debug_level > 0 -# show(obs) -# show(B[i, obs_index][k]) -# end -# u1 = copy(obs[1]) -# u2 = copy(obs[2]) -# v1 = copy(B[i, obs_index][k][3]) -# v2 = copy(B[i, obs_index][k][4]) -# for i in 1:length(v1) -# pop!(u1) -# end -# for j in 1:length(v2) -# popfirst!(u2) -# end -# @assert word_cmp(vcat(u1, v1), obs[1]) == 0 -# @assert word_cmp(vcat(v2, u2), obs[2]) == 0 -# if !has_overlap(g[i], g[s], vcat(u1, B[i, obs_index][k][1]), vcat(B[i, obs_index][k][2], u2), obs[3], obs[4]) -# return true -# end -# end -# end -# end return false end @@ -518,13 +443,12 @@ function is_proper_multiple(obs::ObstructionTriple{T}, obstructions::PriorityQue return false end -function is_redundant(# TODO do we need g in the signature? +function is_redundant( obs::ObstructionTriple{T}, new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, newest_element::FreeAssAlgElem{T}, newest_index::Int ) where T - println("is_redundant in use") w1 = [] w2 = [] for i in 1:length(obs.second_poly.exps[1]) @@ -538,8 +462,6 @@ function is_redundant(# TODO do we need g in the signature? if length(w1) + length(w2) == 0 return false end - #TODO check equality - # maybe w w' = [] yields false? obs1 = ObstructionTriple{T}(obs.first_poly, newest_element, (obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], w1, w2), obs.first_index, newest_index) obs2 = ObstructionTriple{T}(obs.second_poly, newest_element, (obs.pre_and_suffixes[3], obs.pre_and_suffixes[4], w1, w2), obs.second_index, newest_index) o1_bool = !has_overlap(obs1) || is_proper_multiple(obs1, new_obstructions) # TODO maybe only call is_proper_multiple if both obs have no overlap for performance? @@ -581,18 +503,6 @@ function remove_redundancies!( delete!(all_obstructions, obstr_pair[1]) end end - -# for i in 1:s -# k = 1 -# while k <= length(B[i, s]) -# if is_redundant(B[i, s][k], i, s, B, g) -# deleteat!(B[i, s], k) -# del_counter += 1 -# else -# k += 1 -# end -# end -# end # TODO case 4e from Thm 4.1 in Kreuzer Xiu if del_counter > 0 println("deleted obstructions: $del_counter") @@ -616,7 +526,7 @@ function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu if groebner_debug_level > 0 obstr_count = length(result) - #println("$obstr_count many obstructions") + println("$obstr_count many obstructions") end return result end @@ -639,7 +549,6 @@ function add_obstructions!( end end #remove_redundancies!(obstruction_queue, s, g[s]) - #remove_redundancies!(new_B, s, g) TODO match remove_redundancies to new types end @@ -649,68 +558,35 @@ function groebner_basis_buchberger( ) where T <: FieldElement g = copy(g) - println(length(g)) -# interreduce!(g) -# println("interreduced") -# println(length(g)) - +# interreduce!(g) # on some small examples, this increases running time, so it might not be optimal to use this here checked_obstructions = 0 nonzero_reductions = 0 - interreduce_counter = 0 # compute the aho corasick automaton # to make normal form computation more efficient aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) # step 1 obstruction_queue = get_obstructions(g) - - println(length(obstruction_queue)) - while !isempty(obstruction_queue) -# #println(length(obstruction_queue)) obstruction = dequeue!(obstruction_queue) # step3 S = s_polynomial(obstruction) Sp = normal_form(S, g, aut) # or normal_form_weak if groebner_debug_level > 0 checked_obstructions += 1 - if checked_obstructions % 5000 == 0 - #println("checked $checked_obstructions obstructions") - #println(length(obstruction_queue)) - end end if iszero(Sp) - #println("is zero") continue end - #println(Sp) nonzero_reductions += 1 # step4 push!(g, Sp) insert_keyword!(aut, Sp.exps[1], length(g)) - #aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - - interreduce_counter += 1 -# if interreduce_counter > 2000 -# println("starting interreduce") -# interreduce!(g) -# println("done interreducing") -# interreduce_counter = 0 -# obstruction_queue = get_obstructions(g) -# continue -# end -# if groebner_debug_level > 0 -# println("adding new obstructions! checked $checked_obstructions so far") -# end + if groebner_debug_level > 0 + println("adding new obstructions! checked $checked_obstructions so far") + end if nonzero_reductions >= reduction_bound return g end - #debug - if nonzero_reductions > 2000 - # println(length(obstruction_queue)) - # println(length(g)) - # println(nonzero_reductions) - end - #end debug add_obstructions!(obstruction_queue, g) end return g diff --git a/test/generic/FreeAssAhoCorasick-test.jl b/test/generic/FreeAssAhoCorasick-test.jl index 297a928f86..f4279398e8 100644 --- a/test/generic/FreeAssAhoCorasick-test.jl +++ b/test/generic/FreeAssAhoCorasick-test.jl @@ -1,14 +1,14 @@ @testset "Generic.FreeAssAhoCorasick" begin keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] aut = AhoCorasickAutomaton(keywords) - @test search(aut, [10, 4, 1, 2, 3, 4]) == (6, (1, [1, 2, 3, 4])) + @test search(aut, [10, 4, 1, 2, 3, 4]) == AhoCorasickMatch(6, 1, [1, 2, 3, 4]) @test isnothing(search(aut, [])) - @test search(aut, [1, 5, 4, 1, 1, 1, 4, 4]) == (3, (2, [1, 5, 4])) - @test search(aut, [1, 2, 3, 1, 4, 1, 2, 1, 4, 1, 2]) == (7, (3, [4, 1, 2])) - @test search(aut, [2, 1, 2, 3, 1]) == (3, (4, [1, 2])) + @test search(aut, [1, 5, 4, 1, 1, 1, 4, 4]) == AhoCorasickMatch(3, 2, [1, 5, 4]) + @test search(aut, [1, 2, 3, 1, 4, 1, 2, 1, 4, 1, 2]) == AhoCorasickMatch(7, 3, [4, 1, 2]) + @test search(aut, [2, 1, 2, 3, 1]) == AhoCorasickMatch(3, 4, [1, 2]) @test isnothing(search(aut, [1, 3, 1, 5, 1, 4, 8])) @test isnothing(search(aut, [8, 8, 7, 10, 456])) - @test search(aut, [4, 1, 5, 4]) == (4, (2, [1, 5, 4])) + @test search(aut, [4, 1, 5, 4]) == AhoCorasickMatch(4, 2, [1, 5, 4]) @test isnothing(search(aut, [4, 1, 5, 10])) @test !isnothing(AhoCorasickAutomaton(Vector{Int}[])) end diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 2fb00de7bf..58dcc58be6 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -1,21 +1,21 @@ -#@testset "Generic.FreeAssAlgebra.groebner" begin -# -# R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) -# g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, -# (y*x)^3*t + (y*x)^2*t + t + s]) -# @test length(g) >= 5 -# -# # Example 6.1 Kreuzer & Xiu -# R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) -# -# g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) -# AbstractAlgebra.interreduce!(g) -# @test length(g) == 5 -# -# g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) -# AbstractAlgebra.interreduce!(g) -# @test length(g) == 15 -#end +@testset "Generic.FreeAssAlgebra.groebner" begin + + R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) + g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, + (y*x)^3*t + (y*x)^2*t + t + s]) + @test length(g) >= 5 + + # Example 6.1 Kreuzer & Xiu + R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 5 + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 15 +end @testset "Generic.FreeAssociativeAlgebra.groebner.normal_form" begin R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(QQ, ["x", "y", "u", "v", "t", "s"]) # x > y > ... > s @@ -33,51 +33,3 @@ @test gb_divides_leftmost_aho_corasick((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) @test gb_divides_leftmost_aho_corasick((x*s*t).exps[1], aut) == (false, [], [], -1) end - -include("/home/julien/uni/mathe/Promotion/ncmpoly/graph_to_ass_alg.jl") -using DataStructures - -@testset "Generic.FreeAssociativeAlgebra.groebner_basis" begin -# M = [0 1 0 0 1; 1 0 1 1 1; 0 1 0 1 1; 0 1 1 0 1; 1 1 1 1 0] - M = [0 0 0 0 0 1 1; 0 0 0 0 0 1 0; 0 0 0 0 0 0 1; 0 0 0 0 0 0 1; 0 0 0 0 0 0 1; 1 1 0 0 0 0 0; 1 0 1 1 1 0 0] - A, u, r = free_alg_for_graph(M) - g = copy(r) - checked_obstructions = 0 - nonzero_reductions = 0 - reduction_bound = 5000 - # compute the aho corasick automaton - # to make normal form computation more efficient - aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - - suffix_match_vectors = Vector{Vector{Int}}(undef, length(g)) - for i in 1:length(g) - suffix_match_vectors[i] = calc_suffix_match_vector(g[i].exps[1]) - end - # step 1 - obstruction_queue = get_obstructions(g) - while !isempty(obstruction_queue) - # #println(length(obstruction_queue)) - obstruction = dequeue!(obstruction_queue) - # step3 - S = s_polynomial(obstruction) - Sp = normal_form(S, g, aut) # or normal_form_weak - Sp2 = normal_form(S, g, suffix_match_vectors) - @test Sp == Sp2 - if iszero(Sp) - #println("is zero") - continue - end - #println(Sp) - nonzero_reductions += 1 - # step4 - push!(g, Sp) - aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - push!(suffix_match_vectors, calc_suffix_match_vector(Sp2.exps[1])) - - if nonzero_reductions >= reduction_bound - return g - end - add_obstructions!(obstruction_queue, g) - end - return g -end From 135c935f765a1227c14feee4a39836a0c874af4e Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 20 Oct 2022 12:50:49 +0200 Subject: [PATCH 17/80] removed unnecessary dependencies and comments --- Project.toml | 4 ---- src/generic/FreeAssAhoCorasick.jl | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index c5e5b68ae1..63a36f235d 100644 --- a/Project.toml +++ b/Project.toml @@ -3,16 +3,12 @@ uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" version = "0.26.0" [deps] -BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" -Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" -ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RandomExtensions = "fb686558-2515-59ef-acaa-46db3789a887" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl index 86984cf867..5edf82f979 100644 --- a/src/generic/FreeAssAhoCorasick.jl +++ b/src/generic/FreeAssAhoCorasick.jl @@ -3,7 +3,7 @@ # for use e.g. in Groebner Basis computation # ############################################################################### -#TODO how to properly export this? + export search, AhoCorasickAutomaton, AhoCorasickMatch, Word, insert_keyword! using DataStructures @@ -134,8 +134,6 @@ function search(automaton::AhoCorasickAutomaton, word) end if automaton.output[current_state][1] < result.keyword_index result = AhoCorasickMatch(i, automaton.output[current_state][1], automaton.output[current_state][2]) - #push!(output, (i, automaton.output[current_state])) -# union!(output_set, (i, automaton.output[current_state])) end end if result.keyword == [] From 0f7fe94e6a8a963ab519c2b53057338e5b205705 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 10 Mar 2023 14:12:56 +0100 Subject: [PATCH 18/80] added docstrings to all exported functions in AhoCorasick.jl and FreeAssAlgebraGroebner.jl, also renamed FreeAssAhoCorasick.jl to AhoCorasick.jl --- src/Generic.jl | 2 +- .../{FreeAssAhoCorasick.jl => AhoCorasick.jl} | 44 ++++++++++++++++--- src/generic/FreeAssAlgebraGroebner.jl | 33 ++++++++++++-- 3 files changed, 68 insertions(+), 11 deletions(-) rename src/generic/{FreeAssAhoCorasick.jl => AhoCorasick.jl} (79%) diff --git a/src/Generic.jl b/src/Generic.jl index c6d2372fc0..dc3bc05d7f 100644 --- a/src/Generic.jl +++ b/src/Generic.jl @@ -142,7 +142,7 @@ include("generic/MapCache.jl") include("generic/Ideal.jl") -include("generic/FreeAssAhoCorasick.jl") +include("generic/AhoCorasick.jl") include("generic/FreeAssAlgebraGroebner.jl") diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/AhoCorasick.jl similarity index 79% rename from src/generic/FreeAssAhoCorasick.jl rename to src/generic/AhoCorasick.jl index 86984cf867..b4ddc2fced 100644 --- a/src/generic/FreeAssAhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -3,21 +3,37 @@ # for use e.g. in Groebner Basis computation # ############################################################################### -#TODO how to properly export this? -export search, AhoCorasickAutomaton, AhoCorasickMatch, Word, insert_keyword! + +export search, AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton#, AhoCorasickMatch, Word using DataStructures const Word = Vector{Int} +Markdown.@doc doc""" +AhoCorasickAutomaton + +An Aho-Corasick automaton, which can be used to efficiently search for a fixed list of keywords (vectors of Ints) in +arbitrary lists of integers + +# Examples: +```jldoctest; setup = :(using AbstractAlgebra) +julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] +julia> aut = AhoCorasickAutomaton(keywords) +julia> search(aut, [10, 4, 1, 2, 3, 4]) +AhoCorasickMatch(6, 1, [1, 2, 3, 4]) + + +``` +""" +mutable struct AhoCorasickAutomaton + goto::Vector{Dict{Int, Int}} + fail::Vector{Int} """ Output stores for each node a tuple (i, k), where i is the index of the keyword k in the original list of keywords. If several keywords would be the output of the node, only the one with the smallest index is stored """ -mutable struct AhoCorasickAutomaton - goto::Vector{Dict{Int, Int}} - fail::Vector{Int} output::Vector{Tuple{Int, Word}} end @@ -27,6 +43,10 @@ struct AhoCorasickMatch keyword::Word end +function aho_corasick_match(last_position::Int, keyword_index::Int, keyword::Word) + return AhoCorasickMatch(last_position, keyword_index, keyword) +end + Base.hash(m::AhoCorasickMatch) = hash(m.last_position, hash(m.keyword_index, hash(m.keyword))) function ==(m1::AhoCorasickMatch, m2::AhoCorasickMatch) return m1.last_position == m2.last_position && m1.keyword_index == m2.keyword_index && m1.keyword == m2.keyword @@ -39,6 +59,10 @@ function AhoCorasickAutomaton(keywords::Vector{Word}) return automaton end +function aho_corasick_automaton(keywords::Vector{Word}) + return AhoCorasickAutomaton(keywords) +end + function lookup(automaton::AhoCorasickAutomaton, current_state::Int, next_letter::Int) ret_value = get(automaton.goto[current_state], next_letter, nothing) if current_state == 1 && isnothing(ret_value) @@ -109,14 +133,22 @@ function construct_fail!(automaton::AhoCorasickAutomaton) end end +Markdown.@doc doc""" +insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) + +Insert a new keyword into a given Aho-Corasick automaton to avoid having to rebuild the entire +automaton. +""" function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) enter!(aut, keyword, index) aut.fail = ones(Int, length(aut.goto)) construct_fail!(aut) end -""" +Markdown.@doc doc""" +search(automaton::AhoCorasickAutomaton, word) +Search for the first occurrence of a keyword that is stored in `automaton` in the given `word`. """ function search(automaton::AhoCorasickAutomaton, word) current_state = 1 diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 8f01d466c1..c7fa4a33f1 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -3,7 +3,7 @@ # FreeAssAlgebraGroebner.jl : free associative algebra R Groebner basis # ############################################################################### -export groebner_basis, interreduce!, gb_divides_leftmost_aho_corasick, normal_form +export groebner_basis, interreduce!, gb_divides_leftmost, normal_form using DataStructures @@ -47,7 +47,14 @@ function _leading_word(a::FreeAssAlgElem{T}) where T return a.exps[1] end -function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) +Markdown.@doc doc""" +gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) + +If an element of the Groebner basis that is stored in `aut` divides `a`, +return (true, a1, a2, keyword_index), where `keyword_index` is the index of the +keyword that divides `a` such that `a = a1 aut[keyword_index] a2`. +""" +function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) match = search(aut, a) if isnothing(match) return (false, [], [], -1) @@ -56,6 +63,12 @@ function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel +Markdown.@doc doc""" +normal_form(f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton) + +Assuming `g` is a groebner basis and `aut` an Aho-Corasick automaton for the elements of `g`, +compute the normal form of `f` with respect to `g` +""" function normal_form( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, @@ -65,7 +78,7 @@ function normal_form( rexps = Vector{Int}[] rcoeffs = T[] while length(f) > 0 - ok, left, right, match_index = gb_divides_leftmost_aho_corasick(f.exps[1], aut) + ok, left, right, match_index = gb_divides_leftmost(f.exps[1], aut) if ok qi = divexact(f.coeffs[1], g[match_index].coeffs[1]) f = _sub_rest(f, mul_term(qi, left, g[match_index], right), 1) @@ -78,7 +91,6 @@ function normal_form( return FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) end - # normal form with leftmost word divisions function normal_form( f::FreeAssAlgElem{T}, @@ -135,6 +147,13 @@ function normal_form_weak( return f end +Markdown.@doc doc""" +interreduce!(g::Vector{FreeAssAlgElem{T}}) + +Interreduce a given Groebner basis with itself, i.e. compute the normal form of each +element of `g` with respect to the rest of the elements and discard elements with +normal form $0$ and duplicates. +""" function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T i = 1 while length(g) > 1 && length(g) >= i @@ -551,6 +570,12 @@ function groebner_basis_buchberger( return g end +Markdown.@doc doc""" +groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int) + +Compute a groebner basis for the ideal spanned by g. Stop when `reduction_bound` many +non-zero entries have been added to the groebner basis. +""" function groebner_basis( g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int From 51da2f6b0f6a898535ae6299d7cc192cd0439ce7 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 14 Mar 2023 16:02:06 +0100 Subject: [PATCH 19/80] integrated changes from the big renaming --- src/AbstractAlgebra.jl | 2 + src/Generic.jl | 2 +- .../{FreeAssAhoCorasick.jl => AhoCorasick.jl} | 0 src/generic/FreeAssAlgebra.jl | 71 +++++-------------- src/generic/FreeAssAlgebraGroebner.jl | 6 +- src/generic/GenericTypes.jl | 12 ++++ ...hoCorasick-test.jl => AhoCorasick-test.jl} | 3 +- test/generic/FreeAssAlgebraGroebner-test.jl | 8 ++- test/runtests_free_ass_groebner.jl | 2 +- 9 files changed, 42 insertions(+), 64 deletions(-) rename src/generic/{FreeAssAhoCorasick.jl => AhoCorasick.jl} (100%) rename test/generic/{FreeAssAhoCorasick-test.jl => AhoCorasick-test.jl} (86%) diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index a7e32ff5fc..cf34092f4a 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -615,12 +615,14 @@ import .Generic: finish import .Generic: fit! import .Generic: gcd import .Generic: gcdx +import .Generic: groebner_basis import .Generic: has_bottom_neighbor import .Generic: has_left_neighbor import .Generic: hash import .Generic: hooklength import .Generic: image_fn import .Generic: image_map +import .Generic: interreduce! import .Generic: intersection import .Generic: inv! import .Generic: inverse_fn diff --git a/src/Generic.jl b/src/Generic.jl index a8f8966484..2e7ff27e8b 100644 --- a/src/Generic.jl +++ b/src/Generic.jl @@ -323,7 +323,7 @@ include("generic/MapCache.jl") include("generic/Ideal.jl") -include("generic/FreeAssAhoCorasick.jl") +include("generic/AhoCorasick.jl") include("generic/FreeAssAlgebraGroebner.jl") diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/AhoCorasick.jl similarity index 100% rename from src/generic/FreeAssAhoCorasick.jl rename to src/generic/AhoCorasick.jl diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index fb2966a7dc..b8a264d6dc 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -515,63 +515,24 @@ function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) w return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, a.length) end - -function calc_suffix_match_vector(b::Vector{Int}) - pos = 1 - cnd = 0 - suffix_match_vector = Vector{Int}(undef, length(b) + 1) - suffix_match_vector[1] = -1 - while pos < length(b) - if b[pos + 1] == b[cnd + 1] - suffix_match_vector[pos + 1] = suffix_match_vector[cnd + 1] - else - suffix_match_vector[pos + 1] = cnd - while cnd >= 0 && b[pos+1] != b[cnd+1] - cnd = suffix_match_vector[cnd+1] - end - end - pos = pos + 1 - cnd = cnd + 1 - end - suffix_match_vector[pos + 1] = cnd - return suffix_match_vector -end - -function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}) - suffix_match_vector = calc_suffix_match_vector(b) - return word_divides_leftmost(a, b, suffix_match_vector) -end - # return (true, l, r) with a = l*b*r and length(l) minimal # or (false, junk, junk) if a is not two-sided divisible by b -function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}, suffix_match_vector::Vector{Int}) - result_position = -1 - match = false - j = 0 - k = 0 - while j < length(a) - if b[k + 1] == a[j + 1] - j = j+1 - k = k+1 - if k == length(b) - result_position = j - k - match = true - break - end - else - k = suffix_match_vector[k + 1] - if k < 0 - j = j+1 - k = k+1 - end - end - - end - if match - return (true, Int[a[k] for k in 1:result_position], - Int[a[k] for k in 1+result_position+length(b):length(a)]) - end - return (false, Int[], Int[]) +function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}) + n = length(b) + for i in 0:length(a)-n + match = true + for j in 1:n + if b[j] != a[i+j] + match = false + break + end + end + if match + return (true, Int[a[k] for k in 1:i], + Int[a[k] for k in 1+i+n:length(a)]) + end + end + return (false, Int[], Int[]) end # return (true, l, r) with a = l*b*r and length(r) minimal diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 8f01d466c1..e14fd2c069 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -3,7 +3,7 @@ # FreeAssAlgebraGroebner.jl : free associative algebra R Groebner basis # ############################################################################### -export groebner_basis, interreduce!, gb_divides_leftmost_aho_corasick, normal_form +export groebner_basis, interreduce!, normal_form using DataStructures @@ -47,7 +47,7 @@ function _leading_word(a::FreeAssAlgElem{T}) where T return a.exps[1] end -function gb_divides_leftmost_aho_corasick(a::Word, aut::AhoCorasickAutomaton) +function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) match = search(aut, a) if isnothing(match) return (false, [], [], -1) @@ -65,7 +65,7 @@ function normal_form( rexps = Vector{Int}[] rcoeffs = T[] while length(f) > 0 - ok, left, right, match_index = gb_divides_leftmost_aho_corasick(f.exps[1], aut) + ok, left, right, match_index = gb_divides_leftmost(f.exps[1], aut) if ok qi = divexact(f.coeffs[1], g[match_index].coeffs[1]) f = _sub_rest(f, mul_term(qi, left, g[match_index], right), 1) diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index e770d9b86e..39d272baa4 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -1147,6 +1147,18 @@ struct FreeAssAlgExponentWords{T <: AbstractAlgebra.NCRingElem} poly::T end +############################################################################### +# +# AhoCorasickAutomaton +# +############################################################################### + +mutable struct AhoCorasickAutomaton + goto::Vector{Dict{Int, Int}} + fail::Vector{Int} + output::Vector{Tuple{Int, Vector{Int}}} +end + ############################################################################### # # CompositeMap diff --git a/test/generic/FreeAssAhoCorasick-test.jl b/test/generic/AhoCorasick-test.jl similarity index 86% rename from test/generic/FreeAssAhoCorasick-test.jl rename to test/generic/AhoCorasick-test.jl index f4279398e8..ab905efb1f 100644 --- a/test/generic/FreeAssAhoCorasick-test.jl +++ b/test/generic/AhoCorasick-test.jl @@ -1,4 +1,5 @@ -@testset "Generic.FreeAssAhoCorasick" begin +using AbstractAlgebra.Generic: AhoCorasickAutomaton, search, AhoCorasickMatch +@testset "Generic.AhoCorasick" begin keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] aut = AhoCorasickAutomaton(keywords) @test search(aut, [10, 4, 1, 2, 3, 4]) == AhoCorasickMatch(6, 1, [1, 2, 3, 4]) diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 58dcc58be6..480bad2883 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -1,3 +1,5 @@ +include("AhoCorasick-test.jl") +using AbstractAlgebra.Generic: AhoCorasickAutomaton @testset "Generic.FreeAssAlgebra.groebner" begin R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) @@ -29,7 +31,7 @@ end @test normal_form(one(R), ideal_generators, aut) == one(R) @test normal_form(v*y, ideal_generators, aut) == v*y @test normal_form(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators, aut) == zero(R) - @test gb_divides_leftmost_aho_corasick((x*y*u*v*t).exps[1], aut) == (true, [], [3, 4, 5], 1) - @test gb_divides_leftmost_aho_corasick((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) - @test gb_divides_leftmost_aho_corasick((x*s*t).exps[1], aut) == (false, [], [], -1) +# @test gb_divides_leftmost((x*y*u*v*t).exps[1], aut) == (true, [], [3, 4, 5], 1) +# @test gb_divides_leftmost((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) +# @test gb_divides_leftmost((x*s*t).exps[1], aut) == (false, [], [], -1) end diff --git a/test/runtests_free_ass_groebner.jl b/test/runtests_free_ass_groebner.jl index 8b3eb808d9..2e6e122964 100644 --- a/test/runtests_free_ass_groebner.jl +++ b/test/runtests_free_ass_groebner.jl @@ -3,7 +3,7 @@ Pkg.activate("/home/julien/.julia/dev/AbstractAlgebra/") using AbstractAlgebra using AbstractAlgebra.Generic using Test -include("generic/FreeAssAhoCorasick-test.jl") +include("generic/AhoCorasick-test.jl") include("generic/FreeAssAlgebraGroebner-test.jl") From 24adfb8a8466d2f39bb9a06247f0fa45c9375476 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 17 Mar 2023 14:19:59 +0100 Subject: [PATCH 20/80] added documentation of the groebner basis function and aho corasick automata in the free associative algebra documentation --- docs/src/free_associative_algebra.md | 60 +++++++++++++++++++++++++++ src/generic/AhoCorasick.jl | 31 +++++++++++++- src/generic/FreeAssAlgebraGroebner.jl | 8 ++-- src/generic/GenericTypes.jl | 12 ------ 4 files changed, 93 insertions(+), 18 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index 7a28f59ef1..d2e2a199ca 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -218,3 +218,63 @@ julia> collect(exponent_words(3*b*a*c - b + c + 2)) [] ``` +### Groebner bases + +The function `groebner_basis` provides the computation of a groebner basis of an ideal, given a set of +generators of that ideal. +Since such a groebner basis is not necessarily finite, one can additionally pass a `reduction_bound` +to the function, to only compute a partial groebner basis. + +**Examples** + +```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) + +julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) +(Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) + +julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, + (y*x)^3*t + (y*x)^2*t + t + s]) +5-element Vector{AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}}: + u*x*y*x*y*x*y + u*x*y*x*y + u + v + y*x*y*x*y*x*t + y*x*y*x*t + t + s + u*x*s + v*x*t + u*x*y*x*s + v*x*y*x*t + u*x*y*x*y*x*s + v*x*y*x*y*x*t + ``` + + In order to check whether a given element of the algebra is in the ideal generated by a groebner + basis `g`, one can compute its normal form. +julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) +julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, + (y*x)^3*t + (y*x)^2*t + t + s]) +julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) +0 + ``` + + If one has to compute the normal forms of many elements with respect to the same groebner basis, + this can be quite resource heavy. There is a more efficient + variant using [Aho-Corasicks string matching algorithm][https://doi.org/10.1145%2F360825.360855]. + An `AhoCorasickAutomaton` can be easily constructed by calling it with a vector of keywords, + where a keyword is just a `Vector{Int}`. + + + ```jldoctest; setup = :(using AbstractAlgebra.Generic) + julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] + julia> aut = AhoCorasickAutomaton(keywords) + julia> search(aut, [10, 4, 1, 2, 3, 4]) + AhoCorasickMatch(6, 1, [1, 2, 3, 4]) + ``` + + To use this for the normal form computation, one uses the exponent vectors of the generators of + the groebner basis as keywords. + ```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) + + julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) + (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) + + julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) + julia> aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) + julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g, aut) + 0 + ``` + diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index ba55e3a393..3ac3c5b1de 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -7,7 +7,7 @@ export search, AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton, AhoCorasickMatch#, Word -using DataStructures +import DataStructures.Queue const Word = Vector{Int} @@ -18,7 +18,7 @@ An Aho-Corasick automaton, which can be used to efficiently search for a fixed l arbitrary lists of integers # Examples: -```jldoctest; setup = :(using AbstractAlgebra) +```jldoctest; setup = :(using AbstractAlgebra.Generic) julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] julia> aut = AhoCorasickAutomaton(keywords) julia> search(aut, [10, 4, 1, 2, 3, 4]) @@ -38,6 +38,33 @@ the one with the smallest index is stored output::Vector{Tuple{Int, Word}} end +Markdown.@doc doc""" +AhoCorasickMatch(last_position::Int, keyword_index::Int, keyword::Vector{Int}) + +The return value of searching in a given word with an AhoCorasickAutomaton. Contains the position of the last letter in +the word that matches a keyword in the automaton, an index of the keyword that was matched and the keyword itself. + +# Examples: +```jldoctest; setup = :(using AbstractAlgebra.Generic) +julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] +julia> aut = AhoCorasickAutomaton(keywords) +julia> search(aut, [10, 4, 1, 2, 3, 4]) +AhoCorasickMatch(6, 1, [1, 2, 3, 4]) + +julia> search(aut, [10, 4, 1, 2, 3, 4]).last_position +6 + +julia> search(aut, [10, 4, 1, 2, 3, 4]).keyword_index +1 + +julia> search(aut, [10, 4, 1, 2, 3, 4]).keyword +4-element Vector{Int64}: + 1 + 2 + 3 + 4 + +""" struct AhoCorasickMatch last_position::Int keyword_index::Int diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 7a55c582aa..14d9a051b7 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -6,7 +6,8 @@ export groebner_basis, interreduce!, normal_form -using DataStructures +#using DataStructures +import DataStructures.PriorityQueue const groebner_debug_level = 0 const Monomial = Vector{Int} @@ -95,8 +96,7 @@ end # normal form with leftmost word divisions function normal_form( f::FreeAssAlgElem{T}, - g::Vector{FreeAssAlgElem{T}}, - suffix_match_vectors::Vector{Vector{Int}} + g::Vector{FreeAssAlgElem{T}} ) where T <: FieldElement R = parent(f) s = length(g) @@ -105,7 +105,7 @@ function normal_form( while length(f) > 0 i = 1 @label again - ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1], suffix_match_vectors[i]) + ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) if !ok && i < s i += 1 @goto again diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 39d272baa4..e770d9b86e 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -1147,18 +1147,6 @@ struct FreeAssAlgExponentWords{T <: AbstractAlgebra.NCRingElem} poly::T end -############################################################################### -# -# AhoCorasickAutomaton -# -############################################################################### - -mutable struct AhoCorasickAutomaton - goto::Vector{Dict{Int, Int}} - fail::Vector{Int} - output::Vector{Tuple{Int, Vector{Int}}} -end - ############################################################################### # # CompositeMap From 9c6d94cb9c2a909bdd14e47667307bacb580d237 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 17 Mar 2023 15:12:50 +0100 Subject: [PATCH 21/80] fixed typo in the documentation --- docs/src/free_associative_algebra.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index d2e2a199ca..6bee760133 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -230,7 +230,8 @@ to the function, to only compute a partial groebner basis. ```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) -(Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) +(Free associative algebra over Finite field F_2 on x, y, u, v, t, s, +AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) @@ -244,6 +245,7 @@ julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, In order to check whether a given element of the algebra is in the ideal generated by a groebner basis `g`, one can compute its normal form. +```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) @@ -268,9 +270,9 @@ julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) To use this for the normal form computation, one uses the exponent vectors of the generators of the groebner basis as keywords. ```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) - julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) - (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) + (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, + AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) julia> aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) From ff198584a1dc548713051ac1df580bbd61dc169c Mon Sep 17 00:00:00 2001 From: julien-schanz <60349899+julien-schanz@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:14:24 +0100 Subject: [PATCH 22/80] Update free_associative_algebra.md --- docs/src/free_associative_algebra.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index 6bee760133..8ef243d8a7 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -255,7 +255,7 @@ julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) If one has to compute the normal forms of many elements with respect to the same groebner basis, this can be quite resource heavy. There is a more efficient - variant using [Aho-Corasicks string matching algorithm][https://doi.org/10.1145%2F360825.360855]. + variant using [Aho-Corasicks string matching algorithm](https://doi.org/10.1145%2F360825.360855). An `AhoCorasickAutomaton` can be easily constructed by calling it with a vector of keywords, where a keyword is just a `Vector{Int}`. From fb393b211a7d4d407b6d18ec484d0d6cc42d6c1d Mon Sep 17 00:00:00 2001 From: julien-schanz <60349899+julien-schanz@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:19:09 +0100 Subject: [PATCH 23/80] Update free_associative_algebra.md --- docs/src/free_associative_algebra.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index 8ef243d8a7..2a423d43b6 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -233,8 +233,7 @@ julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v" (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) -julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, - (y*x)^3*t + (y*x)^2*t + t + s]) +julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) 5-element Vector{AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}}: u*x*y*x*y*x*y + u*x*y*x*y + u + v y*x*y*x*y*x*t + y*x*y*x*t + t + s @@ -247,8 +246,7 @@ julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, basis `g`, one can compute its normal form. ```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) -julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, - (y*x)^3*t + (y*x)^2*t + t + s]) +julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) 0 ``` From 2abd28f21414d5d6a924a41207fc6c2c40b4b5d2 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 21 Mar 2023 12:20:19 +0100 Subject: [PATCH 24/80] removed tracking from Manifest.toml --- Manifest.toml | 740 -------------------------------------------------- 1 file changed, 740 deletions(-) delete mode 100644 Manifest.toml diff --git a/Manifest.toml b/Manifest.toml deleted file mode 100644 index 384617fd5f..0000000000 --- a/Manifest.toml +++ /dev/null @@ -1,740 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.7.2" -manifest_format = "2.0" - -[[deps.ATK_jll]] -deps = ["Artifacts", "Glib_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "58c36d8a1beeb12d63921bcfaa674baf30a1140e" -uuid = "7b86fcea-f67b-53e1-809c-8f1719c154e8" -version = "2.36.1+0" - -[[deps.AbstractTrees]] -git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.3.4" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.BenchmarkTools]] -deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "4c10eee4af024676200bc7752e536f858c6b8f93" -uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.3.1" - -[[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+0" - -[[deps.Cairo]] -deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] -git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b" -uuid = "159f3aea-2a34-519c-b102-8c37f9878175" -version = "1.0.5" - -[[deps.Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.16.1+1" - -[[deps.CodeTracking]] -deps = ["InteractiveUtils", "UUIDs"] -git-tree-sha1 = "6d4fa04343a7fc9f9cb9cff9558929f3d2752717" -uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" -version = "1.0.9" - -[[deps.ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "a985dc37e357a3b22b260a5def99f3530fb415d3" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.2" - -[[deps.Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.8" - -[[deps.Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "b153278a25dd42c65abbf4e62344f9d22e59191b" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.43.0" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" - -[[deps.Crayons]] -git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.1" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "cc1a8e22627f33c789ab60b36a9132ac050bbf75" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.12" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[deps.Dbus_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "97f1325c10bd02b1cc1882e9c2bf6407ba630ace" -uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" -version = "1.12.16+3" - -[[deps.Debugger]] -deps = ["CodeTracking", "Crayons", "Highlights", "InteractiveUtils", "JuliaInterpreter", "Markdown", "REPL"] -git-tree-sha1 = "2e1376ac793d255c8a312a99b961ef64ae60ffee" -uuid = "31a5f54b-26ea-5ae9-a837-f05ce5417438" -version = "0.7.6" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.6" - -[[deps.Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.4.8+0" - -[[deps.FileIO]] -deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "9267e5f50b0e12fdfd5a2455534345c4cf2c7f7a" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.14.0" - -[[deps.FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.4" - -[[deps.FlameGraphs]] -deps = ["AbstractTrees", "Colors", "FileIO", "FixedPointNumbers", "IndirectArrays", "LeftChildRightSiblingTrees", "Profile"] -git-tree-sha1 = "d9eee53657f6a13ee51120337f98684c9c702264" -uuid = "08572546-2f56-4bcf-ba4e-bab62c3a3f89" -version = "0.2.10" - -[[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.10.4+0" - -[[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" -uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.10+0" - -[[deps.GTK3_jll]] -deps = ["ATK_jll", "Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Libepoxy_jll", "Pango_jll", "Pkg", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXcomposite_jll", "Xorg_libXcursor_jll", "Xorg_libXdamage_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "Xorg_libXrender_jll", "at_spi2_atk_jll", "gdk_pixbuf_jll", "iso_codes_jll", "xkbcommon_jll"] -git-tree-sha1 = "b080a592525632d287aee4637a62682576b7f5e4" -uuid = "77ec8976-b24b-556a-a1bf-49a033a670a6" -version = "3.24.31+0" - -[[deps.Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[deps.Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "a32d672ac2c967f3deb8a81d828afc739c838a06" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.68.3+2" - -[[deps.Graphics]] -deps = ["Colors", "LinearAlgebra", "NaNMath"] -git-tree-sha1 = "1c5a84319923bea76fa145d49e93aa4394c73fc2" -uuid = "a2bd30eb-e257-5431-a919-1863eab51364" -version = "1.1.1" - -[[deps.Graphite2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" -uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" -version = "1.3.14+0" - -[[deps.GroupsCore]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "9e1a5e9f3b81ad6a5c613d181664a0efc6fe6dd7" -uuid = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" -version = "0.4.0" - -[[deps.Gtk]] -deps = ["Cairo", "Cairo_jll", "Dates", "GTK3_jll", "Glib_jll", "Graphics", "JLLWrappers", "Libdl", "Librsvg_jll", "Pkg", "Reexport", "Serialization", "Test", "Xorg_xkeyboard_config_jll", "adwaita_icon_theme_jll", "gdk_pixbuf_jll", "hicolor_icon_theme_jll"] -git-tree-sha1 = "eb7302ec0e1690787f396bc882a7c681d430bfdb" -uuid = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44" -version = "1.2.1" - -[[deps.GtkObservables]] -deps = ["Cairo", "Colors", "Dates", "FixedPointNumbers", "Graphics", "Gtk", "IntervalSets", "LinearAlgebra", "Observables", "Reexport", "RoundingIntegers"] -git-tree-sha1 = "cf87f031fee932b90023ea37207c7a1de8caee6f" -uuid = "8710efd8-4ad6-11eb-33ea-2d5ceb25a41c" -version = "1.2.3" - -[[deps.HarfBuzz_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] -git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" -uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "2.8.1+1" - -[[deps.Highlights]] -deps = ["DocStringExtensions", "InteractiveUtils", "REPL"] -git-tree-sha1 = "d7e1d65e8599f2ee8df09c1461391e66ad9e2885" -uuid = "eafb193a-b7ab-5a9e-9068-77385905fa72" -version = "0.5.1" - -[[deps.IndirectArrays]] -git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" -uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" -version = "1.0.0" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.IntervalSets]] -deps = ["Dates", "Statistics"] -git-tree-sha1 = "ad841eddfb05f6d9be0bff1fa48dcae32f134a2d" -uuid = "8197267c-284f-5f27-9208-e0e47529a953" -version = "0.6.2" - -[[deps.JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.4.1" - -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "2.1.2+0" - -[[deps.JuliaInterpreter]] -deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] -git-tree-sha1 = "52617c41d2761cc05ed81fe779804d3b7f14fff7" -uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" -version = "0.9.13" - -[[deps.LERC_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" -uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "3.0.0+1" - -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" - -[[deps.LeftChildRightSiblingTrees]] -deps = ["AbstractTrees"] -git-tree-sha1 = "b864cb409e8e445688bc478ef87c0afe4f6d1f8d" -uuid = "1d6d02ad-be62-4b6b-8a6d-2f90e265016e" -version = "0.1.3" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" - -[[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[deps.Libepoxy_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "18b65a0eff6b58546bec18065e73f8a04e83758d" -uuid = "42c93a91-0102-5b3f-8f9d-e41de60ac950" -version = "1.5.8+1" - -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" - -[[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.3.0+3" - -[[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.16.1+1" - -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.35.0+0" - -[[deps.Librsvg_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pango_jll", "Pkg", "gdk_pixbuf_jll"] -git-tree-sha1 = "25d5e6b4eb3558613ace1c67d6a871420bfca527" -uuid = "925c91fb-5dd6-59dd-8e8c-345e74382d89" -version = "2.52.4+0" - -[[deps.Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "c9551dd26e31ab17b86cbd00c2ede019c08758eb" -uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.3.0+1" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.36.0+0" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.9" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" - -[[deps.MethodAnalysis]] -deps = ["AbstractTrees"] -git-tree-sha1 = "81e123ea81d6081fe8b733dbe79e1291d55cfb0f" -uuid = "85b6ec6f-f7df-4429-9514-a64bcd9ee824" -version = "0.4.6" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" - -[[deps.NaNMath]] -git-tree-sha1 = "b086b7ea07f8e38cf122f5016af580881ac914fe" -uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "0.3.7" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" - -[[deps.Observables]] -git-tree-sha1 = "dfd8d34871bc3ad08cd16026c1828e271d554db9" -uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.1" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" - -[[deps.OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" - -[[deps.PCRE_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488" -uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc" -version = "8.44.0+0" - -[[deps.Pango_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3a121dfbba67c94a5bec9dde613c3d0cbcf3a12b" -uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.50.3+0" - -[[deps.Parsers]] -deps = ["Dates"] -git-tree-sha1 = "1285416549ccfcdf0c50d4997a94331e88d68413" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.3.1" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.40.1+0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.3.0" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.Profile]] -deps = ["Printf"] -uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" - -[[deps.ProfileView]] -deps = ["Cairo", "Colors", "FileIO", "FlameGraphs", "Graphics", "Gtk", "GtkObservables", "InteractiveUtils", "IntervalSets", "MethodAnalysis", "Preferences", "Profile", "UUIDs"] -git-tree-sha1 = "17c95da7223ca01bccedde748ae4ccfebb494106" -uuid = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7" -version = "1.5.1" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[deps.Random]] -deps = ["SHA", "Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.RandomExtensions]] -deps = ["Random", "SparseArrays"] -git-tree-sha1 = "062986376ce6d394b23d5d90f01d81426113a3c9" -uuid = "fb686558-2515-59ef-acaa-46db3789a887" -version = "0.4.3" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[deps.RoundingIntegers]] -git-tree-sha1 = "99acd97f396ea71a5be06ba6de5c9defe188a778" -uuid = "d5f540fe-1c90-5db3-b776-2e2f362d9394" -version = "1.1.0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[deps.SparseArrays]] -deps = ["LinearAlgebra", "Random"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[[deps.Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[deps.Wayland_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23" -uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.19.0+0" - -[[deps.Wayland_protocols_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" -uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" -version = "1.25.0+0" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.9.12+0" - -[[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.34+0" - -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.6.9+4" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.9+4" - -[[deps.Xorg_libXcomposite_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll"] -git-tree-sha1 = "7c688ca9c957837539bbe1c53629bb871025e423" -uuid = "3c9796d7-64a0-5134-86ad-79f8eb684845" -version = "0.4.5+4" - -[[deps.Xorg_libXcursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" -uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" -version = "1.2.0+4" - -[[deps.Xorg_libXdamage_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll"] -git-tree-sha1 = "fe4ffb2024ba3eddc862c6e1d70e2b070cd1c2bf" -uuid = "0aeada51-83db-5f97-b67e-184615cfc6f6" -version = "1.1.5+4" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.3+4" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" - -[[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" - -[[deps.Xorg_libXi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] -git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" -uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" -version = "1.7.10+4" - -[[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" - -[[deps.Xorg_libXrandr_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" -uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" -version = "1.5.2+4" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" - -[[deps.Xorg_libXtst_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXi_jll"] -git-tree-sha1 = "0c0a60851f44add2a64069ddf213e941c30ed93c" -uuid = "b6f176f1-7aea-5357-ad67-1d3e565ea1c6" -version = "1.2.3+4" - -[[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.0+3" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.13.0+3" - -[[deps.Xorg_libxkbfile_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2" -uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" -version = "1.1.0+4" - -[[deps.Xorg_xkbcomp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"] -git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b" -uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" -version = "1.4.2+4" - -[[deps.Xorg_xkeyboard_config_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"] -git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d" -uuid = "33bec58e-1273-512f-9401-5d533626f822" -version = "2.27.0+4" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.4.0+3" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" - -[[deps.Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" -uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.2+0" - -[[deps.adwaita_icon_theme_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "hicolor_icon_theme_jll"] -git-tree-sha1 = "37c9a36ccb876e02876c8a654f1b2e8c1b443a78" -uuid = "b437f822-2cd6-5e08-a15c-8bac984d38ee" -version = "3.33.92+5" - -[[deps.at_spi2_atk_jll]] -deps = ["ATK_jll", "Artifacts", "JLLWrappers", "Libdl", "Pkg", "XML2_jll", "Xorg_libX11_jll", "at_spi2_core_jll"] -git-tree-sha1 = "f16ae690aca4761f33d2cb338ee9899e541f5eae" -uuid = "de012916-1e3f-58c2-8f29-df3ef51d412d" -version = "2.34.1+4" - -[[deps.at_spi2_core_jll]] -deps = ["Artifacts", "Dbus_jll", "Glib_jll", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXtst_jll"] -git-tree-sha1 = "d2d540cd145f2b2933614649c029d222fe125188" -uuid = "0fc3237b-ac94-5853-b45c-d43d59a06200" -version = "2.34.0+4" - -[[deps.gdk_pixbuf_jll]] -deps = ["Artifacts", "Glib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Xorg_libX11_jll", "libpng_jll"] -git-tree-sha1 = "c23323cd30d60941f8c68419a70905d9bdd92808" -uuid = "da03df04-f53b-5353-a52f-6a8b0620ced0" -version = "2.42.6+1" - -[[deps.hicolor_icon_theme_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b458a6f6fc2b1a8ca74ed63852e4eaf43fb9f5ea" -uuid = "059c91fe-1bad-52ad-bddd-f7b78713c282" -version = "0.17.0+3" - -[[deps.iso_codes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "5ee24c3ae30e006117ec2da5ea50f2ce457c019a" -uuid = "bf975903-5238-5d20-8243-bc370bc1e7e5" -version = "4.3.0+4" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.38+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" - -[[deps.xkbcommon_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6" -uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "0.9.1+5" From e41b0b9abd7e99929a315b129958898239d3ef4f Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 21 Mar 2023 13:37:54 +0100 Subject: [PATCH 25/80] added a test case for normal_form_weak --- src/generic/FreeAssAlgebraGroebner.jl | 2 +- test/generic/FreeAssAlgebraGroebner-test.jl | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 14d9a051b7..3b9595ed91 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -4,7 +4,7 @@ # ############################################################################### -export groebner_basis, interreduce!, normal_form +export groebner_basis, interreduce!, normal_form, normal_form_weak #using DataStructures import DataStructures.PriorityQueue diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 480bad2883..545c9ae64e 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -31,6 +31,9 @@ end @test normal_form(one(R), ideal_generators, aut) == one(R) @test normal_form(v*y, ideal_generators, aut) == v*y @test normal_form(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators, aut) == zero(R) + @test normal_form_weak(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators) == zero(R) + @test normal_form_weak(x*y + u*y) <= x*y + u*y + # @test gb_divides_leftmost((x*y*u*v*t).exps[1], aut) == (true, [], [3, 4, 5], 1) # @test gb_divides_leftmost((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) # @test gb_divides_leftmost((x*s*t).exps[1], aut) == (false, [], [], -1) From d6cb270999a7e6c5514295b5a9728ba9923bc7c9 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 21 Mar 2023 13:40:15 +0100 Subject: [PATCH 26/80] fixed imports in AhoCorasick.jl --- src/generic/AhoCorasick.jl | 2 +- test/generic/FreeAssAlgebraGroebner-test.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 3ac3c5b1de..f2526a120f 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -7,7 +7,7 @@ export search, AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton, AhoCorasickMatch#, Word -import DataStructures.Queue +import DataStructures.Queue, DataStructures.enqueue!, DataStructures.dequeue! const Word = Vector{Int} diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 545c9ae64e..a6478cf8d2 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -32,7 +32,7 @@ end @test normal_form(v*y, ideal_generators, aut) == v*y @test normal_form(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators, aut) == zero(R) @test normal_form_weak(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators) == zero(R) - @test normal_form_weak(x*y + u*y) <= x*y + u*y + @test normal_form_weak(x*y + u*y, ideal_generators) <= x*y + u*y # @test gb_divides_leftmost((x*y*u*v*t).exps[1], aut) == (true, [], [3, 4, 5], 1) # @test gb_divides_leftmost((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) From 6602fc7c3101d7e3bc58e8f2eeaf83920be5c637 Mon Sep 17 00:00:00 2001 From: julien-schanz <60349899+julien-schanz@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:55:34 +0100 Subject: [PATCH 27/80] Update free_associative_algebra.md --- docs/src/free_associative_algebra.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index 2a423d43b6..5db2952b17 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -227,13 +227,13 @@ to the function, to only compute a partial groebner basis. **Examples** -```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) +```jldoctest; setup = :(using AbstractAlgebra) julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) -julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) +julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) 5-element Vector{AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}}: u*x*y*x*y*x*y + u*x*y*x*y + u + v y*x*y*x*y*x*t + y*x*y*x*t + t + s @@ -244,9 +244,9 @@ julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t In order to check whether a given element of the algebra is in the ideal generated by a groebner basis `g`, one can compute its normal form. -```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) +```jldoctest; setup = :(using AbstractAlgebra) julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) -julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) +julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) 0 ``` @@ -258,22 +258,22 @@ julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) where a keyword is just a `Vector{Int}`. - ```jldoctest; setup = :(using AbstractAlgebra.Generic) + ```jldoctest; setup = :(using AbstractAlgebra) julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] - julia> aut = AhoCorasickAutomaton(keywords) - julia> search(aut, [10, 4, 1, 2, 3, 4]) + julia> aut = Generic.AhoCorasickAutomaton(keywords) + julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]) AhoCorasickMatch(6, 1, [1, 2, 3, 4]) ``` To use this for the normal form computation, one uses the exponent vectors of the generators of the groebner basis as keywords. - ```jldoctest; setup = :(using AbstractAlgebra.Generic, AbstractAlgebra) + ```jldoctest; setup = :(using AbstractAlgebra) julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) - julia> g = groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) - julia> aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) + julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) + julia> aut = Generic.AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g, aut) 0 ``` From 6f9afbbd54cfdf8b00c1c38c82ab42b697f38015 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Wed, 22 Mar 2023 16:00:25 +0100 Subject: [PATCH 28/80] incorporated remarks by Max Horn --- docs/src/free_associative_algebra.md | 44 +- src/generic/AhoCorasick.jl | 86 ++- src/generic/FreeAssAlgebra.jl | 862 ++++++++++++++------------ src/generic/FreeAssAlgebraGroebner.jl | 639 ++++++++++--------- test/runtests_free_ass_groebner.jl | 9 - 5 files changed, 842 insertions(+), 798 deletions(-) delete mode 100644 test/runtests_free_ass_groebner.jl diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index 5db2952b17..994cd054ae 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -220,16 +220,16 @@ julia> collect(exponent_words(3*b*a*c - b + c + 2)) ### Groebner bases -The function `groebner_basis` provides the computation of a groebner basis of an ideal, given a set of +The function `groebner_basis` provides the computation of a Groebner basis of an ideal, given a set of generators of that ideal. -Since such a groebner basis is not necessarily finite, one can additionally pass a `reduction_bound` -to the function, to only compute a partial groebner basis. +Since such a Groebner basis is not necessarily finite, one can additionally pass a `reduction_bound` +to the function, to only compute a partial Groebner basis. **Examples** ```jldoctest; setup = :(using AbstractAlgebra) -julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) +julia> R, (x, y, u, v, t, s) = free_associative_algebra(GF(2), ["x", "y", "u", "v", "t", "s"]) (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) @@ -240,41 +240,13 @@ julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y u*x*s + v*x*t u*x*y*x*s + v*x*y*x*t u*x*y*x*y*x*s + v*x*y*x*y*x*t - ``` +``` - In order to check whether a given element of the algebra is in the ideal generated by a groebner - basis `g`, one can compute its normal form. +In order to check whether a given element of the algebra is in the ideal generated by a Groebner +basis `g`, one can compute its normal form. ```jldoctest; setup = :(using AbstractAlgebra) -julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) +julia> R, (x, y, u, v, t, s) = free_associative_algebra(GF(2), ["x", "y", "u", "v", "t", "s"]) julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) 0 ``` - - If one has to compute the normal forms of many elements with respect to the same groebner basis, - this can be quite resource heavy. There is a more efficient - variant using [Aho-Corasicks string matching algorithm](https://doi.org/10.1145%2F360825.360855). - An `AhoCorasickAutomaton` can be easily constructed by calling it with a vector of keywords, - where a keyword is just a `Vector{Int}`. - - - ```jldoctest; setup = :(using AbstractAlgebra) - julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] - julia> aut = Generic.AhoCorasickAutomaton(keywords) - julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]) - AhoCorasickMatch(6, 1, [1, 2, 3, 4]) - ``` - - To use this for the normal form computation, one uses the exponent vectors of the generators of - the groebner basis as keywords. - ```jldoctest; setup = :(using AbstractAlgebra) - julia> R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) - (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, - AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) - - julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) - julia> aut = Generic.AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g, aut) - 0 - ``` - diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index f2526a120f..5323638c1a 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -5,50 +5,50 @@ ############################################################################### -export search, AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton, AhoCorasickMatch#, Word +export search, + AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton, AhoCorasickMatch#, Word import DataStructures.Queue, DataStructures.enqueue!, DataStructures.dequeue! const Word = Vector{Int} Markdown.@doc doc""" -AhoCorasickAutomaton + AhoCorasickAutomaton -An Aho-Corasick automaton, which can be used to efficiently search for a fixed list of keywords (vectors of Ints) in +An Aho-Corasick automaton, which can be used to efficiently search for a fixed list of keywords (vectors of Ints) in arbitrary lists of integers # Examples: ```jldoctest; setup = :(using AbstractAlgebra.Generic) julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] -julia> aut = AhoCorasickAutomaton(keywords) -julia> search(aut, [10, 4, 1, 2, 3, 4]) +julia> aut = aho_corasick_automaton(keywords) +julia> search(aut, [10, 4, 1, 2, 3, 4]) AhoCorasickMatch(6, 1, [1, 2, 3, 4]) ``` -""" -mutable struct AhoCorasickAutomaton - goto::Vector{Dict{Int, Int}} +""" mutable struct AhoCorasickAutomaton + goto::Vector{Dict{Int,Int}} fail::Vector{Int} -""" -Output stores for each node a tuple (i, k), where i is the index of the keyword k in -the original list of keywords. If several keywords would be the output of the node, only -the one with the smallest index is stored -""" - output::Vector{Tuple{Int, Word}} + """ + Output stores for each node a tuple (i, k), where i is the index of the keyword k in + the original list of keywords. If several keywords would be the output of the node, only + the one with the smallest index is stored + """ + output::Vector{Tuple{Int,Word}} end Markdown.@doc doc""" -AhoCorasickMatch(last_position::Int, keyword_index::Int, keyword::Vector{Int}) + AhoCorasickMatch(last_position::Int, keyword_index::Int, keyword::Vector{Int}) -The return value of searching in a given word with an AhoCorasickAutomaton. Contains the position of the last letter in +The return value of searching in a given word with an AhoCorasickAutomaton. Contains the position of the last letter in the word that matches a keyword in the automaton, an index of the keyword that was matched and the keyword itself. # Examples: ```jldoctest; setup = :(using AbstractAlgebra.Generic) julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] -julia> aut = AhoCorasickAutomaton(keywords) -julia> search(aut, [10, 4, 1, 2, 3, 4]) +julia> aut = aho_corasick_automaton(keywords) +julia> search(aut, [10, 4, 1, 2, 3, 4]) AhoCorasickMatch(6, 1, [1, 2, 3, 4]) julia> search(aut, [10, 4, 1, 2, 3, 4]).last_position @@ -64,8 +64,7 @@ julia> search(aut, [10, 4, 1, 2, 3, 4]).keyword 3 4 -""" -struct AhoCorasickMatch +""" struct AhoCorasickMatch last_position::Int keyword_index::Int keyword::Word @@ -75,9 +74,12 @@ function aho_corasick_match(last_position::Int, keyword_index::Int, keyword::Wor return AhoCorasickMatch(last_position, keyword_index, keyword) end -Base.hash(m::AhoCorasickMatch) = hash(m.last_position, hash(m.keyword_index, hash(m.keyword))) +Base.hash(m::AhoCorasickMatch) = + hash(m.last_position, hash(m.keyword_index, hash(m.keyword))) function ==(m1::AhoCorasickMatch, m2::AhoCorasickMatch) - return m1.last_position == m2.last_position && m1.keyword_index == m2.keyword_index && m1.keyword == m2.keyword + return m1.last_position == m2.last_position && + m1.keyword_index == m2.keyword_index && + m1.keyword == m2.keyword end function AhoCorasickAutomaton(keywords::Vector{Word}) @@ -97,7 +99,6 @@ function lookup(automaton::AhoCorasickAutomaton, current_state::Int, next_letter return 1 end return ret_value - end @@ -105,10 +106,8 @@ function Base.length(automaton::AhoCorasickAutomaton) return length(automaton.goto) end - - function new_state!(automaton) - push!(automaton.goto, Dict{Int, Int}()) + push!(automaton.goto, Dict{Int,Int}()) push!(automaton.output, (typemax(Int), [])) push!(automaton.fail, 1) return length(automaton.goto) @@ -116,11 +115,9 @@ end function enter!(automaton::AhoCorasickAutomaton, keyword::Word, current_index) current_state = 1 - for c in keyword - new_state = get(automaton.goto[current_state], c, nothing) - if isnothing(new_state) - new_state = new_state!(automaton) - automaton.goto[current_state][c] = new_state + for c in keyword + new_state = get!(automaton.goto[current_state], c) do + new_state!(automaton) end current_state = new_state end @@ -131,10 +128,8 @@ end function construct_goto!(automaton::AhoCorasickAutomaton, keywords::Vector{Word}) new_state!(automaton) - current_index = 1 - for keyword in keywords + for (current_index, keyword) in enumerate(keywords) enter!(automaton, keyword, current_index) - current_index += 1 end end @@ -153,7 +148,8 @@ function construct_fail!(automaton::AhoCorasickAutomaton) state = automaton.fail[state] end automaton.fail[new_state] = lookup(automaton, state, k) - if automaton.output[new_state][1] > automaton.output[automaton.fail[new_state]][1] + if automaton.output[new_state][1] > + automaton.output[automaton.fail[new_state]][1] automaton.output[new_state] = automaton.output[automaton.fail[new_state]] # TODO check if this is the correct way to update output end @@ -162,26 +158,24 @@ function construct_fail!(automaton::AhoCorasickAutomaton) end Markdown.@doc doc""" -insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) + insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) -Insert a new keyword into a given Aho-Corasick automaton to avoid having to rebuild the entire +Insert a new keyword into a given Aho-Corasick automaton to avoid having to rebuild the entire automaton. -""" -function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) +""" function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) enter!(aut, keyword, index) aut.fail = ones(Int, length(aut.goto)) construct_fail!(aut) end Markdown.@doc doc""" -search(automaton::AhoCorasickAutomaton, word) + search(automaton::AhoCorasickAutomaton, word) Search for the first occurrence of a keyword that is stored in `automaton` in the given `word`. -""" -function search(automaton::AhoCorasickAutomaton, word) +""" function search(automaton::AhoCorasickAutomaton, word) current_state = 1 result = AhoCorasickMatch(typemax(Int), typemax(Int), []) - for i in 1:length(word) + for i = 1:length(word) c = word[i] while true next_state = lookup(automaton, current_state, c) @@ -193,7 +187,11 @@ function search(automaton::AhoCorasickAutomaton, word) end end if automaton.output[current_state][1] < result.keyword_index - result = AhoCorasickMatch(i, automaton.output[current_state][1], automaton.output[current_state][2]) + result = AhoCorasickMatch( + i, + automaton.output[current_state][1], + automaton.output[current_state][2], + ) end end if result.keyword == [] diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index b8a264d6dc..78f91c87c9 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -12,38 +12,42 @@ export exponent_word, set_exponent_word, calc_suffix_match_vector # ############################################################################### -function parent_type(::Type{FreeAssAlgElem{T}}) where T <: RingElement - return FreeAssAlgebra{T} +function parent_type(::Type{FreeAssAlgElem{T}}) where {T<:RingElement} + return FreeAssAlgebra{T} end -function elem_type(::Type{FreeAssAlgebra{T}}) where T <: RingElement - return FreeAssAlgElem{T} +function elem_type(::Type{FreeAssAlgebra{T}}) where {T<:RingElement} + return FreeAssAlgElem{T} end function parent(a::FreeAssAlgElem) - return a.parent + return a.parent end function base_ring(a::FreeAssAlgebra) - return a.base_ring + return a.base_ring end function symbols(a::FreeAssAlgebra) - return a.S + return a.S end function nvars(a::FreeAssAlgebra) - return length(a.S) + return length(a.S) end function length(a::FreeAssAlgElem) - return a.length + return a.length end -function check_parent(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}, throw::Bool = true) where T <: RingElement - b = parent(a) != parent(b) - b & throw && error("Incompatible rings in operation") - return !b +function check_parent( + a::FreeAssAlgElem{T}, + b::FreeAssAlgElem{T}, + throw::Bool = true, +) where {T<:RingElement} + b = parent(a) != parent(b) + b & throw && error("Incompatible rings in operation") + return !b end ############################################################################### @@ -52,55 +56,57 @@ end # ############################################################################### -function Base.deepcopy_internal(a::FreeAssAlgElem{T}, dict::IdDict) where {T <: RingElement} - return FreeAssAlgElem{T}(a.parent, - deepcopy_internal(a.coeffs, dict), - deepcopy_internal(a.exps, dict), - a.length) +function Base.deepcopy_internal(a::FreeAssAlgElem{T}, dict::IdDict) where {T<:RingElement} + return FreeAssAlgElem{T}( + a.parent, + deepcopy_internal(a.coeffs, dict), + deepcopy_internal(a.exps, dict), + a.length, + ) end -function zero(a::FreeAssAlgebra{T}) where T - return FreeAssAlgElem{T}(a, T[], Vector{Int}[], 0) +function zero(a::FreeAssAlgebra{T}) where {T} + return FreeAssAlgElem{T}(a, T[], Vector{Int}[], 0) end -function one(a::FreeAssAlgebra{T}) where T - c = one(base_ring(a)) - !iszero(c) || return zero(a) - return FreeAssAlgElem{T}(a, [c], [Int[]], 1) +function one(a::FreeAssAlgebra{T}) where {T} + c = one(base_ring(a)) + !iszero(c) || return zero(a) + return FreeAssAlgElem{T}(a, [c], [Int[]], 1) end -function iszero(a::FreeAssAlgElem{T}) where T - return length(a) == 0 +function iszero(a::FreeAssAlgElem{T}) where {T} + return length(a) == 0 end -function isone(a::FreeAssAlgElem{T}) where T - if length(a) < 1 - return isone(zero(base_ring(a))) - else - return a.length == 1 && isone(a.coeffs[1]) && isempty(a.exps[1]) - end +function isone(a::FreeAssAlgElem{T}) where {T} + if length(a) < 1 + return isone(zero(base_ring(a))) + else + return a.length == 1 && isone(a.coeffs[1]) && isempty(a.exps[1]) + end end -function gen(a::FreeAssAlgebra{T}, i::Int) where T - 0 < i <= nvars(a) || error("variable index out of range") - c = one(base_ring(a)) - iszero(c) && return zero(a) - return FreeAssAlgElem{T}(a, T[c], [Int[i]], 1) +function gen(a::FreeAssAlgebra{T}, i::Int) where {T} + 0 < i <= nvars(a) || error("variable index out of range") + c = one(base_ring(a)) + iszero(c) && return zero(a) + return FreeAssAlgElem{T}(a, T[c], [Int[i]], 1) end -function gens(a::FreeAssAlgebra{T}) where {T <: RingElement} - return [gen(a, i) for i in 1:nvars(a)] +function gens(a::FreeAssAlgebra{T}) where {T<:RingElement} + return [gen(a, i) for i = 1:nvars(a)] end -function is_gen(a::FreeAssAlgElem{T}) where T +function is_gen(a::FreeAssAlgElem{T}) where {T} if length(a) < 1 return iszero(one(base_ring(a))) else - return a.length == 1 && isone(a.coeffs[1]) && length(a.exps[1]) == 1 + return a.length == 1 && isone(a.coeffs[1]) && length(a.exps[1]) == 1 end end -function is_constant(a::FreeAssAlgElem{T}) where T +function is_constant(a::FreeAssAlgElem{T}) where {T} return length(a) == 0 || (length(a) == 1 && isempty(a.exps[1])) end @@ -110,10 +116,14 @@ end # ############################################################################### -promote_rule(::Type{FreeAssAlgElem{T}}, ::Type{FreeAssAlgElem{T}}) where T <: RingElement = FreeAssAlgElem{T} +promote_rule(::Type{FreeAssAlgElem{T}}, ::Type{FreeAssAlgElem{T}}) where {T<:RingElement} = + FreeAssAlgElem{T} -function promote_rule(::Type{FreeAssAlgElem{T}}, ::Type{U}) where {T <: RingElement, U <: RingElement} - promote_rule(T, U) == T ? FreeAssAlgElem{T} : Union{} +function promote_rule( + ::Type{FreeAssAlgElem{T}}, + ::Type{U}, +) where {T<:RingElement,U<:RingElement} + promote_rule(T, U) == T ? FreeAssAlgElem{T} : Union{} end ############################################################################### @@ -122,34 +132,35 @@ end # ############################################################################### -function (a::FreeAssAlgebra{T})() where T - return zero(a) +function (a::FreeAssAlgebra{T})() where {T} + return zero(a) end -function (a::FreeAssAlgebra{T})(b::T) where T - iszero(b) && return zero(a) - return FreeAssAlgElem{T}(a, T[b], [Int[]], 1) +function (a::FreeAssAlgebra{T})(b::T) where {T} + iszero(b) && return zero(a) + return FreeAssAlgElem{T}(a, T[b], [Int[]], 1) end -function (a::FreeAssAlgebra{T})(b::Integer) where T - iszero(b) && return zero(a) - R = base_ring(a) - return FreeAssAlgElem{T}(a, T[R(b)], [Int[]], 1) +function (a::FreeAssAlgebra{T})(b::Integer) where {T} + iszero(b) && return zero(a) + R = base_ring(a) + return FreeAssAlgElem{T}(a, T[R(b)], [Int[]], 1) end -function (a::FreeAssAlgebra{T})(b::FreeAssAlgElem{T}) where T <: RingElement - parent(b) != a && error("Unable to coerce element") - return b +function (a::FreeAssAlgebra{T})(b::FreeAssAlgElem{T}) where {T<:RingElement} + parent(b) != a && error("Unable to coerce element") + return b end -function (a::FreeAssAlgebra{T})(c::Vector{T}, e::Vector{Vector{Int}}) where T - for ei in e - all(i -> (i <= nvars(a)), ei) || error("variable index out of range") - end - n = length(c) - n == length(e) || error("coefficient array and exponent array should have the same length") - z = FreeAssAlgElem{T}(a, copy(c), copy(e), n) - return combine_like_terms!(sort_terms!(z)) +function (a::FreeAssAlgebra{T})(c::Vector{T}, e::Vector{Vector{Int}}) where {T} + for ei in e + all(i -> (i <= nvars(a)), ei) || error("variable index out of range") + end + n = length(c) + n == length(e) || + error("coefficient array and exponent array should have the same length") + z = FreeAssAlgElem{T}(a, copy(c), copy(e), n) + return combine_like_terms!(sort_terms!(z)) end ############################################################################### @@ -159,20 +170,20 @@ end ############################################################################### function coeff(a::FreeAssAlgElem, i::Int) - 0 < i <= length(a) || error("index out of range") - return a.coeffs[i] + 0 < i <= length(a) || error("index out of range") + return a.coeffs[i] end -function term(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement - 0 < i <= length(a) || error("index out of range") - R = parent(a) - return FreeAssAlgElem{T}(R, [a.coeffs[i]], [a.exps[i]], 1) +function term(a::FreeAssAlgElem{T}, i::Int) where {T<:RingElement} + 0 < i <= length(a) || error("index out of range") + R = parent(a) + return FreeAssAlgElem{T}(R, [a.coeffs[i]], [a.exps[i]], 1) end -function monomial(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement - 0 < i <= length(a) || error("index out of range") - R = parent(a) - return FreeAssAlgElem{T}(R, T[one(base_ring(R))], [a.exps[i]], 1) +function monomial(a::FreeAssAlgElem{T}, i::Int) where {T<:RingElement} + 0 < i <= length(a) || error("index out of range") + R = parent(a) + return FreeAssAlgElem{T}(R, T[one(base_ring(R))], [a.exps[i]], 1) end @doc Markdown.doc""" @@ -182,65 +193,67 @@ Return a vector of variable indices corresponding to the monomial of the $i$-th term of $a$. Term numbering begins at $1$, and the variable indices are given in the order of the variables for the ring. """ -function exponent_word(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement - 0 < i <= length(a) || error("index out of range") - return a.exps[i] +function exponent_word(a::FreeAssAlgElem{T}, i::Int) where {T<:RingElement} + 0 < i <= length(a) || error("index out of range") + return a.exps[i] end function Base.iterate(a::FreeAssAlgExponentWords, state = 0) - state += 1 - state <= length(a.poly) || return nothing - return exponent_word(a.poly, state), state + state += 1 + state <= length(a.poly) || return nothing + return exponent_word(a.poly, state), state end -function leading_coefficient(a::FreeAssAlgElem{T}) where T - return a.length > 0 ? coeff(a, 1) : zero(base_ring(a)) +function leading_coefficient(a::FreeAssAlgElem{T}) where {T} + return a.length > 0 ? coeff(a, 1) : zero(base_ring(a)) end -function leading_monomial(a::FreeAssAlgElem{T}) where T - if length(a) < 1 - throw(ArgumentError("Zero polynomial does not have a leading monomial")) - end - return monomial(a, 1) +function leading_monomial(a::FreeAssAlgElem{T}) where {T} + if length(a) < 1 + throw(ArgumentError("Zero polynomial does not have a leading monomial")) + end + return monomial(a, 1) end -function leading_term(a::FreeAssAlgElem{T}) where T - if length(a) < 1 - throw(ArgumentError("Zero polynomial does not have a leading term")) - end - return term(a, 1) +function leading_term(a::FreeAssAlgElem{T}) where {T} + if length(a) < 1 + throw(ArgumentError("Zero polynomial does not have a leading term")) + end + return term(a, 1) end -function leading_exponent_word(a::FreeAssAlgElem{T}) where T - if length(a) < 1 - throw(ArgumentError("Zero polynomial does not have a leading exponent word")) - end - return exponent_word(a, 1) +function leading_exponent_word(a::FreeAssAlgElem{T}) where {T} + if length(a) < 1 + throw(ArgumentError("Zero polynomial does not have a leading exponent word")) + end + return exponent_word(a, 1) end -function total_degree(a::FreeAssAlgElem{T}) where T - # currently stored in dexlex - return length(a) > 0 ? length(a.exps[1]) : -1 +function total_degree(a::FreeAssAlgElem{T}) where {T} + # currently stored in dexlex + return length(a) > 0 ? length(a.exps[1]) : -1 end -function Base.length(x::FreeAssAlgExponentWords{T}) where {S <: RingElement, - T <: FreeAssAlgElem{S}} - return length(x.poly) +function Base.length( + x::FreeAssAlgExponentWords{T}, +) where {S<:RingElement,T<:FreeAssAlgElem{S}} + return length(x.poly) end -function Base.eltype(x::FreeAssAlgExponentWords{T}) where {S <: RingElement, - T <: FreeAssAlgElem{S}} - return Vector{Int} +function Base.eltype( + x::FreeAssAlgExponentWords{T}, +) where {S<:RingElement,T<:FreeAssAlgElem{S}} + return Vector{Int} end - + ############################################################################### # # Canonicalisation # ############################################################################### -function canonical_unit(a::FreeAssAlgElem{T}) where T <: RingElement - return canonical_unit(leading_coefficient(a)) +function canonical_unit(a::FreeAssAlgElem{T}) where {T<:RingElement} + return canonical_unit(leading_coefficient(a)) end ############################################################################### @@ -249,38 +262,42 @@ end # ############################################################################### -function fit!(a::FreeAssAlgElem{T}, n::Int) where T <: RingElement - if length(a.coeffs) < n - resize!(a.coeffs, n) - end - if length(a.exps) < n - resize!(a.exps, n) - end - return nothing +function fit!(a::FreeAssAlgElem{T}, n::Int) where {T<:RingElement} + if length(a.coeffs) < n + resize!(a.coeffs, n) + end + if length(a.exps) < n + resize!(a.exps, n) + end + return nothing end for T in [RingElem, Integer, Rational, AbstractFloat] - @eval begin - function setcoeff!(a::FreeAssAlgElem{S}, i::Int, c::S) where {S <: $T} - fit!(a, i) - a.coeffs[i] = c - if i > length(a) - a.length = i - end - return a - end - end -end - -function set_exponent_word!(a::FreeAssAlgElem{T}, i::Int, w::Vector{Int}) where T <: RingElement - n = nvars(parent(a)) - all(x -> 0 < x <= n, w) || error("variable index out of range") - fit!(a, i) - a.exps[i] = w - if i > length(a) - a.length = i - end - return a + @eval begin + function setcoeff!(a::FreeAssAlgElem{S}, i::Int, c::S) where {S<:$T} + fit!(a, i) + a.coeffs[i] = c + if i > length(a) + a.length = i + end + return a + end + end +end + +function set_exponent_word!( + a::FreeAssAlgElem{T}, + i::Int, + w::Vector{Int}, +) where {T<:RingElement} + n = nvars(parent(a)) + all(x -> 0 < x <= n, w) || error("variable index out of range") + fit!(a, i) + a.exps[i] = w + if i > length(a) + a.length = i + end + return a end ############################################################################### @@ -289,94 +306,94 @@ end # ############################################################################### -function ==(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T - fl = check_parent(a, b, false) - !fl && return false - return a.length == b.length && - view(a.exps, 1:a.length) == view(b.exps, 1:b.length) && - view(a.coeffs, 1:a.length) == view(b.coeffs, 1:b.length) +function ==(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T} + fl = check_parent(a, b, false) + !fl && return false + return a.length == b.length && + view(a.exps, 1:a.length) == view(b.exps, 1:b.length) && + view(a.coeffs, 1:a.length) == view(b.coeffs, 1:b.length) end function word_cmp(a::Vector{Int}, b::Vector{Int}) - if length(a) > length(b) - return +1 - elseif length(a) < length(b) - return -1 - else - # deglex - for i in 1:length(a) - if a[i] > b[i] - return -1 - elseif a[i] < b[i] - return +1 - end - end - return 0 - end + if length(a) > length(b) + return +1 + elseif length(a) < length(b) + return -1 + else + # deglex + for i = 1:length(a) + if a[i] > b[i] + return -1 + elseif a[i] < b[i] + return +1 + end + end + return 0 + end end function word_gt(a::Vector{Int}, b::Vector{Int}) - return word_cmp(a, b) > 0 -end - -function sort_terms!(z::FreeAssAlgElem{T}) where T - n = length(z) - if n > 1 - p = sortperm(view(z.exps, 1:n), lt = word_gt) - z.coeffs = [z.coeffs[p[i]] for i in 1:n] - z.exps = [z.exps[p[i]] for i in 1:n] - end - return z -end - -function combine_like_terms!(z::FreeAssAlgElem{T}) where T - o = 0 - i = 1 - while i <= z.length - if o > 0 && word_cmp(z.exps[o], z.exps[i]) == 0 - z.coeffs[o] += z.coeffs[i] - else - o += (o < 1 || !iszero(z.coeffs[o])) - z.exps[o] = z.exps[i] - z.coeffs[o] = z.coeffs[i] - end - i += 1 - end - o += (o < 1 || !iszero(z.coeffs[o])) - z.length = o - 1 - return z -end - -function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T - if p == q - return false - end - l = 0 - if length(p.exps) < length(q.exps) - l = length(p.exps) - else - l = length(q.exps) - end - sort_terms!(p) - sort_terms!(q) - for i in 1:l - if word_gt(q.exps[i], p.exps[i]) - return true - elseif word_gt(p.exps[i], q.exps[i]) - return false - elseif p.coeffs[i] == q.coeffs[i] - return p.coeffs[i] < q.coeffs[i] - end + return word_cmp(a, b) > 0 +end + +function sort_terms!(z::FreeAssAlgElem{T}) where {T} + n = length(z) + if n > 1 + p = sortperm(view(z.exps, 1:n), lt = word_gt) + z.coeffs = [z.coeffs[p[i]] for i = 1:n] + z.exps = [z.exps[p[i]] for i = 1:n] + end + return z +end + +function combine_like_terms!(z::FreeAssAlgElem{T}) where {T} + o = 0 + i = 1 + while i <= z.length + if o > 0 && word_cmp(z.exps[o], z.exps[i]) == 0 + z.coeffs[o] += z.coeffs[i] + else + o += (o < 1 || !iszero(z.coeffs[o])) + z.exps[o] = z.exps[i] + z.coeffs[o] = z.coeffs[i] end - if length(p.exps) < length(q.exps) - return true - else - return false + i += 1 + end + o += (o < 1 || !iszero(z.coeffs[o])) + z.length = o - 1 + return z +end + +function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where {T} + if p == q + return false + end + l = 0 + if length(p.exps) < length(q.exps) + l = length(p.exps) + else + l = length(q.exps) + end + sort_terms!(p) + sort_terms!(q) + for i = 1:l + if word_gt(q.exps[i], p.exps[i]) + return true + elseif word_gt(p.exps[i], q.exps[i]) + return false + elseif p.coeffs[i] == q.coeffs[i] + return p.coeffs[i] < q.coeffs[i] end + end + if length(p.exps) < length(q.exps) + return true + else + return false + end end -function <(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T - return isless(p, q) +function <(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where {T} + return isless(p, q) end ############################################################################### @@ -385,120 +402,124 @@ end # ############################################################################### -function -(a::FreeAssAlgElem{T}) where T <: RingElement - n = length(a) - R = parent(a) - zcoeffs = T[-a.coeffs[i] for i in 1:n] - return FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n) -end - -function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T <: RingElement - zcoeffs = T[] - zexps = Vector{Int}[] - for i in 1:a.length, j in 1:b.length - push!(zcoeffs, a.coeffs[i]*b.coeffs[j]) - push!(zexps, vcat(a.exps[i], b.exps[j])) - end - z = FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) - return combine_like_terms!(sort_terms!(z)) -end - -function +(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T <: RingElement - zcoeffs = T[] - zexps = Vector{Int}[] - i = j = 1 - while i <= a.length && j <= b.length - c = word_cmp(a.exps[i], b.exps[j]) - if c < 0 - push!(zcoeffs, b.coeffs[j]) - push!(zexps, b.exps[j]) - j += 1 - elseif c > 0 - push!(zcoeffs, a.coeffs[i]) - push!(zexps, a.exps[i]) - i += 1 - else - s = a.coeffs[i] + b.coeffs[j] - if !iszero(s) - push!(zcoeffs, s) +function -(a::FreeAssAlgElem{T}) where {T<:RingElement} + n = length(a) + R = parent(a) + zcoeffs = T[-a.coeffs[i] for i = 1:n] + return FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n) +end + +function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} + zcoeffs = T[] + zexps = Vector{Int}[] + for i = 1:a.length, j = 1:b.length + push!(zcoeffs, a.coeffs[i] * b.coeffs[j]) + push!(zexps, vcat(a.exps[i], b.exps[j])) + end + z = FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) + return combine_like_terms!(sort_terms!(z)) +end + +function +(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} + zcoeffs = T[] + zexps = Vector{Int}[] + i = j = 1 + while i <= a.length && j <= b.length + c = word_cmp(a.exps[i], b.exps[j]) + if c < 0 + push!(zcoeffs, b.coeffs[j]) + push!(zexps, b.exps[j]) + j += 1 + elseif c > 0 + push!(zcoeffs, a.coeffs[i]) push!(zexps, a.exps[i]) - end - i += 1 - j += 1 - end - end - while i <= a.length - push!(zcoeffs, a.coeffs[i]) - push!(zexps, a.exps[i]) - i += 1 - end - while j <= b.length - push!(zcoeffs, b.coeffs[j]) - push!(zexps, b.exps[j]) - j += 1 - end - return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) + i += 1 + else + s = a.coeffs[i] + b.coeffs[j] + if !iszero(s) + push!(zcoeffs, s) + push!(zexps, a.exps[i]) + end + i += 1 + j += 1 + end + end + while i <= a.length + push!(zcoeffs, a.coeffs[i]) + push!(zexps, a.exps[i]) + i += 1 + end + while j <= b.length + push!(zcoeffs, b.coeffs[j]) + push!(zexps, b.exps[j]) + j += 1 + end + return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) end # a - b ignoring the first "start" terms of both -function _sub_rest(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}, start::Int) where T <: RingElement - zcoeffs = T[] - zexps = Vector{Int}[] - i = j = start + 1 - while i <= a.length && j <= b.length - c = word_cmp(a.exps[i], b.exps[j]) - if c < 0 - push!(zcoeffs, -b.coeffs[j]) - push!(zexps, b.exps[j]) - j += 1 - elseif c > 0 - push!(zcoeffs, a.coeffs[i]) - push!(zexps, a.exps[i]) - i += 1 - else - s = a.coeffs[i] - b.coeffs[j] - if !iszero(s) - push!(zcoeffs, s) +function _sub_rest( + a::FreeAssAlgElem{T}, + b::FreeAssAlgElem{T}, + start::Int, +) where {T<:RingElement} + zcoeffs = T[] + zexps = Vector{Int}[] + i = j = start + 1 + while i <= a.length && j <= b.length + c = word_cmp(a.exps[i], b.exps[j]) + if c < 0 + push!(zcoeffs, -b.coeffs[j]) + push!(zexps, b.exps[j]) + j += 1 + elseif c > 0 + push!(zcoeffs, a.coeffs[i]) push!(zexps, a.exps[i]) - end - i += 1 - j += 1 - end - end - while i <= a.length - push!(zcoeffs, a.coeffs[i]) - push!(zexps, a.exps[i]) - i += 1 - end - while j <= b.length - push!(zcoeffs, -b.coeffs[j]) - push!(zexps, b.exps[j]) - j += 1 - end - return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) -end - -function -(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T <: RingElement - return _sub_rest(a, b, 0) -end - -function ^(a::FreeAssAlgElem{T}, b::Integer) where T <: RingElement - if b == 0 - return one(parent(a)) - elseif b == 1 - return deepcopy(a) - elseif a.length == 1 - if isempty(a.exps[1]) - e = [Int[]] - else - b < 0 && throw(NotInvertibleError(a)) - e = Vector{Int}[reduce(vcat, [a.exps[1] for i in 1:b])] - end - return FreeAssAlgElem{T}(parent(a), [a.coeffs[1]^b], e, 1) - else - b < 0 && throw(NotInvertibleError(a)) - return AbstractAlgebra.internal_power(a, b) - end + i += 1 + else + s = a.coeffs[i] - b.coeffs[j] + if !iszero(s) + push!(zcoeffs, s) + push!(zexps, a.exps[i]) + end + i += 1 + j += 1 + end + end + while i <= a.length + push!(zcoeffs, a.coeffs[i]) + push!(zexps, a.exps[i]) + i += 1 + end + while j <= b.length + push!(zcoeffs, -b.coeffs[j]) + push!(zexps, b.exps[j]) + j += 1 + end + return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) +end + +function -(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} + return _sub_rest(a, b, 0) +end + +function ^(a::FreeAssAlgElem{T}, b::Integer) where {T<:RingElement} + if b == 0 + return one(parent(a)) + elseif b == 1 + return deepcopy(a) + elseif a.length == 1 + if isempty(a.exps[1]) + e = [Int[]] + else + b < 0 && throw(NotInvertibleError(a)) + e = Vector{Int}[reduce(vcat, [a.exps[1] for i = 1:b])] + end + return FreeAssAlgElem{T}(parent(a), [a.coeffs[1]^b], e, 1) + else + b < 0 && throw(NotInvertibleError(a)) + return AbstractAlgebra.internal_power(a, b) + end end ############################################################################### @@ -508,82 +529,88 @@ end ############################################################################### # return c*w*a*wp -function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) where T - zcoeffs = isone(c) ? T[a.coeffs[i] for i in 1:a.length] : - T[c*a.coeffs[i] for i in 1:a.length] - zexps = Vector{Int}[vcat(w, a.exps[i], wp) for i in 1:a.length] - return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, a.length) +function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) where {T} + zcoeffs = + isone(c) ? T[a.coeffs[i] for i = 1:a.length] : T[c * a.coeffs[i] for i = 1:a.length] + zexps = Vector{Int}[vcat(w, a.exps[i], wp) for i = 1:a.length] + return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, a.length) end # return (true, l, r) with a = l*b*r and length(l) minimal # or (false, junk, junk) if a is not two-sided divisible by b function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}) - n = length(b) - for i in 0:length(a)-n - match = true - for j in 1:n - if b[j] != a[i+j] - match = false - break - end - end - if match - return (true, Int[a[k] for k in 1:i], - Int[a[k] for k in 1+i+n:length(a)]) - end - end - return (false, Int[], Int[]) + n = length(b) + for i = 0:length(a)-n + match = true + for j = 1:n + if b[j] != a[i+j] + match = false + break + end + end + if match + return (true, Int[a[k] for k = 1:i], Int[a[k] for k = 1+i+n:length(a)]) + end + end + return (false, Int[], Int[]) end # return (true, l, r) with a = l*b*r and length(r) minimal # or (false, junk, junk) if a is not two-sided divisible by b function word_divides_rightmost(a::Vector{Int}, b::Vector{Int}) - n = length(b) - for i in length(a)-n:-1:0 - match = true - for j in 1:n - if b[j] != a[i+j] - match = false - break - end - end - if match - return (true, Int[a[k] for k in 1:i], - Int[a[k] for k in 1+i+n:length(a)]) - end - end - return (false, Int[], Int[]) -end - - -function AbstractAlgebra.divexact_left(f::FreeAssAlgElem{T}, g::FreeAssAlgElem{T}; check::Bool = true) where T - R = parent(f) - qcoeffs = T[] - qexps = Vector{Int}[] - while length(f) > 0 - ok, ml, mr = word_divides_leftmost(f.exps[1], g.exps[1]) - ok && isempty(ml) || throw(ArgumentError("Not an exact division")) - qi = divexact(f.coeffs[1], g.coeffs[1]) - push!(qcoeffs, qi) - push!(qexps, mr) - f = _sub_rest(f, mul_term(qi, ml, g, mr), 1) # enforce lt cancellation - end - return FreeAssAlgElem{T}(R, qcoeffs, qexps, length(qcoeffs)) -end - -function AbstractAlgebra.divexact_right(f::FreeAssAlgElem{T}, g::FreeAssAlgElem{T}; check::Bool = true) where T - R = parent(f) - qcoeffs = T[] - qexps = Vector{Int}[] - while length(f) > 0 - ok, ml, mr = word_divides_rightmost(f.exps[1], g.exps[1]) - ok && isempty(mr) || throw(ArgumentError("Not an exact division")) - qi = divexact(f.coeffs[1], g.coeffs[1]) - push!(qcoeffs, qi) - push!(qexps, ml) - f = _sub_rest(f, mul_term(qi, ml, g, mr), 1) # enforce lt cancellation - end - return FreeAssAlgElem{T}(R, qcoeffs, qexps, length(qcoeffs)) + n = length(b) + for i = length(a)-n:-1:0 + match = true + for j = 1:n + if b[j] != a[i+j] + match = false + break + end + end + if match + return (true, Int[a[k] for k = 1:i], Int[a[k] for k = 1+i+n:length(a)]) + end + end + return (false, Int[], Int[]) +end + + +function AbstractAlgebra.divexact_left( + f::FreeAssAlgElem{T}, + g::FreeAssAlgElem{T}; + check::Bool = true, +) where {T} + R = parent(f) + qcoeffs = T[] + qexps = Vector{Int}[] + while length(f) > 0 + ok, ml, mr = word_divides_leftmost(f.exps[1], g.exps[1]) + ok && isempty(ml) || throw(ArgumentError("Not an exact division")) + qi = divexact(f.coeffs[1], g.coeffs[1]) + push!(qcoeffs, qi) + push!(qexps, mr) + f = _sub_rest(f, mul_term(qi, ml, g, mr), 1) # enforce lt cancellation + end + return FreeAssAlgElem{T}(R, qcoeffs, qexps, length(qcoeffs)) +end + +function AbstractAlgebra.divexact_right( + f::FreeAssAlgElem{T}, + g::FreeAssAlgElem{T}; + check::Bool = true, +) where {T} + R = parent(f) + qcoeffs = T[] + qexps = Vector{Int}[] + while length(f) > 0 + ok, ml, mr = word_divides_rightmost(f.exps[1], g.exps[1]) + ok && isempty(mr) || throw(ArgumentError("Not an exact division")) + qi = divexact(f.coeffs[1], g.coeffs[1]) + push!(qcoeffs, qi) + push!(qexps, ml) + f = _sub_rest(f, mul_term(qi, ml, g, mr), 1) # enforce lt cancellation + end + return FreeAssAlgElem{T}(R, qcoeffs, qexps, length(qcoeffs)) end @@ -593,13 +620,17 @@ end # ############################################################################### -function divexact(a::FreeAssAlgElem{T}, b::Integer; check::Bool = true) where T <: RingElement +function divexact( + a::FreeAssAlgElem{T}, + b::Integer; + check::Bool = true, +) where {T<:RingElement} - n = length(a) - R = parent(a) - b = base_ring(R)(b) - zcoeffs = T[divexact(a.coeffs[i], b, check = check) for i in 1:n] - return combine_like_terms!(FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n)) + n = length(a) + R = parent(a) + b = base_ring(R)(b) + zcoeffs = T[divexact(a.coeffs[i], b, check = check) for i = 1:n] + return combine_like_terms!(FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n)) end @@ -610,20 +641,34 @@ end ################################################################################ function _change_freeassalg_ring(R, Rx, cached) - P, _ = AbstractAlgebra.free_associative_algebra(R, symbols(Rx); cached=cached) + P, _ = AbstractAlgebra.free_associative_algebra(R, symbols(Rx); cached = cached) return P end -function change_base_ring(R::Ring, a::FreeAssAlgElem{T}; cached=true, parent::AbstractAlgebra.FreeAssAlgebra=_change_freeassalg_ring(R, parent(a), cached)) where T <: RingElement +function change_base_ring( + R::Ring, + a::FreeAssAlgElem{T}; + cached = true, + parent::AbstractAlgebra.FreeAssAlgebra = _change_freeassalg_ring(R, parent(a), cached), +) where {T<:RingElement} base_ring(parent) != R && error("Base rings do not match.") return _map(R, a, parent) end -function map_coefficients(f, a::FreeAssAlgElem{T}; cached=true, parent::AbstractAlgebra.FreeAssAlgebra=_change_freeassalg_ring(parent(f(zero(base_ring(a)))), parent(a), cached)) where T <: RingElement - return _map(f, a, parent) +function map_coefficients( + f, + a::FreeAssAlgElem{T}; + cached = true, + parent::AbstractAlgebra.FreeAssAlgebra = _change_freeassalg_ring( + parent(f(zero(base_ring(a)))), + parent(a), + cached, + ), +) where {T<:RingElement} + return _map(f, a, parent) end -function _map(g, a::FreeAssAlgElem{T}, Rx) where T <: RingElement +function _map(g, a::FreeAssAlgElem{T}, Rx) where {T<:RingElement} cvzip = zip(coefficients(a), exponent_words(a)) M = MPolyBuildCtx(Rx) for (c, v) in cvzip @@ -640,8 +685,11 @@ end # ############################################################################### -function free_associative_algebra(R::AbstractAlgebra.Ring, s::Vector{Symbol}; cached::Bool = true) - parent_obj = FreeAssAlgebra{elem_type(R)}(R, s, cached) - return (parent_obj, gens(parent_obj)) +function free_associative_algebra( + R::AbstractAlgebra.Ring, + s::Vector{Symbol}; + cached::Bool = true, +) + parent_obj = FreeAssAlgebra{elem_type(R)}(R, s, cached) + return (parent_obj, gens(parent_obj)) end - diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 3b9595ed91..11e708b27d 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -12,70 +12,82 @@ import DataStructures.PriorityQueue const groebner_debug_level = 0 const Monomial = Vector{Int} -abstract type Obstruction{T} end +abstract type Obstruction{T} end """ represents the overlap of the leading term of two polynomials """ -struct ObstructionTriple{T} <: Obstruction{T} - first_poly::FreeAssAlgElem{T} - second_poly::FreeAssAlgElem{T} - pre_and_suffixes::NTuple{4, Monomial} - first_index::Int - second_index::Int +struct ObstructionTriple{T} <: Obstruction{T} + first_poly::FreeAssAlgElem{T} + second_poly::FreeAssAlgElem{T} + pre_and_suffixes::NTuple{4,Monomial} + first_index::Int + second_index::Int end -function FreeAssAlgElem{T}(R::FreeAssAlgebra{T}, mon::Monomial) where T - return FreeAssAlgElem{T}(R, [one(base_ring(R))], [mon], 1) +function FreeAssAlgElem{T}(R::FreeAssAlgebra{T}, mon::Monomial) where {T} + return FreeAssAlgElem{T}(R, [one(base_ring(R))], [mon], 1) end """ -takes an obstruction triple (p, q, o) and returns the common multiple +takes an obstruction triple (p, q, o) and returns the common multiple of the leading terms of p and q defined by o TODO documentation """ -function common_multiple_leading_term(ot::ObstructionTriple{T}) where T - return FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * FreeAssAlgElem{T}(parent(ot.first_poly), _leading_word(ot.first_poly)) * FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) +function common_multiple_leading_term(ot::ObstructionTriple{T}) where {T} + return FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * + FreeAssAlgElem{T}(parent(ot.first_poly), _leading_word(ot.first_poly)) * + FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) end -function s_polynomial(ot::ObstructionTriple{T}) where T - first_term = FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * ot.first_poly * FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) - second_term = FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[3]) * ot.second_poly * FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[4]) - return inv(leading_coefficient(ot.first_poly)) * first_term - inv(leading_coefficient(ot.second_poly)) * second_term +function s_polynomial(ot::ObstructionTriple{T}) where {T} + first_term = + FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * + ot.first_poly * + FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) + second_term = + FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[3]) * + ot.second_poly * + FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[4]) + return inv(leading_coefficient(ot.first_poly)) * first_term - + inv(leading_coefficient(ot.second_poly)) * second_term end # skip all of the extra length-checking -function _leading_word(a::FreeAssAlgElem{T}) where T - return a.exps[1] +function _leading_word(a::FreeAssAlgElem{T}) where {T} + return a.exps[1] end Markdown.@doc doc""" gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) -If an element of the Groebner basis that is stored in `aut` divides `a`, -return (true, a1, a2, keyword_index), where `keyword_index` is the index of the +If an element of the Groebner basis that is stored in `aut` divides `a`, +return (true, a1, a2, keyword_index), where `keyword_index` is the index of the keyword that divides `a` such that `a = a1 aut[keyword_index] a2`. -""" -function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) +""" function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) match = search(aut, a) if isnothing(match) return (false, [], [], -1) end - return (true, a[1:match.last_position - length(match.keyword)], a[match.last_position + 1:end], match.keyword_index) + return ( + true, + a[1:(match.last_position - length(match.keyword))], + a[(match.last_position + 1):end], + match.keyword_index, + ) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel Markdown.@doc doc""" -normal_form(f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton) + normal_form(f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton) -Assuming `g` is a groebner basis and `aut` an Aho-Corasick automaton for the elements of `g`, +Assuming `g` is a groebner basis and `aut` an Aho-Corasick automaton for the elements of `g`, compute the normal form of `f` with respect to `g` -""" -function normal_form( - f::FreeAssAlgElem{T}, - g::Vector{FreeAssAlgElem{T}}, - aut::AhoCorasickAutomaton - ) where T +""" function normal_form( + f::FreeAssAlgElem{T}, + g::Vector{FreeAssAlgElem{T}}, + aut::AhoCorasickAutomaton, +) where {T} R = parent(f) rexps = Vector{Int}[] rcoeffs = T[] @@ -87,7 +99,7 @@ function normal_form( else push!(rcoeffs, f.coeffs[1]) push!(rexps, f.exps[1]) - f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f)-1) + f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f) - 1) end end return FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) @@ -95,67 +107,66 @@ end # normal form with leftmost word divisions function normal_form( - f::FreeAssAlgElem{T}, - g::Vector{FreeAssAlgElem{T}} -) where T <: FieldElement - R = parent(f) - s = length(g) - rcoeffs = T[] - rexps = Vector{Int}[] - while length(f) > 0 - i = 1 - @label again + f::FreeAssAlgElem{T}, + g::Vector{FreeAssAlgElem{T}}, +) where {T<:FieldElement} + R = parent(f) + s = length(g) + rcoeffs = T[] + rexps = Vector{Int}[] + while length(f) > 0 + i = 1 + @label again ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) if !ok && i < s - i += 1 - @goto again - end - if ok - qi = divexact(f.coeffs[1], g[i].coeffs[1]) - f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation - else - push!(rcoeffs, f.coeffs[1]) - push!(rexps, f.exps[1]) - f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f)-1) - end - end - r = FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) - return r + i += 1 + @goto again + end + if ok + qi = divexact(f.coeffs[1], g[i].coeffs[1]) + f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation + else + push!(rcoeffs, f.coeffs[1]) + push!(rexps, f.exps[1]) + f = FreeAssAlgElem{T}(R, f.coeffs[2:end], f.exps[2:end], length(f) - 1) + end + end + r = FreeAssAlgElem{T}(R, rcoeffs, rexps, length(rcoeffs)) + return r end # weak normal form with leftmost word divisions function normal_form_weak( - f::FreeAssAlgElem{T}, - g::Vector{FreeAssAlgElem{T}} -) where T <: FieldElement - R = parent(f) - s = length(g) - while length(f) > 0 - i = 1 - @label again - ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) - if !ok && i < s - i += 1 - @goto again - end - if ok - qi = divexact(f.coeffs[1], g[i].coeffs[1]) - f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation - else - break - end - end - return f + f::FreeAssAlgElem{T}, + g::Vector{FreeAssAlgElem{T}}, +) where {T<:FieldElement} + R = parent(f) + s = length(g) + while length(f) > 0 + i = 1 + @label again + ok, ml, mr = word_divides_leftmost(f.exps[1], g[i].exps[1]) + if !ok && i < s + i += 1 + @goto again + end + if ok + qi = divexact(f.coeffs[1], g[i].coeffs[1]) + f = _sub_rest(f, mul_term(qi, ml, g[i], mr), 1) # enforce lt cancelation + else + break + end + end + return f end Markdown.@doc doc""" -interreduce!(g::Vector{FreeAssAlgElem{T}}) + interreduce!(g::Vector{FreeAssAlgElem{T}}) -Interreduce a given Groebner basis with itself, i.e. compute the normal form of each -element of `g` with respect to the rest of the elements and discard elements with +Interreduce a given Groebner basis with itself, i.e. compute the normal form of each +element of `g` with respect to the rest of the elements and discard elements with normal form $0$ and duplicates. -""" -function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T +""" function interreduce!(g::Vector{FreeAssAlgElem{T}}) where {T} i = 1 while length(g) > 1 && length(g) >= i aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) @@ -175,15 +186,15 @@ end ## checks whether there is an overlap between a and b at position i of b # such that b[i:length(b)] = a[1:length(b)-i] function check_left_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) - for j in 0:length(b)-i - if j >= length(a) - return false # this is a center overlap - end - if b[i+j] != a[j+1] - return false - end - end - return true + for j = 0:(length(b) - i) + if j >= length(a) + return false # this is a center overlap + end + if b[i + j] != a[j + 1] + return false + end + end + return true end ### @@ -193,15 +204,15 @@ end # the return vector is of the form [(w_1, w_2^'), ...] # if w_1 or w_2^' is empty, the corresponding obstruction is not returned function left_obstructions(a::Vector{Int}, b::Vector{Int}) - v = Tuple{Vector{Int}, Vector{Int}}[]; - for i in 2:length(b) - if check_left_overlap(a, b, i) - if length(b)-i + 2 <= length(a) # w_2^' should not be empty! - push!(v, (b[1:i-1], a[length(b)-i + 2:length(a)])) - end - end - end - return v + v = Tuple{Vector{Int},Vector{Int}}[] + for i = 2:length(b) + if check_left_overlap(a, b, i) + if length(b) - i + 2 <= length(a) # w_2^' should not be empty! + push!(v, (b[1:(i - 1)], a[(length(b) - i + 2):length(a)])) + end + end + end + return v end @@ -212,31 +223,31 @@ end # the return vector is of the form [(w_2, w_1^'), ...] # if w_1^' or w_2 is empty, the corresponding obstruction is not returned function right_obstructions(a::Vector{Int}, b::Vector{Int}) - return left_obstructions(b, a) + return left_obstructions(b, a) end ### # check, whether a is a true subword of b at index i function check_center_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) - for j in 1:length(a) - if i+j -1 > length(b) - return false - end - if a[j] != b[i + j - 1] - return false - end - end - return true + for j = 1:length(a) + if i + j - 1 > length(b) + return false + end + if a[j] != b[i + j - 1] + return false + end + end + return true end function center_obstructions_first_in_second(a::Vector{Int}, b::Vector{Int}) - v = Tuple{Vector{Int}, Vector{Int}}[] - for i in 1:length(b) - if check_center_overlap(a, b, i) - push!(v, (b[1:i-1], b[i + length(a): length(b)])) - end - end - return v + v = Tuple{Vector{Int},Vector{Int}}[] + for i = 1:length(b) + if check_center_overlap(a, b, i) + push!(v, (b[1:(i - 1)], b[(i + length(a)):length(b)])) + end + end + return v end ## @@ -247,74 +258,74 @@ end # w_i b w_i^' = a # either or both of w_i and w_i^' can be empty function center_obstructions(a::Vector{Int}, b::Vector{Int}) - if length(a) > length(b) - return center_obstructions_first_in_second(b, a) - else - return center_obstructions_first_in_second(a, b) - end + if length(a) > length(b) + return center_obstructions_first_in_second(b, a) + else + return center_obstructions_first_in_second(a, b) + end end # all non-trivial ones function obstructions(a::Vector{Int}, b::Vector{Int}) - one = Int[] # the empty word - res = Tuple{Vector{Int}, Vector{Int}, Vector{Int}, Vector{Int}}[] - for x in center_obstructions_first_in_second(b, a) - push!(res, (one, one, x[1], x[2])) - end - for x in center_obstructions_first_in_second(a, b) - push!(res, (x[1], x[2], one, one)) - end - for x in left_obstructions(a, b) - push!(res, (x[1], one, one, x[2])) - end - for x in left_obstructions(b, a) - push!(res, (one, x[2], x[1], one)) - end - for x in res - @assert vcat(x[1], a, x[2]) == vcat(x[3], b, x[4]) - end - return res + one = Int[] # the empty word + res = Tuple{Vector{Int},Vector{Int},Vector{Int},Vector{Int}}[] + for x in center_obstructions_first_in_second(b, a) + push!(res, (one, one, x[1], x[2])) + end + for x in center_obstructions_first_in_second(a, b) + push!(res, (x[1], x[2], one, one)) + end + for x in left_obstructions(a, b) + push!(res, (x[1], one, one, x[2])) + end + for x in left_obstructions(b, a) + push!(res, (one, x[2], x[1], one)) + end + for x in res + @assert vcat(x[1], a, x[2]) == vcat(x[3], b, x[4]) + end + return res end # all non-trivial self obstructions function obstructions(a::Vector{Int}) - one = Int[] # the empty word - res = Tuple{Vector{Int}, Vector{Int}, Vector{Int}, Vector{Int}}[] - for x in left_obstructions(a, a) - push!(res, (one, x[2], x[1], one)) - end - for x in res - @assert vcat(x[1], a, x[2]) == vcat(x[3], a, x[4]) - end - return res + one = Int[] # the empty word + res = Tuple{Vector{Int},Vector{Int},Vector{Int},Vector{Int}}[] + for x in left_obstructions(a, a) + push!(res, (one, x[2], x[1], one)) + end + for x in res + @assert vcat(x[1], a, x[2]) == vcat(x[3], a, x[4]) + end + return res end # check whether w_2 = v w_1 for some word v function is_subword_right(w_1::Vector{Int}, w_2::Vector{Int}) - if length(w_1) > length(w_2) - return false - end - for i in 0:length(w_1) - 1 - if w_1[length(w_1) - i] != w_2[length(w_2) - i] - return false - end - end - return true + if length(w_1) > length(w_2) + return false + end + for i = 0:(length(w_1) - 1) + if w_1[length(w_1) - i] != w_2[length(w_2) - i] + return false + end + end + return true end # check whether w_2 = w_1 v for some word v function is_subword_left(w_1::Vector{Int}, w_2::Vector{Int}) - if length(w_1) > length(w_2) - return false - end - for i in 1:length(w_1) - if w_1[i] != w_2[i] - return false - end - end - return true + if length(w_1) > length(w_2) + return false + end + for i = 1:length(w_1) + if w_1[i] != w_2[i] + return false + end + end + return true end @@ -323,17 +334,17 @@ end # it holds that u_j == w v_l and u_j^' = v_l^' w^' for some w, w^' # i.e. if obs2 is a subobstruction of obs1 # both w and w^' might be empty -function is_subobstruction(obs1::NTuple{4, Vector{Int}}, obs2::NTuple{4, Vector{Int}}) - if is_subword_right(obs2[3], obs1[3]) && is_subword_left(obs2[4], obs1[4]) - return true - else - return false - end +function is_subobstruction(obs1::NTuple{4,Vector{Int}}, obs2::NTuple{4,Vector{Int}}) + if is_subword_right(obs2[3], obs1[3]) && is_subword_left(obs2[4], obs1[4]) + return true + else + return false + end end -function is_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where T +function is_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where {T} return is_subobstruction(obs1.pre_and_suffixes, obs2.pre_and_suffixes) - + end """ @@ -342,8 +353,12 @@ returns length(ww') thus, if it returns 0 and obs2 is a subobstruction of obs1, they are equal (? is that true?) if obs2 is not a subobstruction of obs1 the return value is useless """ -function get_diff_length_for_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where T - return length(obs1.pre_and_suffixes[3]) - length(obs2.pre_and_suffixes[3]) + length(obs1.pre_and_suffixes[4]) - length(obs2.pre_and_suffixes[4]) +function get_diff_length_for_subobstruction( + obs1::ObstructionTriple{T}, + obs2::ObstructionTriple{T}, +) where {T} + return length(obs1.pre_and_suffixes[3]) - length(obs2.pre_and_suffixes[3]) + + length(obs1.pre_and_suffixes[4]) - length(obs2.pre_and_suffixes[4]) end # check whether there exists a (possibly empty) w^'' such that @@ -360,55 +375,60 @@ end function has_overlap(g2, w2, u2) - lw2 = _leading_word(g2) - return length(w2) < length(lw2) + length(u2) + lw2 = _leading_word(g2) + return length(w2) < length(lw2) + length(u2) end -function has_overlap(obs::ObstructionTriple{T}) where T +function has_overlap(obs::ObstructionTriple{T}) where {T} return has_overlap(obs.second_poly, obs.pre_and_suffixes[2], obs.pre_and_suffixes[4]) end function is_redundant( - obs::ObstructionTriple{T}, - new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}} -) where T - # cases 4b + 4c - for obstruction_pair in new_obstructions - o = obstruction_pair[1] - if o.second_index == obs.second_index - if is_subobstruction(obs, o) - # case 4b - if get_diff_length_for_subobstruction(obs, o) > 0 - return true - # case 4c - elseif obs.first_index > o.first_index - return true - elseif obs.first_index == o.first_index && get_diff_length_for_subobstruction(obs, o) == 0 && - word_gt(obs.pre_and_suffixes[1], o.pre_and_suffixes[1]) - return true - end - end - end - end - return false + obs::ObstructionTriple{T}, + new_obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, +) where {T} + # cases 4b + 4c + for obstruction_pair in new_obstructions + o = obstruction_pair[1] + if o.second_index == obs.second_index + if is_subobstruction(obs, o) + # case 4b + if get_diff_length_for_subobstruction(obs, o) > 0 + return true + # case 4c + elseif obs.first_index > o.first_index + return true + elseif obs.first_index == o.first_index && + get_diff_length_for_subobstruction(obs, o) == 0 && + word_gt(obs.pre_and_suffixes[1], o.pre_and_suffixes[1]) + return true + end + end + end + end + return false end """ check, whether obs1 is a proper multiple of obs2, i.e. they belong to the same polynomials and are of the form obs1 = [w w_i, w_i' w'; w w_j, w_j' w'] and obs2 = [w_i, w_i'; w_j, w_j'] """ -function is_proper_multiple(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where T +function is_proper_multiple( + obs1::ObstructionTriple{T}, + obs2::ObstructionTriple{T}, +) where {T} if obs1.first_poly != obs2.first_poly || obs1.second_poly != obs2.second_poly #TODO compare indices instead? return false end - if is_subword_right(obs2.pre_and_suffixes[1], obs1.pre_and_suffixes[1]) && is_subword_left(obs2.pre_and_suffixes[2], obs1.pre_and_suffixes[2]) + if is_subword_right(obs2.pre_and_suffixes[1], obs1.pre_and_suffixes[1]) && + is_subword_left(obs2.pre_and_suffixes[2], obs1.pre_and_suffixes[2]) w = copy(obs1.pre_and_suffixes[1]) w2 = copy(obs1.pre_and_suffixes[2]) - for _ in 1:length(obs2.pre_and_suffixes[1]) + for _ = 1:length(obs2.pre_and_suffixes[1]) pop!(w) end - for _ in 1:length(obs2.pre_and_suffixes[2]) + for _ = 1:length(obs2.pre_and_suffixes[2]) popfirst!(w2) end if length(w) + length(w2) == 0 @@ -416,14 +436,18 @@ function is_proper_multiple(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{ end @assert obs1.pre_and_suffixes[1] == vcat(w, obs2.pre_and_suffixes[1]) @assert obs1.pre_and_suffixes[2] == vcat(obs2.pre_and_suffixes[2], w2) - return obs1.pre_and_suffixes[3] == vcat(w, obs2.pre_and_suffixes[3]) && obs1.pre_and_suffixes[4] == vcat(obs2.pre_and_suffixes[4], w2) + return obs1.pre_and_suffixes[3] == vcat(w, obs2.pre_and_suffixes[3]) && + obs1.pre_and_suffixes[4] == vcat(obs2.pre_and_suffixes[4], w2) end end """ check, whether obs is a proper multiple of any of the obstructions in the priority queue """ -function is_proper_multiple(obs::ObstructionTriple{T}, obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}) where T +function is_proper_multiple( + obs::ObstructionTriple{T}, + obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, +) where {T} for obspair in obstructions obs2 = obspair[1] if is_proper_multiple(obs, obs2) @@ -434,26 +458,39 @@ function is_proper_multiple(obs::ObstructionTriple{T}, obstructions::PriorityQue end function is_redundant( - obs::ObstructionTriple{T}, - new_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, - newest_element::FreeAssAlgElem{T}, - newest_index::Int - ) where T + obs::ObstructionTriple{T}, + new_obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, + newest_element::FreeAssAlgElem{T}, + newest_index::Int, +) where {T} w1 = [] w2 = [] - for i in 1:length(obs.second_poly.exps[1]) - word_to_check = vcat(obs.pre_and_suffixes[3], obs.second_poly.exps[1], obs.pre_and_suffixes[4]) + for i = 1:length(obs.second_poly.exps[1]) + word_to_check = + vcat(obs.pre_and_suffixes[3], obs.second_poly.exps[1], obs.pre_and_suffixes[4]) if check_center_overlap(newest_element.exps[1], word_to_check, i) - w1 = word_to_check[1:i-1] - w2 = word_to_check[i+length(newest_element.exps[1]):end] + w1 = word_to_check[1:(i - 1)] + w2 = word_to_check[(i + length(newest_element.exps[1])):end] break end end if length(w1) + length(w2) == 0 return false end - obs1 = ObstructionTriple{T}(obs.first_poly, newest_element, (obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], w1, w2), obs.first_index, newest_index) - obs2 = ObstructionTriple{T}(obs.second_poly, newest_element, (obs.pre_and_suffixes[3], obs.pre_and_suffixes[4], w1, w2), obs.second_index, newest_index) + obs1 = ObstructionTriple{T}( + obs.first_poly, + newest_element, + (obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], w1, w2), + obs.first_index, + newest_index, + ) + obs2 = ObstructionTriple{T}( + obs.second_poly, + newest_element, + (obs.pre_and_suffixes[3], obs.pre_and_suffixes[4], w1, w2), + obs.second_index, + newest_index, + ) o1_bool = !has_overlap(obs1) || is_proper_multiple(obs1, new_obstructions) # TODO maybe only call is_proper_multiple if both obs have no overlap for performance? o2_bool = !has_overlap(obs2) || is_proper_multiple(obs2, new_obstructions) return o1_bool && o2_bool @@ -461,44 +498,44 @@ end function remove_redundancies!( - all_obstructions::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, - newest_index::Int, - newest_element::FreeAssAlgElem{T} -) where T - del_counter = 0 - new_obstructions = PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}() - old_obstructions = PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}() - for obstr_pair in all_obstructions - if obstr_pair[1].second_index == newest_index - new_obstructions[obstr_pair[1]] = obstr_pair[2] - else - old_obstructions[obstr_pair[1]] = obstr_pair[2] - end - end - for obstr_pair in new_obstructions - if is_redundant(obstr_pair[1], new_obstructions) - del_counter += 1 - delete!(new_obstructions, obstr_pair[1]) - delete!(all_obstructions, obstr_pair[1]) - end - end - if length(new_obstructions) == 0 - return nothing - end - - for obstr_pair in old_obstructions - if is_redundant(obstr_pair[1], new_obstructions, newest_element, newest_index) - del_counter += 1 - delete!(all_obstructions, obstr_pair[1]) - end - end - # TODO case 4e from Thm 4.1 in Kreuzer Xiu -end - -function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T + all_obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, + newest_index::Int, + newest_element::FreeAssAlgElem{T}, +) where {T} + del_counter = 0 + new_obstructions = PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}() + old_obstructions = PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}() + for obstr_pair in all_obstructions + if obstr_pair[1].second_index == newest_index + new_obstructions[obstr_pair[1]] = obstr_pair[2] + else + old_obstructions[obstr_pair[1]] = obstr_pair[2] + end + end + for obstr_pair in new_obstructions + if is_redundant(obstr_pair[1], new_obstructions) + del_counter += 1 + delete!(new_obstructions, obstr_pair[1]) + delete!(all_obstructions, obstr_pair[1]) + end + end + if length(new_obstructions) == 0 + return nothing + end + + for obstr_pair in old_obstructions + if is_redundant(obstr_pair[1], new_obstructions, newest_element, newest_index) + del_counter += 1 + delete!(all_obstructions, obstr_pair[1]) + end + end + # TODO case 4e from Thm 4.1 in Kreuzer Xiu +end + +function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where {T} s = length(g) - result = PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}() - for i in 1:s, j in 1:i + result = PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}() + for i = 1:s, j = 1:i if i == j obs = obstructions(_leading_word(g[i])) else @@ -515,11 +552,11 @@ end function add_obstructions!( - obstruction_queue::PriorityQueue{Obstruction{T}, FreeAssAlgElem{T}}, - g::Vector{FreeAssAlgElem{T}} -) where T + obstruction_queue::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, + g::Vector{FreeAssAlgElem{T}}, +) where {T} s = length(g) - for i in 1:s + for i = 1:s if i == s obs = obstructions(_leading_word(g[i])) else @@ -535,52 +572,50 @@ end function groebner_basis_buchberger( - g::Vector{FreeAssAlgElem{T}}, - reduction_bound = typemax(Int)::Int -) where T <: FieldElement - - g = copy(g) -# interreduce!(g) # on some small examples, this increases running time, so it might not be optimal to use this here - checked_obstructions = 0 - nonzero_reductions = 0 - # compute the aho corasick automaton - # to make normal form computation more efficient - aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - # step 1 - obstruction_queue = get_obstructions(g) - while !isempty(obstruction_queue) - obstruction = dequeue!(obstruction_queue) - # step3 - S = s_polynomial(obstruction) - Sp = normal_form(S, g, aut) # or normal_form_weak - if iszero(Sp) - continue - end - nonzero_reductions += 1 - # step4 - push!(g, Sp) - insert_keyword!(aut, Sp.exps[1], length(g)) - if groebner_debug_level > 0 - println("adding new obstructions! checked $checked_obstructions so far") - end - if nonzero_reductions >= reduction_bound - return g - end - add_obstructions!(obstruction_queue, g) - end - return g + g::Vector{FreeAssAlgElem{T}}, + reduction_bound = typemax(Int)::Int, +) where {T<:FieldElement} + + g = copy(g) + # interreduce!(g) # on some small examples, this increases running time, so it might not be optimal to use this here + checked_obstructions = 0 + nonzero_reductions = 0 + # compute the aho corasick automaton + # to make normal form computation more efficient + aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) + # step 1 + obstruction_queue = get_obstructions(g) + while !isempty(obstruction_queue) + obstruction = dequeue!(obstruction_queue) + # step3 + S = s_polynomial(obstruction) + Sp = normal_form(S, g, aut) # or normal_form_weak + if iszero(Sp) + continue + end + nonzero_reductions += 1 + # step4 + push!(g, Sp) + insert_keyword!(aut, Sp.exps[1], length(g)) + if groebner_debug_level > 0 + println("adding new obstructions! checked $checked_obstructions so far") + end + if nonzero_reductions >= reduction_bound + return g + end + add_obstructions!(obstruction_queue, g) + end + return g end Markdown.@doc doc""" -groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int) + groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int) Compute a groebner basis for the ideal spanned by g. Stop when `reduction_bound` many non-zero entries have been added to the groebner basis. -""" -function groebner_basis( - g::Vector{FreeAssAlgElem{T}}, - reduction_bound = typemax(Int)::Int -) where T <: FieldElement - return groebner_basis_buchberger(g, reduction_bound) +""" function groebner_basis( + g::Vector{FreeAssAlgElem{T}}, + reduction_bound = typemax(Int)::Int, +) where {T<:FieldElement} + return groebner_basis_buchberger(g, reduction_bound) end - diff --git a/test/runtests_free_ass_groebner.jl b/test/runtests_free_ass_groebner.jl deleted file mode 100644 index 2e6e122964..0000000000 --- a/test/runtests_free_ass_groebner.jl +++ /dev/null @@ -1,9 +0,0 @@ -using Pkg -Pkg.activate("/home/julien/.julia/dev/AbstractAlgebra/") -using AbstractAlgebra -using AbstractAlgebra.Generic -using Test -include("generic/AhoCorasick-test.jl") - - -include("generic/FreeAssAlgebraGroebner-test.jl") From d1e4787791f61240909c0054da18d1c3877657d1 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 14 Mar 2023 19:10:47 +0100 Subject: [PATCH 29/80] Run OscarCI teests on master, too (#1284) --- .github/workflows/oscar.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/oscar.yml b/.github/workflows/oscar.yml index 093229a8bd..f270f81506 100644 --- a/.github/workflows/oscar.yml +++ b/.github/workflows/oscar.yml @@ -1,6 +1,9 @@ name: OscarCI on: + push: + branches: + - master pull_request: branches: - master From 575ea867d0eb6a1993b4aacf8259585f14d71d46 Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Fri, 17 Mar 2023 16:29:20 +0100 Subject: [PATCH 30/80] Update oscar.yml --- .github/workflows/oscar.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/oscar.yml b/.github/workflows/oscar.yml index f270f81506..86dc82dc2f 100644 --- a/.github/workflows/oscar.yml +++ b/.github/workflows/oscar.yml @@ -4,9 +4,9 @@ on: push: branches: - master - pull_request: - branches: - - master + pull_request: + branches: + - master concurrency: # group by workflow and ref; the last slightly strange component ensures that for pull From 88c6df69364080b299a3e26f30d5a60a3b96d0a7 Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Fri, 17 Mar 2023 18:40:43 +0100 Subject: [PATCH 31/80] Fix bug in universal polynomial ring (#1286) Not all multivariate polynomial rings can be constructed with zero indeterminants (e.g. all ring coming from flint). Thus we always use a generic polynomial ring underpinning the universal one. --- src/UnivPoly.jl | 1 - src/generic/GenericTypes.jl | 2 +- src/generic/UnivPoly.jl | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/UnivPoly.jl b/src/UnivPoly.jl index bd5088fe57..6d1aa1f295 100644 --- a/src/UnivPoly.jl +++ b/src/UnivPoly.jl @@ -35,4 +35,3 @@ end function UniversalPolynomialRing(R::Ring; ordering=:lex, cached=true) return Generic.UniversalPolynomialRing(R; ordering=ordering, cached=cached) end - diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index e770d9b86e..5b84fa8a34 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -422,7 +422,7 @@ end const UnivPolyID = CacheDictType{Tuple{Ring, Symbol, DataType}, Ring}() -mutable struct UnivPoly{T <: RingElement, U <: MPoly} <: AbstractAlgebra.UniversalPolyRingElem{T} +mutable struct UnivPoly{T <: RingElement, U <: MPolyRingElem{T}} <: AbstractAlgebra.UniversalPolyRingElem{T} p::U parent::UniversalPolyRing{T} end diff --git a/src/generic/UnivPoly.jl b/src/generic/UnivPoly.jl index 6f0f1f06af..2f48a7ae93 100644 --- a/src/generic/UnivPoly.jl +++ b/src/generic/UnivPoly.jl @@ -35,7 +35,7 @@ parent_type(::Type{UnivPoly{T, U}}) where {T <: RingElement, U <: AbstractAlgebr UniversalPolyRing{T, U} function mpoly_ring(S::UniversalPolyRing{T, U}) where {T <: RingElement, U <: AbstractAlgebra.MPolyRingElem{T}} - return S.mpoly_ring::parent_type(mpoly_type(T)) + return S.mpoly_ring::Generic.MPolyRing{T} end nvars(S::UniversalPolyRing) = length(S.S) @@ -1126,7 +1126,7 @@ end function UniversalPolynomialRing(R::Ring; ordering=:lex, cached=true) T = elem_type(R) - U = mpoly_type(T) + U = Generic.MPoly{T} return UniversalPolyRing{T, U}(R, ordering, cached) end From 2106c6cb6dc894886739a669487def1bbdfd051f Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Fri, 17 Mar 2023 18:41:33 +0100 Subject: [PATCH 32/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f4ec3640af..1a2679b89f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "AbstractAlgebra" uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" -version = "0.28.3-DEV" +version = "0.28.3" [deps] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" From b372561896e5684d180f9ce1b1efb944805b7f97 Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Fri, 17 Mar 2023 18:42:30 +0100 Subject: [PATCH 33/80] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1a2679b89f..011d293a27 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "AbstractAlgebra" uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" -version = "0.28.3" +version = "0.28.4-DEV" [deps] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" From 17eb859b90323776bd2e023c9ca97e0a5e691cc4 Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Sat, 18 Mar 2023 22:47:49 +0100 Subject: [PATCH 34/80] jldoctest: add missing end markers --- src/AliasMacro.jl | 2 ++ src/generic/YoungTabs.jl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AliasMacro.jl b/src/AliasMacro.jl index f3bd59c84d..63b9c8e05d 100644 --- a/src/AliasMacro.jl +++ b/src/AliasMacro.jl @@ -17,11 +17,13 @@ The alias also gets a special docstring that points out that it is an alias # Examples ```jldoctest; setup = :(using AbstractAlgebra) julia> foo(x::Int) = x +foo (generic function with 1 method) julia> @alias bar foo julia> foo === bar true +``` """ macro alias(alias_name::Symbol, real_name::Symbol) result = quote diff --git a/src/generic/YoungTabs.jl b/src/generic/YoungTabs.jl index 5418d4b8c1..ed4a483af6 100644 --- a/src/generic/YoungTabs.jl +++ b/src/generic/YoungTabs.jl @@ -152,7 +152,7 @@ julia> Generic.partitions(5) 4₁1₁ 3₁2₁ 5₁ - +``` """ partitions(n::Integer) = [Partition(n, copy(p), false) for p in AllParts(n)] partitions!(n::Integer) = (Partition(n, p, false) for p in AllParts(n)) From 8c3fcc5f67f2ee39b4c23358186ce8adead88efa Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 16 Mar 2023 01:37:41 +0100 Subject: [PATCH 35/80] Allow / as shortcut for divexact --- src/NCRings.jl | 14 +++++++++++--- src/Rings.jl | 4 ++++ test/Rings-conformance-tests.jl | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/NCRings.jl b/src/NCRings.jl index 254a8b1cbd..b3a3af704f 100644 --- a/src/NCRings.jl +++ b/src/NCRings.jl @@ -90,9 +90,9 @@ function ==(x::NCRingElem, y::NCRingElem) end end - ==(x::NCRingElem, y::NCRingElement) = x == parent(x)(y) - - ==(x::NCRingElement, y::NCRingElem) = parent(y)(x) == y +==(x::NCRingElem, y::NCRingElement) = x == parent(x)(y) + +==(x::NCRingElement, y::NCRingElem) = parent(y)(x) == y function divexact_left(x::NCRingElem, y::NCRingElem; check::Bool = true) return divexact_left(promote(x, y)...) @@ -118,6 +118,14 @@ function divexact_right( return divexact_right(x, parent(x)(y); check = check) end +#Base.:/(x::ModuleElem, y::RingElement) = divexact_right(x, y; check=true) +Base.:/(x::NCRingElem, y::NCRingElement) = divexact_right(x, y; check=true) +Base.:/(x::Union{Integer, Rational, AbstractFloat}, y::NCRingElem) = divexact_right(x, y; check=true) + +#Base.:\(y::RingElement, x::ModuleElem) = divexact_left(x, y; check=true) +Base.:\(y::NCRingElement, x::NCRingElem) = divexact_left(x, y; check=true) +Base.:\(y::NCRingElem, x::Union{Integer, Rational, AbstractFloat}) = divexact_left(x, y; check=true) + Base.literal_pow(::typeof(^), x::NCRingElem, ::Val{p}) where {p} = x^p function check_parent(a::NCRingElement, b::NCRingElement, throw::Bool=true) diff --git a/src/Rings.jl b/src/Rings.jl index 280f011703..4e4ec4ca03 100644 --- a/src/Rings.jl +++ b/src/Rings.jl @@ -30,6 +30,10 @@ divexact_left(x::T, y::T; check::Bool=true) where T <: RingElement = divexact(x, divexact_right(x::T, y::T; check::Bool=true) where T <: RingElement = divexact(x, y; check=check) +Base.:/(x::ModuleElem, y::RingElement) = divexact(x, y; check=true) +Base.:/(x::RingElem, y::RingElement) = divexact(x, y; check=true) +Base.:/(x::Union{Integer, Rational, AbstractFloat}, y::RingElem) = divexact(x, y; check=true) + Base.inv(x::RingElem) = divexact(one(parent(x)), x) function is_divisible_by(x::T, y::T) where T <: RingElem diff --git a/test/Rings-conformance-tests.jl b/test/Rings-conformance-tests.jl index ea1bea517b..9e4ebe1973 100644 --- a/test/Rings-conformance-tests.jl +++ b/test/Rings-conformance-tests.jl @@ -123,9 +123,12 @@ function test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) @test iszero(b) || equality(divexact_left(b*a, b), a) @test iszero(b) || equality(divexact_left(b*a, b, check = true), a) @test iszero(b) || equality(divexact_left(b*a, b, check = false), a) + @test iszero(b) || equality(b \ (b*a), a) + @test iszero(b) || equality(divexact_right(a*b, b), a) @test iszero(b) || equality(divexact_right(a*b, b, check = true), a) @test iszero(b) || equality(divexact_right(a*b, b, check = false), a) + @test iszero(b) || equality((a*b) / b, a) else try t = divexact_left(b*a, b) @@ -134,6 +137,8 @@ function test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) @test equality(b*t, b*a) t = divexact_left(b*a, b, check = false) @test equality(b*t, b*a) + t = b \ (b*a) + @test equality(b*t, b*a) catch end try @@ -143,6 +148,8 @@ function test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) @test equality(t*b, a*b) t = divexact_right(a*b, b, check = false) @test equality(t*b, a*b) + t = (a*b) / b + @test equality(t*b, a*b) catch end end @@ -232,6 +239,9 @@ function test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50) @test iszero(b) || equality(divexact(b*a, b), a) @test iszero(b) || equality(divexact(b*a, b, check = true), a) @test iszero(b) || equality(divexact(b*a, b, check = false), a) + if T isa RingElem + @test iszero(b) || equality((b*a) / b, a) + end else try t = divexact(b*a, b) @@ -240,6 +250,10 @@ function test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50) @test equality(t*b, a*b) t = divexact(b*a, b, check = false) @test equality(t*b, a*b) + if T isa RingElem + t = (b*a) / b + @test equality(t*b, a*b) + end catch end end From 804c4bf290afe4c7b84e7da33a5d29d8ba0162da Mon Sep 17 00:00:00 2001 From: Markus Kurtz Date: Mon, 20 Mar 2023 20:43:46 +0100 Subject: [PATCH 36/80] Move `polynomial_ring` code to abstract level (#1282) --- docs/src/ncpolynomial.md | 10 +--- docs/src/poly_interface.md | 21 +++++-- docs/src/polynomial.md | 18 +----- src/AbstractAlgebra.jl | 14 ++--- src/MPoly.jl | 114 ++++++++++++++++++++---------------- src/NCPoly.jl | 78 ++++++++++++++++++------ src/Poly.jl | 37 +----------- src/generic/GenericTypes.jl | 9 ++- src/generic/MPoly.jl | 27 --------- src/generic/NCPoly.jl | 13 ---- src/generic/Poly.jl | 20 ------- test/generic/MPoly-test.jl | 13 ++++ test/generic/NCPoly-test.jl | 6 ++ test/generic/Poly-test.jl | 6 -- 14 files changed, 183 insertions(+), 203 deletions(-) diff --git a/docs/src/ncpolynomial.md b/docs/src/ncpolynomial.md index f17c2a7038..36bf805db0 100644 --- a/docs/src/ncpolynomial.md +++ b/docs/src/ncpolynomial.md @@ -47,16 +47,10 @@ accept any AbstractAlgebra polynomial type. In order to construct polynomials in AbstractAlgebra.jl, one must first construct the polynomial ring itself. This is accomplished with the following constructor. -```julia -polynomial_ring(R::NCRing, s::AbstractString; cached::Bool = true) +```@docs +polynomial_ring(R::NCRing, s::Symbol; cached::Bool = true) ``` -Given a base ring `R` and string `s` specifying how the generator (variable) should be -printed, return a tuple `S, x` representing the new polynomial ring $S = R[x]$ and the -generator $x$ of the ring. By default the parent object `S` will depend only on `R` and -`x` and will be cached. Setting the optional argument `cached` to `false` will prevent -the parent object `S` from being cached. - A shorthand version of this function is provided: given a base ring `R`, we abbreviate the constructor as follows. diff --git a/docs/src/poly_interface.md b/docs/src/poly_interface.md index 11009a6c84..10eb9d9832 100644 --- a/docs/src/poly_interface.md +++ b/docs/src/poly_interface.md @@ -136,18 +136,31 @@ Return the array `[s]` where `s` is a `Symbol` representing the variable of the polynomial ring. This is provided for uniformity with the multivariate interface, where there is more than one variable and hence an array of symbols. -Custom polynomial types over a given ring should define the following function -which returns the type of a polynomial object over that ring. - ```julia dense_poly_type(::Type{T}) where T <: RingElement ``` -Return the type of a polynomial whose coefficients have the given type. +Return the type of a polynomial whose coefficients have the given type. In our +example `MyPoly{T}`. This function is defined for generic polynomials and only needs to be defined for custom polynomial rings, e.g. ones defined by a C implementation. +```@docs +polynomial_ring_only(R::Ring, s::Symbol; cached::Bool=true) +``` + +The default implementation figures out the appropriate polynomial ring type via +`dense_poly_type` and calls its constructor with `R, s, cached` as arguments. +In our example, this would be + +```julia +MyPolyRing{T}(R, s, cached) +``` + +Accordingly, `polynomial_ring_only` only needs to be defined, if such a +constructor does not exist or other behaviour is wanted. + ### Basic manipulation of rings and elements ```julia diff --git a/docs/src/polynomial.md b/docs/src/polynomial.md index ff88954e6e..95c387838b 100644 --- a/docs/src/polynomial.md +++ b/docs/src/polynomial.md @@ -48,21 +48,15 @@ can accept any AbstractAlgebra polynomial type. In order to construct polynomials in AbstractAlgebra.jl, one must first construct the polynomial ring itself. This is accomplished with the following constructor. -```julia -polynomial_ring(R::Ring, s::AbstractString; cached::Bool = true) +```@docs +polynomial_ring(R::Ring, s::Symbol; cached::Bool = true) ``` -Given a base ring `R` and string `s` specifying how the generator (variable) should be -printed, return a tuple `S, x` representing the new polynomial ring $S = R[x]$ and the -generator $x$ of the ring. By default the parent object `S` will depend only on `R` and -`x` and will be cached. Setting the optional argument `cached` to `false` will prevent -the parent object `S` from being cached. - A shorthand version of this function is provided: given a base ring `R`, we abbreviate the constructor as follows. ```julia -R["x"] +R[:x] ``` It is also possible to create a polynomial ring with default symbol as follows. @@ -84,12 +78,6 @@ generators. **Examples** ```jldoctest -julia> R, x = polynomial_ring(ZZ, "x") -(Univariate Polynomial Ring in x over Integers, x) - -julia> S, y = polynomial_ring(R, "y") -(Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integers, y) - julia> T, z = QQ["z"] (Univariate Polynomial Ring in z over Rationals, z) diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index cf34092f4a..648059ad60 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -602,7 +602,6 @@ import .Generic: cycles import .Generic: defining_polynomial import .Generic: degrees import .Generic: dense_matrix_type -import .Generic: dense_poly_type import .Generic: dim import .Generic: disable_cache! import .Generic: downscale @@ -659,7 +658,6 @@ import .Generic: monomial_iszero import .Generic: monomial_set! import .Generic: monomial! import .Generic: monomials -import .Generic: mpoly_type import .Generic: MPolyBuildCtx import .Generic: mullow_karatsuba import .Generic: ngens @@ -955,6 +953,7 @@ export monomial_to_newton! export monomial! export monomials export mpoly_type +export mpoly_ring_type export MPolyBuildCtx export mul_classical export mul_karatsuba @@ -996,6 +995,7 @@ export pol_length export polcoeff export poly export poly_ring +export poly_ring_type export PolyCoeffs export polynomial export polynomial_ring @@ -1205,12 +1205,12 @@ export Generic # ############################################################################### -getindex(R::NCRing, s::Union{String, Char, Symbol}) = polynomial_ring(R, s) -getindex(R::NCRing, s::Union{String, Char, Symbol}, ss::Union{String, Char}...) = - polynomial_ring(R, [s, ss...]) +getindex(R::NCRing, s::Union{Symbol, AbstractString, Char}) = polynomial_ring(R, s) +getindex(R::NCRing, s::Union{Symbol, AbstractString, Char}, ss::Union{Symbol, AbstractString, Char}...) = + polynomial_ring(R, [Symbol(x) for x in (s, ss...)]) -# syntax x = R["x"]["y"] -getindex(R::Tuple{Union{Ring, NCRing}, Union{PolyRingElem, NCPolyRingElem}}, s::Union{String, Char, Symbol}) = polynomial_ring(R[1], s) +# syntax: Rxy, y = R[:x][:y] +getindex(R::Union{Tuple{PolyRing, PolyRingElem}, Tuple{NCPolyRing, NCPolyRingElem}}, s::Union{Symbol, AbstractString, Char}) = polynomial_ring(R[1], s) ############################################################################### # diff --git a/src/MPoly.jl b/src/MPoly.jl index 3b96f642be..edfb403989 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -16,6 +16,32 @@ coefficient_ring(a::MPolyRingElem) = base_ring(a) coefficient_ring(R::MPolyRing) = base_ring(R) +@doc md""" + mpoly_type(::Type{T}) where T<:RingElement + +The type of multivariate polynomials with coefficients of type `T`. +Falls back to `Generic.MPoly{T}`. + +# Examples +```julia +mpoly_type(typeof(ZZ())) +``` +""" +mpoly_type(::Type{T}) where T<:RingElement = Generic.MPoly{T} + +@doc md""" + mpoly_ring_type(::Type{T}) where T<:Ring + +The type of multivariate polynomial rings with coefficients of type `T`. +Implemented via [`mpoly_type`](@ref). + +# Examples +```julia +mpoly_ring_type(typeof(ZZ)) +``` +""" +mpoly_ring_type(::Type{T}) where T<:Ring = parent_type(mpoly_type(elem_type(T))) + function is_domain_type(::Type{T}) where {S <: RingElement, T <: AbstractAlgebra.MPolyRingElem{S}} return is_domain_type(S) end @@ -1271,69 +1297,57 @@ end ############################################################################### @doc Markdown.doc""" - polynomial_ring(R::AbstractAlgebra.Ring, s::Vector{T}; cached::Bool = true, ordering::Symbol = :lex) where T <: Union{String, Char, Symbol} - -Given a base ring `R` and an array of strings `s` specifying how the -generators (variables) should be printed, return a tuple `T, (x1, x2, ...)` -representing the new polynomial ring $T = R[x1, x2, ...]$ and the generators -$x1, x2, ...$ of the polynomial ring. By default the parent object `T` will -depend only on `R` and `x1, x2, ...` and will be cached. Setting the optional -argument `cached` to `false` will prevent the parent object `T` from being -cached. `S` is a symbol corresponding to the ordering of the polynomial and -can be one of `:lex`, `:deglex` or `:degrevlex`. + polynomial_ring(R::Ring, s::Vector{T}; cached::Bool = true, ordering::Symbol = :lex) where T <: Union{Symbol, AbstractString, Char} + +Given a base ring `R` and a vector `s` of variable names $x1, x2, \dots$ specifying +how the generators (variables) should be printed, return a tuple `S, [x1, x2, ...]` +representing the new polynomial ring $S = R[x1, x2, ...]$ and the generators +$x1, x2, \dots$ of the polynomial ring. + +Mathematically the object `S` depends only on `R` and `x1, x2, ...` and by +default it will be cached, i.e., if `polynomial_ring` is invoked again with the +same arguments, the same (*identical*) ring is returned. Setting the optional +argument `cached` to `false` ensures a distinct new ring is returned, and will +also prevent it from being cached. + +The `ordering` of the polynomial ring can be one of `:lex`, `:deglex` or `:degrevlex`. """ -function polynomial_ring(R::AbstractAlgebra.Ring, s::Vector{String}; - cached::Bool = true, ordering::Symbol = :lex) - return polynomial_ring(R, [Symbol(v) for v in s]; - cached=cached, ordering=ordering) -end +polynomial_ring(R::Ring, s::Union{Tuple{Vararg{T}}, AbstractVector{T}}; kw...) where + T<:Union{Symbol, AbstractString, Char} = + polynomial_ring(R, Symbol[Symbol(x) for x in s]; kw...) -function polynomial_ring(R::AbstractAlgebra.Ring, s::Vector{Char}; - cached::Bool = true, ordering::Symbol = :lex) - return polynomial_ring(R, [Symbol(v) for v in s]; - cached=cached, ordering=ordering) +function polynomial_ring(R::Ring, s::Vector{Symbol}; kw...) + S = polynomial_ring_only(R, s; kw...) + (S, gens(S)) end -function polynomial_ring(R::AbstractAlgebra.Ring, s::Vector{Symbol}; - cached::Bool = true, ordering::Symbol = :lex) - return Generic.polynomial_ring(R, s; cached=cached, ordering=ordering) -end +@doc md""" + polynomial_ring_only(R::Ring, s::Vector{Symbol}; ordering::Symbol=:lex, cached::Bool=true) + +Like [`polynomial_ring(R::Ring, s::Vector{Symbol})`](@ref) but return only the +multivariate polynomial ring. +""" +polynomial_ring_only(R::T, s::Vector{Symbol}; ordering::Symbol=:lex, cached::Bool=true) where T<:Ring = + mpoly_ring_type(T)(R, s, ordering, cached) + +# Alternative constructors @doc Markdown.doc""" - polynomial_ring(R::Ring, n::Int, s::String; cached::Bool = false, ordering::Symbol = :lex) + polynomial_ring(R::Ring, n::Int, s::T = :x; cached, ordering) where T<:Union{Symbol, AbstractString, Char} -Given a string `s` and a number of variables `n` will +Given a symbol, string or character `s` and a number of variables `n` will do the same as the first constructor except that the variables will be automatically numbered. For example if `s` is the string `x` and `n = 3` then the variables will print as `x1`, `x2`, `x3`. - -By default the parent object `S` will depend only on `R` and `(x, ...)` and -will be cached. Setting the optional argument `cached` to `false` will prevent -the parent object `S` from being cached. - -The optional named argument `ordering` can be used to specify an ordering. The -currently supported options are `:lex`, `:deglex` and `:degrevlex`. """ -function polynomial_ring(R::AbstractAlgebra.Ring, n::Int, s::String; - cached::Bool = false, ordering::Symbol = :lex) - return polynomial_ring(R, n, Symbol(s); cached=cached, ordering=ordering) -end +polynomial_ring(R::Ring, n::Int, s::Union{AbstractString, Char}; kw...) = + polynomial_ring(R, n, Symbol(s); kw...) -function polynomial_ring(R::AbstractAlgebra.Ring, n::Int, s::Char; - cached::Bool = false, ordering::Symbol = :lex) - return polynomial_ring(R, n, Symbol(s); cached=cached, ordering=ordering) -end +polynomial_ring(R::Ring, n::Int, s::Symbol=:x; kw...) = + polynomial_ring(R, Symbol.(s, 1:n); kw...) -function polynomial_ring(R::AbstractAlgebra.Ring, n::Int, s::Symbol=:x; - cached::Bool = false, ordering::Symbol = :lex) - return polynomial_ring(R, [Symbol(s, i) for i=1:n], cached = cached, - ordering = ordering) -end - -function MPolyRing(R::Ring, n::Int) - T = elem_type(R) - return Generic.MPolyRing{T}(R, [Symbol("x$i") for i in 1:n], :lex, n, false) -end +MPolyRing(R::Ring, n::Int) = + polynomial_ring_only(R, Symbol.(:x, 1:n); cached=false) ################################################################################ # diff --git a/src/NCPoly.jl b/src/NCPoly.jl index a937ed393d..0fcc3f8e2f 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -18,6 +18,32 @@ function is_exact_type(a::Type{T}) where {S <: NCRingElem, T <: NCPolyRingElem{S return is_exact_type(S) end +@doc md""" + dense_poly_type(::Type{T}) where T<:NCRingElement + +The type of multivariate polynomials with coefficients of type `T`. +Falls back to `Generic.NCPoly{T}` respectively `Generic.Poly{T}`. + +# Examples +```julia +dense_poly_type(typeof(ZZ())) +``` +""" +dense_poly_type(::Type{T}) where T<:NCRingElement = Generic.NCPoly{T} + +@doc md""" + poly_ring_type(::Type{T}) where T<:NCRing + +The type of polynomial rings with coefficients of type `T`. +Implemented via [`dense_poly_type`](@ref). + +# Examples +```julia +poly_ring_type(typeof(ZZ)) +``` +""" +poly_ring_type(::Type{T}) where T<:NCRing = parent_type(dense_poly_type(elem_type(T))) + @doc Markdown.doc""" var(a::NCPolyRing) @@ -657,25 +683,43 @@ rand(S::NCPolyRing, deg_range, v...) = rand(Random.GLOBAL_RNG, S, deg_range, v.. ############################################################################### @doc Markdown.doc""" - polynomial_ring(R::NCRing, s::Union{AbstractString, Char, Symbol}; cached::Bool = true) - -Given a base ring `R` and string `s` specifying how the generator (variable) -should be printed, return a tuple `S, x` representing the new polynomial -ring $S = R[x]$ and the generator $x$ of the ring. By default the parent -object `S` will depend only on `R` and `x` and will be cached. Setting the -optional argument `cached` to `false` will prevent the parent object `S` from -being cached. + polynomial_ring(R::NCRing, s::Union{Symbol, AbstractString, Char}; cached::Bool = true) + +Given a base ring `R` and symbol/string `s` specifying how the generator +(variable) should be printed, return a tuple `S, x` representing the new +polynomial ring $S = R[x]$ and the generator $x$ of the ring. + +By default the parent object `S` depends only on `R` and `x` and will be cached. +Setting the optional argument `cached` to `false` will prevent the parent object `S` from being cached. + +# Examples + +```jldoctest; setup = :(using AbstractAlgebra) +julia> R, x = polynomial_ring(ZZ, :x) +(Univariate Polynomial Ring in x over Integers, x) + +julia> S, y = polynomial_ring(R, :y) +(Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integers, y) +``` """ -polynomial_ring(R::NCRing, s::Union{AbstractString, Char, Symbol}; cached::Bool = true) +polynomial_ring(R::NCRing, s::Union{Symbol, AbstractString, Char}; kw...) -function polynomial_ring(R::NCRing, s::Symbol; cached::Bool = true) - return Generic.polynomial_ring(R, s, cached=cached) -end +polynomial_ring(R::NCRing, s::Union{AbstractString, Char}; kw...) = polynomial_ring(R, Symbol(s); kw...) -function polynomial_ring(R::NCRing, s::AbstractString; cached::Bool = true) - return Generic.polynomial_ring(R, Symbol(s), cached=cached) +function polynomial_ring(R::NCRing, s::Symbol; kw...) + S = polynomial_ring_only(R, s; kw...) + (S, gen(S)) end -function polynomial_ring(R::NCRing, s::Char; cached::Bool = true) - return Generic.polynomial_ring(R, Symbol(s); cached=cached) -end +@doc md""" + polynomial_ring_only(R::NCRing, s::Symbol; cached::Bool=true) + +Like [`polynomial_ring(R::NCRing, s::Symbol)`](@ref) but return only the +polynomial ring. +""" +polynomial_ring_only(R::T, s::Symbol; cached::Bool=true) where T<:NCRing = + poly_ring_type(T)(R, s, cached) + +# Simplified constructor + +PolyRing(R::NCRing) = polynomial_ring_only(R, :x; cached=false) diff --git a/src/Poly.jl b/src/Poly.jl index 1853495397..3df31d1a84 100644 --- a/src/Poly.jl +++ b/src/Poly.jl @@ -20,6 +20,8 @@ coefficient_ring(a::PolynomialElem) = base_ring(a) parent(a::PolynomialElem) = a.parent +dense_poly_type(::Type{T}) where T<:RingElement = Generic.Poly{T} + function is_domain_type(::Type{T}) where {S <: RingElement, T <: PolyRingElem{S}} return is_domain_type(S) end @@ -3427,38 +3429,3 @@ function subst(f::PolyRingElem{T}, a::U) where {T <: RingElement, U} end return s end - -############################################################################### -# -# polynomial_ring constructor -# -############################################################################### - -@doc Markdown.doc""" - polynomial_ring(R::Ring, s::Union{String, Char, Symbol}; cached::Bool = true) - -Given a base ring `R` and string `s` specifying how the generator (variable) -should be printed, return a tuple `S, x` representing the new polynomial -ring $S = R[x]$ and the generator $x$ of the ring. By default the parent -object `S` will depend only on `R` and `x` and will be cached. Setting the -optional argument `cached` to `false` will prevent the parent object `S` from -being cached. -""" -polynomial_ring(R::Ring, s::Union{AbstractString, Char, Symbol}; cached::Bool = true) - -function polynomial_ring(R::Ring, s::Symbol; cached::Bool = true) - return Generic.polynomial_ring(R, s; cached=cached) -end - -function polynomial_ring(R::Ring, s::AbstractString; cached::Bool = true) - return polynomial_ring(R, Symbol(s); cached=cached) -end - -function polynomial_ring(R::Ring, s::Char; cached::Bool = true) - return polynomial_ring(R, Symbol(s); cached=cached) -end - -function PolyRing(R::Ring) - T = elem_type(R) - return Generic.PolyRing{T}(R, :x, false) -end diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 5b84fa8a34..ba9b071189 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -333,7 +333,7 @@ end # :deglex # :degrevlex # -# T is an Int which is the number of variables +# N is an Int which is the number of variables # (plus one if ordered by total degree) mutable struct MPolyRing{T <: RingElement} <: AbstractAlgebra.MPolyRing{T} @@ -351,6 +351,13 @@ mutable struct MPolyRing{T <: RingElement} <: AbstractAlgebra.MPolyRing{T} end end +function MPolyRing{T}(R::Ring, s::Vector{Symbol}, ordering::Symbol=:lex, cached::Bool=true) where T <: RingElement + @assert T == elem_type(R) + N = length(s) + ordering in (:deglex, :degrevlex) && (N+=1) + return MPolyRing{T}(R, s, ordering, N, cached) +end + const MPolyID = CacheDictType{Tuple{Ring, Vector{Symbol}, Symbol, Int}, Ring}() mutable struct MPoly{T <: RingElement} <: AbstractAlgebra.MPolyRingElem{T} diff --git a/src/generic/MPoly.jl b/src/generic/MPoly.jl index 051c8e4104..11762ee236 100644 --- a/src/generic/MPoly.jl +++ b/src/generic/MPoly.jl @@ -16,14 +16,6 @@ parent_type(::Type{MPoly{T}}) where T <: RingElement = MPolyRing{T} elem_type(::Type{MPolyRing{T}}) where T <: RingElement = MPoly{T} -@doc Markdown.doc""" - mpoly_type(::Type{T}) where T <: RingElement - -Return the type of a multivariate polynomial whose coefficients have the given -type. -""" -mpoly_type(::Type{T}) where T <: RingElement = MPoly{T} - base_ring(R::MPolyRing{T}) where T <: RingElement = R.base_ring::parent_type(T) @doc Markdown.doc""" @@ -4128,22 +4120,3 @@ function (a::MPolyRing{T})(b::Vector{T}, m::Vector{Vector{Int}}) where {T <: Rin z = combine_like_terms!(z) return z end - -############################################################################### -# -# polynomial_ring constructor -# -############################################################################### - -function polynomial_ring(R::AbstractAlgebra.Ring, s::Vector{Symbol}; cached::Bool = true, ordering::Symbol = :lex) - T = elem_type(R) - N = (ordering == :deglex || ordering == :degrevlex) ? length(s) + 1 : length(s) - parent_obj = MPolyRing{T}(R, s, ordering, N, cached) - - return tuple(parent_obj, gens(parent_obj)) -end - -function polynomial_ring(R::AbstractAlgebra.Ring, s::Vector{String}; cached::Bool = true, ordering::Symbol = :lex) - return polynomial_ring(R, [Symbol(v) for v in s]; cached=cached, ordering=ordering) -end - diff --git a/src/generic/NCPoly.jl b/src/generic/NCPoly.jl index 3233857ca6..6fb26e5a7f 100644 --- a/src/generic/NCPoly.jl +++ b/src/generic/NCPoly.jl @@ -258,16 +258,3 @@ function (a::NCPolyRing{T})(b::T) where {T <: Integer} z.parent = a return z end - -############################################################################### -# -# polynomial_ring constructor -# -############################################################################### - -function polynomial_ring(R::AbstractAlgebra.NCRing, s::Symbol; cached::Bool = true) - T = elem_type(R) - parent_obj = NCPolyRing{T}(R, s, cached) - - return parent_obj, parent_obj([R(0), R(1)]) -end diff --git a/src/generic/Poly.jl b/src/generic/Poly.jl index c6e3536b94..94dc18380b 100644 --- a/src/generic/Poly.jl +++ b/src/generic/Poly.jl @@ -14,13 +14,6 @@ parent_type(::Type{Poly{T}}) where T <: RingElement = PolyRing{T} elem_type(::Type{PolyRing{T}}) where T <: RingElement = Poly{T} -@doc Markdown.doc""" - dense_poly_type(::Type{T}) where T <: RingElement - -Return the type of a polynomial whose coefficients have the given type. -""" -dense_poly_type(::Type{T}) where T <: RingElement = Poly{T} - ############################################################################### # # Basic manipulation @@ -414,16 +407,3 @@ function (a::PolyRing{T})(b::T) where {T <: Integer} z.parent = a return z end - -############################################################################### -# -# polynomial_ring constructor -# -############################################################################### - -function polynomial_ring(R::AbstractAlgebra.Ring, s::Symbol; cached::Bool = true) - T = elem_type(R) - parent_obj = PolyRing{T}(R, s, cached) - - return parent_obj, parent_obj([R(0), R(1)]) -end diff --git a/test/generic/MPoly-test.jl b/test/generic/MPoly-test.jl index c34a720aef..6c5f07f916 100644 --- a/test/generic/MPoly-test.jl +++ b/test/generic/MPoly-test.jl @@ -112,6 +112,19 @@ @test S isa Generic.MPolyRing{Generic.Poly{BigInt}} @test y isa Generic.MPoly{Generic.Poly{BigInt}} @test z isa Generic.MPoly{Generic.Poly{BigInt}} + + ZZxyz, (x,y,z) = polynomial_ring(ZZ, 'x':'z') + @test ZZxyz isa Generic.MPolyRing + + ZZxyz2, (x2,y2,z2) = polynomial_ring(ZZ, (:x, 'y', GenericString("z"))) + @test ZZxyz == ZZxyz2 + @test (x,y,z) == (x2,y2,z2) + + ZZxyz3, _ = polynomial_ring(ZZ, Union{String,Char,Symbol}["x", 'y', :z]) + @test ZZxyz == ZZxyz3 + + ZZxyz4, _ = ZZ["x", 'y', :z] + @test ZZxyz == ZZxyz4 end @testset "Generic.MPoly.printing" begin diff --git a/test/generic/NCPoly-test.jl b/test/generic/NCPoly-test.jl index 55596009f1..7d697936f4 100644 --- a/test/generic/NCPoly-test.jl +++ b/test/generic/NCPoly-test.jl @@ -423,3 +423,9 @@ end @test derivative(g*f) == derivative(g)*f + g*derivative(f) end end + +@testset "Generic.NCPoly.printing" begin + M = MatrixAlgebra(ZZ, 3) + _, x = M['x'] + @test string(M(-1)*x) isa String +end diff --git a/test/generic/Poly-test.jl b/test/generic/Poly-test.jl index 910a646597..4ab38b64a5 100644 --- a/test/generic/Poly-test.jl +++ b/test/generic/Poly-test.jl @@ -2968,12 +2968,6 @@ end @test map_coefficients(t -> F(t) + 2, f) == 3y^2 + 5y^3 + 4y^6 end -@testset "Generic.Poly.printing" begin - M = MatrixAlgebra(ZZ, 3) - _, x = M['x'] - @test string(M(-1)*x) isa String -end - @testset "Generic.Poly.polynomial_to_power_sums" begin R, x = polynomial_ring(QQ, "x") From d04a58e495906071d97b4c5734baa50d62bf5af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 24 Mar 2023 08:50:04 +0100 Subject: [PATCH 37/80] add `ngens` for `FreeAssAlgebra` (#1295) * add `ngens` for `FreeAssAlgebra` * add `ngens` test for `FreeAssAlgebra` --- src/generic/FreeAssAlgebra.jl | 4 ++++ test/generic/FreeAssAlgebra-test.jl | 1 + 2 files changed, 5 insertions(+) diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 78f91c87c9..919c2397e6 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -87,6 +87,10 @@ function isone(a::FreeAssAlgElem{T}) where {T} end end +function ngens(a::FreeAssAlgebra{T}) where {T} + return nvars(a) +end + function gen(a::FreeAssAlgebra{T}, i::Int) where {T} 0 < i <= nvars(a) || error("variable index out of range") c = one(base_ring(a)) diff --git a/test/generic/FreeAssAlgebra-test.jl b/test/generic/FreeAssAlgebra-test.jl index 142688891d..acdb1c76fc 100644 --- a/test/generic/FreeAssAlgebra-test.jl +++ b/test/generic/FreeAssAlgebra-test.jl @@ -16,6 +16,7 @@ @test parent_type(Generic.FreeAssAlgElem{elem_type(R)}) == Generic.FreeAssAlgebra{elem_type(R)} @test base_ring(S) === R @test coefficient_ring(S) === R + @test ngens(S) == length(gens(S)) @test typeof(S) <: Generic.FreeAssAlgebra From b232a4e5f2ccb9f00352c4d351cbeb2e87b07976 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 24 Mar 2023 14:36:11 +0100 Subject: [PATCH 38/80] OscarCI: fix running without PR_NUMBER, e.g. on master --- .github/workflows/oscar.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/oscar.yml b/.github/workflows/oscar.yml index 86dc82dc2f..636f3c2929 100644 --- a/.github/workflows/oscar.yml +++ b/.github/workflows/oscar.yml @@ -21,7 +21,7 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} env: - PR_NUMBER: ${{github.event.number}} + PR_NUMBER: ${{github.event.number || '0' }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v3 From 6bda6148831cbc055dc1fd16f4a28930c5950a17 Mon Sep 17 00:00:00 2001 From: Tommy Hofmann Date: Fri, 24 Mar 2023 21:12:45 +0100 Subject: [PATCH 39/80] Add is_finite/is_perfect fallbacks --- src/AbstractAlgebra.jl | 1 + src/Rings.jl | 13 +++++++++++++ test/Rings-test.jl | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index 648059ad60..7c48310ca9 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -240,6 +240,7 @@ export IdealSet export IdentityMap export is_irreducible export is_squarefree +export is_perfect export Map export MatAlgebra export MatAlgElem diff --git a/src/Rings.jl b/src/Rings.jl index 4e4ec4ca03..2ad0a7908e 100644 --- a/src/Rings.jl +++ b/src/Rings.jl @@ -167,6 +167,19 @@ function is_square_with_sqrt(a::RingElem) end end +############################################################################### +# +# Ring properties +# +############################################################################### + +is_perfect(F::Field) = characteristic(F) == 0 || F isa FinField || + throw(NotImplementedError(:is_perfect, F)) + +is_finite(F::FinField) = true + +is_finite(F::Field) = characteristic(F) != 0 && throw(NotImplementedError(:is_finite, F)) + ############################################################################### # # Generic and specific rings and fields diff --git a/test/Rings-test.jl b/test/Rings-test.jl index d29667966b..8b37a9f73d 100644 --- a/test/Rings-test.jl +++ b/test/Rings-test.jl @@ -53,4 +53,11 @@ end @test isone(p) && q == zero(Qx) end +@testset "properties" begin + @test is_perfect(QQ) + @test is_perfect(GF(2)) + @test is_finite(GF(2)) + @test !is_finite(QQ) +end + include("julia/Integers-test.jl") From 6fca2385c412824b29014f8792d5530afafbd3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Sat, 25 Mar 2023 11:25:30 +0100 Subject: [PATCH 40/80] make `get_attribute` unambiguous (#1298) --- src/Attributes.jl | 12 ++++++++++++ test/Attributes-test.jl | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/Attributes.jl b/src/Attributes.jl index 09a7b700d1..acd12dd795 100644 --- a/src/Attributes.jl +++ b/src/Attributes.jl @@ -202,6 +202,13 @@ function get_attribute(G::Any, attr::Symbol, default::Any = nothing) return default end +# unambiguous version +function get_attribute(G::Any, attr::Symbol, default::Symbol) + D = _get_attributes(G) + D isa Dict && return get(D, attr, default) + return default +end + """ get_attribute!(f::Function, G::Any, attr::Symbol) @@ -233,6 +240,11 @@ function get_attribute!(G::Any, attr::Symbol, default::Any) return Base.get!(D, attr, default) end +function get_attribute!(G::Any, attr::Symbol, default::Symbol) + D = _get_attributes!(G) + return Base.get!(D, attr, default) +end + """ set_attribute!(G::Any, data::Pair{Symbol, <:Any}...) diff --git a/test/Attributes-test.jl b/test/Attributes-test.jl index 04cb945de4..e9fc99d531 100644 --- a/test/Attributes-test.jl +++ b/test/Attributes-test.jl @@ -108,6 +108,12 @@ end @test get_attribute(() -> 42, x, :bar9) == 0 @test get_attribute(x, :bar9) == 0 + # test get_attribute with symbol entries + set_attribute!(x, :bar10 => :somesymbol) + @test get_attribute(x, :bar10) == :somesymbol + @test get_attribute(x, :bar11) == nothing + @test get_attribute(x, :bar11, :defaultsymbol) == :defaultsymbol + # test get_attribute! with default value for new entry @test get_attribute(x, :bar4) == nothing @test get_attribute!(x, :bar4, 42) == 42 @@ -135,6 +141,13 @@ end @test get_attribute!(Vector{Int}, x, :bar8) isa Vector{Int} @test get_attribute(x, :bar8) == [] + # test get_attribute with symbol entries + set_attribute!(x, :bar12 => :somesymbol) + @test get_attribute(x, :bar12) == :somesymbol + @test get_attribute!(x, :bar12, :defaultsymbol) == :somesymbol + @test get_attribute(x, :bar12) == :somesymbol + @test get_attribute!(x, :bar13, :defaultsymbol) == :defaultsymbol + @test get_attribute(x, :bar13) == :defaultsymbol end From 858232288a4c18f9fa956f25f3209edd788e2c38 Mon Sep 17 00:00:00 2001 From: Markus Kurtz Date: Sat, 25 Mar 2023 11:26:59 +0100 Subject: [PATCH 41/80] Introduce `VarName` (#1291) --- src/AbsMSeries.jl | 32 +++++------- src/AbsSeries.jl | 81 +++++++++--------------------- src/AbstractAlgebra.jl | 27 +++------- src/FreeAssAlgebra.jl | 4 +- src/LaurentMPoly.jl | 19 ++----- src/LaurentPoly.jl | 18 ++----- src/LaurentSeries.jl | 28 ++--------- src/MPoly.jl | 8 +-- src/NCPoly.jl | 6 +-- src/Poly.jl | 28 +++-------- src/PuiseuxSeries.jl | 30 +++-------- src/RationalFunctionField.jl | 23 +-------- src/RelSeries.jl | 96 ++++++++++-------------------------- src/SparsePoly.jl | 12 +---- src/fundamental_interface.jl | 15 ++++++ src/generic/LaurentSeries.jl | 89 ++++++++++----------------------- src/generic/UnivPoly.jl | 4 +- test/generic/Poly-test.jl | 61 ++++++++++++++--------- 18 files changed, 188 insertions(+), 393 deletions(-) diff --git a/src/AbsMSeries.jl b/src/AbsMSeries.jl index 11ca6ead74..d3df992e5e 100644 --- a/src/AbsMSeries.jl +++ b/src/AbsMSeries.jl @@ -196,41 +196,35 @@ end # ############################################################################### +function power_series_ring(R::Ring, weights::Vector{Int}, prec::Int, + s::Vector{Symbol}; cached=true, model=:capped_absolute) + return Generic.power_series_ring(R, weights, prec, s; cached=cached, model=model) +end + function power_series_ring(R::Ring, prec::Vector{Int}, - s::Vector{T}; cached=true, model=:capped_absolute) where - T <: Symbol + s::Vector{Symbol}; cached=true, model=:capped_absolute) return Generic.power_series_ring(R, prec, s; cached=cached, model=model) end function power_series_ring(R::Ring, prec::Vector{Int}, - s::Vector{T}; cached=true, model=:capped_absolute) where - T <: Union{Char, AbstractString} + s::AbstractVector{<:VarName}; cached=true, model=:capped_absolute) sym = [Symbol(v) for v in s] - return Generic.power_series_ring(R, prec, sym; cached=cached, model=model) -end - -function power_series_ring(R::Ring, weights::Vector{Int}, prec::Int, - s::Vector{T}; cached=true, model=:capped_absolute) where - T <: Symbol - return Generic.power_series_ring(R, weights, prec, s; cached=cached, model=model) + return power_series_ring(R, prec, sym; cached=cached, model=model) end function power_series_ring(R::Ring, weights::Vector{Int}, prec::Int, - s::Vector{T}; cached=true, model=:capped_absolute) where - T <: Union{Char, AbstractString} + s::AbstractVector{<:VarName}; cached=true, model=:capped_absolute) sym = [Symbol(v) for v in s] - return Generic.power_series_ring(R, weights, prec, sym; cached=cached, model=model) + return power_series_ring(R, weights, prec, sym; cached=cached, model=model) end function power_series_ring(R::Ring, prec::Int, - s::Vector{T}; cached=true, model=:capped_absolute) where - T <: Symbol + s::Vector{Symbol}; cached=true, model=:capped_absolute) return Generic.power_series_ring(R, prec, s; cached=cached, model=model) end function power_series_ring(R::Ring, prec::Int, - s::Vector{T}; cached=true, model=:capped_absolute) where - T <: Union{Char, AbstractString} + s::AbstractVector{<:VarName}; cached=true, model=:capped_absolute) sym = [Symbol(v) for v in s] - return Generic.power_series_ring(R, prec, sym; cached=cached, model=model) + return power_series_ring(R, prec, sym; cached=cached, model=model) end diff --git a/src/AbsSeries.jl b/src/AbsSeries.jl index 2eb832937e..54171217f6 100644 --- a/src/AbsSeries.jl +++ b/src/AbsSeries.jl @@ -87,8 +87,7 @@ end # ############################################################################### -function similar(x::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, - s::Symbol=var(parent(x)); cached::Bool=true) +function similar(x::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, s::Symbol; cached::Bool=true) TT = elem_type(R) V = Vector{TT}(undef, 0) p = Generic.AbsSeries{TT}(V, 0, max_prec) @@ -104,72 +103,36 @@ function similar(x::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, return p end -function similar(x::AbsPowerSeriesRingElem, R::Ring, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, R, max_precision(parent(x)), var; cached = cached) -end +similar(x::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, R, max_prec, Symbol(var); cached) -function similar(x::AbsPowerSeriesRingElem, max_prec::Int, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, base_ring(x), max_prec, var; cached=cached) -end +similar(x::AbsPowerSeriesRingElem, R::Ring, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, R, max_precision(parent(x)), Symbol(var); cached) -function similar(x::AbsPowerSeriesRingElem, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, base_ring(x), - max_precision(parent(x)), var; cached=cached) -end +similar(x::AbsPowerSeriesRingElem, max_prec::Int, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, base_ring(x), max_prec, Symbol(var); cached) -function similar(x::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, - var::String; cached::Bool=true) - return similar(x, R, max_prec, Symbol(var); cached=cached) -end +similar(x::AbsPowerSeriesRingElem, var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, base_ring(x), + max_precision(parent(x)), Symbol(var); cached) -function similar(x::AbsPowerSeriesRingElem, R::Ring, var::String; cached::Bool=true) - return similar(x, R, max_precision(parent(x)), Symbol(var); cached=cached) -end +zero(a::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, var::VarName=var(parent(x)); cached::Bool=true) = + similar(a, R, max_prec, var; cached) -function similar(x::AbsPowerSeriesRingElem, max_prec::Int, - var::String; cached::Bool=true) - return similar(x, base_ring(x), max_prec, Symbol(var); cached=cached) -end -function similar(x::AbsPowerSeriesRingElem, var::String; cached::Bool=true) - return similar(x, base_ring(x), - max_precision(parent(x)), Symbol(var); cached=cached) -end +zero(a::AbsPowerSeriesRingElem, R::Ring, var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, R, var; cached) -function zero(a::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, R, max_prec, var; cached=cached) -end - -function zero(a::AbsPowerSeriesRingElem, R::Ring, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, R, max_precision(parent(a)), var; cached=cached) -end - -function zero(a::AbsPowerSeriesRingElem, max_prec::Int, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, base_ring(a), max_prec, var; cached=cached) -end - -zero(a::AbsPowerSeriesRingElem, var::Symbol=var(parent(a)); cached::Bool=true) = - similar(a, base_ring(a), max_precision(parent(a)), var; cached=cached) - -function zero(a::AbsPowerSeriesRingElem, R::Ring, max_prec::Int, - var::String; cached::Bool=true) - return zero(a, R, max_prec, Symbol(var); cached=cached) -end -zero(a::AbsPowerSeriesRingElem, R::Ring, var::String; cached::Bool=true) = - zero(a, R, max_precision(parent(a)), Symbol(var); cached=cached) +zero(a::AbsPowerSeriesRingElem, max_prec::Int, var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, max_prec, var; cached) -zero(a::AbsPowerSeriesRingElem, max_prec::Int, var::String; cached::Bool=true) = - zero(a, base_ring(a), max_prec, Symbol(var); cached=cached) -zero(a::AbsPowerSeriesRingElem, var::String; cached::Bool=true) = - zero(a, base_ring(a), max_precision(parent(a)), Symbol(var); cached=cached) +zero(a::AbsPowerSeriesRingElem, var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, var; cached) ############################################################################### # @@ -177,7 +140,7 @@ zero(a::AbsPowerSeriesRingElem, var::String; cached::Bool=true) = # ############################################################################### -function abs_series(R::Ring, arr::Vector{T}, len::Int, prec::Int, var::AbstractString="x"; max_precision::Int=prec, cached::Bool=true) where T +function abs_series(R::Ring, arr::Vector{T}, len::Int, prec::Int, var::VarName=:x; max_precision::Int=prec, cached::Bool=true) where T prec < len && error("Precision too small for given data") TT = elem_type(R) coeffs = T == Any && length(arr) == 0 ? elem_type(R)[] : map(R, arr) diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index 7c48310ca9..fd6e2d31c1 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -277,6 +277,7 @@ export SimpleNumFieldElem export sub! export UniversalPolyRing export UniversalPolyRingElem +export VarName export zero! export zeros export zz @@ -1159,27 +1160,11 @@ function YoungTableau(part::Generic.Partition, fill::Vector{Int}=collect(1:part. Generic.YoungTableau(part, fill) end -function number_field(a::Generic.Poly{Rational{BigInt}}, s::AbstractString, t = "\$"; cached = true) +function number_field(a::Generic.Poly{Rational{BigInt}}, s::VarName, t = "\$"; cached = true) return Generic.number_field(a, Symbol(s), t; cached=cached) end -function number_field(a::Generic.Poly{Rational{BigInt}}, s::Char, t = "\$"; cached = true) - return Generic.number_field(a, Symbol(s), t; cached=cached) -end - -function number_field(a::Generic.Poly{Rational{BigInt}}, s::Symbol, t = "\$"; cached = true) - return Generic.number_field(a, s, t; cached=cached) -end - -function FunctionField(p::Generic.Poly{Generic.RationalFunctionFieldElem{T, U}}, s::Symbol; cached::Bool=true) where {T <: FieldElement, U <: Union{PolyRingElem, MPolyRingElem}} - return Generic.FunctionField(p, s; cached=cached) -end - -function FunctionField(p::Generic.Poly{Generic.RationalFunctionFieldElem{T, U}}, s::AbstractString; cached::Bool=true) where {T <: FieldElement, U <: Union{PolyRingElem, MPolyRingElem}} - return Generic.FunctionField(p, Symbol(s); cached=cached) -end - -function FunctionField(p::Generic.Poly{Generic.RationalFunctionFieldElem{T, U}}, s::Char; cached::Bool=true) where {T <: FieldElement, U <: Union{PolyRingElem, MPolyRingElem}} +function FunctionField(p::Generic.Poly{Generic.RationalFunctionFieldElem{T, U}}, s::VarName; cached::Bool=true) where {T <: FieldElement, U <: Union{PolyRingElem, MPolyRingElem}} return Generic.FunctionField(p, Symbol(s); cached=cached) end @@ -1206,12 +1191,12 @@ export Generic # ############################################################################### -getindex(R::NCRing, s::Union{Symbol, AbstractString, Char}) = polynomial_ring(R, s) -getindex(R::NCRing, s::Union{Symbol, AbstractString, Char}, ss::Union{Symbol, AbstractString, Char}...) = +getindex(R::NCRing, s::VarName) = polynomial_ring(R, s) +getindex(R::NCRing, s::VarName, ss::VarName...) = polynomial_ring(R, [Symbol(x) for x in (s, ss...)]) # syntax: Rxy, y = R[:x][:y] -getindex(R::Union{Tuple{PolyRing, PolyRingElem}, Tuple{NCPolyRing, NCPolyRingElem}}, s::Union{Symbol, AbstractString, Char}) = polynomial_ring(R[1], s) +getindex(R::Union{Tuple{PolyRing, PolyRingElem}, Tuple{NCPolyRing, NCPolyRingElem}}, s::VarName) = polynomial_ring(R[1], s) ############################################################################### # diff --git a/src/FreeAssAlgebra.jl b/src/FreeAssAlgebra.jl index d7bfa11581..47d5b01cbd 100644 --- a/src/FreeAssAlgebra.jl +++ b/src/FreeAssAlgebra.jl @@ -184,7 +184,7 @@ end function free_associative_algebra( R::AbstractAlgebra.Ring, - s::Union{AbstractVector{<:AbstractString}, AbstractVector{Symbol}, AbstractVector{Char}}; + s::AbstractVector{<:VarName}; cached::Bool = true) S = [Symbol(v) for v in s] @@ -202,7 +202,7 @@ end function free_associative_algebra( R::AbstractAlgebra.Ring, n::Int, - s::Union{AbstractString, Symbol, Char}; + s::VarName; cached::Bool = false) S = [Symbol(s, i) for i in 1:n] diff --git a/src/LaurentMPoly.jl b/src/LaurentMPoly.jl index 20fd856f4d..a33cb13a4e 100644 --- a/src/LaurentMPoly.jl +++ b/src/LaurentMPoly.jl @@ -123,7 +123,7 @@ end ############################################################################### @doc Markdown.doc""" - LaurentPolynomialRing(R::AbstractAlgebra.Ring, s::Vector{T}; cached::Bool = true) where T <: Union{String, Char, Symbol} + LaurentPolynomialRing(R::AbstractAlgebra.Ring, s::Vector{T}; cached::Bool = true) where T <: VarName Given a base ring `R` and an array of strings `s` specifying how the generators (variables) should be printed, return a tuple `T, (x1, x2, ...)` @@ -133,11 +133,7 @@ depend only on `R` and `x1, x2, ...` and will be cached. Setting the optional argument `cached` to `false` will prevent the parent object `T` from being cached. """ -function LaurentPolynomialRing(R::AbstractAlgebra.Ring, s::Vector{String}; cached::Bool = true) - return Generic.LaurentPolynomialRing(R, [Symbol(v) for v in s], cached=cached) -end - -function LaurentPolynomialRing(R::AbstractAlgebra.Ring, s::Vector{Char}; cached::Bool = true) +function LaurentPolynomialRing(R::AbstractAlgebra.Ring, s::AbstractVector{<:VarName}; cached::Bool = true) return Generic.LaurentPolynomialRing(R, [Symbol(v) for v in s], cached=cached) end @@ -145,15 +141,6 @@ function LaurentPolynomialRing(R::AbstractAlgebra.Ring, s::Vector{Symbol}; cache return Generic.LaurentPolynomialRing(R, s; cached=cached) end -function LaurentPolynomialRing(R::AbstractAlgebra.Ring, n::Int, s::String; cached::Bool = false) +function LaurentPolynomialRing(R::AbstractAlgebra.Ring, n::Int, s::VarName=:x; cached::Bool = false) return Generic.LaurentPolynomialRing(R, [Symbol(s, i) for i=1:n]; cached=cached) end - -function LaurentPolynomialRing(R::AbstractAlgebra.Ring, n::Int, s::Char; cached::Bool = false) - return Generic.LaurentPolynomialRing(R, [Symbol(s, i) for i=1:n]; cached=cached) -end - -function LaurentPolynomialRing(R::AbstractAlgebra.Ring, n::Int, s::Symbol=:x; cached::Bool = false) - return Generic.LaurentPolynomialRing(R, [Symbol(s, i) for i=1:n], cached = cached) -end - diff --git a/src/LaurentPoly.jl b/src/LaurentPoly.jl index 155949f012..5db389c2eb 100644 --- a/src/LaurentPoly.jl +++ b/src/LaurentPoly.jl @@ -11,7 +11,7 @@ ############################################################################### @doc doc""" - LaurentPolynomialRing(R::Ring, s::Union{AbstractString, Char, Symbol}) + LaurentPolynomialRing(R::Ring, s::VarName) Given a base ring `R` and string `s` specifying how the generator (variable) should be printed, return a tuple `S, x` representing the new Laurent polynomial @@ -29,17 +29,5 @@ julia> rand(R, -3:3, -9:9) -3*x^2 - 8*x + 4 + 3*x^-1 - 6*x^-2 + 9*x^-3 ``` """ -LaurentPolynomialRing(R::Ring, s::Union{AbstractString, Char, Symbol}) - -function LaurentPolynomialRing(R::Ring, s::Symbol; cached::Bool = true) - return Generic.LaurentPolynomialRing(R, s, cached = cached) -end - -function LaurentPolynomialRing(R::Ring, s::Char; cached::Bool = true) - return Generic.LaurentPolynomialRing(R, Symbol(s), cached = cached) -end - -function LaurentPolynomialRing(R::Ring, s::AbstractString; cached::Bool = true) - return Generic.LaurentPolynomialRing(R, Symbol(s), cached = cached) -end - +LaurentPolynomialRing(R::Ring, s::VarName; cached::Bool = true) = + Generic.LaurentPolynomialRing(R, Symbol(s); cached) diff --git a/src/LaurentSeries.jl b/src/LaurentSeries.jl index 13377dc2e6..c9a9364c59 100644 --- a/src/LaurentSeries.jl +++ b/src/LaurentSeries.jl @@ -12,7 +12,7 @@ ############################################################################### @doc Markdown.doc""" - laurent_series_ring(R::Ring, prec::Int, s::Union{Char, AbstractString, Symbol}; cached=true) + laurent_series_ring(R::Ring, prec::Int, s::VarName; cached=true) laurent_series_field(R::Field, prec::Int, s::Union{Char, AbstractString; cached=true) Return a tuple $(S, x)$ consisting of the parent object `S` of a Laurent series @@ -24,27 +24,9 @@ object `S` will be cached so that supplying the same base ring, string and precision in future will return the same parent object and generator. If caching of the parent object is not required, `cached` can be set to `false`. """ -function laurent_series_ring(R::Ring, prec::Int, s::AbstractString; cached=true) - return laurent_series_ring(R, prec, Symbol(s); cached=cached) -end +laurent_series_ring(R::Ring, prec::Int, s::VarName; cached=true) = + Generic.laurent_series_ring(R, prec, Symbol(s); cached) -function laurent_series_ring(R::Ring, prec::Int, s::Symbol; cached=true) - return Generic.laurent_series_ring(R, prec, s; cached=cached) -end - -function laurent_series_ring(R::Ring, prec::Int, s::Char; cached=true) - return laurent_series_ring(R, prec, Symbol(s); cached=cached) -end - -function laurent_series_field(R::Field, prec::Int, s::AbstractString; cached=true) - return laurent_series_field(R, prec, Symbol(s); cached=cached) -end - -function laurent_series_field(R::Field, prec::Int, s::Symbol; cached=true) - return Generic.laurent_series_field(R, prec, s; cached=cached) -end - -function laurent_series_field(R::Field, prec::Int, s::Char; cached=true) - return laurent_series_field(R, prec, Symbol(s); cached=cached) -end +laurent_series_field(R::Field, prec::Int, s::VarName; cached=true) = + Generic.laurent_series_field(R, prec, Symbol(s); cached) diff --git a/src/MPoly.jl b/src/MPoly.jl index edfb403989..200ab5c4cb 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -1297,7 +1297,7 @@ end ############################################################################### @doc Markdown.doc""" - polynomial_ring(R::Ring, s::Vector{T}; cached::Bool = true, ordering::Symbol = :lex) where T <: Union{Symbol, AbstractString, Char} + polynomial_ring(R::Ring, s::Vector{T}; cached::Bool = true, ordering::Symbol = :lex) where T <: VarName Given a base ring `R` and a vector `s` of variable names $x1, x2, \dots$ specifying how the generators (variables) should be printed, return a tuple `S, [x1, x2, ...]` @@ -1313,7 +1313,7 @@ also prevent it from being cached. The `ordering` of the polynomial ring can be one of `:lex`, `:deglex` or `:degrevlex`. """ polynomial_ring(R::Ring, s::Union{Tuple{Vararg{T}}, AbstractVector{T}}; kw...) where - T<:Union{Symbol, AbstractString, Char} = + T<:VarName = polynomial_ring(R, Symbol[Symbol(x) for x in s]; kw...) function polynomial_ring(R::Ring, s::Vector{Symbol}; kw...) @@ -1333,14 +1333,14 @@ polynomial_ring_only(R::T, s::Vector{Symbol}; ordering::Symbol=:lex, cached::Boo # Alternative constructors @doc Markdown.doc""" - polynomial_ring(R::Ring, n::Int, s::T = :x; cached, ordering) where T<:Union{Symbol, AbstractString, Char} + polynomial_ring(R::Ring, n::Int, s::VarName = :x; cached, ordering) Given a symbol, string or character `s` and a number of variables `n` will do the same as the first constructor except that the variables will be automatically numbered. For example if `s` is the string `x` and `n = 3` then the variables will print as `x1`, `x2`, `x3`. """ -polynomial_ring(R::Ring, n::Int, s::Union{AbstractString, Char}; kw...) = +polynomial_ring(R::Ring, n::Int, s::VarNameNonSymbol; kw...) = polynomial_ring(R, n, Symbol(s); kw...) polynomial_ring(R::Ring, n::Int, s::Symbol=:x; kw...) = diff --git a/src/NCPoly.jl b/src/NCPoly.jl index 0fcc3f8e2f..e8ec432b92 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -683,7 +683,7 @@ rand(S::NCPolyRing, deg_range, v...) = rand(Random.GLOBAL_RNG, S, deg_range, v.. ############################################################################### @doc Markdown.doc""" - polynomial_ring(R::NCRing, s::Union{Symbol, AbstractString, Char}; cached::Bool = true) + polynomial_ring(R::NCRing, s::VarName; cached::Bool = true) Given a base ring `R` and symbol/string `s` specifying how the generator (variable) should be printed, return a tuple `S, x` representing the new @@ -702,9 +702,9 @@ julia> S, y = polynomial_ring(R, :y) (Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integers, y) ``` """ -polynomial_ring(R::NCRing, s::Union{Symbol, AbstractString, Char}; kw...) +polynomial_ring(R::NCRing, s::VarName; kw...) -polynomial_ring(R::NCRing, s::Union{AbstractString, Char}; kw...) = polynomial_ring(R, Symbol(s); kw...) +polynomial_ring(R::NCRing, s::VarNameNonSymbol; kw...) = polynomial_ring(R, Symbol(s); kw...) function polynomial_ring(R::NCRing, s::Symbol; kw...) S = polynomial_ring_only(R, s; kw...) diff --git a/src/Poly.jl b/src/Poly.jl index 3df31d1a84..c62e1fc9c8 100644 --- a/src/Poly.jl +++ b/src/Poly.jl @@ -338,7 +338,7 @@ end # ############################################################################### -function similar(x::PolyRingElem, R::Ring, s::Symbol=var(parent(x)); cached::Bool=true) +function similar(x::PolyRingElem, R::Ring, s::Symbol; cached::Bool=true) TT = elem_type(R) V = Vector{TT}(undef, 0) p = Generic.Poly{TT}(V) @@ -353,37 +353,25 @@ function similar(x::PolyRingElem, R::Ring, s::Symbol=var(parent(x)); cached::Boo return p end -function similar(x::PolyRingElem, var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, base_ring(x), var; cached=cached) -end - -function similar(x::PolyRingElem, R::Ring, var::String; cached::Bool=true) - return similar(x, R, Symbol(var); cached=cached) -end +similar(x::PolyRingElem, R::Ring, var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, R, Symbol(var); cached) -function similar(x::PolyRingElem, var::String; cached::Bool=true) - return similar(x, base_ring(x), Symbol(var); cached=cached) -end +similar(x::PolyRingElem, var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, base_ring(x), Symbol(var); cached) -zero(p::PolyRingElem, R::Ring, var::Symbol=var(parent(p)); cached::Bool=true) = +zero(p::PolyRingElem, R::Ring, var::VarName=var(parent(p)); cached::Bool=true) = similar(p, R, var; cached=cached) -zero(p::PolyRingElem, var::Symbol=var(parent(p)); cached::Bool=true) = +zero(p::PolyRingElem, var::VarName=var(parent(p)); cached::Bool=true) = similar(p, base_ring(p), var; cached=cached) -zero(p::PolyRingElem, R::Ring, var::String; cached::Bool=true) = - zero(p, R, Symbol(var); cached=cached) - -zero(p::PolyRingElem, var::String; cached::Bool=true) = - zero(p, base_ring(p), Symbol(var); cached=cached) - ############################################################################### # # polynomial constructor # ############################################################################### -function polynomial(R::Ring, arr::Vector{T}, var::AbstractString="x"; cached::Bool=true) where T +function polynomial(R::Ring, arr::Vector{T}, var::VarName=:x; cached::Bool=true) where T TT = elem_type(R) coeffs = T == Any && length(arr) == 0 ? elem_type(R)[] : map(R, arr) p = Generic.Poly{TT}(coeffs) diff --git a/src/PuiseuxSeries.jl b/src/PuiseuxSeries.jl index 4460a12d9d..e0c43ac136 100644 --- a/src/PuiseuxSeries.jl +++ b/src/PuiseuxSeries.jl @@ -11,8 +11,8 @@ ############################################################################### @doc Markdown.doc""" - PuiseuxSeriesRing(R::Ring, prec::Int, s::Union{Symbol, Char, AbstractString}; cached=true) - PuiseuxSeriesField(R::Field, prec::Int, s::Union{Symbol, Char, AbstractString}; cached = true) + PuiseuxSeriesRing(R::Ring, prec::Int, s::VarName; cached=true) + PuiseuxSeriesField(R::Field, prec::Int, s::VarName; cached = true) Return a tuple $(S, x)$ consisting of the parent object `S` of a Puiseux series ring over the given base ring and a generator `x` for the Puiseux series ring. @@ -24,26 +24,8 @@ object `S` will be cached so that supplying the same base ring, string and precision in future will return the same parent object and generator. If caching of the parent object is not required, `cached` can be set to `false`. """ -function PuiseuxSeriesRing(R::Ring, prec::Int, s::AbstractString; cached=true) - return PuiseuxSeriesRing(R, prec, Symbol(s); cached=cached) -end +PuiseuxSeriesRing(R::Ring, prec::Int, s::VarName; cached=true) = + Generic.PuiseuxSeriesRing(R, prec, Symbol(s); cached) -function PuiseuxSeriesRing(R::Ring, prec::Int, s::Symbol; cached=true) - return Generic.PuiseuxSeriesRing(R, prec, s; cached=cached) -end - -function PuiseuxSeriesRing(R::Ring, prec::Int, s::Char; cached=true) - return PuiseuxSeriesRing(R, prec, Symbol(s); cached=cached) -end - -function PuiseuxSeriesField(R::Field, prec::Int, s::AbstractString; cached = true) - return PuiseuxSeriesField(R, prec, Symbol(s); cached=cached) -end - -function PuiseuxSeriesField(R::Field, prec::Int, s::Symbol; cached = true) - return Generic.PuiseuxSeriesField(R, prec, s; cached=cached) -end - -function PuiseuxSeriesField(R::Field, prec::Int, s::Char; cached = true) - return PuiseuxSeriesField(R, prec, Symbol(s); cached=cached) -end +PuiseuxSeriesField(R::Field, prec::Int, s::VarName; cached=true) = + Generic.PuiseuxSeriesField(R, prec, Symbol(s); cached) diff --git a/src/RationalFunctionField.jl b/src/RationalFunctionField.jl index 250710d7bf..6bc68c1350 100644 --- a/src/RationalFunctionField.jl +++ b/src/RationalFunctionField.jl @@ -10,29 +10,10 @@ # ############################################################################### -function RationalFunctionField(k::Field, s::Symbol; cached=true) - return Generic.RationalFunctionField(k, s; cached=cached) -end - -function RationalFunctionField(k::Field, s::Char; cached=true) +function RationalFunctionField(k::Field, s::VarName; cached=true) return Generic.RationalFunctionField(k, Symbol(s); cached=cached) end -function RationalFunctionField(k::Field, s::AbstractString; cached=true) - return Generic.RationalFunctionField(k, Symbol(s); cached=cached) -end - -function RationalFunctionField(k::Field, s::Vector{Symbol}; cached=true) - return Generic.RationalFunctionField(k, s; cached=cached) -end - -function RationalFunctionField(k::Field, s::Vector{Char}; cached=true) +function RationalFunctionField(k::Field, s::AbstractVector{<:VarName}; cached=true) return Generic.RationalFunctionField(k, [Symbol(si) for si in s]; cached=cached) end - -function RationalFunctionField(k::Field, s::Vector{String}; cached=true) - return Generic.RationalFunctionField(k, [Symbol(si) for si in s]; cached=cached) -end - - - diff --git a/src/RelSeries.jl b/src/RelSeries.jl index b04e3c2a0e..d956d5080e 100644 --- a/src/RelSeries.jl +++ b/src/RelSeries.jl @@ -178,7 +178,7 @@ end ############################################################################### function similar(x::RelPowerSeriesRingElem, R::Ring, max_prec::Int, - s::Symbol=var(parent(x)); cached::Bool=true) + s::Symbol; cached::Bool=true) TT = elem_type(R) V = Vector{TT}(undef, 0) p = Generic.RelSeries{TT}(V, 0, max_prec, max_prec) @@ -194,72 +194,41 @@ function similar(x::RelPowerSeriesRingElem, R::Ring, max_prec::Int, return p end -function similar(x::RelPowerSeriesRingElem, R::Ring, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, R, max_precision(parent(x)), var; cached = cached) -end +similar(x::RelPowerSeriesRingElem, R::Ring, max_prec::Int, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, R, max_prec, Symbol(var); cached) -function similar(x::RelPowerSeriesRingElem, max_prec::Int, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, base_ring(x), max_prec, var; cached=cached) -end +similar(x::RelPowerSeriesRingElem, R::Ring, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, R, max_precision(parent(x)), Symbol(var); cached) -function similar(x::RelPowerSeriesRingElem, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, base_ring(x), - max_precision(parent(x)), var; cached=cached) -end -function similar(x::RelPowerSeriesRingElem, R::Ring, max_prec::Int, - var::String; cached::Bool=true) - return similar(x, R, max_prec, Symbol(var); cached=cached) -end +similar(x::RelPowerSeriesRingElem, max_prec::Int, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, base_ring(x), max_prec, Symbol(var); cached) -function similar(x::RelPowerSeriesRingElem, R::Ring, var::String; cached::Bool=true) - return similar(x, R, max_precision(parent(x)), Symbol(var); cached=cached) -end -function similar(x::RelPowerSeriesRingElem, max_prec::Int, - var::String; cached::Bool=true) - return similar(x, base_ring(x), max_prec, Symbol(var); cached=cached) -end +similar(x::RelPowerSeriesRingElem, var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, base_ring(x), max_precision(parent(x)), Symbol(var); cached) -function similar(x::RelPowerSeriesRingElem, var::String; cached::Bool=true) - return similar(x, base_ring(x), max_precision(parent(x)), - Symbol(var); cached=cached) -end -function zero(a::RelPowerSeriesRingElem, R::Ring, max_prec::Int, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, R, max_prec, var; cached=cached) -end +zero(a::RelPowerSeriesRingElem, R::Ring, max_prec::Int, + var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, R, max_prec, Symbol(var); cached=cached) -function zero(a::RelPowerSeriesRingElem, R::Ring, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, R, max_precision(parent(a)), var; cached=cached) -end -function zero(a::RelPowerSeriesRingElem, max_prec::Int, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, base_ring(a), max_prec, var; cached=cached) -end - -zero(a::RelPowerSeriesRingElem, var::Symbol=var(parent(a)); cached::Bool=true) = - similar(a, base_ring(a), max_precision(parent(a)), var; cached=cached) +zero(a::RelPowerSeriesRingElem, R::Ring, + var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, R, Symbol(var); cached) -function zero(a::RelPowerSeriesRingElem, R::Ring, max_prec::Int, - var::String; cached::Bool=true) - return zero(a, R, max_prec, Symbol(var); cached=cached) -end -zero(a::RelPowerSeriesRingElem, R::Ring, var::String; cached::Bool=true) = - zero(a, R, max_precision(parent(a)), Symbol(var); cached=cached) +zero(a::RelPowerSeriesRingElem, max_prec::Int, + var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, max_prec, Symbol(var); cached) -zero(a::RelPowerSeriesRingElem, max_prec::Int, var::String; cached::Bool=true) = - zero(a, base_ring(a), max_prec, Symbol(var); cached=cached) -zero(a::RelPowerSeriesRingElem, var::String; cached::Bool=true) = - zero(a, base_ring(a), max_precision(parent(a)), Symbol(var); cached=cached) +zero(a::RelPowerSeriesRingElem, var::Symbol=var(parent(a)); cached::Bool=true) = + similar(a, Symbol(var); cached) ############################################################################### # @@ -267,7 +236,7 @@ zero(a::RelPowerSeriesRingElem, var::String; cached::Bool=true) = # ############################################################################### -function rel_series(R::Ring, arr::Vector{T}, len::Int, prec::Int, val::Int, var::AbstractString="x"; max_precision::Int=prec, cached::Bool=true) where T +function rel_series(R::Ring, arr::Vector{T}, len::Int, prec::Int, val::Int, var::VarName=:x; max_precision::Int=prec, cached::Bool=true) where T prec < len + val && error("Precision too small for given data") TT = elem_type(R) coeffs = T == Any && length(arr) == 0 ? elem_type(R)[] : map(R, arr) @@ -1426,7 +1395,7 @@ rand(S::SeriesRing, val_range, v...) = rand(Random.GLOBAL_RNG, S, val_range, v.. ############################################################################### @doc Markdown.doc""" - power_series_ring(R::Ring, prec::Int, s::Union{AbstractString, char, Symbol}; cached=true, model=:capped_relative) + power_series_ring(R::Ring, prec::Int, s::VarName; cached=true, model=:capped_relative) Return a tuple $(S, x)$ consisting of the parent object `S` of a power series ring over the given base ring and a generator `x` for the power series ring. @@ -1439,19 +1408,8 @@ object `S` will be cached so that supplying the same base ring, string and precision in future will return the same parent object and generator. If caching of the parent object is not required, `cached` can be set to `false`. """ -power_series_ring(R::Ring, prec::Int, s::Union{AbstractString, Char, Symbol}; cached=true, model=:capped_relative) - -function power_series_ring(R::Ring, prec::Int, s::Symbol; cached=true, model=:capped_relative) - return Generic.power_series_ring(R, prec, s; cached=cached, model=model) -end - -function power_series_ring(R::Ring, prec::Int, s::Char; cached=true, model=:capped_relative) - return power_series_ring(R, prec, Symbol(s); cached=cached, model=model) -end - -function power_series_ring(R::Ring, prec::Int, s::AbstractString; cached=true, model=:capped_relative) - return power_series_ring(R, prec, Symbol(s); cached=cached, model=model) -end +power_series_ring(R::Ring, prec::Int, s::VarName; cached=true, model=:capped_relative) = + Generic.power_series_ring(R, prec, Symbol(s); cached, model) function AbsPowerSeriesRing(R::Ring, prec::Int) T = elem_type(R) diff --git a/src/SparsePoly.jl b/src/SparsePoly.jl index bcbf1611c6..8a9626ca2d 100644 --- a/src/SparsePoly.jl +++ b/src/SparsePoly.jl @@ -11,7 +11,7 @@ ############################################################################### @doc Markdown.doc""" - SparsePolynomialRing(R::Ring, s::Union{Char, AbstractString, Symbol} cached::Bool = true) + SparsePolynomialRing(R::Ring, s::VarName cached::Bool = true) Given a base ring `R` and a string `s` specifying how the generator (variable) should be printed, return a tuple `S, x` representing the new @@ -20,14 +20,6 @@ ring. By default the parent object `T` will depend only on `R` and `x` and will be cached. Setting the optional argument `cached` to `false` will prevent the parent object `T` from being cached. """ -function SparsePolynomialRing(R::Ring, s::AbstractString; cached::Bool = true) +function SparsePolynomialRing(R::Ring, s::VarName; cached::Bool = true) return Generic.SparsePolynomialRing(R, Symbol(s); cached=cached) end - -function SparsePolynomialRing(R::Ring, s::Char; cached::Bool = true) - return Generic.SparsePolynomialRing(R, Symbol(s); cached=cached) -end - -function SparsePolynomialRing(R::Ring, s::Symbol; cached::Bool = true) - return Generic.SparsePolynomialRing(R, s; cached=cached) -end diff --git a/src/fundamental_interface.jl b/src/fundamental_interface.jl index 5ceef2fcdc..1d5a37a799 100644 --- a/src/fundamental_interface.jl +++ b/src/fundamental_interface.jl @@ -243,6 +243,21 @@ function gen end # """ function gens end +############################################################################### +# +# Variable names +# +############################################################################### + +const VarName = Union{Symbol, AbstractString, Char} + +# Constructors which use `VarName` in their argument list often are implemented +# by first converting all such inputs to use `Symbol` everywhere. For disambiguation +# and to avoid infinite recursion, it then is sometimes necessary to have methods +# in pairs, one using `Symbol`, and one using "`VarName` except for `Symbol`". +# The latter is what `VarNameNonSymbol` is for. +const VarNameNonSymbol = Union{AbstractString, Char} + ############################################################################### # # Unsafe functions diff --git a/src/generic/LaurentSeries.jl b/src/generic/LaurentSeries.jl index b509a77b8a..4090ee9839 100644 --- a/src/generic/LaurentSeries.jl +++ b/src/generic/LaurentSeries.jl @@ -380,7 +380,7 @@ end ############################################################################### function similar(x::LaurentSeriesElem, R::Ring, max_prec::Int, - s::Symbol=var(parent(x)); cached::Bool=true) + s::Symbol; cached::Bool=true) TT = elem_type(R) V = Vector{TT}(undef, 0) p = Generic.LaurentSeriesRingElem{TT}(V, 0, max_prec, max_prec, 1) @@ -397,7 +397,7 @@ function similar(x::LaurentSeriesElem, R::Ring, max_prec::Int, end function similar(x::LaurentSeriesElem, R::Field, max_prec::Int, - s::Symbol=var(parent(x)); cached::Bool=true) + s::Symbol; cached::Bool=true) TT = elem_type(R) V = Vector{TT}(undef, 0) p = Generic.LaurentSeriesFieldElem{TT}(V, 0, max_prec, max_prec, 1) @@ -413,72 +413,35 @@ function similar(x::LaurentSeriesElem, R::Field, max_prec::Int, return p end -function similar(x::LaurentSeriesElem, R::Ring, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, R, max_precision(parent(x)), var; cached = cached) -end - -function similar(x::LaurentSeriesElem, max_prec::Int, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, base_ring(x), max_prec, var; cached=cached) -end - -function similar(x::LaurentSeriesElem, - var::Symbol=var(parent(x)); cached::Bool=true) - return similar(x, base_ring(x), - max_precision(parent(x)), var; cached=cached) -end +similar(x::LaurentSeriesElem, R::Ring, max_prec::Int, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, R, max_prec, Symbol(var); cached) -function similar(x::LaurentSeriesElem, R::Ring, max_prec::Int, - var::String; cached::Bool=true) - return similar(x, R, max_prec, Symbol(var); cached=cached) -end +similar(x::LaurentSeriesElem, R::Ring, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, R, max_precision(parent(x)), Symbol(var); cached) -function similar(x::LaurentSeriesElem, R::Ring, var::String; cached::Bool=true) - return similar(x, R, max_precision(parent(x)), Symbol(var); cached=cached) -end +similar(x::LaurentSeriesElem, max_prec::Int, + var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, base_ring(x), max_prec, Symbol(var); cached) -function similar(x::LaurentSeriesElem, max_prec::Int, - var::String; cached::Bool=true) - return similar(x, base_ring(x), max_prec, Symbol(var); cached=cached) -end +similar(x::LaurentSeriesElem, var::VarName=var(parent(x)); cached::Bool=true) = + similar(x, base_ring(x), max_precision(parent(x)), Symbol(var); cached) -function similar(x::LaurentSeriesElem, var::String; cached::Bool=true) - return similar(x, base_ring(x), max_precision(parent(x)), - Symbol(var); cached=cached) -end - -function zero(a::LaurentSeriesElem, R::Ring, max_prec::Int, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, R, max_prec, var; cached=cached) -end - -function zero(a::LaurentSeriesElem, R::Ring, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, R, max_precision(parent(a)), var; cached=cached) -end - -function zero(a::LaurentSeriesElem, max_prec::Int, - var::Symbol=var(parent(a)); cached::Bool=true) - return similar(a, base_ring(a), max_prec, var; cached=cached) -end - -zero(a::LaurentSeriesElem, var::Symbol=var(parent(a)); cached::Bool=true) = - similar(a, base_ring(a), max_precision(parent(a)), var; cached=cached) - -function zero(a::LaurentSeriesElem, R::Ring, max_prec::Int, - var::String; cached::Bool=true) - return zero(a, R, max_prec, Symbol(var); cached=cached) -end +zero(a::LaurentSeriesElem, R::Ring, max_prec::Int, + var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, R, max_prec, Symbol(var); cached) -zero(a::LaurentSeriesElem, R::Ring, var::String; cached::Bool=true) = - zero(a, R, max_precision(parent(a)), Symbol(var); cached=cached) +zero(a::LaurentSeriesElem, R::Ring, + var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, R, Symbol(var); cached) -zero(a::LaurentSeriesElem, max_prec::Int, var::String; cached::Bool=true) = - zero(a, base_ring(a), max_prec, Symbol(var); cached=cached) +zero(a::LaurentSeriesElem, max_prec::Int, + var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, max_prec, Symbol(var); cached) -zero(a::LaurentSeriesElem, var::String; cached::Bool=true) = - zero(a, base_ring(a), max_precision(parent(a)), Symbol(var); cached=cached) +zero(a::LaurentSeriesElem, var::VarName=var(parent(a)); cached::Bool=true) = + similar(a, Symbol(var); cached) ############################################################################### # @@ -486,7 +449,7 @@ zero(a::LaurentSeriesElem, var::String; cached::Bool=true) = # ############################################################################### -function laurent_series(R::Ring, arr::Vector{T}, len::Int, prec::Int, val::Int, scale::Int, var::AbstractString="x"; max_precision::Int=prec, cached::Bool=true) where T +function laurent_series(R::Ring, arr::Vector{T}, len::Int, prec::Int, val::Int, scale::Int, var::VarName=:x; max_precision::Int=prec, cached::Bool=true) where T scale <= 0 && error("Scale must be positive") prec < (len - 1)*scale + val + 1 && error("Precision too small for given data") TT = elem_type(R) @@ -497,7 +460,7 @@ function laurent_series(R::Ring, arr::Vector{T}, len::Int, prec::Int, val::Int, return p end -function laurent_series(R::Field, arr::Vector{T}, len::Int, prec::Int, val::Int, scale::Int, var::AbstractString="x"; max_precision::Int=prec, cached::Bool=true) where T +function laurent_series(R::Field, arr::Vector{T}, len::Int, prec::Int, val::Int, scale::Int, var::VarName=:x; max_precision::Int=prec, cached::Bool=true) where T scale <= 0 && error("Scale must be positive") prec < (len - 1)*scale + val + 1 && error("Precision too small for given data") TT = elem_type(R) diff --git a/src/generic/UnivPoly.jl b/src/generic/UnivPoly.jl index 2f48a7ae93..d489f078f8 100644 --- a/src/generic/UnivPoly.jl +++ b/src/generic/UnivPoly.jl @@ -260,11 +260,11 @@ function gen(S::UniversalPolyRing{T, U}, s::Symbol) where {T <: RingElement, U < return UnivPoly{T, U}(gen(mpoly_ring(S), i), S) end -gen(S::UniversalPolyRing, s::Union{Char, String}) = gen(S, Symbol(s)) +gen(S::UniversalPolyRing, s::AbstractAlgebra.VarNameNonSymbol) = gen(S, Symbol(s)) gens(S::UniversalPolyRing, v::Vector{Symbol}) = tuple([gen(S, s) for s in v]...) -gens(S::UniversalPolyRing, v::Vector{T}) where T <: Union{Char, String} = gens(S, [Symbol(s) for s in v]) +gens(S::UniversalPolyRing, v::Vector{T}) where T <: AbstractAlgebra.VarNameNonSymbol = gens(S, [Symbol(s) for s in v]) function gen(S::UniversalPolyRing{T, U}, i::Int) where {T, U} i > nvars(S) && error("Variable index out of range") diff --git a/test/generic/Poly-test.jl b/test/generic/Poly-test.jl index 4ab38b64a5..3eb3ed0740 100644 --- a/test/generic/Poly-test.jl +++ b/test/generic/Poly-test.jl @@ -60,46 +60,63 @@ end @test typeof(T) <: Generic.PolyRing - @test isa(z, PolyRingElem) + @testset "Generic.Poly.constructors.elements" begin + @test isa(z, PolyRingElem) - f = x^2 + y^3 + z + 1 + f = x^2 + y^3 + z + 1 - @test isa(f, PolyRingElem) + @test isa(f, PolyRingElem) - g = S(2) + g = S(2) - @test isa(g, PolyRingElem) + @test isa(g, PolyRingElem) - h = S(x^2 + 2x + 1) + h = S(x^2 + 2x + 1) - @test isa(h, PolyRingElem) + @test isa(h, PolyRingElem) - j = T(x + 2) + j = T(x + 2) - @test isa(j, PolyRingElem) + @test isa(j, PolyRingElem) - k = S([x, x + 2, x^2 + 3x + 1]) + k = S([x, x + 2, x^2 + 3x + 1]) - @test isa(k, PolyRingElem) + @test isa(k, PolyRingElem) + + l = S(k) - l = S(k) + @test isa(l, PolyRingElem) - @test isa(l, PolyRingElem) + m = S([1, 2, 3]) - m = S([1, 2, 3]) + @test isa(m, PolyRingElem) - @test isa(m, PolyRingElem) + n = S([ZZ(1), ZZ(2), ZZ(3)]) - n = S([ZZ(1), ZZ(2), ZZ(3)]) + @test isa(n, PolyRingElem) - @test isa(n, PolyRingElem) + @test x in [x, y] + @test x in [y, x] + @test !(x in [y]) - @test x in [x, y] - @test x in [y, x] - @test !(x in [y]) + @test x in keys(Dict(x => 1)) + @test !(y in keys(Dict(x => 1))) + end - @test x in keys(Dict(x => 1)) - @test !(y in keys(Dict(x => 1))) + @testset "Generic.Poly.constructors.varname" begin + Rx1 = polynomial_ring(ZZ, :x) + Rx2 = polynomial_ring(ZZ, 'x') + Rx3 = polynomial_ring(ZZ, "x") + Rx4 = polynomial_ring(ZZ, GenericString("x")) + Rx5 = ZZ[:x] + Rx6 = ZZ[GenericString("x")] + + @test Rx1 == Rx2 + @test Rx1 == Rx3 + @test Rx1 == Rx4 + @test Rx1 == Rx5 + @test Rx1 == Rx6 + end end @testset "Generic.Poly.iterators" begin From 9c754368c190e16b380f1daa8b8e01447c133cc1 Mon Sep 17 00:00:00 2001 From: Markus Kurtz Date: Sat, 25 Mar 2023 11:27:52 +0100 Subject: [PATCH 42/80] Add `*poly*_type` methods for elements (#1290) --- src/MPoly.jl | 22 +++++++++++++++++----- src/NCPoly.jl | 22 +++++++++++++++++----- test/generic/MPoly-test.jl | 6 ++++++ test/generic/NCPoly-test.jl | 6 ++++++ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index 200ab5c4cb..a7572efa94 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -18,29 +18,41 @@ coefficient_ring(R::MPolyRing) = base_ring(R) @doc md""" mpoly_type(::Type{T}) where T<:RingElement + mpoly_type(::T) where T<:RingElement The type of multivariate polynomials with coefficients of type `T`. Falls back to `Generic.MPoly{T}`. # Examples -```julia -mpoly_type(typeof(ZZ())) +```jldoctest; setup = :(using AbstractAlgebra) +julia> mpoly_type(elem_type(AbstractAlgebra.ZZ)) +AbstractAlgebra.Generic.MPoly{BigInt} + +julia> mpoly_type(AbstractAlgebra.ZZ(1)) +AbstractAlgebra.Generic.MPoly{BigInt} ``` """ mpoly_type(::Type{T}) where T<:RingElement = Generic.MPoly{T} +mpoly_type(x) = mpoly_type(typeof(x)) # to stop this method from eternally recursing on itself, we better add ... +mpoly_type(::Type{T}) where T = throw(ArgumentError("Type `$T` must be subtype of `RingElement`.")) @doc md""" mpoly_ring_type(::Type{T}) where T<:Ring + mpoly_ring_type(::T) where T<:Ring The type of multivariate polynomial rings with coefficients of type `T`. Implemented via [`mpoly_type`](@ref). # Examples -```julia -mpoly_ring_type(typeof(ZZ)) +```jldoctest; setup = :(using AbstractAlgebra) +julia> mpoly_ring_type(typeof(AbstractAlgebra.ZZ)) +AbstractAlgebra.Generic.MPolyRing{BigInt} + +julia> mpoly_ring_type(AbstractAlgebra.ZZ) +AbstractAlgebra.Generic.MPolyRing{BigInt} ``` """ -mpoly_ring_type(::Type{T}) where T<:Ring = parent_type(mpoly_type(elem_type(T))) +mpoly_ring_type(x) = parent_type(mpoly_type(elem_type(x))) function is_domain_type(::Type{T}) where {S <: RingElement, T <: AbstractAlgebra.MPolyRingElem{S}} return is_domain_type(S) diff --git a/src/NCPoly.jl b/src/NCPoly.jl index e8ec432b92..3b6e80ed77 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -20,29 +20,41 @@ end @doc md""" dense_poly_type(::Type{T}) where T<:NCRingElement + dense_poly_type(::T) where T<:NCRingElement The type of multivariate polynomials with coefficients of type `T`. Falls back to `Generic.NCPoly{T}` respectively `Generic.Poly{T}`. # Examples -```julia -dense_poly_type(typeof(ZZ())) +```jldoctest; setup = :(using AbstractAlgebra) +julia> dense_poly_type(elem_type(AbstractAlgebra.ZZ)) +AbstractAlgebra.Generic.Poly{BigInt} + +julia> dense_poly_type(AbstractAlgebra.ZZ(1)) +AbstractAlgebra.Generic.Poly{BigInt} ``` """ dense_poly_type(::Type{T}) where T<:NCRingElement = Generic.NCPoly{T} +dense_poly_type(x) = dense_poly_type(typeof(x)) # to stop this method from eternally recursing on itself, we better add ... +dense_poly_type(::Type{T}) where T = throw(ArgumentError("Type `$T` must be subtype of `NCRingElement`.")) @doc md""" poly_ring_type(::Type{T}) where T<:NCRing + poly_ring_type(::T) where T<:NCRing The type of polynomial rings with coefficients of type `T`. Implemented via [`dense_poly_type`](@ref). # Examples -```julia -poly_ring_type(typeof(ZZ)) +```jldoctest; setup = :(using AbstractAlgebra) +julia> poly_ring_type(typeof(AbstractAlgebra.ZZ)) +AbstractAlgebra.Generic.PolyRing{BigInt} + +julia> poly_ring_type(AbstractAlgebra.ZZ) +AbstractAlgebra.Generic.PolyRing{BigInt} ``` """ -poly_ring_type(::Type{T}) where T<:NCRing = parent_type(dense_poly_type(elem_type(T))) +poly_ring_type(x) = parent_type(dense_poly_type(elem_type(x))) @doc Markdown.doc""" var(a::NCPolyRing) diff --git a/test/generic/MPoly-test.jl b/test/generic/MPoly-test.jl index 6c5f07f916..717344ef85 100644 --- a/test/generic/MPoly-test.jl +++ b/test/generic/MPoly-test.jl @@ -1576,3 +1576,9 @@ end R, _ = polynomial_ring(ZZ, Symbol[]) @test R(1) != R(2) end + +@testset "Generic.MPoly.exceptions" begin + @test_throws MethodError polynomial_ring(Char, [:x]) + @test_throws Exception mpoly_ring_type(Char) + @test_throws ArgumentError mpoly_type(Char) +end diff --git a/test/generic/NCPoly-test.jl b/test/generic/NCPoly-test.jl index 7d697936f4..944afc6ebb 100644 --- a/test/generic/NCPoly-test.jl +++ b/test/generic/NCPoly-test.jl @@ -429,3 +429,9 @@ end _, x = M['x'] @test string(M(-1)*x) isa String end + +@testset "Generic.NCPoly.exceptions" begin + @test_throws MethodError polynomial_ring(Char, :x) + @test_throws Exception poly_ring_type(Char) + @test_throws ArgumentError dense_poly_type(Char) +end From 12ee1225cb0f51cd6405108a7e4d01fbda24509e Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Mar 2023 16:05:43 +0200 Subject: [PATCH 43/80] Clarify comments for new get_attribute methods --- src/Attributes.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Attributes.jl b/src/Attributes.jl index acd12dd795..84540972ee 100644 --- a/src/Attributes.jl +++ b/src/Attributes.jl @@ -202,7 +202,8 @@ function get_attribute(G::Any, attr::Symbol, default::Any = nothing) return default end -# unambiguous version +# the following method is necessary to disambiguate between the above +# methods when the default value is a Symbol function get_attribute(G::Any, attr::Symbol, default::Symbol) D = _get_attributes(G) D isa Dict && return get(D, attr, default) @@ -240,6 +241,8 @@ function get_attribute!(G::Any, attr::Symbol, default::Any) return Base.get!(D, attr, default) end +# the following method is necessary to disambiguate between the above +# methods when the default value is a Symbol function get_attribute!(G::Any, attr::Symbol, default::Symbol) D = _get_attributes!(G) return Base.get!(D, attr, default) From 4df7adf13b305bfa892d99869108d9c40f343576 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Mar 2023 17:18:53 +0200 Subject: [PATCH 44/80] Fix spurious test failure in pseudo_inv (#1299) --- test/generic/Matrix-test.jl | 2 +- test/generic/MatrixAlgebra-test.jl | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/test/generic/Matrix-test.jl b/test/generic/Matrix-test.jl index efd78182cd..cbdc2bfec7 100644 --- a/test/generic/Matrix-test.jl +++ b/test/generic/Matrix-test.jl @@ -1543,7 +1543,7 @@ end c = rand(R, -10:10) while rank(c) != 2 - c = rand(R, -10:10) + c = rand(R, -10:10) end @test divexact_left(c*M, c) == M diff --git a/test/generic/MatrixAlgebra-test.jl b/test/generic/MatrixAlgebra-test.jl index 7a0a5da655..97cfc714ce 100644 --- a/test/generic/MatrixAlgebra-test.jl +++ b/test/generic/MatrixAlgebra-test.jl @@ -491,23 +491,28 @@ end S = MatrixAlgebra(R, 2) T = MatrixAlgebra(U, 2) - M = rand(S, -10:10) + for i = 1:50 + M = rand(S, -10:10) - @test divexact(5*M, 5) == M + @test divexact(5*M, 5) == M - c = rand(R, 1:10) # create a regular matrix - while rank(c) < nrows(c) - c = rand(R, 1:10) - end + c = rand(R, 1:10) # create a regular matrix + while rank(c) < nrows(c) + c = rand(R, 1:10) + end - @test divexact_left(c*M, c) == M - @test divexact_right(M*c, c) == M + @test divexact_left(c*M, c) == M + @test divexact_right(M*c, c) == M - N = rand(T, 0:5, -10:10) - d = rand(U, 0:5, -10:10) + N = rand(T, 0:5, -10:10) + d = rand(U, 0:5, -10:10) + while iszero(d) || rank(leading_coefficient(d)) != 2 + d = rand(U, 0:5, -10:10) + end - @test divexact_left(d*N, d) == N - @test divexact_right(N*d, d) == N + @test divexact_left(d*N, d) == N + @test divexact_right(N*d, d) == N + end end @testset "Generic.MatAlg.transpose" begin From f80136712d8eb706c03cae03881421bbf000c2bb Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Mar 2023 18:27:01 +0200 Subject: [PATCH 45/80] Rename poly_ring_type -> dense_poly_ring_type (#1300) * Rename poly_ring_type -> dense_poly_ring_type ... for consistency. We can rename it again in the future, but then together with `dense_poly_type`. * Add some cross references --- src/AbstractAlgebra.jl | 2 +- src/MPoly.jl | 4 ++++ src/NCPoly.jl | 16 ++++++++++------ test/generic/NCPoly-test.jl | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index fd6e2d31c1..d60e7777ae 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -777,6 +777,7 @@ export deflation export degree export degrees export dense_matrix_type +export dense_poly_ring_type export dense_poly_type export derivative export det @@ -997,7 +998,6 @@ export pol_length export polcoeff export poly export poly_ring -export poly_ring_type export PolyCoeffs export polynomial export polynomial_ring diff --git a/src/MPoly.jl b/src/MPoly.jl index a7572efa94..f11da1517b 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -23,6 +23,8 @@ coefficient_ring(R::MPolyRing) = base_ring(R) The type of multivariate polynomials with coefficients of type `T`. Falls back to `Generic.MPoly{T}`. +See also [`mpoly_ring_type`](@ref), [`dense_poly_type`](@ref) and [`dense_poly_ring_type`](@ref). + # Examples ```jldoctest; setup = :(using AbstractAlgebra) julia> mpoly_type(elem_type(AbstractAlgebra.ZZ)) @@ -43,6 +45,8 @@ mpoly_type(::Type{T}) where T = throw(ArgumentError("Type `$T` must be subtype o The type of multivariate polynomial rings with coefficients of type `T`. Implemented via [`mpoly_type`](@ref). +See also [`dense_poly_type`](@ref) and [`dense_poly_ring_type`](@ref). + # Examples ```jldoctest; setup = :(using AbstractAlgebra) julia> mpoly_ring_type(typeof(AbstractAlgebra.ZZ)) diff --git a/src/NCPoly.jl b/src/NCPoly.jl index 3b6e80ed77..8d975a7ec1 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -25,6 +25,8 @@ end The type of multivariate polynomials with coefficients of type `T`. Falls back to `Generic.NCPoly{T}` respectively `Generic.Poly{T}`. +See also [`dense_poly_ring_type`](@ref), [`mpoly_type`](@ref) and [`mpoly_ring_type`](@ref). + # Examples ```jldoctest; setup = :(using AbstractAlgebra) julia> dense_poly_type(elem_type(AbstractAlgebra.ZZ)) @@ -39,22 +41,24 @@ dense_poly_type(x) = dense_poly_type(typeof(x)) # to stop this method from etern dense_poly_type(::Type{T}) where T = throw(ArgumentError("Type `$T` must be subtype of `NCRingElement`.")) @doc md""" - poly_ring_type(::Type{T}) where T<:NCRing - poly_ring_type(::T) where T<:NCRing + dense_poly_ring_type(::Type{T}) where T<:NCRing + dense_poly_ring_type(::T) where T<:NCRing The type of polynomial rings with coefficients of type `T`. Implemented via [`dense_poly_type`](@ref). +See also [`mpoly_type`](@ref) and [`mpoly_ring_type`](@ref). + # Examples ```jldoctest; setup = :(using AbstractAlgebra) -julia> poly_ring_type(typeof(AbstractAlgebra.ZZ)) +julia> dense_poly_ring_type(typeof(AbstractAlgebra.ZZ)) AbstractAlgebra.Generic.PolyRing{BigInt} -julia> poly_ring_type(AbstractAlgebra.ZZ) +julia> dense_poly_ring_type(AbstractAlgebra.ZZ) AbstractAlgebra.Generic.PolyRing{BigInt} ``` """ -poly_ring_type(x) = parent_type(dense_poly_type(elem_type(x))) +dense_poly_ring_type(x) = parent_type(dense_poly_type(elem_type(x))) @doc Markdown.doc""" var(a::NCPolyRing) @@ -730,7 +734,7 @@ Like [`polynomial_ring(R::NCRing, s::Symbol)`](@ref) but return only the polynomial ring. """ polynomial_ring_only(R::T, s::Symbol; cached::Bool=true) where T<:NCRing = - poly_ring_type(T)(R, s, cached) + dense_poly_ring_type(T)(R, s, cached) # Simplified constructor diff --git a/test/generic/NCPoly-test.jl b/test/generic/NCPoly-test.jl index 944afc6ebb..4dd8ce56be 100644 --- a/test/generic/NCPoly-test.jl +++ b/test/generic/NCPoly-test.jl @@ -432,6 +432,6 @@ end @testset "Generic.NCPoly.exceptions" begin @test_throws MethodError polynomial_ring(Char, :x) - @test_throws Exception poly_ring_type(Char) + @test_throws Exception dense_poly_ring_type(Char) @test_throws ArgumentError dense_poly_type(Char) end From 68842fa5492e2d261325107ef69521b992e441e4 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Mar 2023 23:29:35 +0200 Subject: [PATCH 46/80] Version 0.28.4 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 011d293a27..e8a419c4eb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "AbstractAlgebra" uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" -version = "0.28.4-DEV" +version = "0.28.4" [deps] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" From 70601eb120a38833f970ca21a59f8a14a39a2bb2 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Mar 2023 23:29:49 +0200 Subject: [PATCH 47/80] Set DEV version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e8a419c4eb..f386fbd892 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "AbstractAlgebra" uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" -version = "0.28.4" +version = "0.28.5-DEV" [deps] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" From 20f9b7b5a44838ea4938b92bdf89760d9861c616 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 27 Mar 2023 10:05:15 +0200 Subject: [PATCH 48/80] Remove `get_field` and `set_field!` (#1265) There were meant to provide additional generality to maps in Oscar, and allow writing "more generic" code. In practice, as far as I can tell no public code outside of AbstractAlgebra ever used them. As to writing generic code: the idea, as I understand it (which may be wrong) seems to be that certain generic map implementation can use it to forward "arbitrary" map functionality, even beyond what they were made for. But the same could be done by adding explicit forwarding methods, as indeed this patch does for `domain` and `codomain`. This also means that not all map types must actually have fields `domain` and `codomain`, as was the case with the `get_field` mechanism. --- docs/src/map_interface.md | 36 ------------------------- src/AbstractAlgebra.jl | 2 -- src/Generic.jl | 3 +-- src/Map.jl | 11 +++++--- src/generic/GenericTypes.jl | 8 ++---- src/generic/Map.jl | 10 +++++-- src/generic/MapCache.jl | 4 +-- src/generic/ModuleHomomorphism.jl | 8 ++++++ test/generic/Map-test.jl | 6 ++--- test/generic/ModuleHomomorphism-test.jl | 5 ++++ 10 files changed, 35 insertions(+), 58 deletions(-) diff --git a/docs/src/map_interface.md b/docs/src/map_interface.md index d431a1ea85..33e157a087 100644 --- a/docs/src/map_interface.md +++ b/docs/src/map_interface.md @@ -90,42 +90,6 @@ if all features of the generic Map infrastructure are required. It is bad practi write functions for `MyMap` directly instead of `Map(MyMap)`, since other users will be unable to use generic constructions over the map type `MyMap`. -## Getters and setters - -When writing new map types, it is very important to define getters and setters of the -fields of the new map type, rather than to access them directly. - -Let us suppose that the `MyMap` type has a field called `foo`. Rather than access this -field by writing `M.foo`, one must access it using `foo(M)` (at least until Julia 1.1). - -If such a getter only needs to access the field `foo` of `M`, there is a standard way of -defining such a getter and setter when defining a new map type. - -```julia -foo(M::Map(MyMap)) = get_field(M, :foo) -``` - -To set a field of a map, one needs a setter, which can be implemented as follows: - -```julia -set_foo!(M::Map(MyMap), a) = set_field(M, :foo, a) -``` - -In general, setters should be used rarely for map types. - -!!! note - - By providing getter and setter functions, map types need not even contain - fields with the given name. For example, for a `MyMap` map type for maps - between integers, one does not wish to explicitly store the domain and - codomain in `MyMap`. Instead, we can define the getter functions `domain` - and `codomain` to return `JuliaZZ` for any `MyMap` object. - -```julia -domain(M::Map(MyMap)) = JuliaZZ -codomain(M::Map(MyMap)) = JuliaZZ -``` - ## Required functionality for maps All map types must implement a standard interface, which we specify here. diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index d60e7777ae..65a859dd54 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -827,7 +827,6 @@ export gen export gens export get_attribute export get_attribute! -export get_field export gram export has_attribute export has_bottom_neighbor @@ -1063,7 +1062,6 @@ export set_attribute! export set_coefficient! export set_exponent_vector! export set_exponent_word! -export set_field! export set_length! export set_limit! export set_precision! diff --git a/src/Generic.jl b/src/Generic.jl index 2e7ff27e8b..991de388c0 100644 --- a/src/Generic.jl +++ b/src/Generic.jl @@ -165,9 +165,9 @@ import ..AbstractAlgebra: FieldElement import ..AbstractAlgebra: gen import ..AbstractAlgebra: gens import ..AbstractAlgebra: get_cached! -import ..AbstractAlgebra: get_field import ..AbstractAlgebra: GFElem import ..AbstractAlgebra: identity_matrix +import ..AbstractAlgebra: image_fn import ..AbstractAlgebra: inflate import ..AbstractAlgebra: Integers import ..AbstractAlgebra: integral @@ -224,7 +224,6 @@ import ..AbstractAlgebra: Ring import ..AbstractAlgebra: RingElem import ..AbstractAlgebra: RingElement import ..AbstractAlgebra: set_coefficient! -import ..AbstractAlgebra: set_field! import ..AbstractAlgebra: set_length! import ..AbstractAlgebra: set_precision! import ..AbstractAlgebra: set_valuation! diff --git a/src/Map.jl b/src/Map.jl index b14a282877..68fabc4b46 100644 --- a/src/Map.jl +++ b/src/Map.jl @@ -13,11 +13,14 @@ Map(::Type{T}) where T <: Map = supertype(T) Map(::Type{S}) where S <: SetMap = Map{D, C, <:S, T} where {D, C, T} -get_field(M, f) = getfield(M, f) # fall back to Julia builtin -set_field!(M, f, g) = setfield!(M, f, g) # fall back to Julia builtin +function domain end +function codomain end +function image_fn end + +domain(f::Map) = f.domain # fallback, FIXME: to be removed eventually +codomain(f::Map) = f.codomain # fallback, FIXME: to be removed eventually +image_fn(f::Map) = f.image_fn # fallback, FIXME: to be removed eventually -domain(f::Map) = get_field(f, :domain) -codomain(f::Map) = get_field(f, :codomain) function check_composable(a::Map, b::Map) codomain(a) != domain(b) && error("Incompatible maps") diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index ba9b071189..cd78449782 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -1161,13 +1161,11 @@ end ############################################################################### mutable struct CompositeMap{D, C} <: AbstractAlgebra.Map{D, C, AbstractAlgebra.SetMap, CompositeMap} - domain::D - codomain::C map1::AbstractAlgebra.Map map2::AbstractAlgebra.Map function CompositeMap(map1, map2) - return new{typeof(domain(map1)), typeof(codomain(map2))}(domain(map1), codomain(map2), map1, map2) + return new{typeof(domain(map1)), typeof(codomain(map2))}(map1, map2) end end @@ -1200,14 +1198,12 @@ end ############################################################################### mutable struct FunctionalCompositeMap{D, C} <: AbstractAlgebra.Map{D, C, AbstractAlgebra.FunctionalMap, FunctionalCompositeMap} - domain::D - codomain::C map1::AbstractAlgebra.Map map2::AbstractAlgebra.Map fn_cache::Function function FunctionalCompositeMap(map1::Map(AbstractAlgebra.FunctionalMap){D, U}, map2::Map(AbstractAlgebra.FunctionalMap){U, C}) where {D, U, C} - return new{D, C}(domain(map1), codomain(map2), map1, map2) + return new{D, C}(map1, map2) end end diff --git a/src/generic/Map.jl b/src/generic/Map.jl index 6ae19c3886..09a1d3b3bb 100644 --- a/src/generic/Map.jl +++ b/src/generic/Map.jl @@ -41,7 +41,8 @@ end (f::IdentityMap)(a) = a -codomain(f::AbstractAlgebra.Map(AbstractAlgebra.IdentityMap){D, C}) where {D, C} = domain(f) +domain(f::IdentityMap) = f.domain +codomain(f::IdentityMap) = f.domain function show(io::IO, M::IdentityMap) println(io, "Identity map with") @@ -74,7 +75,9 @@ Base.inv(f::AbstractAlgebra.Map(AbstractAlgebra.IdentityMap)) = f # ################################################################################ -image_fn(f::AbstractAlgebra.Map(AbstractAlgebra.FunctionalMap)) = get_field(f, :image_fn) +domain(f::FunctionalMap) = f.domain +codomain(f::FunctionalMap) = f.codomain +image_fn(f::FunctionalMap) = f.image_fn function (f::FunctionalMap{D, C})(a) where {D, C} parent(a) != domain(f) && throw(DomainError(f)) @@ -99,6 +102,9 @@ end # ################################################################################ +domain(f::FunctionalCompositeMap) = domain(f.map1) +codomain(f::FunctionalCompositeMap) = codomain(f.map2) + # This is a device to prevent Julia trying to compute # the types of a very long composition of closures struct UntypedFunction <: Function diff --git a/src/generic/MapCache.jl b/src/generic/MapCache.jl index 396a1d9cda..16afdb377e 100644 --- a/src/generic/MapCache.jl +++ b/src/generic/MapCache.jl @@ -4,11 +4,9 @@ # ################################################################################ -get_field(M::MapCache, f) = getfield(M.map, f) # pass accessors through -set_field!(M::MapCache, f) = setfield(M.map, f) # pass accessors through - domain(M::MapCache{D, C}) where {D, C} = domain(M.map)::D codomain(M::MapCache{D, C}) where {D, C} = codomain(M.map)::C +image_fn(M::MapCache{D, C}) where {D, C} = image_fn(M.map) function set_limit!(M::MapCache, limit::Int) limit < 0 && error("Limit must be nonnegative") diff --git a/src/generic/ModuleHomomorphism.jl b/src/generic/ModuleHomomorphism.jl index 6633000669..4aae8af50b 100644 --- a/src/generic/ModuleHomomorphism.jl +++ b/src/generic/ModuleHomomorphism.jl @@ -10,6 +10,14 @@ # ############################################################################### +domain(f::ModuleHomomorphism) = f.domain +codomain(f::ModuleHomomorphism) = f.codomain +image_fn(f::ModuleHomomorphism) = f.image_fn + +domain(f::ModuleIsomorphism) = f.domain +codomain(f::ModuleIsomorphism) = f.codomain +image_fn(f::ModuleIsomorphism) = f.image_fn + inverse_mat(f::Map(ModuleIsomorphism)) = f.inverse_matrix inverse_image_fn(f::Map(ModuleIsomorphism)) = f.inverse_image_fn diff --git a/test/generic/Map-test.jl b/test/generic/Map-test.jl index 493546384f..fe7bbcb4d7 100644 --- a/test/generic/Map-test.jl +++ b/test/generic/Map-test.jl @@ -2,10 +2,10 @@ mutable struct MyMap <: Map{AbstractAlgebra.Integers{BigInt}, AbstractAlgebra.In a::Int end -Generic.domain(f::Map(MyMap)) = AbstractAlgebra.JuliaZZ -Generic.codomain(f::Map(MyMap)) = AbstractAlgebra.JuliaZZ +Generic.domain(f::MyMap) = AbstractAlgebra.JuliaZZ +Generic.codomain(f::MyMap) = AbstractAlgebra.JuliaZZ -a(f::Map(MyMap)) = Generic.get_field(f, :a) +a(f::MyMap) = f.a (f::MyMap)(x) = a(f)*(x + 1) diff --git a/test/generic/ModuleHomomorphism-test.jl b/test/generic/ModuleHomomorphism-test.jl index 6f1f304819..0109b9307d 100644 --- a/test/generic/ModuleHomomorphism-test.jl +++ b/test/generic/ModuleHomomorphism-test.jl @@ -3,6 +3,9 @@ f = ModuleHomomorphism(M, M, matrix(ZZ, 2, 2, [1, 2, 3, 4])) + @test domain(f) == M + @test codomain(f) == M + @test isa(f, Generic.Map(FunctionalMap)) m = M([ZZ(1), ZZ(2)]) @@ -119,6 +122,8 @@ end N = randmat_with_rank(S, n, -10:10) f = ModuleIsomorphism(M, M, N) + @test domain(f) == M + @test codomain(f) == M @test mat(f) == N @test N*inverse_mat(f) == 1 From dfe937136582aeb1b49b1c63459a735f7819377f Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 27 Mar 2023 10:12:13 +0200 Subject: [PATCH 49/80] Enhance *poly*_type methods (#1303) They all now accept both rings, ring elements, or their types. Also fix the docstrings --- src/MPoly.jl | 33 +++++++++++++++++++++++++-------- src/NCPoly.jl | 33 +++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index f11da1517b..1b2d44b14d 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -19,44 +19,61 @@ coefficient_ring(R::MPolyRing) = base_ring(R) @doc md""" mpoly_type(::Type{T}) where T<:RingElement mpoly_type(::T) where T<:RingElement + mpoly_type(::Type{S}) where S<:Ring + mpoly_type(::S) where S<:Ring -The type of multivariate polynomials with coefficients of type `T`. +The type of multivariate polynomials with coefficients of type `T` respectively `elem_type(S)`. Falls back to `Generic.MPoly{T}`. See also [`mpoly_ring_type`](@ref), [`dense_poly_type`](@ref) and [`dense_poly_ring_type`](@ref). # Examples ```jldoctest; setup = :(using AbstractAlgebra) +julia> mpoly_type(AbstractAlgebra.ZZ(1)) +AbstractAlgebra.Generic.MPoly{BigInt} + julia> mpoly_type(elem_type(AbstractAlgebra.ZZ)) AbstractAlgebra.Generic.MPoly{BigInt} -julia> mpoly_type(AbstractAlgebra.ZZ(1)) +julia> mpoly_type(AbstractAlgebra.ZZ) +AbstractAlgebra.Generic.MPoly{BigInt} + +julia> mpoly_type(typeof(AbstractAlgebra.ZZ)) AbstractAlgebra.Generic.MPoly{BigInt} ``` """ mpoly_type(::Type{T}) where T<:RingElement = Generic.MPoly{T} +mpoly_type(::Type{S}) where S<:Ring = mpoly_type(elem_type(S)) mpoly_type(x) = mpoly_type(typeof(x)) # to stop this method from eternally recursing on itself, we better add ... mpoly_type(::Type{T}) where T = throw(ArgumentError("Type `$T` must be subtype of `RingElement`.")) @doc md""" - mpoly_ring_type(::Type{T}) where T<:Ring - mpoly_ring_type(::T) where T<:Ring + mpoly_ring_type(::Type{T}) where T<:RingElement + mpoly_ring_type(::T) where T<:RingElement + mpoly_ring_type(::Type{S}) where S<:Ring + mpoly_ring_type(::S) where S<:Ring -The type of multivariate polynomial rings with coefficients of type `T`. -Implemented via [`mpoly_type`](@ref). +The type of multivariate polynomial rings with coefficients of type `T` +respectively `elem_type(S)`. Implemented via [`mpoly_type`](@ref). See also [`dense_poly_type`](@ref) and [`dense_poly_ring_type`](@ref). # Examples ```jldoctest; setup = :(using AbstractAlgebra) -julia> mpoly_ring_type(typeof(AbstractAlgebra.ZZ)) +julia> mpoly_ring_type(AbstractAlgebra.ZZ(1)) +AbstractAlgebra.Generic.MPolyRing{BigInt} + +julia> mpoly_ring_type(elem_type(AbstractAlgebra.ZZ)) AbstractAlgebra.Generic.MPolyRing{BigInt} julia> mpoly_ring_type(AbstractAlgebra.ZZ) AbstractAlgebra.Generic.MPolyRing{BigInt} + +julia> mpoly_ring_type(typeof(AbstractAlgebra.ZZ)) +AbstractAlgebra.Generic.MPolyRing{BigInt} ``` """ -mpoly_ring_type(x) = parent_type(mpoly_type(elem_type(x))) +mpoly_ring_type(x) = parent_type(mpoly_type(x)) function is_domain_type(::Type{T}) where {S <: RingElement, T <: AbstractAlgebra.MPolyRingElem{S}} return is_domain_type(S) diff --git a/src/NCPoly.jl b/src/NCPoly.jl index 8d975a7ec1..e1b8f467a1 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -21,44 +21,61 @@ end @doc md""" dense_poly_type(::Type{T}) where T<:NCRingElement dense_poly_type(::T) where T<:NCRingElement + dense_poly_type(::Type{S}) where S<:NCRing + dense_poly_type(::S) where S<:NCRing -The type of multivariate polynomials with coefficients of type `T`. +The type of univariate polynomials with coefficients of type `T` respectively `elem_type(S)`. Falls back to `Generic.NCPoly{T}` respectively `Generic.Poly{T}`. See also [`dense_poly_ring_type`](@ref), [`mpoly_type`](@ref) and [`mpoly_ring_type`](@ref). # Examples ```jldoctest; setup = :(using AbstractAlgebra) +julia> dense_poly_type(AbstractAlgebra.ZZ(1)) +AbstractAlgebra.Generic.Poly{BigInt} + julia> dense_poly_type(elem_type(AbstractAlgebra.ZZ)) AbstractAlgebra.Generic.Poly{BigInt} -julia> dense_poly_type(AbstractAlgebra.ZZ(1)) +julia> dense_poly_type(AbstractAlgebra.ZZ) +AbstractAlgebra.Generic.Poly{BigInt} + +julia> dense_poly_type(typeof(AbstractAlgebra.ZZ)) AbstractAlgebra.Generic.Poly{BigInt} ``` """ dense_poly_type(::Type{T}) where T<:NCRingElement = Generic.NCPoly{T} +dense_poly_type(::Type{S}) where S<:NCRing = dense_poly_type(elem_type(S)) dense_poly_type(x) = dense_poly_type(typeof(x)) # to stop this method from eternally recursing on itself, we better add ... dense_poly_type(::Type{T}) where T = throw(ArgumentError("Type `$T` must be subtype of `NCRingElement`.")) @doc md""" - dense_poly_ring_type(::Type{T}) where T<:NCRing - dense_poly_ring_type(::T) where T<:NCRing + dense_poly_ring_type(::Type{T}) where T<:NCRingElement + dense_poly_ring_type(::T) where T<:NCRingElement + dense_poly_ring_type(::Type{S}) where S<:NCRing + dense_poly_ring_type(::S) where S<:NCRing -The type of polynomial rings with coefficients of type `T`. -Implemented via [`dense_poly_type`](@ref). +The type of univariate polynomial rings with coefficients of type `T` respectively +`elem_type(S)`. Implemented via [`dense_poly_type`](@ref). See also [`mpoly_type`](@ref) and [`mpoly_ring_type`](@ref). # Examples ```jldoctest; setup = :(using AbstractAlgebra) -julia> dense_poly_ring_type(typeof(AbstractAlgebra.ZZ)) +julia> dense_poly_ring_type(AbstractAlgebra.ZZ(1)) +AbstractAlgebra.Generic.PolyRing{BigInt} + +julia> dense_poly_ring_type(elem_type(AbstractAlgebra.ZZ)) AbstractAlgebra.Generic.PolyRing{BigInt} julia> dense_poly_ring_type(AbstractAlgebra.ZZ) AbstractAlgebra.Generic.PolyRing{BigInt} + +julia> dense_poly_ring_type(typeof(AbstractAlgebra.ZZ)) +AbstractAlgebra.Generic.PolyRing{BigInt} ``` """ -dense_poly_ring_type(x) = parent_type(dense_poly_type(elem_type(x))) +dense_poly_ring_type(x) = parent_type(dense_poly_type(x)) @doc Markdown.doc""" var(a::NCPolyRing) From 817d467c8d9d9dfc876bb8f3a09206f701b2215e Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 5 May 2022 15:28:40 +0200 Subject: [PATCH 50/80] add some Groebner machinery for FreeAssociativeAlgebra --- src/AbstractAlgebra.jl | 1 + src/Generic.jl | 1 - src/generic/FreeAssAlgebraGroebner.jl | 5 +++- test/generic/FreeAssAlgebraGroebner-test.jl | 33 +++++++++++---------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index 65a859dd54..e929fc776f 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -593,6 +593,7 @@ include("Generic.jl") # Do not import div, divrem, exp, inv, log, sqrt, numerator and denominator # as we have our own + import .Generic: @perm_str import .Generic: abs_series_type import .Generic: base_field diff --git a/src/Generic.jl b/src/Generic.jl index 991de388c0..05a34617ce 100644 --- a/src/Generic.jl +++ b/src/Generic.jl @@ -326,7 +326,6 @@ include("generic/AhoCorasick.jl") include("generic/FreeAssAlgebraGroebner.jl") - ############################################################################### # # Temporary miscellaneous files being moved from Hecke.jl diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 11e708b27d..440bb9403c 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -223,6 +223,7 @@ end # the return vector is of the form [(w_2, w_1^'), ...] # if w_1^' or w_2 is empty, the corresponding obstruction is not returned function right_obstructions(a::Vector{Int}, b::Vector{Int}) + return left_obstructions(b, a) end @@ -613,9 +614,11 @@ Markdown.@doc doc""" Compute a groebner basis for the ideal spanned by g. Stop when `reduction_bound` many non-zero entries have been added to the groebner basis. -""" function groebner_basis( +""" +function groebner_basis( g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int, ) where {T<:FieldElement} return groebner_basis_buchberger(g, reduction_bound) end + diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index a6478cf8d2..dceb9ea661 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -1,22 +1,22 @@ include("AhoCorasick-test.jl") using AbstractAlgebra.Generic: AhoCorasickAutomaton @testset "Generic.FreeAssAlgebra.groebner" begin - - R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) - g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, - (y*x)^3*t + (y*x)^2*t + t + s]) - @test length(g) >= 5 - - # Example 6.1 Kreuzer & Xiu - R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) - - g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) - AbstractAlgebra.interreduce!(g) - @test length(g) == 5 - - g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) - AbstractAlgebra.interreduce!(g) - @test length(g) == 15 + + R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) + g = AbstractAlgebra.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, + (y*x)^3*t + (y*x)^2*t + t + s]) + @test length(g) >= 5 + + # Example 6.1 Kreuzer & Xiu + R, (a, b) = FreeAssociativeAlgebra(QQ, ["a", "b"]) + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 5 + + g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) + AbstractAlgebra.interreduce!(g) + @test length(g) == 15 end @testset "Generic.FreeAssociativeAlgebra.groebner.normal_form" begin @@ -38,3 +38,4 @@ end # @test gb_divides_leftmost((x*u*y*t*(s*t - t*s)).exps[1], aut) == (true, [1], [5, 6], 2) # @test gb_divides_leftmost((x*s*t).exps[1], aut) == (false, [], [], -1) end + From a964c18681ab005bb76241c28b5db430fc29f0ce Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 26 May 2022 21:21:01 +0200 Subject: [PATCH 51/80] replaced the divides_leftmost substring check with a kmp variant for asymptotic linear running time instead of quadratic and replaced the storage of obstructions by a priority queue to remove unnecessary running time there --- Project.toml | 2 ++ src/generic/FreeAssAlgebraGroebner.jl | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f386fbd892..9fe7a33fd7 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,8 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" +Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" +ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RandomExtensions = "fb686558-2515-59ef-acaa-46db3789a887" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 440bb9403c..d86c50317a 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -576,7 +576,6 @@ function groebner_basis_buchberger( g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int, ) where {T<:FieldElement} - g = copy(g) # interreduce!(g) # on some small examples, this increases running time, so it might not be optimal to use this here checked_obstructions = 0 From 8e3cae3c5b5e8e5a861cfd1c518bdd4be58c4c07 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 18 Jul 2022 14:51:12 +0200 Subject: [PATCH 52/80] cleaned the code a bit --- src/generic/FreeAssAlgebraGroebner.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index d86c50317a..0ea524eac0 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -3,7 +3,7 @@ # FreeAssAlgebraGroebner.jl : free associative algebra R Groebner basis # ############################################################################### - +# export groebner_basis, interreduce!, normal_form, normal_form_weak #using DataStructures From e9d34e70b2381493a1296bf6b1d6dc8a147f64aa Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 20 Oct 2022 12:50:49 +0200 Subject: [PATCH 53/80] removed unnecessary dependencies and comments --- Project.toml | 2 - src/generic/FreeAssAhoCorasick.jl | 143 ++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 src/generic/FreeAssAhoCorasick.jl diff --git a/Project.toml b/Project.toml index 9fe7a33fd7..f386fbd892 100644 --- a/Project.toml +++ b/Project.toml @@ -9,8 +9,6 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" -Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" -ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RandomExtensions = "fb686558-2515-59ef-acaa-46db3789a887" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl new file mode 100644 index 0000000000..5edf82f979 --- /dev/null +++ b/src/generic/FreeAssAhoCorasick.jl @@ -0,0 +1,143 @@ +# +# FreeAssAhoCorasick.jl : implement bulk divide check for leading terms of free associative Algebra elements +# for use e.g. in Groebner Basis computation +# +############################################################################### + +export search, AhoCorasickAutomaton, AhoCorasickMatch, Word, insert_keyword! + +using DataStructures + +const Word = Vector{Int} + +""" +Output stores for each node a tuple (i, k), where i is the index of the keyword k in +the original list of keywords. If several keywords would be the output of the node, only +the one with the smallest index is stored +""" +mutable struct AhoCorasickAutomaton + goto::Vector{Dict{Int, Int}} + fail::Vector{Int} + output::Vector{Tuple{Int, Word}} +end + +struct AhoCorasickMatch + last_position::Int + keyword_index::Int + keyword::Word +end + +Base.hash(m::AhoCorasickMatch) = hash(m.last_position, hash(m.keyword_index, hash(m.keyword))) +function ==(m1::AhoCorasickMatch, m2::AhoCorasickMatch) + return m1.last_position == m2.last_position && m1.keyword_index == m2.keyword_index && m1.keyword == m2.keyword +end + +function AhoCorasickAutomaton(keywords::Vector{Word}) + automaton = AhoCorasickAutomaton([], [], []) + construct_goto!(automaton, keywords) + construct_fail!(automaton) + return automaton +end + +function lookup(automaton::AhoCorasickAutomaton, current_state::Int, next_letter::Int) + ret_value = get(automaton.goto[current_state], next_letter, nothing) + if current_state == 1 && isnothing(ret_value) + return 1 + end + return ret_value + +end + + +function Base.length(automaton::AhoCorasickAutomaton) + return length(automaton.goto) +end + + + +function new_state!(automaton) + push!(automaton.goto, Dict{Int, Int}()) + push!(automaton.output, (typemax(Int), [])) + push!(automaton.fail, 1) + return length(automaton.goto) +end + +function enter!(automaton::AhoCorasickAutomaton, keyword::Word, current_index) + current_state = 1 + for c in keyword + new_state = get(automaton.goto[current_state], c, nothing) + if isnothing(new_state) + new_state = new_state!(automaton) + automaton.goto[current_state][c] = new_state + end + current_state = new_state + end + if automaton.output[current_state][1] > current_index + automaton.output[current_state] = (current_index, keyword) + end +end + +function construct_goto!(automaton::AhoCorasickAutomaton, keywords::Vector{Word}) + new_state!(automaton) + current_index = 1 + for keyword in keywords + enter!(automaton, keyword, current_index) + current_index += 1 + end +end + +function construct_fail!(automaton::AhoCorasickAutomaton) + q = Queue{Int}() + for v in values(automaton.goto[1]) + enqueue!(q, v) + end + while !isempty(q) + current_state = dequeue!(q) + for k in keys(automaton.goto[current_state]) + new_state = lookup(automaton, current_state, k) + enqueue!(q, new_state) + state = automaton.fail[current_state] + while isnothing(lookup(automaton, state, k)) + state = automaton.fail[state] + end + automaton.fail[new_state] = lookup(automaton, state, k) + if automaton.output[new_state][1] > automaton.output[automaton.fail[new_state]][1] + automaton.output[new_state] = automaton.output[automaton.fail[new_state]] # TODO check if this is the correct way to update output + end + + end + end +end + +function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) + enter!(aut, keyword, index) + aut.fail = ones(Int, length(aut.goto)) + construct_fail!(aut) +end + +""" + +""" +function search(automaton::AhoCorasickAutomaton, word) + current_state = 1 + result = AhoCorasickMatch(typemax(Int), typemax(Int), []) + for i in 1:length(word) + c = word[i] + while true + next_state = lookup(automaton, current_state, c) + if !isnothing(next_state) + current_state = next_state + break + else + current_state = automaton.fail[current_state] + end + end + if automaton.output[current_state][1] < result.keyword_index + result = AhoCorasickMatch(i, automaton.output[current_state][1], automaton.output[current_state][2]) + end + end + if result.keyword == [] + return nothing + end + return result +end From d74b254fdc0d32e9e6ca9fec8c865da4a8b0e9b2 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 14 Mar 2023 16:02:06 +0100 Subject: [PATCH 54/80] integrated changes from the big renaming --- src/generic/FreeAssAhoCorasick.jl | 143 ----------------------------- src/generic/GenericTypes.jl | 12 +++ test/runtests_free_ass_groebner.jl | 9 ++ 3 files changed, 21 insertions(+), 143 deletions(-) delete mode 100644 src/generic/FreeAssAhoCorasick.jl create mode 100644 test/runtests_free_ass_groebner.jl diff --git a/src/generic/FreeAssAhoCorasick.jl b/src/generic/FreeAssAhoCorasick.jl deleted file mode 100644 index 5edf82f979..0000000000 --- a/src/generic/FreeAssAhoCorasick.jl +++ /dev/null @@ -1,143 +0,0 @@ -# -# FreeAssAhoCorasick.jl : implement bulk divide check for leading terms of free associative Algebra elements -# for use e.g. in Groebner Basis computation -# -############################################################################### - -export search, AhoCorasickAutomaton, AhoCorasickMatch, Word, insert_keyword! - -using DataStructures - -const Word = Vector{Int} - -""" -Output stores for each node a tuple (i, k), where i is the index of the keyword k in -the original list of keywords. If several keywords would be the output of the node, only -the one with the smallest index is stored -""" -mutable struct AhoCorasickAutomaton - goto::Vector{Dict{Int, Int}} - fail::Vector{Int} - output::Vector{Tuple{Int, Word}} -end - -struct AhoCorasickMatch - last_position::Int - keyword_index::Int - keyword::Word -end - -Base.hash(m::AhoCorasickMatch) = hash(m.last_position, hash(m.keyword_index, hash(m.keyword))) -function ==(m1::AhoCorasickMatch, m2::AhoCorasickMatch) - return m1.last_position == m2.last_position && m1.keyword_index == m2.keyword_index && m1.keyword == m2.keyword -end - -function AhoCorasickAutomaton(keywords::Vector{Word}) - automaton = AhoCorasickAutomaton([], [], []) - construct_goto!(automaton, keywords) - construct_fail!(automaton) - return automaton -end - -function lookup(automaton::AhoCorasickAutomaton, current_state::Int, next_letter::Int) - ret_value = get(automaton.goto[current_state], next_letter, nothing) - if current_state == 1 && isnothing(ret_value) - return 1 - end - return ret_value - -end - - -function Base.length(automaton::AhoCorasickAutomaton) - return length(automaton.goto) -end - - - -function new_state!(automaton) - push!(automaton.goto, Dict{Int, Int}()) - push!(automaton.output, (typemax(Int), [])) - push!(automaton.fail, 1) - return length(automaton.goto) -end - -function enter!(automaton::AhoCorasickAutomaton, keyword::Word, current_index) - current_state = 1 - for c in keyword - new_state = get(automaton.goto[current_state], c, nothing) - if isnothing(new_state) - new_state = new_state!(automaton) - automaton.goto[current_state][c] = new_state - end - current_state = new_state - end - if automaton.output[current_state][1] > current_index - automaton.output[current_state] = (current_index, keyword) - end -end - -function construct_goto!(automaton::AhoCorasickAutomaton, keywords::Vector{Word}) - new_state!(automaton) - current_index = 1 - for keyword in keywords - enter!(automaton, keyword, current_index) - current_index += 1 - end -end - -function construct_fail!(automaton::AhoCorasickAutomaton) - q = Queue{Int}() - for v in values(automaton.goto[1]) - enqueue!(q, v) - end - while !isempty(q) - current_state = dequeue!(q) - for k in keys(automaton.goto[current_state]) - new_state = lookup(automaton, current_state, k) - enqueue!(q, new_state) - state = automaton.fail[current_state] - while isnothing(lookup(automaton, state, k)) - state = automaton.fail[state] - end - automaton.fail[new_state] = lookup(automaton, state, k) - if automaton.output[new_state][1] > automaton.output[automaton.fail[new_state]][1] - automaton.output[new_state] = automaton.output[automaton.fail[new_state]] # TODO check if this is the correct way to update output - end - - end - end -end - -function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) - enter!(aut, keyword, index) - aut.fail = ones(Int, length(aut.goto)) - construct_fail!(aut) -end - -""" - -""" -function search(automaton::AhoCorasickAutomaton, word) - current_state = 1 - result = AhoCorasickMatch(typemax(Int), typemax(Int), []) - for i in 1:length(word) - c = word[i] - while true - next_state = lookup(automaton, current_state, c) - if !isnothing(next_state) - current_state = next_state - break - else - current_state = automaton.fail[current_state] - end - end - if automaton.output[current_state][1] < result.keyword_index - result = AhoCorasickMatch(i, automaton.output[current_state][1], automaton.output[current_state][2]) - end - end - if result.keyword == [] - return nothing - end - return result -end diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index cd78449782..42a1e827ac 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -1154,6 +1154,18 @@ struct FreeAssAlgExponentWords{T <: AbstractAlgebra.NCRingElem} poly::T end +############################################################################### +# +# AhoCorasickAutomaton +# +############################################################################### + +mutable struct AhoCorasickAutomaton + goto::Vector{Dict{Int, Int}} + fail::Vector{Int} + output::Vector{Tuple{Int, Vector{Int}}} +end + ############################################################################### # # CompositeMap diff --git a/test/runtests_free_ass_groebner.jl b/test/runtests_free_ass_groebner.jl new file mode 100644 index 0000000000..2e6e122964 --- /dev/null +++ b/test/runtests_free_ass_groebner.jl @@ -0,0 +1,9 @@ +using Pkg +Pkg.activate("/home/julien/.julia/dev/AbstractAlgebra/") +using AbstractAlgebra +using AbstractAlgebra.Generic +using Test +include("generic/AhoCorasick-test.jl") + + +include("generic/FreeAssAlgebraGroebner-test.jl") From 6739af9a1c61c28a8ecc472be10b5ced8612ff4e Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 10 Mar 2023 14:12:56 +0100 Subject: [PATCH 55/80] added docstrings to all exported functions in AhoCorasick.jl and FreeAssAlgebraGroebner.jl, also renamed FreeAssAhoCorasick.jl to AhoCorasick.jl --- src/generic/AhoCorasick.jl | 3 --- src/generic/FreeAssAlgebraGroebner.jl | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 5323638c1a..f2ae5e29c4 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -4,7 +4,6 @@ # ############################################################################### - export search, AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton, AhoCorasickMatch#, Word @@ -24,8 +23,6 @@ julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] julia> aut = aho_corasick_automaton(keywords) julia> search(aut, [10, 4, 1, 2, 3, 4]) AhoCorasickMatch(6, 1, [1, 2, 3, 4]) - - ``` """ mutable struct AhoCorasickAutomaton goto::Vector{Dict{Int,Int}} diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 0ea524eac0..d86c50317a 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -3,7 +3,7 @@ # FreeAssAlgebraGroebner.jl : free associative algebra R Groebner basis # ############################################################################### -# + export groebner_basis, interreduce!, normal_form, normal_form_weak #using DataStructures From 344f3c6ee2390f50f972854c4f630dc936138274 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 17 Mar 2023 14:19:59 +0100 Subject: [PATCH 56/80] added documentation of the groebner basis function and aho corasick automata in the free associative algebra documentation --- src/generic/GenericTypes.jl | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 42a1e827ac..cd78449782 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -1154,18 +1154,6 @@ struct FreeAssAlgExponentWords{T <: AbstractAlgebra.NCRingElem} poly::T end -############################################################################### -# -# AhoCorasickAutomaton -# -############################################################################### - -mutable struct AhoCorasickAutomaton - goto::Vector{Dict{Int, Int}} - fail::Vector{Int} - output::Vector{Tuple{Int, Vector{Int}}} -end - ############################################################################### # # CompositeMap From df7c3e1754c867544aed09d540d3ad08de3b3980 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Wed, 22 Mar 2023 16:00:25 +0100 Subject: [PATCH 57/80] incorporated remarks by Max Horn --- src/generic/AhoCorasick.jl | 1 - src/generic/FreeAssAlgebra.jl | 2 +- src/generic/FreeAssAlgebraGroebner.jl | 4 ---- test/runtests_free_ass_groebner.jl | 9 --------- 4 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 test/runtests_free_ass_groebner.jl diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index f2ae5e29c4..dec4fb055e 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -6,7 +6,6 @@ export search, AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton, AhoCorasickMatch#, Word - import DataStructures.Queue, DataStructures.enqueue!, DataStructures.dequeue! const Word = Vector{Int} diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 919c2397e6..36e9184065 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -99,7 +99,7 @@ function gen(a::FreeAssAlgebra{T}, i::Int) where {T} end function gens(a::FreeAssAlgebra{T}) where {T<:RingElement} - return [gen(a, i) for i = 1:nvars(a)] + return [gen(a, i) for i = 1:ngens(a)] end function is_gen(a::FreeAssAlgElem{T}) where {T} diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index d86c50317a..f71a7fbd56 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -223,7 +223,6 @@ end # the return vector is of the form [(w_2, w_1^'), ...] # if w_1^' or w_2 is empty, the corresponding obstruction is not returned function right_obstructions(a::Vector{Int}, b::Vector{Int}) - return left_obstructions(b, a) end @@ -374,13 +373,11 @@ function has_overlap(g1, g2, w1, w2, u1, u2) return length(w2) < length(lw2) + length(u2) end - function has_overlap(g2, w2, u2) lw2 = _leading_word(g2) return length(w2) < length(lw2) + length(u2) end - function has_overlap(obs::ObstructionTriple{T}) where {T} return has_overlap(obs.second_poly, obs.pre_and_suffixes[2], obs.pre_and_suffixes[4]) end @@ -620,4 +617,3 @@ function groebner_basis( ) where {T<:FieldElement} return groebner_basis_buchberger(g, reduction_bound) end - diff --git a/test/runtests_free_ass_groebner.jl b/test/runtests_free_ass_groebner.jl deleted file mode 100644 index 2e6e122964..0000000000 --- a/test/runtests_free_ass_groebner.jl +++ /dev/null @@ -1,9 +0,0 @@ -using Pkg -Pkg.activate("/home/julien/.julia/dev/AbstractAlgebra/") -using AbstractAlgebra -using AbstractAlgebra.Generic -using Test -include("generic/AhoCorasick-test.jl") - - -include("generic/FreeAssAlgebraGroebner-test.jl") From 6393fc5d9445cb00fc29d1a4a0a2aa096506c590 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 27 Mar 2023 14:28:16 +0200 Subject: [PATCH 58/80] added the importing of priority queue enqueueing and dequeueing in FreeAssAlgebraGroebner.jl --- src/generic/FreeAssAlgebraGroebner.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index f71a7fbd56..aa0ec9b543 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -6,8 +6,7 @@ export groebner_basis, interreduce!, normal_form, normal_form_weak -#using DataStructures -import DataStructures.PriorityQueue +import DataStructures.PriorityQueue, DataStructures.enqueue!, DataStructures.dequeue! const groebner_debug_level = 0 const Monomial = Vector{Int} From c0e094d05b78721998b2270042d69527e9d883d0 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 27 Mar 2023 16:31:17 +0200 Subject: [PATCH 59/80] fixed the docstrings and the export/import statements --- docs/src/free_associative_algebra.md | 6 ++- src/generic/AhoCorasick.jl | 65 +++++++++++++++++---------- src/generic/FreeAssAlgebraGroebner.jl | 28 +++++++----- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index 994cd054ae..b295b2cf96 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -245,8 +245,10 @@ julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y In order to check whether a given element of the algebra is in the ideal generated by a Groebner basis `g`, one can compute its normal form. ```jldoctest; setup = :(using AbstractAlgebra) -julia> R, (x, y, u, v, t, s) = free_associative_algebra(GF(2), ["x", "y", "u", "v", "t", "s"]) -julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) +julia> R, (x, y, u, v, t, s) = free_associative_algebra(GF(2), ["x", "y", "u", "v", "t", "s"]); + +julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]); + julia> normal_form(u*(x*y)^3*s*t + u*(x*y)^2*s*t +u*s*t + v*s*t, g) 0 ``` diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index dec4fb055e..182d7cede9 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -4,26 +4,39 @@ # ############################################################################### -export search, - AhoCorasickAutomaton, insert_keyword!, aho_corasick_automaton, AhoCorasickMatch#, Word -import DataStructures.Queue, DataStructures.enqueue!, DataStructures.dequeue! +export aho_corasick_automaton + +export AhoCorasickAutomaton + +export AhoCorasickMatch + +export insert_keyword! + +export search + +#export Word +# +import DataStructures: Queue, enqueue!, dequeue! const Word = Vector{Int} -Markdown.@doc doc""" +@doc Markdown.doc""" AhoCorasickAutomaton An Aho-Corasick automaton, which can be used to efficiently search for a fixed list of keywords (vectors of Ints) in arbitrary lists of integers # Examples: -```jldoctest; setup = :(using AbstractAlgebra.Generic) -julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] -julia> aut = aho_corasick_automaton(keywords) -julia> search(aut, [10, 4, 1, 2, 3, 4]) +```jldoctest; setup = :(using AbstractAlgebra) +julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; + +julia> aut = Generic.aho_corasick_automaton(keywords); + +julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]) AhoCorasickMatch(6, 1, [1, 2, 3, 4]) ``` -""" mutable struct AhoCorasickAutomaton +""" +mutable struct AhoCorasickAutomaton goto::Vector{Dict{Int,Int}} fail::Vector{Int} """ @@ -34,33 +47,36 @@ AhoCorasickMatch(6, 1, [1, 2, 3, 4]) output::Vector{Tuple{Int,Word}} end -Markdown.@doc doc""" +@doc Markdown.doc""" AhoCorasickMatch(last_position::Int, keyword_index::Int, keyword::Vector{Int}) The return value of searching in a given word with an AhoCorasickAutomaton. Contains the position of the last letter in the word that matches a keyword in the automaton, an index of the keyword that was matched and the keyword itself. # Examples: -```jldoctest; setup = :(using AbstractAlgebra.Generic) -julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] -julia> aut = aho_corasick_automaton(keywords) -julia> search(aut, [10, 4, 1, 2, 3, 4]) +```jldoctest; setup = :(using AbstractAlgebra) +julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; + +julia> aut = Generic.aho_corasick_automaton(keywords); + +julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]); AhoCorasickMatch(6, 1, [1, 2, 3, 4]) -julia> search(aut, [10, 4, 1, 2, 3, 4]).last_position +julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]).last_position 6 -julia> search(aut, [10, 4, 1, 2, 3, 4]).keyword_index +julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]).keyword_index 1 -julia> search(aut, [10, 4, 1, 2, 3, 4]).keyword +julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]).keyword 4-element Vector{Int64}: 1 2 3 4 -""" struct AhoCorasickMatch +""" +struct AhoCorasickMatch last_position::Int keyword_index::Int keyword::Word @@ -112,10 +128,9 @@ end function enter!(automaton::AhoCorasickAutomaton, keyword::Word, current_index) current_state = 1 for c in keyword - new_state = get!(automaton.goto[current_state], c) do + current_state = get!(automaton.goto[current_state], c) do new_state!(automaton) end - current_state = new_state end if automaton.output[current_state][1] > current_index automaton.output[current_state] = (current_index, keyword) @@ -153,22 +168,24 @@ function construct_fail!(automaton::AhoCorasickAutomaton) end end -Markdown.@doc doc""" +@doc Markdown.doc""" insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) Insert a new keyword into a given Aho-Corasick automaton to avoid having to rebuild the entire automaton. -""" function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) +""" +function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) enter!(aut, keyword, index) aut.fail = ones(Int, length(aut.goto)) construct_fail!(aut) end -Markdown.@doc doc""" +@doc Markdown.doc""" search(automaton::AhoCorasickAutomaton, word) Search for the first occurrence of a keyword that is stored in `automaton` in the given `word`. -""" function search(automaton::AhoCorasickAutomaton, word) +""" +function search(automaton::AhoCorasickAutomaton, word) current_state = 1 result = AhoCorasickMatch(typemax(Int), typemax(Int), []) for i = 1:length(word) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index aa0ec9b543..4967293aaf 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -4,9 +4,15 @@ # ############################################################################### -export groebner_basis, interreduce!, normal_form, normal_form_weak +export groebner_basis -import DataStructures.PriorityQueue, DataStructures.enqueue!, DataStructures.dequeue! +export interreduce! + +export normal_form + +export normal_form_weak + +import DataStructures: PriorityQueue, enqueue!, dequeue! const groebner_debug_level = 0 const Monomial = Vector{Int} @@ -15,7 +21,6 @@ abstract type Obstruction{T} end """ represents the overlap of the leading term of two polynomials """ - struct ObstructionTriple{T} <: Obstruction{T} first_poly::FreeAssAlgElem{T} second_poly::FreeAssAlgElem{T} @@ -57,13 +62,14 @@ function _leading_word(a::FreeAssAlgElem{T}) where {T} return a.exps[1] end -Markdown.@doc doc""" +@doc Markdown.doc""" gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) If an element of the Groebner basis that is stored in `aut` divides `a`, return (true, a1, a2, keyword_index), where `keyword_index` is the index of the keyword that divides `a` such that `a = a1 aut[keyword_index] a2`. -""" function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) +""" +function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) match = search(aut, a) if isnothing(match) return (false, [], [], -1) @@ -77,12 +83,13 @@ keyword that divides `a` such that `a = a1 aut[keyword_index] a2`. end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel -Markdown.@doc doc""" +@doc Markdown.doc""" normal_form(f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton) Assuming `g` is a groebner basis and `aut` an Aho-Corasick automaton for the elements of `g`, compute the normal form of `f` with respect to `g` -""" function normal_form( +""" +function normal_form( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton, @@ -159,13 +166,14 @@ function normal_form_weak( return f end -Markdown.@doc doc""" +@doc Markdown.doc""" interreduce!(g::Vector{FreeAssAlgElem{T}}) Interreduce a given Groebner basis with itself, i.e. compute the normal form of each element of `g` with respect to the rest of the elements and discard elements with normal form $0$ and duplicates. -""" function interreduce!(g::Vector{FreeAssAlgElem{T}}) where {T} +""" +function interreduce!(g::Vector{FreeAssAlgElem{T}}) where {T} i = 1 while length(g) > 1 && length(g) >= i aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) @@ -604,7 +612,7 @@ function groebner_basis_buchberger( return g end -Markdown.@doc doc""" +@doc Markdown.doc""" groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int) Compute a groebner basis for the ideal spanned by g. Stop when `reduction_bound` many From 5e3880f537d398a96dce8c3300123bac133ea9eb Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 28 Mar 2023 13:29:09 +0200 Subject: [PATCH 60/80] imported normal_form_weak in the test --- test/generic/FreeAssAlgebraGroebner-test.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index dceb9ea661..0e62495e03 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -1,5 +1,6 @@ include("AhoCorasick-test.jl") using AbstractAlgebra.Generic: AhoCorasickAutomaton +import AbstractAlgebra.Generic: normal_form_weak @testset "Generic.FreeAssAlgebra.groebner" begin R, (x, y, u, v, t, s) = FreeAssociativeAlgebra(GF(2), ["x", "y", "u", "v", "t", "s"]) From 10b91347e25d8f94b26245ab193d023c8b519863 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 3 Apr 2023 17:24:19 +0200 Subject: [PATCH 61/80] added test cases for the normal form without aho-corasick-automaton and for groebner basis computation while removing redundancies --- src/generic/FreeAssAlgebraGroebner.jl | 9 ++++++++- test/generic/FreeAssAlgebraGroebner-test.jl | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 4967293aaf..b90f14893c 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -443,6 +443,8 @@ function is_proper_multiple( @assert obs1.pre_and_suffixes[2] == vcat(obs2.pre_and_suffixes[2], w2) return obs1.pre_and_suffixes[3] == vcat(w, obs2.pre_and_suffixes[3]) && obs1.pre_and_suffixes[4] == vcat(obs2.pre_and_suffixes[4], w2) + else + return false end end @@ -579,6 +581,7 @@ end function groebner_basis_buchberger( g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int, + remove_redundancies = false ) where {T<:FieldElement} g = copy(g) # interreduce!(g) # on some small examples, this increases running time, so it might not be optimal to use this here @@ -608,6 +611,9 @@ function groebner_basis_buchberger( return g end add_obstructions!(obstruction_queue, g) + if remove_redundancies + remove_redundancies!(obstruction_queue, length(g), g[length(g)]) + end end return g end @@ -621,6 +627,7 @@ non-zero entries have been added to the groebner basis. function groebner_basis( g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int, + remove_redundancies = false ) where {T<:FieldElement} - return groebner_basis_buchberger(g, reduction_bound) + return groebner_basis_buchberger(g, reduction_bound, remove_redundancies) end diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 0e62495e03..cde6073d85 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -18,6 +18,10 @@ import AbstractAlgebra.Generic: normal_form_weak g = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1]) AbstractAlgebra.interreduce!(g) @test length(g) == 15 + + g2 = AbstractAlgebra.groebner_basis([a^2 - 1, b^3 - 1, (a*b*a*b*a*b^2)^2 - 1], typemax(Int), true) + @test g2 == g # make sure removing redundant obstructions in the computation does not change the groebner basis + end @testset "Generic.FreeAssociativeAlgebra.groebner.normal_form" begin @@ -31,6 +35,12 @@ end @test normal_form(x*y*t + y, ideal_generators, aut) == y @test normal_form(one(R), ideal_generators, aut) == one(R) @test normal_form(v*y, ideal_generators, aut) == v*y + @test normal_form(x*y, ideal_generators) == zero(R) + @test normal_form(u*y*t, ideal_generators) == zero(R) + @test normal_form(s*t - t*s, ideal_generators) == zero(R) + @test normal_form(x*y*t + y, ideal_generators) == y + @test normal_form(one(R), ideal_generators) == one(R) + @test normal_form(v*y, ideal_generators) == v*y @test normal_form(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators, aut) == zero(R) @test normal_form_weak(x*y*v*v + t*s*x*y*v + y*s*t - y*t*s + v*x*y*y*s + v*x*x*y*s, ideal_generators) == zero(R) @test normal_form_weak(x*y + u*y, ideal_generators) <= x*y + u*y From 64838795e3c38cb3c209a519ca86773407aa0a0f Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 6 Apr 2023 12:35:03 +0200 Subject: [PATCH 62/80] changed all instances of for i = back into for i in and expanded on the documentation of ObstructionTriple in FreeAssAlgebraGroebner.jl --- docs/src/free_associative_algebra.md | 1 - src/AbstractAlgebra.jl | 2 -- src/generic/AhoCorasick.jl | 8 +++---- src/generic/FreeAssAlgebra.jl | 34 +++++++++++++-------------- src/generic/FreeAssAlgebraGroebner.jl | 32 +++++++++++++++---------- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index b295b2cf96..f91a4a4c44 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -228,7 +228,6 @@ to the function, to only compute a partial Groebner basis. **Examples** ```jldoctest; setup = :(using AbstractAlgebra) - julia> R, (x, y, u, v, t, s) = free_associative_algebra(GF(2), ["x", "y", "u", "v", "t", "s"]) (Free associative algebra over Finite field F_2 on x, y, u, v, t, s, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) diff --git a/src/AbstractAlgebra.jl b/src/AbstractAlgebra.jl index e929fc776f..13f024c1d1 100644 --- a/src/AbstractAlgebra.jl +++ b/src/AbstractAlgebra.jl @@ -593,7 +593,6 @@ include("Generic.jl") # Do not import div, divrem, exp, inv, log, sqrt, numerator and denominator # as we have our own - import .Generic: @perm_str import .Generic: abs_series_type import .Generic: base_field @@ -717,7 +716,6 @@ import .Generic: LocElem import .Generic: roots import .Generic: sturm_sequence - # Do not export inv, div, divrem, exp, log, sqrt, numerator and denominator as we define our own export _check_dim export _checkbounds diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 182d7cede9..a8d0f16e8c 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -33,7 +33,7 @@ julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; julia> aut = Generic.aho_corasick_automaton(keywords); julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]) -AhoCorasickMatch(6, 1, [1, 2, 3, 4]) +AbstractAlgebra.Generic.AhoCorasickMatch(6, 1, [1, 2, 3, 4]) ``` """ mutable struct AhoCorasickAutomaton @@ -86,8 +86,8 @@ function aho_corasick_match(last_position::Int, keyword_index::Int, keyword::Wor return AhoCorasickMatch(last_position, keyword_index, keyword) end -Base.hash(m::AhoCorasickMatch) = - hash(m.last_position, hash(m.keyword_index, hash(m.keyword))) +Base.hash(m::AhoCorasickMatch) = hash(m.last_position, hash(m.keyword_index, + hash(m.keyword))) function ==(m1::AhoCorasickMatch, m2::AhoCorasickMatch) return m1.last_position == m2.last_position && m1.keyword_index == m2.keyword_index && @@ -188,7 +188,7 @@ Search for the first occurrence of a keyword that is stored in `automaton` in th function search(automaton::AhoCorasickAutomaton, word) current_state = 1 result = AhoCorasickMatch(typemax(Int), typemax(Int), []) - for i = 1:length(word) + for i in 1:length(word) c = word[i] while true next_state = lookup(automaton, current_state, c) diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 36e9184065..e5ffc8a38c 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -99,7 +99,7 @@ function gen(a::FreeAssAlgebra{T}, i::Int) where {T} end function gens(a::FreeAssAlgebra{T}) where {T<:RingElement} - return [gen(a, i) for i = 1:ngens(a)] + return [gen(a, i) for i in 1:ngens(a)] end function is_gen(a::FreeAssAlgElem{T}) where {T} @@ -325,7 +325,7 @@ function word_cmp(a::Vector{Int}, b::Vector{Int}) return -1 else # deglex - for i = 1:length(a) + for i in 1:length(a) if a[i] > b[i] return -1 elseif a[i] < b[i] @@ -344,8 +344,8 @@ function sort_terms!(z::FreeAssAlgElem{T}) where {T} n = length(z) if n > 1 p = sortperm(view(z.exps, 1:n), lt = word_gt) - z.coeffs = [z.coeffs[p[i]] for i = 1:n] - z.exps = [z.exps[p[i]] for i = 1:n] + z.coeffs = [z.coeffs[p[i]] for i in 1:n] + z.exps = [z.exps[p[i]] for i in 1:n] end return z end @@ -380,7 +380,7 @@ function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where {T} end sort_terms!(p) sort_terms!(q) - for i = 1:l + for i in 1:l if word_gt(q.exps[i], p.exps[i]) return true elseif word_gt(p.exps[i], q.exps[i]) @@ -409,14 +409,14 @@ end function -(a::FreeAssAlgElem{T}) where {T<:RingElement} n = length(a) R = parent(a) - zcoeffs = T[-a.coeffs[i] for i = 1:n] + zcoeffs = T[-a.coeffs[i] for i in 1:n] return FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n) end function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} zcoeffs = T[] zexps = Vector{Int}[] - for i = 1:a.length, j = 1:b.length + for i in 1:a.length, j = 1:b.length push!(zcoeffs, a.coeffs[i] * b.coeffs[j]) push!(zexps, vcat(a.exps[i], b.exps[j])) end @@ -517,7 +517,7 @@ function ^(a::FreeAssAlgElem{T}, b::Integer) where {T<:RingElement} e = [Int[]] else b < 0 && throw(NotInvertibleError(a)) - e = Vector{Int}[reduce(vcat, [a.exps[1] for i = 1:b])] + e = Vector{Int}[reduce(vcat, [a.exps[1] for i in 1:b])] end return FreeAssAlgElem{T}(parent(a), [a.coeffs[1]^b], e, 1) else @@ -535,8 +535,8 @@ end # return c*w*a*wp function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) where {T} zcoeffs = - isone(c) ? T[a.coeffs[i] for i = 1:a.length] : T[c * a.coeffs[i] for i = 1:a.length] - zexps = Vector{Int}[vcat(w, a.exps[i], wp) for i = 1:a.length] + isone(c) ? T[a.coeffs[i] for i in 1:a.length] : T[c * a.coeffs[i] for i in 1:a.length] + zexps = Vector{Int}[vcat(w, a.exps[i], wp) for i in 1:a.length] return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, a.length) end @@ -544,16 +544,16 @@ end # or (false, junk, junk) if a is not two-sided divisible by b function word_divides_leftmost(a::Vector{Int}, b::Vector{Int}) n = length(b) - for i = 0:length(a)-n + for i in 0:length(a)-n match = true - for j = 1:n + for j in 1:n if b[j] != a[i+j] match = false break end end if match - return (true, Int[a[k] for k = 1:i], Int[a[k] for k = 1+i+n:length(a)]) + return (true, Int[a[k] for k in 1:i], Int[a[k] for k in 1+i+n:length(a)]) end end return (false, Int[], Int[]) @@ -563,16 +563,16 @@ end # or (false, junk, junk) if a is not two-sided divisible by b function word_divides_rightmost(a::Vector{Int}, b::Vector{Int}) n = length(b) - for i = length(a)-n:-1:0 + for i in length(a)-n:-1:0 match = true - for j = 1:n + for j in 1:n if b[j] != a[i+j] match = false break end end if match - return (true, Int[a[k] for k = 1:i], Int[a[k] for k = 1+i+n:length(a)]) + return (true, Int[a[k] for k in 1:i], Int[a[k] for k in 1+i+n:length(a)]) end end return (false, Int[], Int[]) @@ -633,7 +633,7 @@ function divexact( n = length(a) R = parent(a) b = base_ring(R)(b) - zcoeffs = T[divexact(a.coeffs[i], b, check = check) for i = 1:n] + zcoeffs = T[divexact(a.coeffs[i], b, check = check) for i in 1:n] return combine_like_terms!(FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n)) end diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index b90f14893c..9cb4af1a20 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -19,7 +19,15 @@ const Monomial = Vector{Int} abstract type Obstruction{T} end """ -represents the overlap of the leading term of two polynomials +Represents the overlap of the leading term of the two polynomials +`first_poly` and `second_poly`. Here, `first_index` and `second_index` +are the indices of `first_poly` and `second_poly` respectively +in the Groebner basis. +The `pre_and_suffixes` are of the form +[w_i, w_i'; w_j, w_j'] +where `i = first_index`, `j = second_index` and satisfy that +w_i g_i w_i' = w_j g_j w_j' +where `g_i` is `first_poly` and `g_j` is `second_poly`. """ struct ObstructionTriple{T} <: Obstruction{T} first_poly::FreeAssAlgElem{T} @@ -193,7 +201,7 @@ end ## checks whether there is an overlap between a and b at position i of b # such that b[i:length(b)] = a[1:length(b)-i] function check_left_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) - for j = 0:(length(b) - i) + for j in 0:(length(b) - i) if j >= length(a) return false # this is a center overlap end @@ -212,7 +220,7 @@ end # if w_1 or w_2^' is empty, the corresponding obstruction is not returned function left_obstructions(a::Vector{Int}, b::Vector{Int}) v = Tuple{Vector{Int},Vector{Int}}[] - for i = 2:length(b) + for i in 2:length(b) if check_left_overlap(a, b, i) if length(b) - i + 2 <= length(a) # w_2^' should not be empty! push!(v, (b[1:(i - 1)], a[(length(b) - i + 2):length(a)])) @@ -236,7 +244,7 @@ end ### # check, whether a is a true subword of b at index i function check_center_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) - for j = 1:length(a) + for j in 1:length(a) if i + j - 1 > length(b) return false end @@ -249,7 +257,7 @@ end function center_obstructions_first_in_second(a::Vector{Int}, b::Vector{Int}) v = Tuple{Vector{Int},Vector{Int}}[] - for i = 1:length(b) + for i in 1:length(b) if check_center_overlap(a, b, i) push!(v, (b[1:(i - 1)], b[(i + length(a)):length(b)])) end @@ -313,7 +321,7 @@ function is_subword_right(w_1::Vector{Int}, w_2::Vector{Int}) if length(w_1) > length(w_2) return false end - for i = 0:(length(w_1) - 1) + for i in 0:(length(w_1) - 1) if w_1[length(w_1) - i] != w_2[length(w_2) - i] return false end @@ -327,7 +335,7 @@ function is_subword_left(w_1::Vector{Int}, w_2::Vector{Int}) if length(w_1) > length(w_2) return false end - for i = 1:length(w_1) + for i in 1:length(w_1) if w_1[i] != w_2[i] return false end @@ -430,10 +438,10 @@ function is_proper_multiple( is_subword_left(obs2.pre_and_suffixes[2], obs1.pre_and_suffixes[2]) w = copy(obs1.pre_and_suffixes[1]) w2 = copy(obs1.pre_and_suffixes[2]) - for _ = 1:length(obs2.pre_and_suffixes[1]) + for _ in 1:length(obs2.pre_and_suffixes[1]) pop!(w) end - for _ = 1:length(obs2.pre_and_suffixes[2]) + for _ in 1:length(obs2.pre_and_suffixes[2]) popfirst!(w2) end if length(w) + length(w2) == 0 @@ -472,7 +480,7 @@ function is_redundant( ) where {T} w1 = [] w2 = [] - for i = 1:length(obs.second_poly.exps[1]) + for i in 1:length(obs.second_poly.exps[1]) word_to_check = vcat(obs.pre_and_suffixes[3], obs.second_poly.exps[1], obs.pre_and_suffixes[4]) if check_center_overlap(newest_element.exps[1], word_to_check, i) @@ -542,7 +550,7 @@ end function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where {T} s = length(g) result = PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}() - for i = 1:s, j = 1:i + for i in 1:s, j in 1:i if i == j obs = obstructions(_leading_word(g[i])) else @@ -563,7 +571,7 @@ function add_obstructions!( g::Vector{FreeAssAlgElem{T}}, ) where {T} s = length(g) - for i = 1:s + for i in 1:s if i == s obs = obstructions(_leading_word(g[i])) else From 4b2b0496ff34a19b817b6bcfc920aa7de62d0afa Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 15 May 2023 14:49:11 +0200 Subject: [PATCH 63/80] changed @doc Markdown.doc to @doc or @doc raw in comments --- src/generic/AhoCorasick.jl | 8 ++++---- src/generic/FreeAssAlgebra.jl | 2 +- src/generic/FreeAssAlgebraGroebner.jl | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 182d7cede9..89a2545205 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -20,7 +20,7 @@ import DataStructures: Queue, enqueue!, dequeue! const Word = Vector{Int} -@doc Markdown.doc""" +@doc """ AhoCorasickAutomaton An Aho-Corasick automaton, which can be used to efficiently search for a fixed list of keywords (vectors of Ints) in @@ -47,7 +47,7 @@ mutable struct AhoCorasickAutomaton output::Vector{Tuple{Int,Word}} end -@doc Markdown.doc""" +@doc """ AhoCorasickMatch(last_position::Int, keyword_index::Int, keyword::Vector{Int}) The return value of searching in a given word with an AhoCorasickAutomaton. Contains the position of the last letter in @@ -168,7 +168,7 @@ function construct_fail!(automaton::AhoCorasickAutomaton) end end -@doc Markdown.doc""" +@doc """ insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) Insert a new keyword into a given Aho-Corasick automaton to avoid having to rebuild the entire @@ -180,7 +180,7 @@ function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) construct_fail!(aut) end -@doc Markdown.doc""" +@doc """ search(automaton::AhoCorasickAutomaton, word) Search for the first occurrence of a keyword that is stored in `automaton` in the given `word`. diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 36e9184065..6fe7158345 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -190,7 +190,7 @@ function monomial(a::FreeAssAlgElem{T}, i::Int) where {T<:RingElement} return FreeAssAlgElem{T}(R, T[one(base_ring(R))], [a.exps[i]], 1) end -@doc Markdown.doc""" +@doc raw""" exponent_word(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement Return a vector of variable indices corresponding to the monomial of the diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 4967293aaf..6d5bdc2d60 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -62,7 +62,7 @@ function _leading_word(a::FreeAssAlgElem{T}) where {T} return a.exps[1] end -@doc Markdown.doc""" +@doc """ gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) If an element of the Groebner basis that is stored in `aut` divides `a`, @@ -83,7 +83,7 @@ function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) end # implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel -@doc Markdown.doc""" +@doc """ normal_form(f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton) Assuming `g` is a groebner basis and `aut` an Aho-Corasick automaton for the elements of `g`, @@ -166,7 +166,7 @@ function normal_form_weak( return f end -@doc Markdown.doc""" +@doc raw""" interreduce!(g::Vector{FreeAssAlgElem{T}}) Interreduce a given Groebner basis with itself, i.e. compute the normal form of each @@ -612,7 +612,7 @@ function groebner_basis_buchberger( return g end -@doc Markdown.doc""" +@doc """ groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int) Compute a groebner basis for the ideal spanned by g. Stop when `reduction_bound` many From f06043a0b6308e1bf0fee8f62abc7f7a8f3de87a Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 29 Jun 2023 11:28:40 +0200 Subject: [PATCH 64/80] removed the braces around type variable instantiation --- src/generic/AhoCorasick.jl | 4 +- src/generic/FreeAssAlgebra.jl | 76 +++++++++++++++++------------------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 985c2fab38..76445d7be8 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -26,7 +26,7 @@ const Word = Vector{Int} An Aho-Corasick automaton, which can be used to efficiently search for a fixed list of keywords (vectors of Ints) in arbitrary lists of integers -# Examples: +# Examples ```jldoctest; setup = :(using AbstractAlgebra) julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; @@ -53,7 +53,7 @@ end The return value of searching in a given word with an AhoCorasickAutomaton. Contains the position of the last letter in the word that matches a keyword in the automaton, an index of the keyword that was matched and the keyword itself. -# Examples: +# Examples ```jldoctest; setup = :(using AbstractAlgebra) julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 3e11320d0a..7e0faa6c50 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -13,11 +13,11 @@ export exponent_word, set_exponent_word! # ############################################################################### -function parent_type(::Type{FreeAssAlgElem{T}}) where {T<:RingElement} +function parent_type(::Type{FreeAssAlgElem{T}}) where T<:RingElement return FreeAssAlgebra{T} end -function elem_type(::Type{FreeAssAlgebra{T}}) where {T<:RingElement} +function elem_type(::Type{FreeAssAlgebra{T}}) where T<:RingElement return FreeAssAlgElem{T} end @@ -45,7 +45,7 @@ function check_parent( a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}, throw::Bool = true, -) where {T<:RingElement} +) where T<:RingElement b = parent(a) != parent(b) b & throw && error("Incompatible rings in operation") return !b @@ -57,7 +57,7 @@ end # ############################################################################### -function Base.deepcopy_internal(a::FreeAssAlgElem{T}, dict::IdDict) where {T<:RingElement} +function Base.deepcopy_internal(a::FreeAssAlgElem{T}, dict::IdDict) where T<:RingElement return FreeAssAlgElem{T}( a.parent, deepcopy_internal(a.coeffs, dict), @@ -66,17 +66,17 @@ function Base.deepcopy_internal(a::FreeAssAlgElem{T}, dict::IdDict) where {T<:Ri ) end -function zero(a::FreeAssAlgebra{T}) where {T} +function zero(a::FreeAssAlgebra{T}) where T return FreeAssAlgElem{T}(a, T[], Vector{Int}[], 0) end -function one(a::FreeAssAlgebra{T}) where {T} +function one(a::FreeAssAlgebra{T}) where T c = one(base_ring(a)) !iszero(c) || return zero(a) return FreeAssAlgElem{T}(a, [c], [Int[]], 1) end -function iszero(a::FreeAssAlgElem{T}) where {T} +function iszero(a::FreeAssAlgElem{T}) where T return length(a) == 0 end @@ -99,7 +99,7 @@ function gen(a::FreeAssAlgebra{T}, i::Int) where T return FreeAssAlgElem{T}(a, T[c], [Int[i]], 1) end -function gens(a::FreeAssAlgebra{T}) where {T <: RingElement} +function gens(a::FreeAssAlgebra{T}) where T <: RingElement return [gen(a, i) for i in 1:ngens(a)] end @@ -111,7 +111,7 @@ function is_gen(a::FreeAssAlgElem{T}) where T end end -function is_constant(a::FreeAssAlgElem{T}) where {T} +function is_constant(a::FreeAssAlgElem{T}) where T return length(a) == 0 || (length(a) == 1 && isempty(a.exps[1])) end @@ -121,7 +121,7 @@ end # ############################################################################### -promote_rule(::Type{FreeAssAlgElem{T}}, ::Type{FreeAssAlgElem{T}}) where {T<:RingElement} = +promote_rule(::Type{FreeAssAlgElem{T}}, ::Type{FreeAssAlgElem{T}}) where T<:RingElement = FreeAssAlgElem{T} function promote_rule( @@ -137,22 +137,22 @@ end # ############################################################################### -function (a::FreeAssAlgebra{T})() where {T} +function (a::FreeAssAlgebra{T})() where T return zero(a) end -function (a::FreeAssAlgebra{T})(b::T) where {T} +function (a::FreeAssAlgebra{T})(b::T) where T iszero(b) && return zero(a) return FreeAssAlgElem{T}(a, T[b], [Int[]], 1) end -function (a::FreeAssAlgebra{T})(b::Integer) where {T} +function (a::FreeAssAlgebra{T})(b::Integer) where T iszero(b) && return zero(a) R = base_ring(a) return FreeAssAlgElem{T}(a, T[R(b)], [Int[]], 1) end -function (a::FreeAssAlgebra{T})(b::FreeAssAlgElem{T}) where {T<:RingElement} +function (a::FreeAssAlgebra{T})(b::FreeAssAlgElem{T}) where T<:RingElement parent(b) != a && error("Unable to coerce element") return b end @@ -209,32 +209,32 @@ function Base.iterate(a::FreeAssAlgExponentWords, state = 0) return exponent_word(a.poly, state), state end -function leading_coefficient(a::FreeAssAlgElem{T}) where {T} +function leading_coefficient(a::FreeAssAlgElem{T}) where T return a.length > 0 ? coeff(a, 1) : zero(base_ring(a)) end -function leading_monomial(a::FreeAssAlgElem{T}) where {T} +function leading_monomial(a::FreeAssAlgElem{T}) where T if length(a) < 1 throw(ArgumentError("Zero polynomial does not have a leading monomial")) end return monomial(a, 1) end -function leading_term(a::FreeAssAlgElem{T}) where {T} +function leading_term(a::FreeAssAlgElem{T}) where T if length(a) < 1 throw(ArgumentError("Zero polynomial does not have a leading term")) end return term(a, 1) end -function leading_exponent_word(a::FreeAssAlgElem{T}) where {T} +function leading_exponent_word(a::FreeAssAlgElem{T}) where T if length(a) < 1 throw(ArgumentError("Zero polynomial does not have a leading exponent word")) end return exponent_word(a, 1) end -function total_degree(a::FreeAssAlgElem{T}) where {T} +function total_degree(a::FreeAssAlgElem{T}) where T # currently stored in dexlex return length(a) > 0 ? length(a.exps[1]) : -1 end @@ -257,7 +257,7 @@ end # ############################################################################### -function canonical_unit(a::FreeAssAlgElem{T}) where {T<:RingElement} +function canonical_unit(a::FreeAssAlgElem{T}) where T<:RingElement return canonical_unit(leading_coefficient(a)) end @@ -267,7 +267,7 @@ end # ############################################################################### -function fit!(a::FreeAssAlgElem{T}, n::Int) where {T<:RingElement} +function fit!(a::FreeAssAlgElem{T}, n::Int) where T<:RingElement if length(a.coeffs) < n resize!(a.coeffs, n) end @@ -279,7 +279,7 @@ end for T in [RingElem, Integer, Rational, AbstractFloat] @eval begin - function setcoeff!(a::FreeAssAlgElem{S}, i::Int, c::S) where {S<:$T} + function setcoeff!(a::FreeAssAlgElem{S}, i::Int, c::S) where S<:$T fit!(a, i) a.coeffs[i] = c if i > length(a) @@ -308,7 +308,7 @@ end # ############################################################################### -function ==(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T} +function ==(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T fl = check_parent(a, b, false) !fl && return false return a.length == b.length && @@ -338,7 +338,7 @@ function word_gt(a::Vector{Int}, b::Vector{Int}) return word_cmp(a, b) > 0 end -function sort_terms!(z::FreeAssAlgElem{T}) where {T} +function sort_terms!(z::FreeAssAlgElem{T}) where T n = length(z) if n > 1 p = sortperm(view(z.exps, 1:n), lt = word_gt) @@ -348,7 +348,7 @@ function sort_terms!(z::FreeAssAlgElem{T}) where {T} return z end -function combine_like_terms!(z::FreeAssAlgElem{T}) where {T} +function combine_like_terms!(z::FreeAssAlgElem{T}) where T o = 0 i = 1 while i <= z.length @@ -366,7 +366,7 @@ function combine_like_terms!(z::FreeAssAlgElem{T}) where {T} return z end -function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where {T} +function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T if p == q return false end @@ -394,7 +394,7 @@ function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where {T} end end -function <(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where {T} +function <(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T return isless(p, q) end @@ -404,14 +404,14 @@ end # ############################################################################### -function -(a::FreeAssAlgElem{T}) where {T<:RingElement} +function -(a::FreeAssAlgElem{T}) where T<:RingElement n = length(a) R = parent(a) zcoeffs = T[-a.coeffs[i] for i in 1:n] return FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n) end -function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} +function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T<:RingElement zcoeffs = T[] zexps = Vector{Int}[] for i in 1:a.length, j = 1:b.length @@ -422,7 +422,7 @@ function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} return combine_like_terms!(sort_terms!(z)) end -function +(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} +function +(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T<:RingElement zcoeffs = T[] zexps = Vector{Int}[] i = j = 1 @@ -464,7 +464,7 @@ function _sub_rest( a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}, start::Int, -) where {T<:RingElement} +) where T<:RingElement zcoeffs = T[] zexps = Vector{Int}[] i = j = start + 1 @@ -501,11 +501,11 @@ function _sub_rest( return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) end -function -(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where {T<:RingElement} +function -(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T<:RingElement return _sub_rest(a, b, 0) end -function ^(a::FreeAssAlgElem{T}, b::Integer) where {T<:RingElement} +function ^(a::FreeAssAlgElem{T}, b::Integer) where T<:RingElement if b == 0 return one(parent(a)) elseif b == 1 @@ -531,7 +531,7 @@ end ############################################################################### # return c*w*a*wp -function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) where {T} +function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) where T zcoeffs = isone(c) ? T[a.coeffs[i] for i in 1:a.length] : T[c * a.coeffs[i] for i in 1:a.length] zexps = Vector{Int}[vcat(w, a.exps[i], wp) for i in 1:a.length] @@ -581,7 +581,7 @@ function AbstractAlgebra.divexact_left( f::FreeAssAlgElem{T}, g::FreeAssAlgElem{T}; check::Bool = true, -) where {T} +) where T R = parent(f) qcoeffs = T[] qexps = Vector{Int}[] @@ -600,7 +600,7 @@ function AbstractAlgebra.divexact_right( f::FreeAssAlgElem{T}, g::FreeAssAlgElem{T}; check::Bool = true, -) where {T} +) where T R = parent(f) qcoeffs = T[] qexps = Vector{Int}[] @@ -626,7 +626,7 @@ function divexact( a::FreeAssAlgElem{T}, b::Integer; check::Bool = true, -) where {T<:RingElement} +) where T<:RingElement n = length(a) R = parent(a) @@ -657,7 +657,7 @@ function map_coefficients(f, a::FreeAssAlgElem{T}; cached::Bool=true, parent::Ab return _map(f, a, parent) end -function _map(g, a::FreeAssAlgElem{T}, Rx) where {T<:RingElement} +function _map(g, a::FreeAssAlgElem{T}, Rx) where T<:RingElement cvzip = zip(coefficients(a), exponent_words(a)) M = MPolyBuildCtx(Rx) for (c, v) in cvzip From 8f90c18880d833a4f11171beac8928a6c5df8ec5 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 29 Jun 2023 12:55:23 +0200 Subject: [PATCH 65/80] reformatted FreeAssAlgebra.jl for consistent whitespace --- src/generic/FreeAssAlgebra.jl | 146 +++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 62 deletions(-) diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index dc1fc6453f..1a4935d130 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -12,11 +12,11 @@ export exponent_word, set_exponent_word! # ############################################################################### -function parent_type(::Type{FreeAssAlgElem{T}}) where T<:RingElement +function parent_type(::Type{FreeAssAlgElem{T}}) where T <: RingElement return FreeAssAlgebra{T} end -function elem_type(::Type{FreeAssAlgebra{T}}) where T<:RingElement +function elem_type(::Type{FreeAssAlgebra{T}}) where T <: RingElement return FreeAssAlgElem{T} end @@ -44,7 +44,7 @@ function check_parent( a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}, throw::Bool = true, -) where T<:RingElement +) where T <: RingElement b = parent(a) != parent(b) b & throw && error("Incompatible rings in operation") return !b @@ -56,7 +56,7 @@ end # ############################################################################### -function Base.deepcopy_internal(a::FreeAssAlgElem{T}, dict::IdDict) where T<:RingElement +function Base.deepcopy_internal(a::FreeAssAlgElem{T}, dict::IdDict) where T <: RingElement return FreeAssAlgElem{T}( a.parent, deepcopy_internal(a.coeffs, dict), @@ -80,26 +80,26 @@ function iszero(a::FreeAssAlgElem{T}) where T end function isone(a::FreeAssAlgElem{T}) where T - if length(a) < 1 - return isone(zero(base_ring(a))) - else - return a.length == 1 && isone(a.coeffs[1]) && isempty(a.exps[1]) - end + if length(a) < 1 + return isone(zero(base_ring(a))) + else + return a.length == 1 && isone(a.coeffs[1]) && isempty(a.exps[1]) + end end function ngens(a::FreeAssAlgebra{T}) where T - return nvars(a) + return nvars(a) end function gen(a::FreeAssAlgebra{T}, i::Int) where T - @boundscheck 1 <= i <= ngens(a) || throw(ArgumentError("variable index out of range")) - c = one(base_ring(a)) - iszero(c) && return zero(a) - return FreeAssAlgElem{T}(a, T[c], [Int[i]], 1) + @boundscheck 1 <= i <= ngens(a) || throw(ArgumentError("variable index out of range")) + c = one(base_ring(a)) + iszero(c) && return zero(a) + return FreeAssAlgElem{T}(a, T[c], [Int[i]], 1) end function gens(a::FreeAssAlgebra{T}) where T <: RingElement - return [gen(a, i) for i in 1:ngens(a)] + return [gen(a, i) for i in 1:ngens(a)] end function is_gen(a::FreeAssAlgElem{T}) where T @@ -120,13 +120,13 @@ end # ############################################################################### -promote_rule(::Type{FreeAssAlgElem{T}}, ::Type{FreeAssAlgElem{T}}) where T<:RingElement = +promote_rule(::Type{FreeAssAlgElem{T}}, ::Type{FreeAssAlgElem{T}}) where T <: RingElement = FreeAssAlgElem{T} function promote_rule( ::Type{FreeAssAlgElem{T}}, ::Type{U}, -) where {T<:RingElement,U<:RingElement} +) where {T <: RingElement, U <: RingElement} promote_rule(T, U) == T ? FreeAssAlgElem{T} : Union{} end @@ -151,19 +151,21 @@ function (a::FreeAssAlgebra{T})(b::Integer) where T return FreeAssAlgElem{T}(a, T[R(b)], [Int[]], 1) end -function (a::FreeAssAlgebra{T})(b::FreeAssAlgElem{T}) where T<:RingElement +function (a::FreeAssAlgebra{T})(b::FreeAssAlgElem{T}) where T <: RingElement parent(b) != a && error("Unable to coerce element") return b end function (a::FreeAssAlgebra{T})(c::Vector{T}, e::Vector{Vector{Int}}) where T - for ei in e - @boundscheck all(i -> (1 <= i <= nvars(a)), ei) || throw(ArgumentError("variable index out of range")) - end - n = length(c) - n == length(e) || error("coefficient array and exponent array should have the same length") - z = FreeAssAlgElem{T}(a, copy(c), copy(e), n) - return combine_like_terms!(sort_terms!(z)) + for ei in e + @boundscheck all(i -> (1 <= i <= nvars(a)), ei) || + throw(ArgumentError("variable index out of range")) + end + n = length(c) + n == length(e) || + error("coefficient array and exponent array should have the same length") + z = FreeAssAlgElem{T}(a, copy(c), copy(e), n) + return combine_like_terms!(sort_terms!(z)) end ############################################################################### @@ -173,20 +175,20 @@ end ############################################################################### function coeff(a::FreeAssAlgElem, i::Int) - @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) - return a.coeffs[i] + @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) + return a.coeffs[i] end function term(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement - @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) - R = parent(a) - return FreeAssAlgElem{T}(R, [a.coeffs[i]], [a.exps[i]], 1) + @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) + R = parent(a) + return FreeAssAlgElem{T}(R, [a.coeffs[i]], [a.exps[i]], 1) end function monomial(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement - @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) - R = parent(a) - return FreeAssAlgElem{T}(R, T[one(base_ring(R))], [a.exps[i]], 1) + @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) + R = parent(a) + return FreeAssAlgElem{T}(R, T[one(base_ring(R))], [a.exps[i]], 1) end @doc raw""" @@ -198,8 +200,8 @@ indices are given in the order of the variables for the ring. """ function exponent_word(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement - @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) - return a.exps[i] + @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) + return a.exps[i] end function Base.iterate(a::FreeAssAlgExponentWords, state = 0) @@ -240,13 +242,13 @@ end function Base.length( x::FreeAssAlgExponentWords{T}, -) where {S<:RingElement,T<:FreeAssAlgElem{S}} +) where {S <: RingElement, T <: FreeAssAlgElem{S}} return length(x.poly) end function Base.eltype( x::FreeAssAlgExponentWords{T}, -) where {S<:RingElement,T<:FreeAssAlgElem{S}} +) where {S <: RingElement, T <: FreeAssAlgElem{S}} return Vector{Int} end @@ -256,7 +258,7 @@ end # ############################################################################### -function canonical_unit(a::FreeAssAlgElem{T}) where T<:RingElement +function canonical_unit(a::FreeAssAlgElem{T}) where T <: RingElement return canonical_unit(leading_coefficient(a)) end @@ -266,7 +268,7 @@ end # ############################################################################### -function fit!(a::FreeAssAlgElem{T}, n::Int) where T<:RingElement +function fit!(a::FreeAssAlgElem{T}, n::Int) where T <: RingElement if length(a.coeffs) < n resize!(a.coeffs, n) end @@ -278,7 +280,7 @@ end for T in [RingElem, Integer, Rational, AbstractFloat] @eval begin - function setcoeff!(a::FreeAssAlgElem{S}, i::Int, c::S) where S<:$T + function setcoeff!(a::FreeAssAlgElem{S}, i::Int, c::S) where S <: $T fit!(a, i) a.coeffs[i] = c if i > length(a) @@ -290,15 +292,20 @@ for T in [RingElem, Integer, Rational, AbstractFloat] end -function set_exponent_word!(a::FreeAssAlgElem{T}, i::Int, w::Vector{Int}) where T <: RingElement - n = nvars(parent(a)) - @boundscheck all(x -> 1 <= x <= n, w) || throw(ArgumentError("variable index out of range")) - fit!(a, i) - a.exps[i] = w - if i > length(a) - a.length = i - end - return a +function set_exponent_word!( + a::FreeAssAlgElem{T}, + i::Int, + w::Vector{Int}, +) where T <: RingElement + n = nvars(parent(a)) + @boundscheck all(x -> 1 <= x <= n, w) || + throw(ArgumentError("variable index out of range")) + fit!(a, i) + a.exps[i] = w + if i > length(a) + a.length = i + end + return a end ############################################################################### @@ -403,17 +410,17 @@ end # ############################################################################### -function -(a::FreeAssAlgElem{T}) where T<:RingElement +function -(a::FreeAssAlgElem{T}) where T <: RingElement n = length(a) R = parent(a) zcoeffs = T[-a.coeffs[i] for i in 1:n] return FreeAssAlgElem{T}(R, zcoeffs, copy(a.exps), n) end -function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T<:RingElement +function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T <: RingElement zcoeffs = T[] zexps = Vector{Int}[] - for i in 1:a.length, j = 1:b.length + for i in 1:a.length, j in 1:b.length push!(zcoeffs, a.coeffs[i] * b.coeffs[j]) push!(zexps, vcat(a.exps[i], b.exps[j])) end @@ -421,7 +428,7 @@ function *(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T<:RingElement return combine_like_terms!(sort_terms!(z)) end -function +(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T<:RingElement +function +(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T <: RingElement zcoeffs = T[] zexps = Vector{Int}[] i = j = 1 @@ -463,7 +470,7 @@ function _sub_rest( a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}, start::Int, -) where T<:RingElement +) where T <: RingElement zcoeffs = T[] zexps = Vector{Int}[] i = j = start + 1 @@ -500,11 +507,11 @@ function _sub_rest( return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, length(zcoeffs)) end -function -(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T<:RingElement +function -(a::FreeAssAlgElem{T}, b::FreeAssAlgElem{T}) where T <: RingElement return _sub_rest(a, b, 0) end -function ^(a::FreeAssAlgElem{T}, b::Integer) where T<:RingElement +function ^(a::FreeAssAlgElem{T}, b::Integer) where T <: RingElement if b == 0 return one(parent(a)) elseif b == 1 @@ -532,7 +539,8 @@ end # return c*w*a*wp function mul_term(c::T, w::Vector{Int}, a::FreeAssAlgElem{T}, wp::Vector{Int}) where T zcoeffs = - isone(c) ? T[a.coeffs[i] for i in 1:a.length] : T[c * a.coeffs[i] for i in 1:a.length] + isone(c) ? T[a.coeffs[i] for i in 1:a.length] : + T[c * a.coeffs[i] for i in 1:a.length] zexps = Vector{Int}[vcat(w, a.exps[i], wp) for i in 1:a.length] return FreeAssAlgElem{T}(parent(a), zcoeffs, zexps, a.length) end @@ -625,7 +633,7 @@ function divexact( a::FreeAssAlgElem{T}, b::Integer; check::Bool = true, -) where T<:RingElement +) where T <: RingElement n = length(a) R = parent(a) @@ -647,16 +655,30 @@ function _change_freeassalg_ring(R, Rx, cached) end -function change_base_ring(R::Ring, a::FreeAssAlgElem{T}; cached::Bool=true, parent::AbstractAlgebra.FreeAssAlgebra=_change_freeassalg_ring(R, parent(a), cached)) where T <: RingElement +function change_base_ring( + R::Ring, + a::FreeAssAlgElem{T}; + cached::Bool = true, + parent::AbstractAlgebra.FreeAssAlgebra = _change_freeassalg_ring(R, parent(a), cached), +) where T <: RingElement base_ring(parent) != R && error("Base rings do not match.") return _map(R, a, parent) end -function map_coefficients(f, a::FreeAssAlgElem{T}; cached::Bool=true, parent::AbstractAlgebra.FreeAssAlgebra=_change_freeassalg_ring(parent(f(zero(base_ring(a)))), parent(a), cached)) where T <: RingElement - return _map(f, a, parent) +function map_coefficients( + f, + a::FreeAssAlgElem{T}; + cached::Bool = true, + parent::AbstractAlgebra.FreeAssAlgebra = _change_freeassalg_ring( + parent(f(zero(base_ring(a)))), + parent(a), + cached, + ), +) where T <: RingElement + return _map(f, a, parent) end -function _map(g, a::FreeAssAlgElem{T}, Rx) where T<:RingElement +function _map(g, a::FreeAssAlgElem{T}, Rx) where T <: RingElement cvzip = zip(coefficients(a), exponent_words(a)) M = MPolyBuildCtx(Rx) for (c, v) in cvzip From e5d32be4842d6c0a7a1d87ac93b3af8dc34d7d69 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 29 Jun 2023 15:16:29 +0200 Subject: [PATCH 66/80] added a compat entry for DataStructures --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 52a616b8b0..1d508bf15a 100644 --- a/Project.toml +++ b/Project.toml @@ -15,6 +15,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] +DataStructures = "0.18.13" GroupsCore = "0.4.0" MacroTools = "0.5" Preferences = "1" From 9a29b09a633036b3857e953bf3d0253d37422384 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 3 Jul 2023 14:03:18 +0200 Subject: [PATCH 67/80] fixed a doctest in free_associative_algebra.md and added a simple test case for aho corasick automata --- docs/src/free_associative_algebra.md | 3 +-- test/generic/AhoCorasick-test.jl | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index 58488b8056..fdeaa0d9c6 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -229,8 +229,7 @@ to the function, to only compute a partial Groebner basis. ```jldoctest; setup = :(using AbstractAlgebra) julia> R, (x, y, u, v, t, s) = free_associative_algebra(GF(2), ["x", "y", "u", "v", "t", "s"]) -(Free associative algebra over Finite field F_2 on x, y, u, v, t, s, -AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) +(Free associative algebra on 6 indeterminates over finite field F_2, AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}[x, y, u, v, t, s]) julia> g = Generic.groebner_basis([u*(x*y)^3 + u*(x*y)^2 + u + v, (y*x)^3*t + (y*x)^2*t + t + s]) 5-element Vector{AbstractAlgebra.Generic.FreeAssAlgElem{AbstractAlgebra.GFElem{Int64}}}: diff --git a/test/generic/AhoCorasick-test.jl b/test/generic/AhoCorasick-test.jl index ab905efb1f..01c9084a97 100644 --- a/test/generic/AhoCorasick-test.jl +++ b/test/generic/AhoCorasick-test.jl @@ -1,8 +1,9 @@ -using AbstractAlgebra.Generic: AhoCorasickAutomaton, search, AhoCorasickMatch +using AbstractAlgebra.Generic: AhoCorasickAutomaton, search, AhoCorasickMatch, aho_corasick_automaton @testset "Generic.AhoCorasick" begin keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]] - aut = AhoCorasickAutomaton(keywords) + aut = aho_corasick_automaton(keywords) @test search(aut, [10, 4, 1, 2, 3, 4]) == AhoCorasickMatch(6, 1, [1, 2, 3, 4]) + @test hash(search(aut, [10, 4, 1, 2, 3, 4])) == hash(AhoCorasickMatch(6, 1, [1, 2, 3, 4])) @test isnothing(search(aut, [])) @test search(aut, [1, 5, 4, 1, 1, 1, 4, 4]) == AhoCorasickMatch(3, 2, [1, 5, 4]) @test search(aut, [1, 2, 3, 1, 4, 1, 2, 1, 4, 1, 2]) == AhoCorasickMatch(7, 3, [4, 1, 2]) From 1bb6853e214789201bfedfb2162d93d89c7a66ec Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 4 Jul 2023 16:18:14 +0200 Subject: [PATCH 68/80] added access functions to the entries of AhoCorasickMatch and adapted the documentation, also added a documentation for isless in FreeAssAlgebra.jl --- src/generic/AhoCorasick.jl | 35 +++++++++++++++++++----- src/generic/FreeAssAlgebra.jl | 50 ++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 76445d7be8..76d0a3a8f7 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -59,22 +59,22 @@ julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; julia> aut = Generic.aho_corasick_automaton(keywords); -julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]); +julia> result = Generic.search(aut, [10, 4, 1, 2, 3, 4]); AhoCorasickMatch(6, 1, [1, 2, 3, 4]) -julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]).last_position +julia> last_position(result) 6 -julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]).keyword_index +julia> keyword_index(result) 1 -julia> Generic.search(aut, [10, 4, 1, 2, 3, 4]).keyword +julia> keyword(result) 4-element Vector{Int64}: 1 2 3 4 - +``` """ struct AhoCorasickMatch last_position::Int @@ -82,12 +82,33 @@ struct AhoCorasickMatch keyword::Word end +""" +returns the last position of the match in the word that was searched +""" +function last_position(match::AhoCorasickMatch) + return match.last_position +end + +""" +returns the index of the keyword in the corresponding aho corasick automaton +""" +function keyword_index(match::AhoCorasickMatch) + return match.keyword_index +end + +""" +returns the keyword corresponding to the match +""" +function keyword(match::AhoCorasickMatch) + return match.keyword +end + function aho_corasick_match(last_position::Int, keyword_index::Int, keyword::Word) return AhoCorasickMatch(last_position, keyword_index, keyword) end -Base.hash(m::AhoCorasickMatch) = hash(m.last_position, hash(m.keyword_index, - hash(m.keyword))) +Base.hash(m::AhoCorasickMatch, h::UInt) = hash(m.last_position, hash(m.keyword_index, + hash(m.keyword, h))) function ==(m1::AhoCorasickMatch, m2::AhoCorasickMatch) return m1.last_position == m2.last_position && m1.keyword_index == m2.keyword_index && diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index 1a4935d130..3e91bc36e0 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -372,24 +372,52 @@ function combine_like_terms!(z::FreeAssAlgElem{T}) where T return z end + +@doc """ + isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T + +Implements the degree lexicographic ordering on terms, i.e. +first, the degrees of the largest monomials are compared, and if they +are the same, they are compared lexicographically and if they are still the same, +the coefficients are compared. +If everything is still the same, the next largest monomial is compared +and lastly the number of monomials is compared. +Since the coefficients are also compared, this only works when the +coefficient Ring implements isless. + +The order of letters is the reverse of the order given when initialising the algebra. + +# Examples +```jldoctest; setup = :(using AbstractAlgebra) +julia> R, (x, y) = free_associative_algebra(QQ, ["x", "y"]); + +julia> x < y^2 +true + +julia> x^2 < x^2 + y +true + +julia> y < x +true + +julia> x^2 < 2*x^2 +true +``` +""" function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T if p == q return false end - l = 0 - if length(p.exps) < length(q.exps) - l = length(p.exps) - else - l = length(q.exps) - end + l = min(length(p.exps), length(q.exps)) sort_terms!(p) sort_terms!(q) for i in 1:l - if word_gt(q.exps[i], p.exps[i]) + c = word_cmp(q.exps[i], p.exps[i]) + if c > 0 return true - elseif word_gt(p.exps[i], q.exps[i]) + elseif c < 0 return false - elseif p.coeffs[i] == q.coeffs[i] + elseif p.coeffs[i] != q.coeffs[i] return p.coeffs[i] < q.coeffs[i] end end @@ -400,10 +428,6 @@ function isless(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T end end -function <(p::FreeAssAlgElem{T}, q::FreeAssAlgElem{T}) where T - return isless(p, q) -end - ############################################################################### # # Arithmetic From a3dd37939d30ccb04a29d4481f62c6a08eb7e42a Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 6 Jul 2023 14:44:52 +0200 Subject: [PATCH 69/80] worked in the remarks for AhoCorasick.jl --- src/generic/AhoCorasick.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 76d0a3a8f7..9c9fad0db1 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -202,11 +202,11 @@ function insert_keyword!(aut::AhoCorasickAutomaton, keyword::Word, index::Int) end @doc """ - search(automaton::AhoCorasickAutomaton, word) + search(automaton::AhoCorasickAutomaton, word::Word) Search for the first occurrence of a keyword that is stored in `automaton` in the given `word`. """ -function search(automaton::AhoCorasickAutomaton, word) +function search(automaton::AhoCorasickAutomaton, word::Word) current_state = 1 result = AhoCorasickMatch(typemax(Int), typemax(Int), []) for i in 1:length(word) @@ -228,7 +228,7 @@ function search(automaton::AhoCorasickAutomaton, word) ) end end - if result.keyword == [] + if isempty(result.keyword) return nothing end return result From 29d7b3798b32e52bb7a91e9f7a2114fbeaff2da7 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 6 Jul 2023 14:52:13 +0200 Subject: [PATCH 70/80] worked in the remarks for FreeAssAlgebraGroebner.jl, in particular changed some things about the obstruction functions and made the use of Monomial more consistent --- src/generic/FreeAssAlgebraGroebner.jl | 157 +++++++++++++------------- 1 file changed, 78 insertions(+), 79 deletions(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index e972ff8f3d..033a95f656 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -14,7 +14,6 @@ export normal_form_weak import DataStructures: PriorityQueue, enqueue!, dequeue! -const groebner_debug_level = 0 const Monomial = Vector{Int} abstract type Obstruction{T} end @@ -23,20 +22,32 @@ Represents the overlap of the leading term of the two polynomials `first_poly` and `second_poly`. Here, `first_index` and `second_index` are the indices of `first_poly` and `second_poly` respectively in the Groebner basis. -The `pre_and_suffixes` are of the form -[w_i, w_i'; w_j, w_j'] -where `i = first_index`, `j = second_index` and satisfy that -w_i g_i w_i' = w_j g_j w_j' +The first and second prefix and suffix satisfy +first_prefix g_i first_suffix = second_prefix g_j second_suffix where `g_i` is `first_poly` and `g_j` is `second_poly`. """ struct ObstructionTriple{T} <: Obstruction{T} first_poly::FreeAssAlgElem{T} second_poly::FreeAssAlgElem{T} - pre_and_suffixes::NTuple{4,Monomial} + first_prefix::Monomial + first_suffix::Monomial + second_prefix::Monomial + second_suffix::Monomial first_index::Int second_index::Int end +function ObstructionTriple{T}(first_poly::FreeAssAlgElem{T}, + second_poly::FreeAssAlgElem{T}, + pre_and_suffixes::NTuple{4, Monomial}, + first_index::Int, + second_index::Int + ) where T + return ObstructionTriple{T}(first_poly, second_poly, pre_and_suffixes[1], + pre_and_suffixes[2], pre_and_suffixes[3], + pre_and_suffixes[4], first_index, second_index) +end + function FreeAssAlgElem{T}(R::FreeAssAlgebra{T}, mon::Monomial) where {T} return FreeAssAlgElem{T}(R, [one(base_ring(R))], [mon], 1) end @@ -47,20 +58,20 @@ of the leading terms of p and q defined by o TODO documentation """ function common_multiple_leading_term(ot::ObstructionTriple{T}) where {T} - return FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * + return FreeAssAlgElem{T}(parent(ot.first_poly), ot.first_prefix) * FreeAssAlgElem{T}(parent(ot.first_poly), _leading_word(ot.first_poly)) * - FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) + FreeAssAlgElem{T}(parent(ot.first_poly), ot.first_suffix) end function s_polynomial(ot::ObstructionTriple{T}) where {T} first_term = - FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[1]) * + FreeAssAlgElem{T}(parent(ot.first_poly), ot.first_prefix) * ot.first_poly * - FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[2]) + FreeAssAlgElem{T}(parent(ot.first_poly), ot.first_suffix) second_term = - FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[3]) * + FreeAssAlgElem{T}(parent(ot.first_poly), ot.second_prefix) * ot.second_poly * - FreeAssAlgElem{T}(parent(ot.first_poly), ot.pre_and_suffixes[4]) + FreeAssAlgElem{T}(parent(ot.first_poly), ot.second_suffix) return inv(leading_coefficient(ot.first_poly)) * first_term - inv(leading_coefficient(ot.second_poly)) * second_term end @@ -71,7 +82,7 @@ function _leading_word(a::FreeAssAlgElem{T}) where {T} end @doc """ -gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) + gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) If an element of the Groebner basis that is stored in `aut` divides `a`, return (true, a1, a2, keyword_index), where `keyword_index` is the index of the @@ -103,7 +114,7 @@ function normal_form( aut::AhoCorasickAutomaton, ) where {T} R = parent(f) - rexps = Vector{Int}[] + rexps = Monomial[] rcoeffs = T[] while length(f) > 0 ok, left, right, match_index = gb_divides_leftmost(f.exps[1], aut) @@ -127,7 +138,7 @@ function normal_form( R = parent(f) s = length(g) rcoeffs = T[] - rexps = Vector{Int}[] + rexps = Monomial[] while length(f) > 0 i = 1 @label again @@ -200,11 +211,11 @@ end ## checks whether there is an overlap between a and b at position i of b # such that b[i:length(b)] = a[1:length(b)-i] -function check_left_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) +function check_left_overlap(a::Monomial, b::Monomial, i::Int) + if length(b) - i >= length(a) + return false # this is a not a left overlap but might be a center overlap + end for j in 0:(length(b) - i) - if j >= length(a) - return false # this is a center overlap - end if b[i + j] != a[j + 1] return false end @@ -218,8 +229,8 @@ end # where length(w_1) < length(b) and length(w_2^') < length(a) # the return vector is of the form [(w_1, w_2^'), ...] # if w_1 or w_2^' is empty, the corresponding obstruction is not returned -function left_obstructions(a::Vector{Int}, b::Vector{Int}) - v = Tuple{Vector{Int},Vector{Int}}[] +function left_obstructions(a::Monomial, b::Monomial) + v = Tuple{Monomial,Monomial}[] for i in 2:length(b) if check_left_overlap(a, b, i) if length(b) - i + 2 <= length(a) # w_2^' should not be empty! @@ -237,27 +248,22 @@ end # where length(w_1^') < length(b) and length(w_2) < length(a) # the return vector is of the form [(w_2, w_1^'), ...] # if w_1^' or w_2 is empty, the corresponding obstruction is not returned -function right_obstructions(a::Vector{Int}, b::Vector{Int}) +function right_obstructions(a::Monomial, b::Monomial) return left_obstructions(b, a) end ### -# check, whether a is a true subword of b at index i +# check whether a is a subword of b starting at index i +# a == b is also allowed function check_center_overlap(a::Vector{Int}, b::Vector{Int}, i::Int) - for j in 1:length(a) - if i + j - 1 > length(b) - return false - end - if a[j] != b[i + j - 1] - return false - end - end - return true + i + length(a) - 1 <= length(b) || return false + return all(j -> a[j] == b[i + j - 1], 1:length(a)) end -function center_obstructions_first_in_second(a::Vector{Int}, b::Vector{Int}) - v = Tuple{Vector{Int},Vector{Int}}[] - for i in 1:length(b) + +function center_obstructions_first_in_second(a::Monomial, b::Monomial) + v = Tuple{Monomial,Monomial}[] + for i in 1:length(b)-length(a) + 1 if check_center_overlap(a, b, i) push!(v, (b[1:(i - 1)], b[(i + length(a)):length(b)])) end @@ -272,7 +278,7 @@ end # or # w_i b w_i^' = a # either or both of w_i and w_i^' can be empty -function center_obstructions(a::Vector{Int}, b::Vector{Int}) +function center_obstructions(a::Monomial, b::Monomial) if length(a) > length(b) return center_obstructions_first_in_second(b, a) else @@ -281,9 +287,9 @@ function center_obstructions(a::Vector{Int}, b::Vector{Int}) end # all non-trivial ones -function obstructions(a::Vector{Int}, b::Vector{Int}) +function obstructions(a::Monomial, b::Monomial) one = Int[] # the empty word - res = Tuple{Vector{Int},Vector{Int},Vector{Int},Vector{Int}}[] + res = Tuple{Monomial,Monomial,Monomial,Monomial}[] for x in center_obstructions_first_in_second(b, a) push!(res, (one, one, x[1], x[2])) end @@ -303,9 +309,9 @@ function obstructions(a::Vector{Int}, b::Vector{Int}) end # all non-trivial self obstructions -function obstructions(a::Vector{Int}) +function obstructions(a::Monomial) one = Int[] # the empty word - res = Tuple{Vector{Int},Vector{Int},Vector{Int},Vector{Int}}[] + res = Tuple{Monomial,Monomial,Monomial,Monomial}[] for x in left_obstructions(a, a) push!(res, (one, x[2], x[1], one)) end @@ -317,7 +323,7 @@ end # check whether w_2 = v w_1 for some word v -function is_subword_right(w_1::Vector{Int}, w_2::Vector{Int}) +function is_subword_right(w_1::Monomial, w_2::Monomial) if length(w_1) > length(w_2) return false end @@ -331,7 +337,7 @@ end # check whether w_2 = w_1 v for some word v -function is_subword_left(w_1::Vector{Int}, w_2::Vector{Int}) +function is_subword_left(w_1::Monomial, w_2::Monomial) if length(w_1) > length(w_2) return false end @@ -345,35 +351,32 @@ end ### -# check if for obs1 = (w_i, w_i^'; u_j, u_j^') and obs2 = (w_k, w_k^'; v_l, v_l^') +# check if obs2 is a subobstructon of obs1, i.e. if +# the second pre-and suffix of obs2 are right- respectively left subwords of the second pre-and suffix of obs1. +# In other words, check if for obs1 = (w_i, w_i^'; u_j, u_j^') and obs2 = (w_k, w_k^'; v_l, v_l^') # it holds that u_j == w v_l and u_j^' = v_l^' w^' for some w, w^' -# i.e. if obs2 is a subobstruction of obs1 # both w and w^' might be empty -function is_subobstruction(obs1::NTuple{4,Vector{Int}}, obs2::NTuple{4,Vector{Int}}) - if is_subword_right(obs2[3], obs1[3]) && is_subword_left(obs2[4], obs1[4]) - return true - else - return false - end +function is_subobstruction(obs1_second_prefix::Monomial, obs1_second_suffix::Monomial, + obs2_second_prefix::Monomial, obs2_second_suffix) + return is_subword_right(obs2_second_prefix, obs1_second_prefix) && is_subword_left(obs2_second_suffix, obs1_second_suffix) end function is_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where {T} - return is_subobstruction(obs1.pre_and_suffixes, obs2.pre_and_suffixes) - + return is_subobstruction(obs1.second_prefix, obs1.second_suffix, obs2.second_prefix, obs2.second_suffix) end """ if obs2 is a subobstruction of obs1, i.e. obs1[3] = w obs2[3] and obs1[4] = obs2[4]w', returns length(ww') -thus, if it returns 0 and obs2 is a subobstruction of obs1, they are equal (? is that true?) +thus, if it returns 0 and obs2 is a subobstruction of obs1, they are equal if obs2 is not a subobstruction of obs1 the return value is useless """ function get_diff_length_for_subobstruction( obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}, ) where {T} - return length(obs1.pre_and_suffixes[3]) - length(obs2.pre_and_suffixes[3]) + - length(obs1.pre_and_suffixes[4]) - length(obs2.pre_and_suffixes[4]) + return length(obs1.second_prefix) - length(obs2.second_prefix) + + length(obs1.second_suffix) - length(obs2.second_suffix) end # check whether there exists a (possibly empty) w^'' such that @@ -394,7 +397,7 @@ function has_overlap(g2, w2, u2) end function has_overlap(obs::ObstructionTriple{T}) where {T} - return has_overlap(obs.second_poly, obs.pre_and_suffixes[2], obs.pre_and_suffixes[4]) + return has_overlap(obs.second_poly, obs.first_suffix, obs.second_suffix) end function is_redundant( @@ -414,7 +417,7 @@ function is_redundant( return true elseif obs.first_index == o.first_index && get_diff_length_for_subobstruction(obs, o) == 0 && - word_gt(obs.pre_and_suffixes[1], o.pre_and_suffixes[1]) + word_gt(obs.first_prefix, o.first_prefix) return true end end @@ -434,23 +437,23 @@ function is_proper_multiple( if obs1.first_poly != obs2.first_poly || obs1.second_poly != obs2.second_poly #TODO compare indices instead? return false end - if is_subword_right(obs2.pre_and_suffixes[1], obs1.pre_and_suffixes[1]) && - is_subword_left(obs2.pre_and_suffixes[2], obs1.pre_and_suffixes[2]) - w = copy(obs1.pre_and_suffixes[1]) - w2 = copy(obs1.pre_and_suffixes[2]) - for _ in 1:length(obs2.pre_and_suffixes[1]) + if is_subword_right(obs2.first_prefix, obs1.first_prefix) && + is_subword_left(obs2.first_suffix, obs1.first_suffix) + w = copy(obs1.first_prefix) + w2 = copy(obs1.first_suffix) + for _ in 1:length(obs2.first_prefix) pop!(w) end - for _ in 1:length(obs2.pre_and_suffixes[2]) + for _ in 1:length(obs2.first_suffix) popfirst!(w2) end if length(w) + length(w2) == 0 return false end - @assert obs1.pre_and_suffixes[1] == vcat(w, obs2.pre_and_suffixes[1]) - @assert obs1.pre_and_suffixes[2] == vcat(obs2.pre_and_suffixes[2], w2) - return obs1.pre_and_suffixes[3] == vcat(w, obs2.pre_and_suffixes[3]) && - obs1.pre_and_suffixes[4] == vcat(obs2.pre_and_suffixes[4], w2) + @assert obs1.first_prefix == vcat(w, obs2.first_prefix) + @assert obs1.first_suffix == vcat(obs2.first_suffix, w2) + return obs1.second_prefix == vcat(w, obs2.second_prefix) && + obs1.second_suffix == vcat(obs2.second_suffix, w2) else return false end @@ -482,7 +485,7 @@ function is_redundant( w2 = [] for i in 1:length(obs.second_poly.exps[1]) word_to_check = - vcat(obs.pre_and_suffixes[3], obs.second_poly.exps[1], obs.pre_and_suffixes[4]) + vcat(obs.second_prefix, obs.second_poly.exps[1], obs.second_suffix) if check_center_overlap(newest_element.exps[1], word_to_check, i) w1 = word_to_check[1:(i - 1)] w2 = word_to_check[(i + length(newest_element.exps[1])):end] @@ -495,14 +498,14 @@ function is_redundant( obs1 = ObstructionTriple{T}( obs.first_poly, newest_element, - (obs.pre_and_suffixes[1], obs.pre_and_suffixes[2], w1, w2), + obs.first_prefix, obs.first_suffix, w1, w2, obs.first_index, newest_index, ) obs2 = ObstructionTriple{T}( obs.second_poly, newest_element, - (obs.pre_and_suffixes[3], obs.pre_and_suffixes[4], w1, w2), + obs.second_prefix, obs.second_suffix, w1, w2, obs.second_index, newest_index, ) @@ -588,12 +591,11 @@ end function groebner_basis_buchberger( g::Vector{FreeAssAlgElem{T}}, - reduction_bound = typemax(Int)::Int, - remove_redundancies = false + reduction_bound::Int = typemax(Int), + remove_redundancies::Bool = false ) where {T<:FieldElement} g = copy(g) # interreduce!(g) # on some small examples, this increases running time, so it might not be optimal to use this here - checked_obstructions = 0 nonzero_reductions = 0 # compute the aho corasick automaton # to make normal form computation more efficient @@ -612,9 +614,6 @@ function groebner_basis_buchberger( # step4 push!(g, Sp) insert_keyword!(aut, Sp.exps[1], length(g)) - if groebner_debug_level > 0 - println("adding new obstructions! checked $checked_obstructions so far") - end if nonzero_reductions >= reduction_bound return g end @@ -627,15 +626,15 @@ function groebner_basis_buchberger( end @doc """ - groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound = typemax(Int)::Int) + groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound::Int = typemax(Int), remove_redundancies::Bool = false) Compute a groebner basis for the ideal spanned by g. Stop when `reduction_bound` many non-zero entries have been added to the groebner basis. """ function groebner_basis( g::Vector{FreeAssAlgElem{T}}, - reduction_bound = typemax(Int)::Int, - remove_redundancies = false + reduction_bound::Int = typemax(Int), + remove_redundancies::Bool = false ) where {T<:FieldElement} return groebner_basis_buchberger(g, reduction_bound, remove_redundancies) end From 56c13051bfe9b083c68f375e1073aa0356961843 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Thu, 6 Jul 2023 14:53:09 +0200 Subject: [PATCH 71/80] added some test cases for overlap and obstruction functions in FreeAssAlgebraGroebner.jl --- test/generic/AhoCorasick-test.jl | 2 +- test/generic/FreeAssAlgebraGroebner-test.jl | 34 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/test/generic/AhoCorasick-test.jl b/test/generic/AhoCorasick-test.jl index 01c9084a97..70f45e0b7c 100644 --- a/test/generic/AhoCorasick-test.jl +++ b/test/generic/AhoCorasick-test.jl @@ -4,7 +4,7 @@ using AbstractAlgebra.Generic: AhoCorasickAutomaton, search, AhoCorasickMatch, a aut = aho_corasick_automaton(keywords) @test search(aut, [10, 4, 1, 2, 3, 4]) == AhoCorasickMatch(6, 1, [1, 2, 3, 4]) @test hash(search(aut, [10, 4, 1, 2, 3, 4])) == hash(AhoCorasickMatch(6, 1, [1, 2, 3, 4])) - @test isnothing(search(aut, [])) + @test isnothing(search(aut, Int[])) @test search(aut, [1, 5, 4, 1, 1, 1, 4, 4]) == AhoCorasickMatch(3, 2, [1, 5, 4]) @test search(aut, [1, 2, 3, 1, 4, 1, 2, 1, 4, 1, 2]) == AhoCorasickMatch(7, 3, [4, 1, 2]) @test search(aut, [2, 1, 2, 3, 1]) == AhoCorasickMatch(3, 4, [1, 2]) diff --git a/test/generic/FreeAssAlgebraGroebner-test.jl b/test/generic/FreeAssAlgebraGroebner-test.jl index 44192077a5..3e89daf20f 100644 --- a/test/generic/FreeAssAlgebraGroebner-test.jl +++ b/test/generic/FreeAssAlgebraGroebner-test.jl @@ -51,3 +51,37 @@ end # @test gb_divides_leftmost((x*s*t).exps[1], aut) == (false, [], [], -1) end +@testset "Generic.FreeAssociativeAlgebra.groebner.overlaps_and_obstructions" begin + w1 = [1, 1, 2, 1, 3] + w2 = [2, 1, 3, 4, 3, 4] + w3 = [1, 3, 4] + w4 = [1, 1, 2, 1] + w5 = [5, 1, 3, 4, 4, 2, 1] + @test AbstractAlgebra.Generic.check_left_overlap(w2, w1, 3) + @test !AbstractAlgebra.Generic.check_left_overlap(w1, w2, 3) + @test AbstractAlgebra.Generic.check_center_overlap(w3, w2, 2) + @test AbstractAlgebra.Generic.check_center_overlap(w4, w1, 1) + @test !AbstractAlgebra.Generic.check_left_overlap(w4, w1, 1) + R, (x, y, z) = FreeAssociativeAlgebra(QQ, ["x", "y", "z"]) + poly1 = x*y*x*x*z + poly2 = y*x*x*z*y*y + poly3 = x*y*x*y + poly4 = y*x*y*z + poly5 = x*y + poly6 = x*y*x*y*z*x*y + lw1 = AbstractAlgebra.Generic._leading_word(poly1) + lw2 = AbstractAlgebra.Generic._leading_word(poly2) + lw3 = AbstractAlgebra.Generic._leading_word(poly3) + lw4 = AbstractAlgebra.Generic._leading_word(poly4) + lw5 = AbstractAlgebra.Generic._leading_word(poly5) + lw6 = AbstractAlgebra.Generic._leading_word(poly6) + ot = AbstractAlgebra.Generic.ObstructionTriple{Rational{BigInt}}( poly1, poly2, [], [2, 2], [1], [], 1, 2) + + @test AbstractAlgebra.Generic.has_overlap(ot) + @test length(AbstractAlgebra.Generic.left_obstructions(lw2, lw1)) == 1 + @test isempty(AbstractAlgebra.Generic.left_obstructions(lw1, lw2)) + @test length(AbstractAlgebra.Generic.right_obstructions(lw3, lw4)) == 2 + @test isempty(AbstractAlgebra.Generic.left_obstructions(lw3, lw4)) + @test length(AbstractAlgebra.Generic.center_obstructions(lw5, lw6)) == 3 + @test length(AbstractAlgebra.Generic.get_obstructions([poly1, poly2])) == 2 +end From e433bd3b65199c0fa06ac59e52e20a1d1981606c Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 18 Jul 2023 12:29:39 +0200 Subject: [PATCH 72/80] removed a semicolon from the doctest --- src/generic/AhoCorasick.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 9c9fad0db1..a2fef59fbb 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -59,7 +59,7 @@ julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; julia> aut = Generic.aho_corasick_automaton(keywords); -julia> result = Generic.search(aut, [10, 4, 1, 2, 3, 4]); +julia> result = Generic.search(aut, [10, 4, 1, 2, 3, 4]) AhoCorasickMatch(6, 1, [1, 2, 3, 4]) julia> last_position(result) From 64a8871266d77a2740867ac374cbfc5a88001da5 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Tue, 18 Jul 2023 12:38:07 +0200 Subject: [PATCH 73/80] exported the new access functions for AhoCorasickMatch and fixed the doctest --- src/generic/AhoCorasick.jl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index a2fef59fbb..c1bb0fb4ef 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -14,6 +14,12 @@ export insert_keyword! export search +export last_position + +export keyword_index + +export keyword + #export Word # import DataStructures: Queue, enqueue!, dequeue! @@ -62,13 +68,13 @@ julia> aut = Generic.aho_corasick_automaton(keywords); julia> result = Generic.search(aut, [10, 4, 1, 2, 3, 4]) AhoCorasickMatch(6, 1, [1, 2, 3, 4]) -julia> last_position(result) +julia> Generic.last_position(result) 6 -julia> keyword_index(result) +julia> Generic.keyword_index(result) 1 -julia> keyword(result) +julia> Generic.keyword(result) 4-element Vector{Int64}: 1 2 From 5e55c8735ac69e678b89b2f55060016b84df981b Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 24 Jul 2023 16:53:06 +0200 Subject: [PATCH 74/80] fixed the aho corasick doctest again --- src/generic/AhoCorasick.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index c1bb0fb4ef..1ab7e15ba9 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -66,7 +66,7 @@ julia> keywords = [[1, 2, 3, 4], [1, 5, 4], [4, 1, 2], [1, 2]]; julia> aut = Generic.aho_corasick_automaton(keywords); julia> result = Generic.search(aut, [10, 4, 1, 2, 3, 4]) -AhoCorasickMatch(6, 1, [1, 2, 3, 4]) +AbstractAlgebra.Generic.AhoCorasickMatch(6, 1, [1, 2, 3, 4]) julia> Generic.last_position(result) 6 From 9cce960a6405b7f1618043febf73a9f022fdc495 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 24 Jul 2023 21:31:59 +0200 Subject: [PATCH 75/80] fixed the failure of making docs by changing CurrentModule in free_associative_algebra.md from AbstractAlgebra to AbstractAlgebra.Generic --- docs/src/free_associative_algebra.md | 2 +- src/generic/FreeAssAlgebra.jl | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/src/free_associative_algebra.md b/docs/src/free_associative_algebra.md index fdeaa0d9c6..cca5add4df 100644 --- a/docs/src/free_associative_algebra.md +++ b/docs/src/free_associative_algebra.md @@ -1,5 +1,5 @@ ```@meta -CurrentModule = AbstractAlgebra +CurrentModule = AbstractAlgebra.Generic DocTestSetup = quote using AbstractAlgebra end diff --git a/src/generic/FreeAssAlgebra.jl b/src/generic/FreeAssAlgebra.jl index c249e03787..2f18087a2b 100644 --- a/src/generic/FreeAssAlgebra.jl +++ b/src/generic/FreeAssAlgebra.jl @@ -198,7 +198,6 @@ Return a vector of variable indices corresponding to the monomial of the $i$-th term of $a$. Term numbering begins at $1$, and the variable indices are given in the order of the variables for the ring. """ - function exponent_word(a::FreeAssAlgElem{T}, i::Int) where T <: RingElement @boundscheck 1 <= i <= length(a) || throw(ArgumentError("index out of range")) return a.exps[i] From 62185dfb9c21f7bca30af10f76305fe09eedf5d6 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Mon, 24 Jul 2023 22:33:25 +0200 Subject: [PATCH 76/80] removed trailing newline in .gitignore and unnecessary braces around type parameters in FreeAssAlgebraGroebner.jl --- .gitignore | 1 - src/generic/FreeAssAlgebraGroebner.jl | 42 +++++++++++++-------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 670a2123de..de729c50ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /docs/build/ Manifest.toml .DS_Store - diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index 033a95f656..ca41e7bfa1 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -48,7 +48,7 @@ function ObstructionTriple{T}(first_poly::FreeAssAlgElem{T}, pre_and_suffixes[4], first_index, second_index) end -function FreeAssAlgElem{T}(R::FreeAssAlgebra{T}, mon::Monomial) where {T} +function FreeAssAlgElem{T}(R::FreeAssAlgebra{T}, mon::Monomial) where T return FreeAssAlgElem{T}(R, [one(base_ring(R))], [mon], 1) end @@ -57,13 +57,13 @@ takes an obstruction triple (p, q, o) and returns the common multiple of the leading terms of p and q defined by o TODO documentation """ -function common_multiple_leading_term(ot::ObstructionTriple{T}) where {T} +function common_multiple_leading_term(ot::ObstructionTriple{T}) where T return FreeAssAlgElem{T}(parent(ot.first_poly), ot.first_prefix) * FreeAssAlgElem{T}(parent(ot.first_poly), _leading_word(ot.first_poly)) * FreeAssAlgElem{T}(parent(ot.first_poly), ot.first_suffix) end -function s_polynomial(ot::ObstructionTriple{T}) where {T} +function s_polynomial(ot::ObstructionTriple{T}) where T first_term = FreeAssAlgElem{T}(parent(ot.first_poly), ot.first_prefix) * ot.first_poly * @@ -77,7 +77,7 @@ function s_polynomial(ot::ObstructionTriple{T}) where {T} end # skip all of the extra length-checking -function _leading_word(a::FreeAssAlgElem{T}) where {T} +function _leading_word(a::FreeAssAlgElem{T}) where T return a.exps[1] end @@ -112,7 +112,7 @@ function normal_form( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton, -) where {T} +) where T R = parent(f) rexps = Monomial[] rcoeffs = T[] @@ -134,7 +134,7 @@ end function normal_form( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, -) where {T<:FieldElement} +) where T<:FieldElement R = parent(f) s = length(g) rcoeffs = T[] @@ -164,7 +164,7 @@ end function normal_form_weak( f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, -) where {T<:FieldElement} +) where T<:FieldElement R = parent(f) s = length(g) while length(f) > 0 @@ -186,13 +186,13 @@ function normal_form_weak( end @doc raw""" - interreduce!(g::Vector{FreeAssAlgElem{T}}) + interreduce!(g::Vector{FreeAssAlgElem{T}}) where T Interreduce a given Groebner basis with itself, i.e. compute the normal form of each element of `g` with respect to the rest of the elements and discard elements with normal form $0$ and duplicates. """ -function interreduce!(g::Vector{FreeAssAlgElem{T}}) where {T} +function interreduce!(g::Vector{FreeAssAlgElem{T}}) where T i = 1 while length(g) > 1 && length(g) >= i aut = AhoCorasickAutomaton([g_j.exps[1] for g_j in g[1:end .!= i]]) @@ -361,7 +361,7 @@ function is_subobstruction(obs1_second_prefix::Monomial, obs1_second_suffix::Mon return is_subword_right(obs2_second_prefix, obs1_second_prefix) && is_subword_left(obs2_second_suffix, obs1_second_suffix) end -function is_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where {T} +function is_subobstruction(obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}) where T return is_subobstruction(obs1.second_prefix, obs1.second_suffix, obs2.second_prefix, obs2.second_suffix) end @@ -374,7 +374,7 @@ if obs2 is not a subobstruction of obs1 the return value is useless function get_diff_length_for_subobstruction( obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}, -) where {T} +) where T return length(obs1.second_prefix) - length(obs2.second_prefix) + length(obs1.second_suffix) - length(obs2.second_suffix) end @@ -396,14 +396,14 @@ function has_overlap(g2, w2, u2) return length(w2) < length(lw2) + length(u2) end -function has_overlap(obs::ObstructionTriple{T}) where {T} +function has_overlap(obs::ObstructionTriple{T}) where T return has_overlap(obs.second_poly, obs.first_suffix, obs.second_suffix) end function is_redundant( obs::ObstructionTriple{T}, new_obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, -) where {T} +) where T # cases 4b + 4c for obstruction_pair in new_obstructions o = obstruction_pair[1] @@ -433,7 +433,7 @@ obs1 = [w w_i, w_i' w'; w w_j, w_j' w'] and obs2 = [w_i, w_i'; w_j, w_j'] function is_proper_multiple( obs1::ObstructionTriple{T}, obs2::ObstructionTriple{T}, -) where {T} +) where T if obs1.first_poly != obs2.first_poly || obs1.second_poly != obs2.second_poly #TODO compare indices instead? return false end @@ -465,7 +465,7 @@ check, whether obs is a proper multiple of any of the obstructions in the priori function is_proper_multiple( obs::ObstructionTriple{T}, obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, -) where {T} +) where T for obspair in obstructions obs2 = obspair[1] if is_proper_multiple(obs, obs2) @@ -480,7 +480,7 @@ function is_redundant( new_obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, newest_element::FreeAssAlgElem{T}, newest_index::Int, -) where {T} +) where T w1 = [] w2 = [] for i in 1:length(obs.second_poly.exps[1]) @@ -519,7 +519,7 @@ function remove_redundancies!( all_obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, newest_index::Int, newest_element::FreeAssAlgElem{T}, -) where {T} +) where T del_counter = 0 new_obstructions = PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}() old_obstructions = PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}() @@ -550,7 +550,7 @@ function remove_redundancies!( # TODO case 4e from Thm 4.1 in Kreuzer Xiu end -function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where {T} +function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T s = length(g) result = PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}() for i in 1:s, j in 1:i @@ -572,7 +572,7 @@ end function add_obstructions!( obstruction_queue::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, g::Vector{FreeAssAlgElem{T}}, -) where {T} +) where T s = length(g) for i in 1:s if i == s @@ -593,7 +593,7 @@ function groebner_basis_buchberger( g::Vector{FreeAssAlgElem{T}}, reduction_bound::Int = typemax(Int), remove_redundancies::Bool = false -) where {T<:FieldElement} +) where T<:FieldElement g = copy(g) # interreduce!(g) # on some small examples, this increases running time, so it might not be optimal to use this here nonzero_reductions = 0 @@ -635,6 +635,6 @@ function groebner_basis( g::Vector{FreeAssAlgElem{T}}, reduction_bound::Int = typemax(Int), remove_redundancies::Bool = false -) where {T<:FieldElement} +) where T<:FieldElement return groebner_basis_buchberger(g, reduction_bound, remove_redundancies) end From 80578a9cb30dd0da6ac4f81fc49df9b8699ff65f Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 25 Aug 2023 16:12:01 +0200 Subject: [PATCH 77/80] worked in comments by Max Horn --- src/generic/FreeAssAlgebraGroebner.jl | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index ca41e7bfa1..bcbe014b4c 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -91,7 +91,7 @@ keyword that divides `a` such that `a = a1 aut[keyword_index] a2`. function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) match = search(aut, a) if isnothing(match) - return (false, [], [], -1) + return (false, Word(), Word(), -1) end return ( true, @@ -101,11 +101,11 @@ function gb_divides_leftmost(a::Word, aut::AhoCorasickAutomaton) ) end -# implementation of the normal form function using aho corasick to check for all groebner basis elements in parallel +# implementation of the normal form function using aho corasick to check for all Groebner basis elements in parallel @doc """ normal_form(f::FreeAssAlgElem{T}, g::Vector{FreeAssAlgElem{T}}, aut::AhoCorasickAutomaton) -Assuming `g` is a groebner basis and `aut` an Aho-Corasick automaton for the elements of `g`, +Assuming `g` is a Groebner basis and `aut` an Aho-Corasick automaton for the elements of `g`, compute the normal form of `f` with respect to `g` """ function normal_form( @@ -246,10 +246,12 @@ end # find all non-trivial right-obstructions of a and b # i.e. all words w_2 and w_1^' s.t. a w_1^' = w_2 b # where length(w_1^') < length(b) and length(w_2) < length(a) -# the return vector is of the form [(w_2, w_1^'), ...] +# the return vector is of the form [(w_1^', w_2), ...] # if w_1^' or w_2 is empty, the corresponding obstruction is not returned function right_obstructions(a::Monomial, b::Monomial) - return left_obstructions(b, a) + left_obstr = left_obstructions(b, a) + right_obstr = [(w1, w2) for (w2, w1) in left_obstr] + return right_obstr end ### @@ -404,15 +406,13 @@ function is_redundant( obs::ObstructionTriple{T}, new_obstructions::PriorityQueue{Obstruction{T},FreeAssAlgElem{T}}, ) where T - # cases 4b + 4c + # case 4b from Thm. 4.2.22 in Non-Commutative Groebner Bases and Applications by Xingqiang Xiu for obstruction_pair in new_obstructions o = obstruction_pair[1] if o.second_index == obs.second_index if is_subobstruction(obs, o) - # case 4b if get_diff_length_for_subobstruction(obs, o) > 0 return true - # case 4c elseif obs.first_index > o.first_index return true elseif obs.first_index == o.first_index && @@ -600,18 +600,18 @@ function groebner_basis_buchberger( # compute the aho corasick automaton # to make normal form computation more efficient aut = AhoCorasickAutomaton([g_i.exps[1] for g_i in g]) - # step 1 + # step 1 from Thm. 5.2.12 Noncommutative Groebner Bases and Applications, Xingqiang Xiu obstruction_queue = get_obstructions(g) - while !isempty(obstruction_queue) + while !isempty(obstruction_queue) # step 2 obstruction = dequeue!(obstruction_queue) - # step3 + # step 3 S = s_polynomial(obstruction) Sp = normal_form(S, g, aut) # or normal_form_weak if iszero(Sp) continue end nonzero_reductions += 1 - # step4 + # step 4 push!(g, Sp) insert_keyword!(aut, Sp.exps[1], length(g)) if nonzero_reductions >= reduction_bound @@ -628,8 +628,10 @@ end @doc """ groebner_basis(g::Vector{FreeAssAlgElem{T}}, reduction_bound::Int = typemax(Int), remove_redundancies::Bool = false) -Compute a groebner basis for the ideal spanned by g. Stop when `reduction_bound` many -non-zero entries have been added to the groebner basis. +Compute a Groebner basis for the ideal generated by `g`. Stop when `reduction_bound` many +non-zero entries have been added to the Groebner basis. If the computation stops due to the bound being exceeded, +the result is in general not an actual Groebner basis, just a subset of one. However, whenever the normal form with +respect to this incomplete Groebner basis is `0`, it will also be `0` with respect to the full Groebner basis. """ function groebner_basis( g::Vector{FreeAssAlgElem{T}}, From 8f223b5b37a78170c85099e286a4efd63ab8f338 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 25 Aug 2023 20:29:10 +0200 Subject: [PATCH 78/80] removed the DataStructures dependency by adding the file PriorityQueue.jl and adding a simple Queue implementation to AhoCorasick.jl --- Project.toml | 2 - src/generic/AhoCorasick.jl | 20 +- src/generic/FreeAssAlgebraGroebner.jl | 8 +- src/generic/PriorityQueue.jl | 437 ++++++++++++++++++++++++++ 4 files changed, 458 insertions(+), 9 deletions(-) create mode 100644 src/generic/PriorityQueue.jl diff --git a/Project.toml b/Project.toml index 381eed4fec..af0650fa1a 100644 --- a/Project.toml +++ b/Project.toml @@ -3,7 +3,6 @@ uuid = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" version = "0.31.0" [deps] -DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -15,7 +14,6 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -DataStructures = "0.18.13" GroupsCore = "0.4.0" MacroTools = "0.5" Preferences = "1" diff --git a/src/generic/AhoCorasick.jl b/src/generic/AhoCorasick.jl index 1ab7e15ba9..93044f3395 100644 --- a/src/generic/AhoCorasick.jl +++ b/src/generic/AhoCorasick.jl @@ -21,11 +21,25 @@ export keyword_index export keyword #export Word -# -import DataStructures: Queue, enqueue!, dequeue! const Word = Vector{Int} +struct Queue{T} + data::Vector{T} +end + +function Queue{T}() where T + return Queue{T}(T[]) +end + +function enqueue!(q::Queue{T}, val::T) where T + push!(q.data, val) +end +function dequeue!(q::Queue) + return popfirst!(q.data) +end +isempty(q::Queue) = isempty(q.data) + @doc """ AhoCorasickAutomaton @@ -188,7 +202,7 @@ function construct_fail!(automaton::AhoCorasickAutomaton) automaton.fail[new_state] = lookup(automaton, state, k) if automaton.output[new_state][1] > automaton.output[automaton.fail[new_state]][1] - automaton.output[new_state] = automaton.output[automaton.fail[new_state]] # TODO check if this is the correct way to update output + automaton.output[new_state] = automaton.output[automaton.fail[new_state]] end end diff --git a/src/generic/FreeAssAlgebraGroebner.jl b/src/generic/FreeAssAlgebraGroebner.jl index ca41e7bfa1..8f439b77f6 100644 --- a/src/generic/FreeAssAlgebraGroebner.jl +++ b/src/generic/FreeAssAlgebraGroebner.jl @@ -12,7 +12,7 @@ export normal_form export normal_form_weak -import DataStructures: PriorityQueue, enqueue!, dequeue! +include("PriorityQueue.jl") const Monomial = Vector{Int} @@ -561,7 +561,7 @@ function get_obstructions(g::Vector{FreeAssAlgElem{T}}) where T end for o in obs triple = ObstructionTriple{T}(g[i], g[j], o, i, j) - enqueue!(result, triple, common_multiple_leading_term(triple)) + push!(result, triple => common_multiple_leading_term(triple)) end end # TODO maybe here some redundancies can be removed too, check Kreuzer Xiu @@ -582,7 +582,7 @@ function add_obstructions!( end for o in obs triple = ObstructionTriple{T}(g[i], g[s], o, i, s) - enqueue!(obstruction_queue, triple, common_multiple_leading_term(triple)) + push!(obstruction_queue, triple => common_multiple_leading_term(triple)) end end #remove_redundancies!(obstruction_queue, s, g[s]) #TODO too slow in practice @@ -603,7 +603,7 @@ function groebner_basis_buchberger( # step 1 obstruction_queue = get_obstructions(g) while !isempty(obstruction_queue) - obstruction = dequeue!(obstruction_queue) + obstruction = popfirst!(obstruction_queue)[1] # step3 S = s_polynomial(obstruction) Sp = normal_form(S, g, aut) # or normal_form_weak diff --git a/src/generic/PriorityQueue.jl b/src/generic/PriorityQueue.jl new file mode 100644 index 0000000000..442cc8ff03 --- /dev/null +++ b/src/generic/PriorityQueue.jl @@ -0,0 +1,437 @@ +# The contents of this file were taken from the Package DataStructures.jl: https://juliacollections.github.io/DataStructures.jl/latest/ + +# This file contains code that was formerly a part of Julia. License is MIT: http://julialang.org/license + +# PriorityQueue +# ------------- + +using Base: Ordering, ForwardOrdering, Forward, ReverseOrdering, Reverse, lt + +# Binary heap indexing +heapleft(i::Integer) = 2i +heapright(i::Integer) = 2i + 1 +heapparent(i::Integer) = div(i, 2) + +""" + PriorityQueue{K, V}([ord]) + +Construct a new `PriorityQueue`, with keys of type `K` and values/priorities +of type `V`. If an order is not given, the priority queue is min-ordered using +the default comparison for `V`. + +A `PriorityQueue` acts like a `Dict`, mapping values to their +priorities. New elements are added using `push!` and retrieved +using `popfirst!` or `popat!` based on their priority. + +Parameters +--------- + +`K::Type` Data type for the keys + +`V::Type` Data type for the values/priorities + +`ord::Base.Ordering` Priority queue ordering + +# Examples +```jldoctest +julia> PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: + "c" => 1 + "a" => 2 + "b" => 3 +``` +""" +struct PriorityQueue{K,V,O<:Ordering} <: AbstractDict{K,V} + # Binary heap of (element, priority) pairs. + xs::Vector{Pair{K,V}} + o::O + + # Map elements to their index in xs + index::Dict{K, Int} + + function PriorityQueue{K,V,O}(o::O) where {K,V,O<:Ordering} + new{K,V,O}(Vector{Pair{K,V}}(), o, Dict{K, Int}()) + end + + PriorityQueue{K, V, O}(xs::Vector{Pair{K,V}}, o::O, index::Dict{K, Int}) where {K,V,O<:Ordering} = new(xs, o, index) + + function PriorityQueue{K,V,O}(o::O, itr) where {K,V,O<:Ordering} + xs = Vector{Pair{K,V}}(undef, length(itr)) + index = Dict{K, Int}() + for (i, (k, v)) in enumerate(itr) + xs[i] = Pair{K,V}(k, v) + if haskey(index, k) + throw(ArgumentError("PriorityQueue keys must be unique")) + end + index[k] = i + end + pq = new{K,V,O}(xs, o, index) + + # heapify + for i in heapparent(length(pq.xs)):-1:1 + percolate_down!(pq, i) + end + + return pq + end +end + +# A copy constructor +PriorityQueue(xs::Vector{Pair{K,V}}, o::O, index::Dict{K, Int}) where {K,V,O<:Ordering} = + PriorityQueue{K,V,O}(xs, o, index) + +# Any-Any constructors +PriorityQueue(o::Ordering=Forward) = PriorityQueue{Any,Any,typeof(o)}(o) + +# Construction from Pairs +PriorityQueue(ps::Pair...) = PriorityQueue(Forward, ps) +PriorityQueue(o::Ordering, ps::Pair...) = PriorityQueue(o, ps) +PriorityQueue{K,V}(ps::Pair...) where {K,V} = PriorityQueue{K,V,ForwardOrdering}(Forward, ps) +PriorityQueue{K,V}(o::Ord, ps::Pair...) where {K,V,Ord<:Ordering} = PriorityQueue{K,V,Ord}(o, ps) + +# Construction specifying Key/Value types +# e.g., PriorityQueue{Int,Float64}([1=>1, 2=>2.0]) +PriorityQueue{K,V}(kv) where {K,V} = PriorityQueue{K,V}(Forward, kv) +function PriorityQueue{K,V}(o::Ord, kv) where {K,V,Ord<:Ordering} + try + PriorityQueue{K,V,Ord}(o, kv) + catch e + if not_iterator_of_pairs(kv) + throw(ArgumentError("PriorityQueue(kv): kv needs to be an iterator of tuples or pairs")) + else + rethrow(e) + end + end +end + +# Construction inferring Key/Value types from input +# e.g. PriorityQueue{} + +PriorityQueue(o1::Ordering, o2::Ordering) = throw(ArgumentError("PriorityQueue with two parameters must be called with an Ordering and an iterable of pairs")) +PriorityQueue(kv, o::Ordering=Forward) = PriorityQueue(o, kv) +function PriorityQueue(o::Ordering, kv) + try + _priority_queue_with_eltype(o, kv, eltype(kv)) + catch e + if not_iterator_of_pairs(kv) + throw(ArgumentError("PriorityQueue(kv): kv needs to be an iterator of tuples or pairs")) + else + rethrow(e) + end + end +end + +_priority_queue_with_eltype(o::Ord, ps, ::Type{Pair{K,V}} ) where {K,V,Ord} = PriorityQueue{ K, V,Ord}(o, ps) +_priority_queue_with_eltype(o::Ord, kv, ::Type{Tuple{K,V}}) where {K,V,Ord} = PriorityQueue{ K, V,Ord}(o, kv) +_priority_queue_with_eltype(o::Ord, ps, ::Type{Pair{K}} ) where {K, Ord} = PriorityQueue{ K,Any,Ord}(o, ps) +_priority_queue_with_eltype(o::Ord, kv, ::Type ) where { Ord} = PriorityQueue{Any,Any,Ord}(o, kv) + +## TODO: It seems impossible (or at least very challenging) to create the eltype below. +## If deemed possible, please create a test and uncomment this definition. +# _priority_queue_with_eltype{ D,Ord}(o::Ord, ps, ::Type{Pair{K,V} where K}) = PriorityQueue{Any, D,Ord}(o, ps) + + +""" + length(pq::PriorityQueue) + +Return the number of pairs (`k`, `v`) in the priority queue `pq`. +""" +Base.length(pq::PriorityQueue) = length(pq.xs) + +""" + isempty(pq::PriorityQueue) + +Verify if priority queue `pq` is empty. +""" +Base.isempty(pq::PriorityQueue) = isempty(pq.xs) + +""" + haskey(pq::PriorityQueue, key) + +Verify if priority queue `pq` has `key` in its keys. + +# Example + +```jldoctest +julia> pq = PriorityQueue("a" => 1, "b" => 2, "c" => 3) +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: + "a" => 1 + "b" => 2 + "c" => 3 + +julia> haskey(pq, "a") +true + +julia> haskey(pq, "e") +false +``` +""" +Base.haskey(pq::PriorityQueue, key) = haskey(pq.index, key) + +""" + first(pq::PriorityQueue) + +Return the lowest priority pair (`k`, `v`) from `pq` without removing it from the +priority queue. +""" +Base.first(pq::PriorityQueue) = first(pq.xs) + +function percolate_down!(pq::PriorityQueue, i::Integer) + x = pq.xs[i] + @inbounds while (l = heapleft(i)) <= length(pq) + r = heapright(i) + j = r > length(pq) || lt(pq.o, pq.xs[l].second, pq.xs[r].second) ? l : r + xj = pq.xs[j] + if lt(pq.o, xj.second, x.second) + pq.index[xj.first] = i + pq.xs[i] = xj + i = j + else + break + end + end + pq.index[x.first] = i + pq.xs[i] = x +end + + +function percolate_up!(pq::PriorityQueue, i::Integer) + x = pq.xs[i] + @inbounds while i > 1 + j = heapparent(i) + xj = pq.xs[j] + if lt(pq.o, x.second, xj.second) + pq.index[xj.first] = i + pq.xs[i] = xj + i = j + else + break + end + end + pq.index[x.first] = i + pq.xs[i] = x +end + +# Equivalent to percolate_up! with an element having lower priority than any other +function force_up!(pq::PriorityQueue, i::Integer) + x = pq.xs[i] + @inbounds while i > 1 + j = heapparent(i) + pq.index[pq.xs[j].first] = i + pq.xs[i] = pq.xs[j] + i = j + end + pq.index[x.first] = i + pq.xs[i] = x +end + +Base.getindex(pq::PriorityQueue, key) = pq.xs[pq.index[key]].second + +function Base.get(pq::PriorityQueue, key, default) + i = get(pq.index, key, 0) + i == 0 ? default : pq.xs[i].second +end + +function Base.get!(pq::PriorityQueue, key, default) + i = get(pq.index, key, 0) + if i == 0 + push!(pq, key=>default) + return default + else + return pq.xs[i].second + end +end + +# Change the priority of an existing element, or enqueue it if it isn't present. +function Base.setindex!(pq::PriorityQueue{K, V}, value, key) where {K,V} + i = get(pq.index, key, 0) + if i != 0 + @inbounds oldvalue = pq.xs[i].second + pq.xs[i] = Pair{K,V}(key, value) + if lt(pq.o, oldvalue, value) + percolate_down!(pq, i) + else + percolate_up!(pq, i) + end + else + push!(pq, key=>value) + end + return value +end + +""" + push!(pq::PriorityQueue{K,V}, pair::Pair{K,V}) where {K,V} + +Insert the a key `k` into a priority queue `pq` with priority `v`. + +# Examples + +```jldoctest +julia> a = PriorityQueue("a" => 1, "b" => 2, "c" => 3, "e" => 5) +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 4 entries: + "a" => 1 + "b" => 2 + "c" => 3 + "e" => 5 + +julia> push!(a, "d" => 4) +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 5 entries: + "a" => 1 + "b" => 2 + "c" => 3 + "d" => 4 + "e" => 5 +``` +""" +function Base.push!(pq::PriorityQueue{K,V}, pair::Pair{K,V}) where {K,V} + key = pair.first + if haskey(pq, key) + throw(ArgumentError("PriorityQueue keys must be unique")) + end + push!(pq.xs, pair) + pq.index[key] = length(pq) + percolate_up!(pq, length(pq)) + + return pq +end + +Base.push!(pq::PriorityQueue{K,V}, kv::Pair) where {K,V} = push!(pq, Pair{K,V}(kv.first, kv.second)) + +""" + popfirst!(pq::PriorityQueue) + +Remove and return the lowest priority key and value from a priority queue `pq` as a pair. + +# Examples + +```jldoctest +julia> a = PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: + "c" => 1 + "a" => 2 + "b" => 3 + +julia> popfirst!(a) +"c" => 1 + +julia> a +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 2 entries: + "a" => 2 + "b" => 3 +``` +""" +function Base.popfirst!(pq::PriorityQueue) + x = pq.xs[1] + y = pop!(pq.xs) + if !isempty(pq) + @inbounds pq.xs[1] = y + pq.index[y.first] = 1 + percolate_down!(pq, 1) + end + delete!(pq.index, x.first) + return x +end + +if isdefined(Base, :popat!) # We will overload if it is defined, else we define on our own + import Base: popat! +end + +function popat!(pq::PriorityQueue, key) + idx = pq.index[key] + force_up!(pq, idx) + popfirst!(pq) +end + +""" + delete!(pq::PriorityQueue, key) + +Delete the mapping for the given `key` in a priority queue `pq` and return the priority queue. + +# Examples + +```jldoctest +julia> q = PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: + "c" => 1 + "a" => 2 + "b" => 3 +julia> delete!(q, "b") +PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 2 entries: + "c" => 1 + "a" => 2 +``` +""" +function Base.delete!(pq::PriorityQueue, key) + popat!(pq, key) + return pq +end + +""" + empty!(pq::PriorityQueue) + +Reset priority queue `pq`. +""" +function Base.empty!(pq::PriorityQueue) + empty!(pq.xs) + empty!(pq.index) + return pq +end + +Base.empty(pq::PriorityQueue) = PriorityQueue(empty(pq.xs), pq.o, empty(pq.index)) + +#Base.merge!(d::SortedDict, other::PriorityQueue) = invoke(merge!, Tuple{AbstractDict, PriorityQueue}, d, other) + +function Base.merge!(d::AbstractDict, other::PriorityQueue) + next = iterate(other, false) + while next !== nothing + (k, v), state = next + d[k] = v + next = iterate(other, state) + end + return d +end + +function Base.merge!(combine::Function, d::AbstractDict, other::PriorityQueue) + next = iterate(other, false) + while next !== nothing + (k, v), state = next + d[k] = haskey(d, k) ? combine(d[k], v) : v + next = iterate(other, state) + end + return d +end + +# Opaque not to be exported. +mutable struct _PQIteratorState{K, V, O <: Ordering} + pq::PriorityQueue{K, V, O} + _PQIteratorState{K, V, O}(pq::PriorityQueue{K, V, O}) where {K, V, O <: Ordering} = new(pq) +end + +_PQIteratorState(pq::PriorityQueue{K, V, O}) where {K, V, O <: Ordering} = _PQIteratorState{K, V, O}(pq) + +# Unordered iteration through key value pairs in a PriorityQueue +# O(n) iteration. +function _iterate(pq::PriorityQueue, state) + (k, idx), i = state + return (pq.xs[idx], i) +end +_iterate(pq::PriorityQueue, ::Nothing) = nothing + +Base.iterate(pq::PriorityQueue, ::Nothing) = nothing + +function Base.iterate(pq::PriorityQueue, ordered::Bool=true) + if ordered + isempty(pq) && return nothing + state = _PQIteratorState(PriorityQueue(copy(pq.xs), pq.o, copy(pq.index))) + return popfirst!(state.pq), state + else + _iterate(pq, iterate(pq.index)) + end +end + +function Base.iterate(pq::PriorityQueue, state::_PQIteratorState) + isempty(state.pq) && return nothing + return popfirst!(state.pq), state +end + +Base.iterate(pq::PriorityQueue, i) = _iterate(pq, iterate(pq.index, i)) From 9a37fd10fb2f4dba6b2e411567ac7ef83e476666 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 25 Aug 2023 20:50:39 +0200 Subject: [PATCH 79/80] export PriorityQueue added to PriorityQueue.jl --- src/generic/PriorityQueue.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generic/PriorityQueue.jl b/src/generic/PriorityQueue.jl index 442cc8ff03..2acded425a 100644 --- a/src/generic/PriorityQueue.jl +++ b/src/generic/PriorityQueue.jl @@ -5,6 +5,8 @@ # PriorityQueue # ------------- +export PriorityQueue + using Base: Ordering, ForwardOrdering, Forward, ReverseOrdering, Reverse, lt # Binary heap indexing From 03527de2ec3a9f8eaa69fb5170691345e89e6c09 Mon Sep 17 00:00:00 2001 From: Julien Schanz Date: Fri, 25 Aug 2023 22:38:20 +0200 Subject: [PATCH 80/80] fixed PriorityQueue.jl doctests --- src/generic/PriorityQueue.jl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/generic/PriorityQueue.jl b/src/generic/PriorityQueue.jl index 2acded425a..131294352e 100644 --- a/src/generic/PriorityQueue.jl +++ b/src/generic/PriorityQueue.jl @@ -35,9 +35,9 @@ Parameters `ord::Base.Ordering` Priority queue ordering # Examples -```jldoctest -julia> PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: +```jldoctest; setup = :(using AbstractAlgebra) +julia> Generic.PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: "c" => 1 "a" => 2 "b" => 3 @@ -154,9 +154,9 @@ Verify if priority queue `pq` has `key` in its keys. # Example -```jldoctest -julia> pq = PriorityQueue("a" => 1, "b" => 2, "c" => 3) -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: +```jldoctest; setup = :(using AbstractAlgebra) +julia> pq = Generic.PriorityQueue("a" => 1, "b" => 2, "c" => 3) +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: "a" => 1 "b" => 2 "c" => 3 @@ -268,16 +268,16 @@ Insert the a key `k` into a priority queue `pq` with priority `v`. # Examples -```jldoctest -julia> a = PriorityQueue("a" => 1, "b" => 2, "c" => 3, "e" => 5) -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 4 entries: +```jldoctest; setup = :(using AbstractAlgebra) +julia> a = Generic.PriorityQueue("a" => 1, "b" => 2, "c" => 3, "e" => 5) +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 4 entries: "a" => 1 "b" => 2 "c" => 3 "e" => 5 julia> push!(a, "d" => 4) -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 5 entries: +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 5 entries: "a" => 1 "b" => 2 "c" => 3 @@ -306,9 +306,9 @@ Remove and return the lowest priority key and value from a priority queue `pq` a # Examples -```jldoctest -julia> a = PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: +```jldoctest; setup = :(using AbstractAlgebra) +julia> a = Generic.PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: "c" => 1 "a" => 2 "b" => 3 @@ -317,7 +317,7 @@ julia> popfirst!(a) "c" => 1 julia> a -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 2 entries: +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 2 entries: "a" => 2 "b" => 3 ``` @@ -351,14 +351,14 @@ Delete the mapping for the given `key` in a priority queue `pq` and return the p # Examples -```jldoctest -julia> q = PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: +```jldoctest; setup = :(using AbstractAlgebra) +julia> q = Generic.PriorityQueue(Base.Order.Forward, "a" => 2, "b" => 3, "c" => 1) +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 3 entries: "c" => 1 "a" => 2 "b" => 3 julia> delete!(q, "b") -PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 2 entries: +AbstractAlgebra.Generic.PriorityQueue{String, Int64, Base.Order.ForwardOrdering} with 2 entries: "c" => 1 "a" => 2 ```