diff --git a/examples/BinomIdeal.jl b/examples/BinomIdeal.jl index 8e0f5333bde6..3278293a02d4 100644 --- a/examples/BinomIdeal.jl +++ b/examples/BinomIdeal.jl @@ -1,10 +1,8 @@ module BinomialIdeal using Oscar -Oscar.example("QQAbAndPChars.jl") -using Main.QQAbModule -import Main.QQAbModule: my_product, QQAbElem, PCharSaturateAll, LatticeEqual -import lib4ti2_jll +using Oscar: PartialCharacter, have_same_span, partial_character, saturations +import Oscar.lib4ti2_jll using DelimitedFiles @@ -30,7 +28,7 @@ import Oscar.Singular: std, Ideal, lead_exponent #markov4ti2(L::ZZMatrix) #"=="(I::Singular.sideal,J::Singular.sideal) #isSubset(I::Singular.sideal,J::Singular.sideal) -#idealFromCharacter(P::PChar, R::Singular.PolyRing) +#idealFromCharacter(P::PartialCharacter, R::Singular.PolyRing) #partialCharacterFromIdeal(I::Singular.sideal, R::Singular.PolyRing) #cellularStandardMonomials(I::Singular.sideal) #witnessMonomials(I::Singular.sideal) @@ -536,14 +534,14 @@ end # ################################################################################### -function idealFromCharacter(P::PChar, R::Singular.PolyRing) +function idealFromCharacter(P::PartialCharacter, R::Singular.PolyRing) #input: partial character P and a polynomial ring R #output: the ideal $I_+(P)=\langle x^{u_+}- P(u)x^{u_-} \mid u \in P.A \rangle$ @assert Nemo.ncols(P.A)==Singular.nvars(R) #test if the domain of the partial character is the zero lattice Zero=matrix(FlintZZ,1,Nemo.ncols(P.A),zeros(Int64,1,Nemo.ncols(P.A))) - if Nemo.nrows(P.A)==1 && LatticeEqual(P.A,Zero)==true + if Nemo.nrows(P.A)==1 && have_same_span(P.A,Zero)==true return Ideal(R,zero(R)) end @@ -609,7 +607,7 @@ function idealFromCharacter(P::PChar, R::Singular.PolyRing) return saturate(I,Ideal(R,varProduct))[1] end -function makeBinomials(P::PChar, R::Singular.PolyRing) +function makeBinomials(P::PartialCharacter, R::Singular.PolyRing) #output: ideal generated by the binomials corresponding to the generators of the domain P.A of the partial character P #Note: This is not the ideal I_+(P)!! @@ -648,7 +646,7 @@ function partialCharacterFromIdeal(I::Singular.sideal, R::Singular.PolyRing) Delta=cell[2] #cell variables if size(Delta,1)==0 - P=PChar(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) + P=partial_character(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) return P end @@ -673,7 +671,7 @@ argument, so we make an array prodDeltaC, push to it, then splice it in #if Singular.ngens(J)==0 || (Singular.ngens(J)==1 && J[1]== R(0)) #return another trivial character #lattice has only one generator, namely the zero vector - P=PChar(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) + P=partial_character(matrix(FlintZZ,1,Singular.nvars(R), zeros(Int64,1,Singular.nvars(R))), [QQAbField()(1)], Set{Int64}(Delta)) return P end #now case if J \neq 0 @@ -712,7 +710,7 @@ argument, so we make an array prodDeltaC, push to it, then splice it in #delete zero rows in the hnf of vsMat so that we do not get problems when considering a #saturation vsMat=deleteZerosInHNF(vsMat) - P=PChar(vsMat, images , Set{Int64}(Delta)) + P=partial_character(vsMat, images , Set{Int64}(Delta)) return P end @@ -789,6 +787,11 @@ function cellularStandardMonomials(I::Singular.sideal) return Result end +function my_product(P::Array) + T = ntuple(x->P[x], length(P)) + return Iterators.product(T...) +end + function witnessMonomials(I::Singular.sideal) #input: cellular binomial ideal #output: M_{emb}(I) (not the ideal, but the generators of it in an array) @@ -802,7 +805,7 @@ function witnessMonomials(I::Singular.sideal) R=Singular.base_ring(I) Delta=cell[2] - #compute the PChar corresponding to I and the standard monomials of I \cap k[N^Delta] + #compute the PartialCharacter corresponding to I and the standard monomials of I \cap k[N^Delta] P=partialCharacterFromIdeal(I, R) M=cellularStandardMonomials(I) #array of standard monomials, this is our to-do list Memb=Singular.spoly[] #this will hold our set of witness monomials @@ -886,7 +889,7 @@ function cellularAssociatedPrimes(I::Singular.sideal) Im=Singular.quotient(I,Ideal(I.base_ring,m)) Pm=partialCharacterFromIdeal(Im,Im.base_ring) #now compute all saturations of the partial character Pm - PmSat=PCharSaturateAll(Pm) + PmSat=saturations(Pm) for P in PmSat Ass=[Ass; (idealFromCharacter(P, I.base_ring)+idealDeltaC)] end @@ -922,7 +925,7 @@ function cellularMinimalAssociatedPrimes(I::Singular.sideal) end P=partialCharacterFromIdeal(I,I.base_ring) - PSat=PCharSaturateAll(P) + PSat=saturations(P) minAss=Array{Singular.sideal}[] #this will hold the set of minimal associated primes #construct the ideal (x_i \mid i \in \Delta^c) diff --git a/examples/BinomIdeal2.jl b/examples/BinomIdeal2.jl index 18b3100d1fad..ec0e0c037bc2 100644 --- a/examples/BinomIdeal2.jl +++ b/examples/BinomIdeal2.jl @@ -1,9 +1,7 @@ module BinomialIdeal using Oscar -Oscar.include("../experimental/Rings/QQAbAndPChars.jl") -using Oscar.QQAbModule -import Oscar.QQAbModule: QQAbElem, have_same_span +using Oscar: PartialCharacter, have_same_span, partial_character import Oscar.lib4ti2_jll #using DelimitedFiles @@ -31,7 +29,7 @@ import Oscar.Singular: std, Ideal, lead_exponent #markov4ti2(L::ZZMatrix) #"=="(I::Singular.sideal,J::Singular.sideal) #isSubset(I::Singular.sideal,J::Singular.sideal) -#idealFromCharacter(P::PChar, R::Singular.PolyRing) +#idealFromCharacter(P::PartialCharacter, R::Singular.PolyRing) #partialCharacterFromIdeal(I::Singular.sideal, R::Singular.PolyRing) #cellularStandardMonomials(I::Singular.sideal) #witnessMonomials(I::Singular.sideal) @@ -795,8 +793,8 @@ function cellularStandardMonomials(I::Singular.sideal) end function my_product(P::Array) - T = ntuple(x->P[x], length(P)) - return Iterators.product(T...) + T = ntuple(x->P[x], length(P)) + return Iterators.product(T...) end function witnessMonomials(I::Singular.sideal) @@ -812,7 +810,7 @@ function witnessMonomials(I::Singular.sideal) R=Singular.base_ring(I) Delta=cell[2] - #compute the PChar corresponding to I and the standard monomials of I \cap k[N^Delta] + #compute the PartialCharacter corresponding to I and the standard monomials of I \cap k[N^Delta] P=partialCharacterFromIdeal(I, R) M=cellularStandardMonomials(I) #array of standard monomials, this is our to-do list Memb=Singular.spoly[] #this will hold our set of witness monomials @@ -932,7 +930,7 @@ function cellularMinimalAssociatedPrimes(I::Singular.sideal) end P=partialCharacterFromIdeal(I,I.base_ring) - PSat=PCharSaturateAll(P) + PSat=saturations(P) minAss=Array{Singular.sideal}[] #this will hold the set of minimal associated primes #construct the ideal (x_i \mid i \in \Delta^c) diff --git a/experimental/Experimental.jl b/experimental/Experimental.jl index d4f9c803efc0..d86047fd06f7 100644 --- a/experimental/Experimental.jl +++ b/experimental/Experimental.jl @@ -5,7 +5,6 @@ const expdir = joinpath(@__DIR__, "../experimental") const oldexppkgs = [ "GModule", - "Rings", "Schemes", "FTheoryTools" # Must be loaded after the schemes. ] diff --git a/experimental/NoExperimental_whitelist_.jl b/experimental/NoExperimental_whitelist_.jl index 0f193288cd64..89d4e2707125 100644 --- a/experimental/NoExperimental_whitelist_.jl +++ b/experimental/NoExperimental_whitelist_.jl @@ -7,6 +7,5 @@ whitelist = String[ "GModule", # many doctest failures and `MethodError: no method matching numerator(::QQPolyRingElem)` "InvariantTheory", # `undefined binding 'linearly_reductive_group' in `@docs` block in src/InvariantTheory/reductive_groups.md:53-55` and more docs errors` "ModStd", # `MethodError: no method matching monomial(::QQMPolyRing, ::Vector{Int64})` and many similar errors - "Rings", # used by Rings/binomial_ideals.jl, see https://github.com/oscar-system/Oscar.jl/blob/13282dfd07b6aee58e433a45353f48261cda787b/src/Oscar.jl#L268 "Schemes", # TODO: untangle src/AlgebraicGeometry/Schemes/ and experimental/Schemes/ ] diff --git a/experimental/Rings/QQAbAndPChars.jl b/experimental/Rings/QQAbAndPChars.jl deleted file mode 100644 index 2e6d7237c3fc..000000000000 --- a/experimental/Rings/QQAbAndPChars.jl +++ /dev/null @@ -1,134 +0,0 @@ -module QQAbModule - -using ..Oscar - -import Oscar: IJuliaMime - -############################################################################### -# -# Partial character functions -# -############################################################################### - -mutable struct PartialCharacter{T} - #A has generators of the lattice in rows - A::ZZMatrix - #images of the generators are saved in b - b::Vector{T} - #Delta are the indices of the cellular variables of the associated ideal - #(the partial character is a partial character on Z^Delta) - D::Set{Int64} - function PartialCharacter{T}() where T - return new{T}() - end - - function PartialCharacter{T}(mat::ZZMatrix, vals::Vector{T}) where T - z = new{T}() - z.A = mat - z.b = vals - return z - end -end - -function partial_character(A::ZZMatrix, vals::Vector{T}, variables::Set{Int} = Set{Int}()) where T <: FieldElem - @assert nrows(A) == length(vals) - z = PartialCharacter{T}(A, vals) - if !isempty(variables) - z.D = variables - end - return z -end - -function (Chi::PartialCharacter)(b::ZZMatrix) - @assert nrows(b) == 1 - @assert Nemo.ncols(b) == Nemo.ncols(Chi.A) - s = solve(Chi.A, b, side = :left) - return evaluate(FacElem(Dict([(Chi.b[i], s[1, i]) for i = 1:length(Chi.b)]))) -end - -function (Chi::PartialCharacter)(b::Vector{ZZRingElem}) - return Chi(matrix(FlintZZ, 1, length(b), b)) -end - -function have_same_domain(P::PartialCharacter, Q::PartialCharacter) - return have_same_span(P.A, Q.A) -end - -function have_same_span(A::ZZMatrix, B::ZZMatrix) - @assert ncols(A) == ncols(B) - return hnf(A) == hnf(B) -end - - - -function Base.:(==)(P::PartialCharacter{T}, Q::PartialCharacter{T}) where T <: FieldElem - if P === Q - return true - end - if !have_same_domain(P, Q) - return false - end - #now test if the values taken on the generators of the lattices are equal - for i = 1:nrows(P.A) - TestVec = view(P.A, i:i, 1:Nemo.ncols(P.A)) - if P(TestVec) != Q(TestVec) - return false - end - end - return true -end - -function saturations(L::PartialCharacter{QQAbElem{T}}) where T - #computes all saturations of the partial character L - res = PartialCharacter{QQAbElem{T}}[] - - #first handle case where the domain of the partial character is the zero lattice - #in this case return L - if iszero(L.A) - push!(res, L) - return res - end - - #now not trivial case - H = hnf(transpose(L.A)) - H = view(H, 1:ncols(H), 1:ncols(H)) - i, d = pseudo_inv(H) #iH = d I_n - #so, saturation is i' * H // d - S = divexact(transpose(i)*L.A, d) - - B = Vector{Vector{QQAbElem{T}}}() - for k = 1:nrows(H) - c = i[1, k] - for j = 2:ncols(H) - c = gcd(c, i[j, k]) - if isone(c) - break - end - end - mu = evaluate(FacElem(Dict(Tuple{QQAbElem{T}, ZZRingElem}[(L.b[j], div(i[j, k], c)) for j = 1:ncols(H)]))) - mu1 = roots(mu, Int(div(d, c))) - push!(B, mu1) - end - it = Hecke.cartesian_product_iterator(UnitRange{Int}[1:length(x) for x in B]) - vT = Vector{Vector{QQAbElem{T}}}() - for I in it - push!(vT, [B[i][I[i]] for i = 1:length(B)]) - end - - for k = 1:length(vT) - #check if PChar(S,vT[k],L.D) puts on the right value on the lattice generators of L - Pnew = partial_character(S, vT[k], L.D) - flag = true #flag if value on lattice generators is right - for i = 1:Nemo.nrows(L.A) - if Pnew(sub(L.A, i:i ,1:Nemo.ncols(L.A))) != L.b[i] - flag = false - println("found wrong saturation (for information), we delete it") - end - end - if flag - push!(res, Pnew) - end - end - return res -end -end diff --git a/experimental/Rings/Rings.jl b/experimental/Rings/Rings.jl deleted file mode 100644 index 535eca01c206..000000000000 --- a/experimental/Rings/Rings.jl +++ /dev/null @@ -1 +0,0 @@ -include("QQAbAndPChars.jl") diff --git a/src/Oscar.jl b/src/Oscar.jl index 70bc9ebfded1..7fd65e7e602a 100644 --- a/src/Oscar.jl +++ b/src/Oscar.jl @@ -265,8 +265,6 @@ include("Serialization/main.jl") include("../experimental/Experimental.jl") -include("Rings/binomial_ideals.jl") # uses QQAbModule from experimental/Rings/QQAbAndPChars.jl - if is_dev # include("../examples/ModStdNF.jl") # include("../examples/ModStdQ.jl") diff --git a/src/Rings/AbelianClosure.jl b/src/Rings/AbelianClosure.jl index e97129d83245..ab42a9a0d47e 100644 --- a/src/Rings/AbelianClosure.jl +++ b/src/Rings/AbelianClosure.jl @@ -19,7 +19,7 @@ # Note that there are two possibilities construct a nth root of unity when n is # even and n%4!=0. either we can construct the field Q(z_n) or we take -z_(n/2) # as a primitive n-th root. to change between these two options, use -# PCharSaturateAll with allroots or allrootsNew (change this in the code) +# saturations with allroots or allrootsNew (change this in the code) abstract type CyclotomicField end @@ -1153,7 +1153,7 @@ function square_root_in_cyclotomic_field(F::QQAbField, n::Int, N::Int) end """ - quadratic_irrationality_info(a::QQAbModule.QQAbElem) + quadratic_irrationality_info(a::QQAbElem) Return `(x, y, n)`, where `x`, `y` are of type `QQFieldElem` and `n` is a squarefree integer, such that `a == x + y sqrt(n)` holds. diff --git a/src/Rings/Rings.jl b/src/Rings/Rings.jl index d6c23a41e8d2..e247fec42a6b 100644 --- a/src/Rings/Rings.jl +++ b/src/Rings/Rings.jl @@ -30,6 +30,7 @@ include("FunctionField.jl") include("AbelianClosure.jl") include("AlgClosureFp.jl") include("Laurent.jl") +include("binomial_ideals.jl") include("PBWAlgebra.jl") include("PBWAlgebraQuo.jl") diff --git a/src/Rings/binomial_ideals.jl b/src/Rings/binomial_ideals.jl index 7df9a8f23bc3..85a1e8c73a55 100644 --- a/src/Rings/binomial_ideals.jl +++ b/src/Rings/binomial_ideals.jl @@ -359,13 +359,130 @@ end # ################################################################################### -function ideal_from_character(P::QQAbModule.PartialCharacter, R::MPolyRing) +mutable struct PartialCharacter{T} + A::ZZMatrix # generators of the lattice in rows + b::Vector{T} # images of the generators + D::Set{Int64} # indices of the cellular variables of the associated ideal + # (the partial character is a partial character on Z^D) + + function PartialCharacter{T}() where {T} + return new{T}() + end + + function PartialCharacter{T}(mat::ZZMatrix, vals::Vector{T}) where {T} + return new{T}(mat, vals) + end +end + +function partial_character( + A::ZZMatrix, vals::Vector{T}, variables::Set{Int}=Set{Int}() +) where {T<:FieldElem} + @assert nrows(A) == length(vals) + z = PartialCharacter{T}(A, vals) + if !isempty(variables) + z.D = variables + end + return z +end + +function (Chi::PartialCharacter)(b::ZZMatrix) + @assert nrows(b) == 1 + @assert Nemo.ncols(b) == Nemo.ncols(Chi.A) + s = solve(Chi.A, b; side=:left) + return evaluate(FacElem(Dict((Chi.b[i], s[1, i]) for i in 1:length(Chi.b)))) +end + +function (Chi::PartialCharacter)(b::Vector{ZZRingElem}) + return Chi(matrix(FlintZZ, 1, length(b), b)) +end + +function have_same_domain(P::PartialCharacter, Q::PartialCharacter) + return have_same_span(P.A, Q.A) +end + +function have_same_span(A::ZZMatrix, B::ZZMatrix) + @assert ncols(A) == ncols(B) + return hnf(A) == hnf(B) +end + +function Base.:(==)(P::PartialCharacter{T}, Q::PartialCharacter{T}) where {T<:FieldElem} + P === Q && return true + !have_same_domain(P, Q) && return false + + # now test if the values taken on the generators of the lattices are equal + for i in 1:nrows(P.A) + test_vec = view(P.A, i:i, :) + if P(test_vec) != Q(test_vec) + return false + end + end + return true +end + +function saturations(L::PartialCharacter{QQAbElem{T}}) where {T} + #computes all saturations of the partial character L + res = PartialCharacter{QQAbElem{T}}[] + + #first handle case where the domain of the partial character is the zero lattice + #in this case return L + if iszero(L.A) + push!(res, L) + return res + end + + #now non-trivial case + H = hnf(transpose(L.A)) + H = view(H, 1:ncols(H), 1:ncols(H)) + i, d = pseudo_inv(H) #iH = d I_n + #so, saturation is i' * H // d + S = divexact(transpose(i) * L.A, d) + + B = Vector{Vector{QQAbElem{T}}}() + for k in 1:nrows(H) + c = i[1, k] + for j in 2:ncols(H) + c = gcd(c, i[j, k]) + if isone(c) + break + end + end + mu = evaluate( + FacElem(Dict{QQAbElem{T},ZZRingElem}((L.b[j], div(i[j, k], c)) for j in 1:ncols(H))) + ) + mu1 = roots(mu, Int(div(d, c))) + push!(B, mu1) + end + it = Hecke.cartesian_product_iterator(UnitRange{Int}[1:length(x) for x in B]) + vT = Vector{Vector{QQAbElem{T}}}() + for I in it + push!(vT, [B[i][I[i]] for i in 1:length(B)]) + end + + for k in 1:length(vT) + #check if partial_character(S,vT[k],L.D) puts on the right value on the lattice generators of L + Pnew = partial_character(S, vT[k], L.D) + flag = true #flag if value on lattice generators is right + for i in 1:Nemo.nrows(L.A) + if Pnew(sub(L.A, i:i, 1:Nemo.ncols(L.A))) != L.b[i] + flag = false + println("found wrong saturation (for information), we delete it") + end + end + if flag + push!(res, Pnew) + end + end + return res +end + + +function ideal_from_character(P::PartialCharacter, R::MPolyRing) #input: partial character P and a polynomial ring R #output: the ideal $I_+(P)=\langle x^{u_+}- P(u)x^{u_-} \mid u \in P.A \rangle$ @assert ncols(P.A) == nvars(R) #test if the domain of the partial character is the zero lattice - if isone(nrows(P.A)) && QQAbModule.have_same_span(P.A, zero_matrix(FlintZZ, 1, ncols(P.A))) + if isone(nrows(P.A)) && have_same_span(P.A, zero_matrix(FlintZZ, 1, ncols(P.A))) return ideal(R, zero(R)) end @@ -425,7 +542,7 @@ function ideal_from_character(P::QQAbModule.PartialCharacter, R::MPolyRing) return I; end -function _make_binomials(P::QQAbModule.PartialCharacter, R::MPolyRing) +function _make_binomials(P::PartialCharacter, R::MPolyRing) #output: ideal generated by the binomials corresponding to the generators of the domain P.A of the partial character P #Note: This is not the ideal I_+(P)!! @assert ncols(P.A) == nvars(R) @@ -465,7 +582,7 @@ function partial_character_from_ideal(I::MPolyIdeal, R::MPolyRing) Delta = cell[2] #cell variables if isempty(Delta) - return QQAbModule.partial_character(zero_matrix(FlintZZ, 1, nvars(R)), [one(QQAbModule.QQAb)], Set{Int64}()) + return partial_character(zero_matrix(FlintZZ, 1, nvars(R)), [one(QQAb)], Set{Int64}()) end #now consider the case where Delta is not empty @@ -481,7 +598,7 @@ function partial_character_from_ideal(I::MPolyIdeal, R::MPolyRing) end QQAbcl, = abelian_closure(QQ) if iszero(J) - return QQAbModule.partial_character(zero_matrix(FlintZZ, 1, nvars(R)), [one(QQAbcl)], Set{Int64}()) + return partial_character(zero_matrix(FlintZZ, 1, nvars(R)), [one(QQAbcl)], Set{Int64}()) end #now case if J \neq 0 #let ts be a list of minimal binomial generators for J @@ -510,7 +627,7 @@ function partial_character_from_ideal(I::MPolyIdeal, R::MPolyRing) i -= 1 end vs = view(vs, 1:i, 1:nvars(R)) - return QQAbModule.partial_character(vs, images, Set{Int64}(Delta)) + return partial_character(vs, images, Set{Int64}(Delta)) end ################################################################################### @@ -709,7 +826,7 @@ function cellular_associated_primes(I::MPolyIdeal{QQMPolyRingElem}, RQQAb::MPoly error("Input ideal is not cellular") end - associated_primes = Vector{MPolyIdeal{Generic.MPoly{QQAbModule.QQAbElem{AbsSimpleNumFieldElem}}}}() #this will hold the set of associated primes of I + associated_primes = Vector{MPolyIdeal{Generic.MPoly{QQAbElem{AbsSimpleNumFieldElem}}}}() #this will hold the set of associated primes of I R = base_ring(I) U = cellular_standard_monomials(I) #set of standard monomials @@ -727,7 +844,7 @@ function cellular_associated_primes(I::MPolyIdeal{QQMPolyRingElem}, RQQAb::MPoly Im = quotient(I, ideal(R, m)) Pm = partial_character_from_ideal(Im, R) #now compute all saturations of the partial character Pm - PmSat = QQAbModule.saturations(Pm) + PmSat = saturations(Pm) for P in PmSat new_id = ideal_from_character(P, RQQAb) + idealDeltaC push!(associated_primes, new_id) @@ -795,7 +912,7 @@ function cellular_minimal_associated_primes(I::MPolyIdeal{QQMPolyRingElem}) P = partial_character_from_ideal(I, R) QQAbcl, = abelian_closure(QQ) RQQAb = polynomial_ring(QQAbcl, symbols(R))[1] - PSat = QQAbModule.saturations(P) + PSat = saturations(P) minimal_associated = Vector{MPolyIdeal{Generic.MPoly{QQAbElem{AbsSimpleNumFieldElem}}}}() #this will hold the set of minimal associated primes #construct the ideal (x_i \mid i \in \Delta^c)