Skip to content

Commit 2d00591

Browse files
feat: add fast in-place Polynomial + constant implementation
1 parent 632b2a6 commit 2d00591

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

src/operators.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,47 @@ function MA.operate_to!(
9292
return output
9393
end
9494

95+
"""
96+
_constant_term_idx(p::Polynomial)
97+
98+
Return the index of the constant term in `p` according to its monomial ordering.
99+
"""
100+
_constant_term_idx(p::Polynomial{V, M, T}) where {V, M <: Reverse, T} = lastindex(p.x)
101+
_constant_term_idx(p::Polynomial) = firstindex(p.x)
102+
103+
"""
104+
_insert_constant_term!(p::Polynomial)
105+
106+
Insert a constant (degree 0) term into polynomial `p` at the appropriate position for the
107+
monomial ordering of `p`. Does not check if a constant term already exists.
108+
"""
109+
function _insert_constant_term!(p::Polynomial{V, M, T}) where {V, M <: Reverse, T}
110+
push!(MP.coefficients(p), zero(T))
111+
push!(MP.monomials(p).Z, zeros(Int, length(MP.variables(p))))
112+
return p
113+
end
114+
115+
function _insert_constant_term!(p::Polynomial{V, M, T}) where {V, M, T}
116+
insert!(MP.coefficients(p), 1, zero(T))
117+
insert!(MP.monomials(p).Z, 1, zeros(Int, length(MP.variables(p))))
118+
return p
119+
end
120+
121+
function MA.operate!(op::Union{typeof(+), typeof(-)}, p::Polynomial{V, M, T}, x::T) where {V, M, T}
122+
c_idx = _constant_term_idx(p)
123+
if MP.nterms(p) == 0 || !MP.isconstant(MP.terms(p)[c_idx])
124+
_insert_constant_term!(p)
125+
c_idx = _constant_term_idx(p)
126+
end
127+
coeffs = MP.coefficients(p)
128+
coeffs[c_idx] = op(coeffs[c_idx], x)
129+
if iszero(coeffs[c_idx])
130+
deleteat!(coeffs, c_idx)
131+
deleteat!(MP.monomials(p), c_idx)
132+
end
133+
return p
134+
end
135+
95136
function MA.operate!(
96137
op::Union{typeof(+),typeof(-)},
97138
p::Polynomial,

test/mutable_arithmetics.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,42 @@ using DynamicPolynomials
3535
@test q == x + y + 1
3636
end
3737
end
38+
39+
@testset "Fast path cases: $ord" for ord in [
40+
InverseLexOrder,
41+
LexOrder,
42+
Graded{InverseLexOrder},
43+
Graded{LexOrder},
44+
Reverse{InverseLexOrder},
45+
Reverse{LexOrder},
46+
Graded{Reverse{InverseLexOrder}},
47+
Graded{Reverse{LexOrder}},
48+
Reverse{Graded{InverseLexOrder}},
49+
Reverse{Graded{LexOrder}},
50+
]
51+
@polyvar x y z monomial_order=ord
52+
53+
# allocation tests vary between Julia versions, so they're upper bounds
54+
@testset "Polynomial + constant" begin
55+
poly = 2 * x^2 + 3 * x * y + z * y^2
56+
poly2 = copy(poly)
57+
result = poly + 2
58+
MA.operate!(+, poly, 2)
59+
@test isequal(poly, result)
60+
# down from 576 using the generic method
61+
@test (@allocated MA.operate!(+, poly2, 2)) <= 272
62+
# subsequent additions don't allocate
63+
@test (@allocated MA.operate!(+, poly2, 2)) == 0
64+
65+
# also test `-`
66+
poly = 2 * x^2 + 3 * x * y + z * y^2
67+
poly2 = copy(poly)
68+
result = poly - 2
69+
MA.operate!(-, poly, 2)
70+
@test isequal(poly, result)
71+
# down from 576 using the generic method
72+
@test (@allocated MA.operate!(-, poly2, 2)) <= 272
73+
# subsequent additions don't allocate
74+
@test (@allocated MA.operate!(-, poly2, 2)) == 0
75+
end
76+
end

0 commit comments

Comments
 (0)