Skip to content

Commit 0a6e99f

Browse files
authored
"Inplace" iterators over the coefficients, monomials, terms and exponent vectors of a multivariate polynomial (#2196)
1 parent 995a497 commit 0a6e99f

File tree

7 files changed

+198
-79
lines changed

7 files changed

+198
-79
lines changed

src/MPoly.jl

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -488,51 +488,99 @@ function is_monomial(x::MPolyRingElem{T}) where T <: RingElement
488488
return length(x) == 1 && isone(first(coefficients(x)))
489489
end
490490

491+
function exponent_vector!(e::Vector{S}, a::MPolyRingElem{T}, i::Int) where {T <: RingElement, S}
492+
return S.(exponent_vector(a, i))
493+
end
494+
495+
function coeff!(c::T, a::MPolyRingElem{T}, i::Int) where {T <: RingElement}
496+
return coeff(a, i)
497+
end
498+
499+
function term!(t::T, a::T, i::Int) where {T <: MPolyRingElem}
500+
return term(a, i)
501+
end
502+
503+
function monomial!(m::T, a::T, i::Int) where {T <: MPolyRingElem}
504+
return monomial(a, i)
505+
end
506+
491507
###############################################################################
492508
#
493509
# Iterators
494510
#
495511
###############################################################################
496512

497513
@doc raw"""
498-
coefficients(a::MPolyRingElem{T}) where T <: RingElement
514+
coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
499515
500516
Return an iterator for the coefficients of the given polynomial. To retrieve
501517
an array of the coefficients, use `collect(coefficients(a))`.
518+
519+
If `inplace` is `true`, the elements of the iterator may share their memory. This
520+
means that an element returned by the iterator may be overwritten 'in place' in
521+
the next iteration step. This may result in significantly fewer memory allocations.
522+
However, using the in-place version is only meaningful, if just one element of
523+
the iterator is needed at any time. For example, calling `collect` on this
524+
iterator will not give useful results.
502525
"""
503-
function coefficients(a::MPolyRingElem{T}) where T <: RingElement
504-
return Generic.MPolyCoeffs(a)
526+
function coefficients(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
527+
return Generic.MPolyCoeffs(a, inplace=inplace)
505528
end
506529

507530
@doc raw"""
508-
exponent_vectors(a::MPolyRingElem{T}) where T <: RingElement
531+
exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
509532
510533
Return an iterator for the exponent vectors of the given polynomial. To
511534
retrieve an array of the exponent vectors, use
512535
`collect(exponent_vectors(a))`.
536+
537+
If `inplace` is `true`, the elements of the iterator may share their memory. This
538+
means that an element returned by the iterator may be overwritten 'in place' in
539+
the next iteration step. This may result in significantly fewer memory allocations.
540+
However, using the in-place version is only meaningful, if just one element of
541+
the iterator is needed at any time. For example, calling `collect` on this
542+
iterator will not give useful results.
513543
"""
514-
function exponent_vectors(a::MPolyRingElem{T}) where T <: RingElement
515-
return Generic.MPolyExponentVectors(a)
544+
function exponent_vectors(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
545+
return Generic.MPolyExponentVectors(a, inplace=inplace)
546+
end
547+
548+
function exponent_vectors(::Type{Vector{S}}, a::MPolyRingElem{T}; inplace::Bool = false) where {T <: RingElement, S}
549+
return Generic.MPolyExponentVectors(Vector{S}, a, inplace=inplace)
516550
end
517551

518552
@doc raw"""
519-
monomials(a::MPolyRingElem{T}) where T <: RingElement
553+
monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
520554
521555
Return an iterator for the monomials of the given polynomial. To retrieve
522556
an array of the monomials, use `collect(monomials(a))`.
557+
558+
If `inplace` is `true`, the elements of the iterator may share their memory. This
559+
means that an element returned by the iterator may be overwritten 'in place' in
560+
the next iteration step. This may result in significantly fewer memory allocations.
561+
However, using the in-place version is only meaningful, if just one element of
562+
the iterator is needed at any time. For example, calling `collect` on this
563+
iterator will not give useful results.
523564
"""
524-
function monomials(a::MPolyRingElem{T}) where T <: RingElement
525-
return Generic.MPolyMonomials(a)
565+
function monomials(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
566+
return Generic.MPolyMonomials(a, inplace=inplace)
526567
end
527568

528569
@doc raw"""
529-
terms(a::MPolyRingElem{T}) where T <: RingElement
570+
terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
530571
531572
Return an iterator for the terms of the given polynomial. To retrieve
532573
an array of the terms, use `collect(terms(a))`.
574+
575+
If `inplace` is `true`, the elements of the iterator may share their memory. This
576+
means that an element returned by the iterator may be overwritten 'in place' in
577+
the next iteration step. This may result in significantly fewer memory allocations.
578+
However, using the in-place version is only meaningful, if just one element of
579+
the iterator is needed at any time. For example, calling `collect` on this
580+
iterator will not give useful results.
533581
"""
534-
function terms(a::MPolyRingElem{T}) where T <: RingElement
535-
return Generic.MPolyTerms(a)
582+
function terms(a::MPolyRingElem{T}; inplace::Bool = false) where T <: RingElement
583+
return Generic.MPolyTerms(a, inplace=inplace)
536584
end
537585

538586
###############################################################################

src/exports.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export check_composable
154154
export check_parent
155155
export codomain
156156
export coeff
157+
export coeff!
157158
export coefficient_ring
158159
export coefficient_ring_type
159160
export coefficients
@@ -211,6 +212,7 @@ export evaluate
211212
export exp_gcd
212213
export exponent
213214
export exponent_vector
215+
export exponent_vector!
214216
export exponent_vectors
215217
export exponent_word
216218
export exponent_words
@@ -579,6 +581,7 @@ export symbols
579581
export tail
580582
export term
581583
export terms
584+
export term!
582585
export to_univariate
583586
export total_degree
584587
export total_ring_of_fractions

src/generic/GenericTypes.jl

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -385,20 +385,67 @@ end
385385

386386
# Iterators
387387

388-
struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem}
389-
poly::T
388+
mutable struct MPolyCoeffs{T <: AbstractAlgebra.NCRingElem, S <: AbstractAlgebra.RingElement}
389+
poly::T
390+
inplace::Bool
391+
temp::S # only used if inplace == true
392+
393+
function MPolyCoeffs(f::AbstractAlgebra.NCRingElem; inplace::Bool = false)
394+
I = new{typeof(f), elem_type(coefficient_ring_type(f))}(f, inplace)
395+
if inplace
396+
I.temp = zero(coefficient_ring(parent(f)))
397+
end
398+
return I
399+
end
390400
end
391401

392-
struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem}
393-
poly::T
402+
# S may be the type of anything that can store an exponent vector, for example
403+
# Vector{Int}, ZZMatrix, ...
404+
mutable struct MPolyExponentVectors{T <: AbstractAlgebra.RingElem, S}
405+
poly::T
406+
inplace::Bool
407+
temp::S # only used if inplace == true
408+
409+
function MPolyExponentVectors(f::AbstractAlgebra.NCRingElem; inplace::Bool = false)
410+
return MPolyExponentVectors(Vector{Int}, f, inplace=inplace)
411+
end
412+
413+
function MPolyExponentVectors(::Type{Vector{S}}, f::AbstractAlgebra.NCRingElem; inplace::Bool = false) where S
414+
I = new{typeof(f), Vector{S}}(f, inplace)
415+
if inplace
416+
# Don't use `zeros`: If S === ZZRingElem, then all the entries would be identical
417+
I.temp = [zero(S) for _ in 1:nvars(parent(f))]
418+
end
419+
return I
420+
end
394421
end
395422

396-
struct MPolyTerms{T <: AbstractAlgebra.NCRingElem}
397-
poly::T
423+
mutable struct MPolyTerms{T <: AbstractAlgebra.NCRingElem}
424+
poly::T
425+
inplace::Bool
426+
temp::T # only used if inplace == true
427+
428+
function MPolyTerms(f::AbstractAlgebra.NCRingElem; inplace::Bool = false)
429+
I = new{typeof(f)}(f, inplace)
430+
if inplace
431+
I.temp = zero(parent(f))
432+
end
433+
return I
434+
end
398435
end
399436

400-
struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem}
401-
poly::T
437+
mutable struct MPolyMonomials{T <: AbstractAlgebra.NCRingElem}
438+
poly::T
439+
inplace::Bool
440+
temp::T # only used if inplace == true
441+
442+
function MPolyMonomials(f::AbstractAlgebra.NCRingElem; inplace::Bool = false)
443+
I = new{typeof(f)}(f, inplace)
444+
if inplace
445+
I.temp = zero(parent(f))
446+
end
447+
return I
448+
end
402449
end
403450

404451
mutable struct MPolyBuildCtx{T, S}

src/generic/MPoly.jl

Lines changed: 56 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -125,19 +125,31 @@ are given in the order of the variables for the ring, as supplied when the
125125
ring was created.
126126
"""
127127
function exponent_vector(a::MPoly{T}, i::Int) where T <: RingElement
128+
e = Vector{Int}(undef, nvars(parent(a)))
129+
return exponent_vector!(e, a, i)
130+
end
131+
132+
function exponent_vector!(e::Vector{S}, a::MPoly{T}, i::Int) where {T <: RingElement, S}
133+
@assert length(e) == nvars(parent(a))
128134
A = a.exps
129135
N = size(A, 1)
130136

131137
ord = internal_ordering(parent(a))
132138
if ord == :lex
133-
return [Int(A[j, i]) for j in N:-1:1]
139+
range = N:-1:1
134140
elseif ord == :deglex
135-
return [Int(A[j, i]) for j in N - 1:-1:1]
141+
range = N - 1:-1:1
136142
elseif ord == :degrevlex
137-
return [Int(A[j, i]) for j in 1:N - 1]
143+
range = 1:N - 1
138144
else
139145
error("invalid ordering")
140146
end
147+
k = 1
148+
for j in range
149+
e[k] = S(A[j, i])
150+
k += 1
151+
end
152+
return e
141153
end
142154

143155
@doc raw"""
@@ -635,6 +647,11 @@ function coeff(x::MPoly, i::Int)
635647
return x.coeffs[i]
636648
end
637649

650+
# Only for compatibility, we can't do anything in place here
651+
function coeff!(c::T, x::MPoly{T}, i::Int) where T <: RingElement
652+
return x.coeffs[i]
653+
end
654+
638655
function trailing_coefficient(p::MPoly{T}) where T <: RingElement
639656
@req !iszero(p) "Zero polynomial does not have a leading monomial"
640657
return coeff(p, length(p))
@@ -664,7 +681,9 @@ function monomial!(m::MPoly{T}, x::MPoly{T}, i::Int) where T <: RingElement
664681
N = size(x.exps, 1)
665682
fit!(m, 1)
666683
monomial_set!(m.exps, 1, x.exps, i, N)
667-
m.coeffs[1] = one(base_ring(x))
684+
if !isassigned(m.coeffs, 1) || !is_one(m.coeffs[1])
685+
m.coeffs[1] = one(base_ring(x))
686+
end
668687
m.length = 1
669688
return m
670689
end
@@ -675,11 +694,17 @@ end
675694
Return the $i$-th nonzero term of the polynomial $x$ (as a polynomial).
676695
"""
677696
function term(x::MPoly, i::Int)
678-
R = base_ring(x)
697+
y = zero(parent(x))
698+
return term!(y, x, i)
699+
end
700+
701+
function term!(y::T, x::T, i::Int) where T <: MPoly
679702
N = size(x.exps, 1)
680-
exps = Matrix{UInt}(undef, N, 1)
681-
monomial_set!(exps, 1, x.exps, i, N)
682-
return parent(x)([deepcopy(x.coeffs[i])], exps)
703+
fit!(y, 1)
704+
monomial_set!(y.exps, 1, x.exps, i, N)
705+
y.coeffs[1] = deepcopy(x.coeffs[i])
706+
y.length = 1
707+
return y
683708
end
684709

685710
@doc raw"""
@@ -804,69 +829,41 @@ Base.copy(f::Generic.MPoly) = deepcopy(f)
804829
#
805830
###############################################################################
806831

807-
function Base.iterate(x::MPolyCoeffs)
808-
if length(x.poly) >= 1
809-
return coeff(x.poly, 1), 1
810-
else
811-
return nothing
812-
end
813-
end
814-
815-
function Base.iterate(x::MPolyCoeffs, state)
816-
state += 1
817-
if length(x.poly) >= state
818-
return coeff(x.poly, state), state
819-
else
820-
return nothing
821-
end
822-
end
823-
824-
function Base.iterate(x::MPolyExponentVectors)
825-
if length(x.poly) >= 1
826-
return exponent_vector(x.poly, 1), 1
827-
else
828-
return nothing
829-
end
830-
end
831-
832-
function Base.iterate(x::MPolyExponentVectors, state)
833-
state += 1
834-
if length(x.poly) >= state
835-
return exponent_vector(x.poly, state), state
836-
else
837-
return nothing
838-
end
839-
end
840-
841-
function Base.iterate(x::MPolyTerms)
842-
if length(x.poly) >= 1
843-
return term(x.poly, 1), 1
832+
function Base.iterate(x::MPolyCoeffs, state::Union{Nothing, Int} = nothing)
833+
s = isnothing(state) ? 1 : state + 1
834+
if length(x.poly) >= s
835+
c = x.inplace ? coeff!(x.temp, x.poly, s) : coeff(x.poly, s)
836+
return c, s
844837
else
845838
return nothing
846839
end
847840
end
848841

849-
function Base.iterate(x::MPolyTerms, state)
850-
state += 1
851-
if length(x.poly) >= state
852-
return term(x.poly, state), state
842+
function Base.iterate(x::MPolyExponentVectors, state::Union{Nothing, Int} = nothing)
843+
s = isnothing(state) ? 1 : state + 1
844+
if length(x.poly) >= s
845+
v = x.inplace ? exponent_vector!(x.temp, x.poly, s) : exponent_vector(x.poly, s)
846+
return v, s
853847
else
854848
return nothing
855849
end
856850
end
857851

858-
function Base.iterate(x::MPolyMonomials)
859-
if length(x.poly) >= 1
860-
return monomial(x.poly, 1), 1
852+
function Base.iterate(x::MPolyTerms, state::Union{Nothing, Int} = nothing)
853+
s = isnothing(state) ? 1 : state + 1
854+
if length(x.poly) >= s
855+
t = x.inplace ? term!(x.temp, x.poly, s) : term(x.poly, s)
856+
return t, s
861857
else
862858
return nothing
863859
end
864860
end
865861

866-
function Base.iterate(x::MPolyMonomials, state)
867-
state += 1
868-
if length(x.poly) >= state
869-
return monomial(x.poly, state), state
862+
function Base.iterate(x::MPolyMonomials, state::Union{Nothing, Int} = nothing)
863+
s = isnothing(state) ? 1 : state + 1
864+
if length(x.poly) >= s
865+
m = x.inplace ? monomial!(x.temp, x.poly, s) : monomial(x.poly, s)
866+
return m, s
870867
else
871868
return nothing
872869
end
@@ -876,12 +873,12 @@ function Base.length(x::Union{MPolyCoeffs, MPolyExponentVectors, MPolyTerms, MPo
876873
return length(x.poly)
877874
end
878875

879-
function Base.eltype(::Type{MPolyCoeffs{T}}) where T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement
876+
function Base.eltype(::Type{MPolyCoeffs{T, S}}) where {T <: AbstractAlgebra.MPolyRingElem, S <: RingElement}
880877
return S
881878
end
882879

883-
function Base.eltype(::Type{MPolyExponentVectors{T}}) where T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement
884-
return Vector{Int}
880+
function Base.eltype(::Type{MPolyExponentVectors{T, V}}) where {V, T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement}
881+
return V
885882
end
886883

887884
function Base.eltype(::Type{MPolyMonomials{T}}) where T <: AbstractAlgebra.MPolyRingElem{S} where S <: RingElement

0 commit comments

Comments
 (0)