Skip to content

Commit fed1a36

Browse files
authored
Zero-allocation terms/coefficients/monomials for AbstractTermLike (#210)
* Zero-allocation terms/coefficients/monomials for AbstractTermLike * Fix * Fix
1 parent 721a926 commit fed1a36

File tree

7 files changed

+41
-13
lines changed

7 files changed

+41
-13
lines changed

src/MultivariatePolynomials.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ abstract type AbstractPolynomial{T} <: AbstractPolynomialLike{T} end
6363
const APL{T} = AbstractPolynomialLike{T}
6464

6565
include("zip.jl")
66+
include("lazy_iterators.jl")
6667

6768
include("variable.jl")
6869
include("monomial.jl")
@@ -91,7 +92,6 @@ include("chain_rules.jl")
9192

9293
include("default_term.jl")
9394
include("sequences.jl")
94-
include("lazy_iterators.jl")
9595
include("default_polynomial.jl")
9696

9797
end # module

src/default_polynomial.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,13 @@ end
150150
Base.copy(p::VectorPolynomial) = MA.mutable_copy(p)
151151

152152
function grlex end
153-
function MA.operate!(op::Union{typeof(+), typeof(-)}, p::Polynomial{T,TT}, q::Polynomial) where {T,TT}
153+
function MA.operate!(op::Union{typeof(+), typeof(-)}, p::Polynomial{T,TT}, q::Union{Polynomial,AbstractTermLike}) where {T,TT}
154154
get1 = let p=p
155155
i -> p.terms[i]
156156
end
157157
get2 = let p=p
158158
i -> begin
159-
t = q.terms[i]
159+
t = terms(q)[i]
160160
TT(MA.scaling_convert(T, MA.operate(op, coefficient(t))), monomial(t))
161161
end
162162
end
@@ -173,15 +173,15 @@ function MA.operate!(op::Union{typeof(+), typeof(-)}, p::Polynomial{T,TT}, q::Po
173173
if t isa Int && j isa Int
174174
t = get1(t)
175175
end
176-
grlex(monomial(q.terms[j]), monomial(t))
176+
grlex(monomials(q)[j], monomial(t))
177177
end
178178
end
179179
combine = let p=p, q=q
180180
(i, j) -> begin
181181
if i isa Int
182-
p.terms[i] = Term(MA.operate!!(op, coefficient(p.terms[i]), coefficient(q.terms[j])), monomial(p.terms[i]))
182+
p.terms[i] = Term(MA.operate!!(op, coefficient(p.terms[i]), coefficients(q)[j]), monomial(p.terms[i]))
183183
else
184-
typeof(i)(MA.operate!!(op, coefficient(i), coefficient(q.terms[j])), monomial(i))
184+
typeof(i)(MA.operate!!(op, coefficient(i), coefficients(q)[j]), monomial(i))
185185
end
186186
end
187187
end

src/lazy_iterators.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
# Allows `terms(::AbstractTerm)` to not allocate and be type stable
2+
struct OneOrZeroElementVector{T} <: AbstractVector{T}
3+
empty::Bool
4+
el::T
5+
end
6+
7+
Base.size(it::OneOrZeroElementVector) = (length(it),)
8+
9+
Base.length(it::OneOrZeroElementVector) = Int(!it.empty)
10+
11+
function Base.iterate(it::OneOrZeroElementVector)
12+
if it.empty
13+
return nothing
14+
else
15+
return it.el, nothing
16+
end
17+
end
18+
Base.iterate(::OneOrZeroElementVector, ::Nothing) = nothing
19+
20+
Base.getindex(it::OneOrZeroElementVector, i::Integer) = it.el
21+
22+
Iterators.reverse(it::OneOrZeroElementVector) = it
23+
124
# Copied from MathOptInterface
225
"""
326
struct LazyMap{T, VT}

src/monovec.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export monovec, monovectype, emptymonovec, sortmonovec, mergemonovec
22

3-
monomials(v::AbstractVariable, args...) = monomials((v,), args...)
3+
monomials(v::AbstractVariable, degree, args...) = monomials((v,), degree, args...)
44

55
"""
66
emptymonovec(p::AbstractPolynomialLike)

src/polynomial.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ Returns an iterator over the nonzero terms of the polynomial `p` sorted in the d
147147
148148
Calling `terms` on ``4x^2y + xy + 2x`` should return an iterator of ``[4x^2y, xy, 2x]``.
149149
"""
150-
terms(t::AbstractTermLike) = iszero(t) ? termtype(t)[] : [term(t)]
150+
terms(t::AbstractTermLike) = OneOrZeroElementVector(iszero(t), term(t))
151151
terms(p::AbstractPolynomialLike) = terms(polynomial(p))
152152

153153
"""
@@ -178,7 +178,7 @@ Returns an iterator over the coefficients of the monomials of `X` in `p` where `
178178
Calling `coefficients` on ``4x^2y + xy + 2x`` should return an iterator of ``[4, 1, 2]``.
179179
Calling `coefficients(4x^2*y + x*y + 2x + 3, [x, 1, x*y, y])` should return an iterator of ``[2, 3, 1, 0]``.
180180
"""
181-
coefficients(p::APL) = coefficient.(terms(p))
181+
coefficients(p::APL{T}) where {T} = LazyMap{T}(coefficient, terms(p))
182182
function coefficients(p::APL{T}, X::AbstractVector) where T
183183
σ, mv = sortmonovec(X)
184184
@assert length(mv) == length(X) # no duplicate in X
@@ -214,6 +214,7 @@ Calling `monomials` on ``4x^2y + xy + 2x`` should return an iterator of ``[x^2y,
214214
Calling `monomials((x, y), [1, 3], m -> degree(m, y) != 1)` should return `[x^3, x*y^2, y^3, x]` where `x^2*y` and `y` have been excluded by the filter.
215215
"""
216216
monomials(p::APL) = monovec(monomial.(terms(p)))
217+
monomials(t::AbstractTermLike) = OneOrZeroElementVector(iszero(t), monomial(t))
217218

218219
function isconstant(p::APL)
219220
n = nterms(p)

src/term.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ When applied to a monomial, it create a term of type `AbstractTerm{Int}`.
2424
function term end
2525
term(coef, var::AbstractVariable) = term(coef, monomial(var))
2626
term(coef, mono::AbstractMonomialLike) = termtype(mono, typeof(coef))(coef, mono)
27-
term(p::APL) = convert(termtype(p), p)
27+
term(p::APL) = convert(termtype(typeof(p)), p)
2828

2929
"""
3030
termtype(p::AbstractPolynomialLike)

test/allocations.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ using TypedPolynomials
1818
function test_polynomial_merge()
1919
@polyvar x
2020
p = x^2 + x + 1
21-
q = 2x + 3
22-
alloc_test(0) do
23-
add!!(p, q)
21+
for q in [x, x^2, 2x, 2x + 3]
22+
alloc_test(0) do
23+
add!!(p, q)
24+
end
25+
alloc_test(0) do
26+
sub!!(p, q)
27+
end
2428
end
2529
end
2630

0 commit comments

Comments
 (0)