From ff7c58dc6e9bae3d03b16bf1c177f6c65342e3c9 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Fri, 24 Oct 2025 15:11:50 +0200 Subject: [PATCH 01/14] Towards inplace iterators for exponents of multivariate polynomials etc. --- src/MPoly.jl | 20 ++++--- src/exports.jl | 3 + src/generic/GenericTypes.jl | 10 +++- src/generic/MPoly.jl | 109 ++++++++++++++++++------------------ src/generic/exports.jl | 3 + 5 files changed, 80 insertions(+), 65 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index c7396bebe7..24b6bfb1b7 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -500,8 +500,9 @@ end Return an iterator for the coefficients of the given polynomial. To retrieve an array of the coefficients, use `collect(coefficients(a))`. """ -function coefficients(a::MPolyRingElem{T}) where T <: RingElement - return Generic.MPolyCoeffs(a) +function coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement + t = zero(coefficient_ring(parent(a))) + return Generic.MPolyCoeffs(a, inplace, t) end @doc raw""" @@ -511,8 +512,9 @@ Return an iterator for the exponent vectors of the given polynomial. To retrieve an array of the exponent vectors, use `collect(exponent_vectors(a))`. """ -function exponent_vectors(a::MPolyRingElem{T}) where T <: RingElement - return Generic.MPolyExponentVectors(a) +function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement + t = zeros(Int, nvars(parent(a))) + return Generic.MPolyExponentVectors(a, inplace, t) end @doc raw""" @@ -521,8 +523,9 @@ end Return an iterator for the monomials of the given polynomial. To retrieve an array of the monomials, use `collect(monomials(a))`. """ -function monomials(a::MPolyRingElem{T}) where T <: RingElement - return Generic.MPolyMonomials(a) +function monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement + t = zero(parent(a)) + return Generic.MPolyMonomials(a, inplace, t) end @doc raw""" @@ -531,8 +534,9 @@ end Return an iterator for the terms of the given polynomial. To retrieve an array of the terms, use `collect(terms(a))`. """ -function terms(a::MPolyRingElem{T}) where T <: RingElement - return Generic.MPolyTerms(a) +function terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement + t = zero(parent(a)) + return Generic.MPolyTerms(a, inplace, t) end ############################################################################### diff --git a/src/exports.jl b/src/exports.jl index 964222f42b..049c9c7dbf 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -154,6 +154,7 @@ export check_composable export check_parent export codomain export coeff +export coeff! export coefficient_ring export coefficient_ring_type export coefficients @@ -211,6 +212,7 @@ export evaluate export exp_gcd export exponent export exponent_vector +export exponent_vector! export exponent_vectors export exponent_word export exponent_words @@ -579,6 +581,7 @@ export symbols export tail export term export terms +export term! export to_univariate export total_degree export total_ring_of_fractions diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index bdb61fde40..4351334f26 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -385,20 +385,28 @@ end # Iterators -struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem} +struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra.RingElement} poly::T + inplace::Bool + temp::S # only used if inplace == true end struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem} poly::T + inplace::Bool + temp::Vector{Int} # only used if inplace == true end struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} poly::T + inplace::Bool + temp::T # only used if inplace == true end struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} poly::T + inplace::Bool + temp::T # only used if inplace == true end mutable struct MPolyBuildCtx{T, S} diff --git a/src/generic/MPoly.jl b/src/generic/MPoly.jl index 29a5b337da..4f130b2354 100644 --- a/src/generic/MPoly.jl +++ b/src/generic/MPoly.jl @@ -125,19 +125,31 @@ are given in the order of the variables for the ring, as supplied when the ring was created. """ function exponent_vector(a::MPoly{T}, i::Int) where T <: RingElement + e = Vector{Int}(undef, nvars(parent(a))) + return exponent_vector!(e, a, i) +end + +function exponent_vector!(e::Vector{Int}, a::MPoly{T}, i::Int) where T <: RingElement + @assert length(e) == nvars(parent(a)) A = a.exps N = size(A, 1) ord = internal_ordering(parent(a)) if ord == :lex - return [Int(A[j, i]) for j in N:-1:1] + range = N:-1:1 elseif ord == :deglex - return [Int(A[j, i]) for j in N - 1:-1:1] + range = N - 1:-1:1 elseif ord == :degrevlex - return [Int(A[j, i]) for j in 1:N - 1] + range = 1:N - 1 else error("invalid ordering") end + k = 1 + for j in range + e[k] = Int(A[j, i]) + k += 1 + end + return e end @doc raw""" @@ -635,6 +647,11 @@ function coeff(x::MPoly, i::Int) return x.coeffs[i] end +# Only for compatibility, we can't do anything in place here +function coeff!(c::T, x::MPoly{T}, i::Int) where T <: RingElement + return x.coeffs[i] +end + function trailing_coefficient(p::MPoly{T}) where T <: RingElement @req !iszero(p) "Zero polynomial does not have a leading monomial" return coeff(p, length(p)) @@ -664,7 +681,9 @@ function monomial!(m::MPoly{T}, x::MPoly{T}, i::Int) where T <: RingElement N = size(x.exps, 1) fit!(m, 1) monomial_set!(m.exps, 1, x.exps, i, N) - m.coeffs[1] = one(base_ring(x)) + if !isassigned(m.coeffs, 1) || !is_one(m.coeffs[1]) + m.coeffs[1] = one(base_ring(x)) + end m.length = 1 return m end @@ -675,11 +694,17 @@ end Return the $i$-th nonzero term of the polynomial $x$ (as a polynomial). """ function term(x::MPoly, i::Int) - R = base_ring(x) + y = zero(parent(x)) + return term!(y, x, i) +end + +function term!(y::T, x::T, i::Int) where T <: MPoly N = size(x.exps, 1) - exps = Matrix{UInt}(undef, N, 1) - monomial_set!(exps, 1, x.exps, i, N) - return parent(x)([deepcopy(x.coeffs[i])], exps) + fit!(y, 1) + monomial_set!(y.exps, 1, x.exps, i, N) + y.coeffs[1] = deepcopy(x.coeffs[i]) + y.length = 1 + return y end @doc raw""" @@ -804,69 +829,41 @@ Base.copy(f::Generic.MPoly) = deepcopy(f) # ############################################################################### -function Base.iterate(x::MPolyCoeffs) - if length(x.poly) >= 1 - return coeff(x.poly, 1), 1 - else - return nothing - end -end - -function Base.iterate(x::MPolyCoeffs, state) - state += 1 - if length(x.poly) >= state - return coeff(x.poly, state), state - else - return nothing - end -end - -function Base.iterate(x::MPolyExponentVectors) - if length(x.poly) >= 1 - return exponent_vector(x.poly, 1), 1 - else - return nothing - end -end - -function Base.iterate(x::MPolyExponentVectors, state) - state += 1 - if length(x.poly) >= state - return exponent_vector(x.poly, state), state - else - return nothing - end -end - -function Base.iterate(x::MPolyTerms) - if length(x.poly) >= 1 - return term(x.poly, 1), 1 +function Base.iterate(x::MPolyCoeffs, state::Union{Nothing, Int} = nothing) + s = isnothing(state) ? 1 : state + 1 + if length(x.poly) >= s + c = x.inplace ? coeff!(x.temp, x.poly, s) : coeff(x.poly, s) + return c, s else return nothing end end -function Base.iterate(x::MPolyTerms, state) - state += 1 - if length(x.poly) >= state - return term(x.poly, state), state +function Base.iterate(x::MPolyExponentVectors, state::Union{Nothing, Int} = nothing) + s = isnothing(state) ? 1 : state + 1 + if length(x.poly) >= s + v = x.inplace ? exponent_vector!(x.temp, x.poly, s) : exponent_vector(x.poly, s) + return v, s else return nothing end end -function Base.iterate(x::MPolyMonomials) - if length(x.poly) >= 1 - return monomial(x.poly, 1), 1 +function Base.iterate(x::MPolyTerms, state::Union{Nothing, Int} = nothing) + s = isnothing(state) ? 1 : state + 1 + if length(x.poly) >= s + t = x.inplace ? term!(x.temp, x.poly, s) : term(x.poly, s) + return t, s else return nothing end end -function Base.iterate(x::MPolyMonomials, state) - state += 1 - if length(x.poly) >= state - return monomial(x.poly, state), state +function Base.iterate(x::MPolyMonomials, state::Union{Nothing, Int} = nothing) + s = isnothing(state) ? 1 : state + 1 + if length(x.poly) >= s + m = x.inplace ? monomial!(x.temp, x.poly, s) : monomial(x.poly, s) + return m, s else return nothing end diff --git a/src/generic/exports.jl b/src/generic/exports.jl index 62b978d326..938c95db7a 100644 --- a/src/generic/exports.jl +++ b/src/generic/exports.jl @@ -12,6 +12,7 @@ export abs_series_type export base_field export basis export character +export coeff! export collength export combine_like_terms! export cycles @@ -25,6 +26,7 @@ export enable_cache! export exp_gcd export exponent export exponent_vector +export exponent_vector! export exponent_word export falling_factorial export finish @@ -122,6 +124,7 @@ export summands export supermodule export term export terms +export term! export to_univariate export total_degree export trailing_coefficient From c4ef7caf660c89ca7d1496c0e48bd57eb856b5b4 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 29 Oct 2025 14:20:24 +0100 Subject: [PATCH 02/14] Add one-element constructors --- src/generic/GenericTypes.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 4351334f26..9072298eb7 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -391,24 +391,40 @@ struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra.RingEle temp::S # only used if inplace == true end +function MPolyCoeffs(f::AbstractAlgebra.NCRingElem) + return MPolyCoeffs(f, false, zero(coefficient_ring(parent(f)))) +end + struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem} poly::T inplace::Bool temp::Vector{Int} # only used if inplace == true end +function MPolyExponentVectors(f::AbstractAlgebra.RingElem) + return MPolyExponentVectors(f, false, Vector{Int}()) +end + struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} poly::T inplace::Bool temp::T # only used if inplace == true end +function MPolyTerms(f::AbstractAlgebra.NCRingElem) + return MPolyTerms(f, false, zero(parent(f))) +end + struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} poly::T inplace::Bool temp::T # only used if inplace == true end +function MPolyMonomials(f::NCRingElem) + return MPolyMonomials(f, false, zero(parent(f))) +end + mutable struct MPolyBuildCtx{T, S} poly::T state::S From 13bc03088f98681e4d4796a782429cf1e7c806b5 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 29 Oct 2025 14:48:19 +0100 Subject: [PATCH 03/14] Fix type signature --- src/generic/MPoly.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic/MPoly.jl b/src/generic/MPoly.jl index 4f130b2354..a57c77ca11 100644 --- a/src/generic/MPoly.jl +++ b/src/generic/MPoly.jl @@ -873,7 +873,7 @@ function Base.length(x::Union{MPolyCoeffs, MPolyExponentVectors, MPolyTerms, MPo return length(x.poly) end -function Base.eltype(::Type{MPolyCoeffs{T}}) where T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement +function Base.eltype(::Type{MPolyCoeffs{T, S}}) where {T <: AbstractAlgebra.MPolyRingElem, S <: RingElement} return S end From 7ebd24fa893ccb5286fd461ff176171bb312b8a2 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 29 Oct 2025 16:04:03 +0100 Subject: [PATCH 04/14] Allow "arbitrary" types for exponent vectors --- src/MPoly.jl | 6 +++++- src/generic/GenericTypes.jl | 6 ++++-- src/generic/MPoly.jl | 8 ++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index 24b6bfb1b7..abcde12211 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -513,7 +513,11 @@ retrieve an array of the exponent vectors, use `collect(exponent_vectors(a))`. """ function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - t = zeros(Int, nvars(parent(a))) + return exponent_vectors(Vector{Int}, a, inplace=inplace) +end + +function exponent_vectors(::Type{Vector{S}}, a::MPolyRingElem{T}; inplace::Bool = false) where {T <: RingElement, S} + t = zeros(S, nvars(parent(a))) return Generic.MPolyExponentVectors(a, inplace, t) end diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 9072298eb7..be65203391 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -395,10 +395,12 @@ function MPolyCoeffs(f::AbstractAlgebra.NCRingElem) return MPolyCoeffs(f, false, zero(coefficient_ring(parent(f)))) end -struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem} +# S may be the type of anything that can store an exponent vector, for example +# Vector{Int}, ZZMatrix, ... +struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S} poly::T inplace::Bool - temp::Vector{Int} # only used if inplace == true + temp::S # only used if inplace == true end function MPolyExponentVectors(f::AbstractAlgebra.RingElem) diff --git a/src/generic/MPoly.jl b/src/generic/MPoly.jl index a57c77ca11..f8e37b72ae 100644 --- a/src/generic/MPoly.jl +++ b/src/generic/MPoly.jl @@ -129,7 +129,7 @@ function exponent_vector(a::MPoly{T}, i::Int) where T <: RingElement return exponent_vector!(e, a, i) end -function exponent_vector!(e::Vector{Int}, a::MPoly{T}, i::Int) where T <: RingElement +function exponent_vector!(e::Vector{S}, a::MPoly{T}, i::Int) where {T <: RingElement, S} @assert length(e) == nvars(parent(a)) A = a.exps N = size(A, 1) @@ -146,7 +146,7 @@ function exponent_vector!(e::Vector{Int}, a::MPoly{T}, i::Int) where T <: RingEl end k = 1 for j in range - e[k] = Int(A[j, i]) + e[k] = S(A[j, i]) k += 1 end return e @@ -877,8 +877,8 @@ function Base.eltype(::Type{MPolyCoeffs{T, S}}) where {T <: AbstractAlgebra.MPol return S end -function Base.eltype(::Type{MPolyExponentVectors{T}}) where T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement - return Vector{Int} +function Base.eltype(::Type{MPolyExponentVectors{T, V}}) where {V, T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement} + return V end function Base.eltype(::Type{MPolyMonomials{T}}) where T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement From 9100d4f55edfba5db5835c7265010f2df6d8a6c7 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 29 Oct 2025 16:19:35 +0100 Subject: [PATCH 05/14] Add a small test --- test/generic/MPoly-test.jl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/generic/MPoly-test.jl b/test/generic/MPoly-test.jl index 8e7d36cd91..4a3a10bd0e 100644 --- a/test/generic/MPoly-test.jl +++ b/test/generic/MPoly-test.jl @@ -1877,3 +1877,20 @@ end R2, (x2, y2) = polynomial_ring(QQ, [:x, :y]) @test_throws ErrorException z1 + y2 end + +@testset "Generic.MPoly.Iterators" begin + R, (x, y, z) = polynomial_ring(QQ, [:x, :y, :z]) + f = x * y + 2 * x - 3 * z + + @test @inferred collect(exponent_vectors(f)) == [[1, 1, 0], [1, 0, 0], [0, 0, 1]] + @test @inferred collect(exponent_vectors(Vector{UInt}, f)) == [UInt[1, 1, 0], UInt[1, 0, 0], UInt[0, 0, 1]] + @test @inferred collect(coefficients(f)) == [QQ(1), QQ(2), QQ(-3)] + @test @inferred collect(terms(f)) == [x * y, 2 * x, -3 * z] + @test @inferred collect(monomials(f)) == [x * y, x, z] + + @test @inferred first(exponent_vectors(f, inplace = true)) == [1, 1, 0] + @test @inferred first(exponent_vectors(Vector{UInt}, f, inplace = true)) == UInt[1, 1, 0] + @test @inferred first(coefficients(f, inplace = true)) == QQ(1) + @test @inferred first(monomials(f, inplace = true)) == x * y + @test @inferred first(terms(f, inplace = true)) == x * y +end From 8bd71efdb8dddb2070b21041f22d918c121dd1b3 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 29 Oct 2025 16:55:27 +0100 Subject: [PATCH 06/14] Make it Nemo proof --- src/MPoly.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index abcde12211..b404cb3ef2 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -517,7 +517,8 @@ function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: end function exponent_vectors(::Type{Vector{S}}, a::MPolyRingElem{T}; inplace::Bool = false) where {T <: RingElement, S} - t = zeros(S, nvars(parent(a))) + # Don't use `zeros`: If S === ZZRingElem, then all the entries would be identical + t = [zero(S) for _ in 1:nvars(parent(a))] return Generic.MPolyExponentVectors(a, inplace, t) end From c774ba02e006b62850128814660ed0d7a0061408 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 5 Nov 2025 15:22:22 +0100 Subject: [PATCH 07/14] Only allocate when asked --- src/MPoly.jl | 30 +++++++++++++------ src/generic/GenericTypes.jl | 60 +++++++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index b404cb3ef2..1e17b03985 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -501,8 +501,11 @@ Return an iterator for the coefficients of the given polynomial. To retrieve an array of the coefficients, use `collect(coefficients(a))`. """ function coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - t = zero(coefficient_ring(parent(a))) - return Generic.MPolyCoeffs(a, inplace, t) + if inplace + t = zero(coefficient_ring(parent(a))) + return Generic.MPolyCoeffs(a, inplace, t) + end + return Generic.MPolyCoeffs(a) end @doc raw""" @@ -517,9 +520,12 @@ function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: end function exponent_vectors(::Type{Vector{S}}, a::MPolyRingElem{T}; inplace::Bool = false) where {T <: RingElement, S} - # Don't use `zeros`: If S === ZZRingElem, then all the entries would be identical - t = [zero(S) for _ in 1:nvars(parent(a))] - return Generic.MPolyExponentVectors(a, inplace, t) + if inplace + # Don't use `zeros`: If S === ZZRingElem, then all the entries would be identical + t = [zero(S) for _ in 1:nvars(parent(a))] + return Generic.MPolyExponentVectors(a, inplace, t) + end + return Generic.MPolyExponentVectors(Vector{S}, a) end @doc raw""" @@ -529,8 +535,11 @@ Return an iterator for the monomials of the given polynomial. To retrieve an array of the monomials, use `collect(monomials(a))`. """ function monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - t = zero(parent(a)) - return Generic.MPolyMonomials(a, inplace, t) + if inplace + t = zero(parent(a)) + return Generic.MPolyMonomials(a, inplace, t) + end + return Generic.MPolyMonomials(a) end @doc raw""" @@ -540,8 +549,11 @@ Return an iterator for the terms of the given polynomial. To retrieve an array of the terms, use `collect(terms(a))`. """ function terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - t = zero(parent(a)) - return Generic.MPolyTerms(a, inplace, t) + if inplace + t = zero(parent(a)) + return Generic.MPolyTerms(a, inplace, t) + end + return Generic.MPolyTerms(a) end ############################################################################### diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index be65203391..78b5a312c9 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -385,46 +385,74 @@ end # Iterators -struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra.RingElement} +mutable struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra.RingElement} poly::T inplace::Bool temp::S # only used if inplace == true -end -function MPolyCoeffs(f::AbstractAlgebra.NCRingElem) - return MPolyCoeffs(f, false, zero(coefficient_ring(parent(f)))) + function MPolyCoeffs(f::AbstractAlgebra.NCRingElem) + I = new{typeof(f), elem_type(coefficient_ring_type(f))}() + I.poly = f + I.inplace = false + return I + end + + function MPolyCoeffs(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::AbstractAlgebra.RingElement) + return new{typeof(f), typeof(temp)}(f, inplace, temp) + end end # S may be the type of anything that can store an exponent vector, for example # Vector{Int}, ZZMatrix, ... -struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S} +mutable struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S} poly::T inplace::Bool temp::S # only used if inplace == true -end -function MPolyExponentVectors(f::AbstractAlgebra.RingElem) - return MPolyExponentVectors(f, false, Vector{Int}()) + function MPolyExponentVectors(::Type{S}, f::AbstractAlgebra.NCRingElem) where S + I = new{typeof(f), S}() + I.poly = f + I.inplace = false + return I + end + + function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::S) where S + return new{typeof(f), S}(f, inplace, temp) + end end -struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} +mutable struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} poly::T inplace::Bool temp::T # only used if inplace == true -end -function MPolyTerms(f::AbstractAlgebra.NCRingElem) - return MPolyTerms(f, false, zero(parent(f))) + function MPolyTerms(f::AbstractAlgebra.NCRingElem) + I = new{typeof(f)}() + I.poly = f + I.inplace = false + return I + end + + function MPolyTerms(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} + return new{T}(f, inplace, temp) + end end -struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} +mutable struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} poly::T inplace::Bool temp::T # only used if inplace == true -end -function MPolyMonomials(f::NCRingElem) - return MPolyMonomials(f, false, zero(parent(f))) + function MPolyMonomials(f::AbstractAlgebra.NCRingElem) + I = new{typeof(f)}() + I.poly = f + I.inplace = false + return I + end + + function MPolyMonomials(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} + return new{T}(f, inplace, temp) + end end mutable struct MPolyBuildCtx{T, S} From 49c5192f8d35e9621aab02999fb94da0248652e3 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 5 Nov 2025 15:35:09 +0100 Subject: [PATCH 08/14] Provide "abstract" fallbacks --- src/MPoly.jl | 16 ++++++++++++++++ src/generic/imports.jl | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/src/MPoly.jl b/src/MPoly.jl index 1e17b03985..8389d2f660 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -488,6 +488,22 @@ function is_monomial(x::MPolyRingElem{T}) where T <: RingElement return length(x) == 1 && isone(first(coefficients(x))) end +function exponent_vector!(e::Vector{S}, a::MPolyRingElem{T}, i::Int) where {T <: RingElement, S} + return S.(exponent_vector(a, i)) +end + +function coeff!(c::T, a::MPolyRingElem{T}, i::Int) where {T <: RingElement} + return coeff(a, i) +end + +function term!(t::T, a::T, i::Int) where {T <: MPolyRingElem} + return term(a, i) +end + +function monomial!(m::T, a::T, i::Int) where {T <: MPolyRingElem} + return monomial(a, i) +end + ############################################################################### # # Iterators diff --git a/src/generic/imports.jl b/src/generic/imports.jl index a99bd175ee..928fd83b8f 100644 --- a/src/generic/imports.jl +++ b/src/generic/imports.jl @@ -107,6 +107,7 @@ import ..AbstractAlgebra: characteristic import ..AbstractAlgebra: check_parent import ..AbstractAlgebra: codomain import ..AbstractAlgebra: coeff +import ..AbstractAlgebra: coeff! import ..AbstractAlgebra: coefficient_ring import ..AbstractAlgebra: coefficient_ring_type import ..AbstractAlgebra: coefficients @@ -131,6 +132,7 @@ import ..AbstractAlgebra: elem_type import ..AbstractAlgebra: evaluate import ..AbstractAlgebra: exp import ..AbstractAlgebra: exponent_vectors +import ..AbstractAlgebra: exponent_vector! import ..AbstractAlgebra: expressify import ..AbstractAlgebra: factor import ..AbstractAlgebra: factor_squarefree @@ -178,6 +180,7 @@ import ..AbstractAlgebra: max_precision import ..AbstractAlgebra: minpoly import ..AbstractAlgebra: modulus import ..AbstractAlgebra: monomials +import ..AbstractAlgebra: monomial! import ..AbstractAlgebra: mul! import ..AbstractAlgebra: mul_classical import ..AbstractAlgebra: mul_karatsuba @@ -217,6 +220,7 @@ import ..AbstractAlgebra: symbols import ..AbstractAlgebra: tail import ..AbstractAlgebra: term_degree import ..AbstractAlgebra: terms +import ..AbstractAlgebra: term! import ..AbstractAlgebra: terms_degrees import ..AbstractAlgebra: terse import ..AbstractAlgebra: to_univariate From 3057fdcecb5a3a4e5d666a902cb2335d27829dee Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 5 Nov 2025 15:40:47 +0100 Subject: [PATCH 09/14] Re-add the "default" constructor --- src/generic/GenericTypes.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 78b5a312c9..4c4aa420b4 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -416,6 +416,10 @@ mutable struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S} return I end + function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem) + return MPolyExponentVectors(Vector{Int}, f) + end + function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::S) where S return new{typeof(f), S}(f, inplace, temp) end From 4a0ae6d0e4e7a5f6eec2327b10af99fd1cc44289 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 5 Nov 2025 16:28:20 +0100 Subject: [PATCH 10/14] Do it differently --- src/MPoly.jl | 27 ++------ src/generic/GenericTypes.jl | 121 ++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 76 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index 8389d2f660..864ba7710c 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -517,11 +517,7 @@ Return an iterator for the coefficients of the given polynomial. To retrieve an array of the coefficients, use `collect(coefficients(a))`. """ function coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - if inplace - t = zero(coefficient_ring(parent(a))) - return Generic.MPolyCoeffs(a, inplace, t) - end - return Generic.MPolyCoeffs(a) + return Generic.MPolyCoeffs(a, inplace=inplace) end @doc raw""" @@ -532,16 +528,11 @@ retrieve an array of the exponent vectors, use `collect(exponent_vectors(a))`. """ function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - return exponent_vectors(Vector{Int}, a, inplace=inplace) + return Generic.MPolyExponentVectors(a, inplace=inplace) end function exponent_vectors(::Type{Vector{S}}, a::MPolyRingElem{T}; inplace::Bool = false) where {T <: RingElement, S} - if inplace - # Don't use `zeros`: If S === ZZRingElem, then all the entries would be identical - t = [zero(S) for _ in 1:nvars(parent(a))] - return Generic.MPolyExponentVectors(a, inplace, t) - end - return Generic.MPolyExponentVectors(Vector{S}, a) + return Generic.MPolyExponentVectors(Vector{S}, a, inplace=inplace) end @doc raw""" @@ -551,11 +542,7 @@ Return an iterator for the monomials of the given polynomial. To retrieve an array of the monomials, use `collect(monomials(a))`. """ function monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - if inplace - t = zero(parent(a)) - return Generic.MPolyMonomials(a, inplace, t) - end - return Generic.MPolyMonomials(a) + return Generic.MPolyMonomials(a, inplace=inplace) end @doc raw""" @@ -565,11 +552,7 @@ Return an iterator for the terms of the given polynomial. To retrieve an array of the terms, use `collect(terms(a))`. """ function terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement - if inplace - t = zero(parent(a)) - return Generic.MPolyTerms(a, inplace, t) - end - return Generic.MPolyTerms(a) + return Generic.MPolyTerms(a, inplace=inplace) end ############################################################################### diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index 4c4aa420b4..d0ab9feed7 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -386,77 +386,90 @@ end # Iterators mutable struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra.RingElement} - poly::T - inplace::Bool - temp::S # only used if inplace == true - - function MPolyCoeffs(f::AbstractAlgebra.NCRingElem) - I = new{typeof(f), elem_type(coefficient_ring_type(f))}() - I.poly = f - I.inplace = false - return I - end + poly::T + inplace::Bool + temp::S # only used if inplace == true + + function MPolyCoeffs(f::AbstractAlgebra.NCRingElem; inplace::Bool = false) + I = new{typeof(f), elem_type(coefficient_ring_type(f))}() + I.poly = f + I.inplace = inplace + if inplace + I.temp = zero(coefficient_ring(parent(f))) + end + return I + end - function MPolyCoeffs(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::AbstractAlgebra.RingElement) - return new{typeof(f), typeof(temp)}(f, inplace, temp) - end + function MPolyCoeffs(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::AbstractAlgebra.RingElement) + return new{typeof(f), typeof(temp)}(f, inplace, temp) + end end # S may be the type of anything that can store an exponent vector, for example # Vector{Int}, ZZMatrix, ... mutable struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S} - poly::T - inplace::Bool - temp::S # only used if inplace == true + poly::T + inplace::Bool + temp::S # only used if inplace == true - function MPolyExponentVectors(::Type{S}, f::AbstractAlgebra.NCRingElem) where S - I = new{typeof(f), S}() - I.poly = f - I.inplace = false - return I - end + function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem; inplace::Bool = false) + return MPolyExponentVectors(Vector{Int}, f, inplace=inplace) + end - function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem) - return MPolyExponentVectors(Vector{Int}, f) - end + function MPolyExponentVectors(::Type{Vector{S}}, f::AbstractAlgebra.NCRingElem; inplace::Bool = false) where S + I = new{typeof(f), Vector{S}}() + I.poly = f + I.inplace = inplace + if inplace + # Don't use `zeros`: If S === ZZRingElem, then all the entries would be identical + I.temp = [zero(S) for _ in 1:nvars(parent(f))] + end + return I + end - function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::S) where S - return new{typeof(f), S}(f, inplace, temp) - end + function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::S) where S + return new{typeof(f), S}(f, inplace, temp) + end end mutable struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} - poly::T - inplace::Bool - temp::T # only used if inplace == true - - function MPolyTerms(f::AbstractAlgebra.NCRingElem) - I = new{typeof(f)}() - I.poly = f - I.inplace = false - return I - end + poly::T + inplace::Bool + temp::T # only used if inplace == true + + function MPolyTerms(f::AbstractAlgebra.NCRingElem; inplace::Bool = false) + I = new{typeof(f)}() + I.poly = f + I.inplace = inplace + if inplace + I.temp = zero(parent(f)) + end + return I + end - function MPolyTerms(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} - return new{T}(f, inplace, temp) - end + function MPolyTerms(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} + return new{T}(f, inplace, temp) + end end mutable struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} - poly::T - inplace::Bool - temp::T # only used if inplace == true - - function MPolyMonomials(f::AbstractAlgebra.NCRingElem) - I = new{typeof(f)}() - I.poly = f - I.inplace = false - return I - end + poly::T + inplace::Bool + temp::T # only used if inplace == true + + function MPolyMonomials(f::AbstractAlgebra.NCRingElem; inplace::Bool = false) + I = new{typeof(f)}() + I.poly = f + I.inplace = inplace + if inplace + I.temp = zero(parent(f)) + end + return I + end - function MPolyMonomials(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} - return new{T}(f, inplace, temp) - end + function MPolyMonomials(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} + return new{T}(f, inplace, temp) + end end mutable struct MPolyBuildCtx{T, S} From 7d73418c470d85783daa5943aaa52d007bca98b0 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Thu, 6 Nov 2025 09:41:32 +0100 Subject: [PATCH 11/14] Remove some constructors --- src/generic/GenericTypes.jl | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index d0ab9feed7..a32fa3358f 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -399,10 +399,6 @@ mutable struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra end return I end - - function MPolyCoeffs(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::AbstractAlgebra.RingElement) - return new{typeof(f), typeof(temp)}(f, inplace, temp) - end end # S may be the type of anything that can store an exponent vector, for example @@ -426,10 +422,6 @@ mutable struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S} end return I end - - function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem, inplace::Bool, temp::S) where S - return new{typeof(f), S}(f, inplace, temp) - end end mutable struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} @@ -446,10 +438,6 @@ mutable struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} end return I end - - function MPolyTerms(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} - return new{T}(f, inplace, temp) - end end mutable struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} @@ -466,10 +454,6 @@ mutable struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} end return I end - - function MPolyMonomials(f::T, inplace::Bool, temp::T) where {T <: AbstractAlgebra.NCRingElem} - return new{T}(f, inplace, temp) - end end mutable struct MPolyBuildCtx{T, S} From ef3b5933d3ddb0b4e7499e01496517d684c53193 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Thu, 6 Nov 2025 09:54:49 +0100 Subject: [PATCH 12/14] Increase the odds --- src/generic/GenericTypes.jl | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index a32fa3358f..d35094a6b8 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -391,9 +391,7 @@ mutable struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra temp::S # only used if inplace == true function MPolyCoeffs(f::AbstractAlgebra.NCRingElem; inplace::Bool = false) - I = new{typeof(f), elem_type(coefficient_ring_type(f))}() - I.poly = f - I.inplace = inplace + I = new{typeof(f), elem_type(coefficient_ring_type(f))}(f, inplace) if inplace I.temp = zero(coefficient_ring(parent(f))) end @@ -413,9 +411,7 @@ mutable struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S} end function MPolyExponentVectors(::Type{Vector{S}}, f::AbstractAlgebra.NCRingElem; inplace::Bool = false) where S - I = new{typeof(f), Vector{S}}() - I.poly = f - I.inplace = inplace + I = new{typeof(f), Vector{S}}(f, inplace) if inplace # Don't use `zeros`: If S === ZZRingElem, then all the entries would be identical I.temp = [zero(S) for _ in 1:nvars(parent(f))] @@ -430,9 +426,7 @@ mutable struct MPolyTerms{T <: AbstractAlgebra.NCRingElem} temp::T # only used if inplace == true function MPolyTerms(f::AbstractAlgebra.NCRingElem; inplace::Bool = false) - I = new{typeof(f)}() - I.poly = f - I.inplace = inplace + I = new{typeof(f)}(f, inplace) if inplace I.temp = zero(parent(f)) end @@ -446,9 +440,7 @@ mutable struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem} temp::T # only used if inplace == true function MPolyMonomials(f::AbstractAlgebra.NCRingElem; inplace::Bool = false) - I = new{typeof(f)}() - I.poly = f - I.inplace = inplace + I = new{typeof(f)}(f, inplace) if inplace I.temp = zero(parent(f)) end From d6e24a05566ea35e1c7db3c31eac5b42b3b6fb13 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Mon, 10 Nov 2025 17:10:03 +0100 Subject: [PATCH 13/14] Add some documentation --- src/MPoly.jl | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/MPoly.jl b/src/MPoly.jl index 864ba7710c..4de6fb165c 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -511,21 +511,29 @@ end ############################################################################### @doc raw""" - coefficients(a::MPolyRingElem{T}) where T <: RingElement + coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement Return an iterator for the coefficients of the given polynomial. To retrieve an array of the coefficients, use `collect(coefficients(a))`. + +If `inplace` is `true`, the elements of the iterator may share their memory. This +means that an element returned by the iterator may be overwritten 'in place' in +the next iteration step. This may result in significantly fewer memory allocations. """ function coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyCoeffs(a, inplace=inplace) end @doc raw""" - exponent_vectors(a::MPolyRingElem{T}) where T <: RingElement + exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement Return an iterator for the exponent vectors of the given polynomial. To retrieve an array of the exponent vectors, use `collect(exponent_vectors(a))`. + +If `inplace` is `true`, the elements of the iterator may share their memory. This +means that an element returned by the iterator may be overwritten 'in place' in +the next iteration step. This may result in significantly fewer memory allocations. """ function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyExponentVectors(a, inplace=inplace) @@ -536,20 +544,28 @@ function exponent_vectors(::Type{Vector{S}}, a::MPolyRingElem{T}; inplace::Bool end @doc raw""" - monomials(a::MPolyRingElem{T}) where T <: RingElement + monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement Return an iterator for the monomials of the given polynomial. To retrieve an array of the monomials, use `collect(monomials(a))`. + +If `inplace` is `true`, the elements of the iterator may share their memory. This +means that an element returned by the iterator may be overwritten 'in place' in +the next iteration step. This may result in significantly fewer memory allocations. """ function monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyMonomials(a, inplace=inplace) end @doc raw""" - terms(a::MPolyRingElem{T}) where T <: RingElement + terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement Return an iterator for the terms of the given polynomial. To retrieve an array of the terms, use `collect(terms(a))`. + +If `inplace` is `true`, the elements of the iterator may share their memory. This +means that an element returned by the iterator may be overwritten 'in place' in +the next iteration step. This may result in significantly fewer memory allocations. """ function terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyTerms(a, inplace=inplace) From d62e1c50103b6c26968a843794f58db476868d2e Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 12 Nov 2025 12:38:58 +0100 Subject: [PATCH 14/14] Add more documentation --- src/MPoly.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/MPoly.jl b/src/MPoly.jl index 4de6fb165c..96a668f193 100644 --- a/src/MPoly.jl +++ b/src/MPoly.jl @@ -519,6 +519,9 @@ an array of the coefficients, use `collect(coefficients(a))`. If `inplace` is `true`, the elements of the iterator may share their memory. This means that an element returned by the iterator may be overwritten 'in place' in the next iteration step. This may result in significantly fewer memory allocations. +However, using the in-place version is only meaningful, if just one element of +the iterator is needed at any time. For example, calling `collect` on this +iterator will not give useful results. """ function coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyCoeffs(a, inplace=inplace) @@ -534,6 +537,9 @@ retrieve an array of the exponent vectors, use If `inplace` is `true`, the elements of the iterator may share their memory. This means that an element returned by the iterator may be overwritten 'in place' in the next iteration step. This may result in significantly fewer memory allocations. +However, using the in-place version is only meaningful, if just one element of +the iterator is needed at any time. For example, calling `collect` on this +iterator will not give useful results. """ function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyExponentVectors(a, inplace=inplace) @@ -552,6 +558,9 @@ an array of the monomials, use `collect(monomials(a))`. If `inplace` is `true`, the elements of the iterator may share their memory. This means that an element returned by the iterator may be overwritten 'in place' in the next iteration step. This may result in significantly fewer memory allocations. +However, using the in-place version is only meaningful, if just one element of +the iterator is needed at any time. For example, calling `collect` on this +iterator will not give useful results. """ function monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyMonomials(a, inplace=inplace) @@ -566,6 +575,9 @@ an array of the terms, use `collect(terms(a))`. If `inplace` is `true`, the elements of the iterator may share their memory. This means that an element returned by the iterator may be overwritten 'in place' in the next iteration step. This may result in significantly fewer memory allocations. +However, using the in-place version is only meaningful, if just one element of +the iterator is needed at any time. For example, calling `collect` on this +iterator will not give useful results. """ function terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement return Generic.MPolyTerms(a, inplace=inplace)