| Line | Exclusive | Inclusive | Code |
|---|---|---|---|
| 1 | # We reverse the order of comparisons here so that the result | ||
| 2 | # of x < y is equal to the result of Monomial(x) < Monomial(y) | ||
| 3 | Base.@pure Base.isless(v1::AbstractVariable, v2::AbstractVariable) = name(v1) > name(v2) | ||
| 4 | Base.isless(m1::AbstractTermLike, m2::AbstractTermLike) = isless(promote(m1, m2)...) | ||
| 5 | |||
| 6 | function Base.isless(t1::AbstractTerm, t2::AbstractTerm) | ||
| 7 | 15 (2 %) | 256 (31 %) |
241 (100 %)
samples spent calling
<
if monomial(t1) < monomial(t2)
|
| 8 | true | ||
| 9 | 56 (7 %) |
56 (100 %)
samples spent calling
==
elseif monomial(t1) == monomial(t2)
|
|
| 10 | 36 (4 %) | abs(coefficient(t1)) < abs(coefficient(t2)) | |
| 11 | else | ||
| 12 | false | ||
| 13 | end | ||
| 14 | end | ||
| 15 | |||
| 16 | # promoting multiplication is not a good idea | ||
| 17 | # For example a polynomial of Float64 * a polynomial of JuMP affine expression | ||
| 18 | # is a polynomial of JuMP affine expression but if we promote it would be a | ||
| 19 | # polynomial of quadratic expression | ||
| 20 | for op in [:+, :-, :(==)] | ||
| 21 | @eval Base.$op(p1::APL, p2::APL) = $op(promote(p1, p2)...) | ||
| 22 | end | ||
| 23 | # Promotion between `I` and `1` is `Any`. | ||
| 24 | # Promotion between `I` and `2I` is `UniformScaling`. | ||
| 25 | for op in [:+, :-] | ||
| 26 | @eval Base.$op(p1::APL, p2::APL{<:LinearAlgebra.UniformScaling}) = $op(p1, mapcoefficientsnz(J -> J.λ, p2)) | ||
| 27 | @eval Base.$op(p1::APL{<:LinearAlgebra.UniformScaling}, p2::APL) = $op(mapcoefficientsnz(J -> J.λ, p1), p2) | ||
| 28 | @eval Base.$op(p1::APL{<:LinearAlgebra.UniformScaling}, p2::APL{<:LinearAlgebra.UniformScaling}) = $op(mapcoefficientsnz(J -> J.λ, p1), p2) | ||
| 29 | end | ||
| 30 | Base.isapprox(p1::APL, p2::APL; kwargs...) = isapprox(promote(p1, p2)...; kwargs...) | ||
| 31 | |||
| 32 | # @eval $op(p::APL, α) = $op(promote(p, α)...) would be less efficient | ||
| 33 | for (op, fun) in [(:+, :plusconstant), (:-, :minusconstant), (:*, :multconstant), (:(==), :eqconstant)] | ||
| 34 | @eval Base.$op(p::APL, α) = $fun(p, α) | ||
| 35 | @eval Base.$op(α, p::APL) = $fun(α, p) | ||
| 36 | end | ||
| 37 | # Fix ambiguity between above methods and methods in MA | ||
| 38 | Base.:+(::MA.Zero, p::APL) = MA.copy_if_mutable(p) | ||
| 39 | Base.:+(p::APL, ::MA.Zero) = MA.copy_if_mutable(p) | ||
| 40 | |||
| 41 | # Special case AbstractArrays of APLs | ||
| 42 | # We add these instead of relying on the broadcasting API since the above method definitions are very wide. | ||
| 43 | # In particular, there is support for Matrices as coefficents. In order to avoid issues like #104 we therefore | ||
| 44 | # explicitly define this (instead of implictly getting unexpected results). | ||
| 45 | for op in [:+, :-] | ||
| 46 | @eval Base.$op(p::APL, A::AbstractArray{<:APL}) = map(f -> $op(p, f), A) | ||
| 47 | @eval Base.$op(A::AbstractArray{<:APL}, p::APL) = map(f -> $op(f, p), A) | ||
| 48 | end | ||
| 49 | Base.:*(p::APL, A::AbstractArray) = map(f -> p * f, A) | ||
| 50 | Base.:*(A::AbstractArray, p::APL) = map(f -> f * p, A) | ||
| 51 | Base.:/(A::AbstractArray, p::APL) = map(f -> f / p, A) | ||
| 52 | |||
| 53 | constant_function(::typeof(+)) = plusconstant | ||
| 54 | constant_function(::typeof(-)) = minusconstant | ||
| 55 | MA.mutable_operate!(op::Union{typeof(+), typeof(-)}, p::APL, α) = MA.mutable_operate!(constant_function(op), p, α) | ||
| 56 | MA.mutable_operate_to!(output::AbstractPolynomial, op::typeof(*), α, p::APL) = MA.mutable_operate_to!(output, multconstant, α, p) | ||
| 57 | MA.mutable_operate_to!(output::AbstractPolynomial, op::typeof(*), p::APL, α) = MA.mutable_operate_to!(output, multconstant, p, α) | ||
| 58 | |||
| 59 | function polynomial_merge!( | ||
| 60 | n1::Int, n2::Int, get1::Function, get2::Function, | ||
| 61 | set::Function, push::Function, compare_monomials::Function, | ||
| 62 | combine::Function, keep::Function, resize::Function) | ||
| 63 | buffer = nothing | ||
| 64 | i = j = k = 1 | ||
| 65 | # Invariant: | ||
| 66 | # The terms p[0] -> p[k-1] are sorted and are smaller than the remaining terms. | ||
| 67 | # The terms p[k] -> p[i-1] are garbage. | ||
| 68 | # The terms p[i] -> p[end] are sorted and still need to be added. | ||
| 69 | # The terms q[j] -> p[end] are sorted and still need to be added. | ||
| 70 | # If `buffer` is not empty: | ||
| 71 | # The terms in `buffer` are sorted and still need to be added. | ||
| 72 | # Moreover, they are smaller than the terms p[i] -> p[end]. | ||
| 73 | while i <= n1 && j <= n2 | ||
| 74 | @assert buffer === nothing || isempty(buffer) | ||
| 75 | comp = compare_monomials(i, j) | ||
| 76 | if comp > 0 | ||
| 77 | if k == i | ||
| 78 | t0 = get1(i) | ||
| 79 | if buffer === nothing | ||
| 80 | buffer = DataStructures.Queue{typeof(t0)}() | ||
| 81 | end | ||
| 82 | DataStructures.enqueue!(buffer, t0) | ||
| 83 | i += 1 | ||
| 84 | end | ||
| 85 | set(k, get2(j)) | ||
| 86 | j += 1 | ||
| 87 | k += 1 | ||
| 88 | elseif iszero(comp) | ||
| 89 | combine(i, j) | ||
| 90 | if keep(i) | ||
| 91 | if k != i | ||
| 92 | @assert k < i | ||
| 93 | set(k, get1(i)) | ||
| 94 | end | ||
| 95 | k += 1 | ||
| 96 | end | ||
| 97 | i += 1 | ||
| 98 | j += 1 | ||
| 99 | else | ||
| 100 | if k != i | ||
| 101 | set(k, get1(i)) | ||
| 102 | end | ||
| 103 | i += 1 | ||
| 104 | k += 1 | ||
| 105 | end | ||
| 106 | while buffer !== nothing && !isempty(buffer) && j <= n2 | ||
| 107 | @assert i == k | ||
| 108 | t = first(buffer) | ||
| 109 | comp = compare_monomials(t, j) | ||
| 110 | if comp >= 0 | ||
| 111 | if comp > 0 | ||
| 112 | t = get2(j) | ||
| 113 | else | ||
| 114 | t = combine(t, j) | ||
| 115 | end | ||
| 116 | j += 1 | ||
| 117 | end | ||
| 118 | if comp <= 0 | ||
| 119 | DataStructures.dequeue!(buffer) | ||
| 120 | end | ||
| 121 | # if `comp` is zero, we called `combine` so `t` | ||
| 122 | # might not be kept. If `comp` is not zero, we | ||
| 123 | # skip the `keep` call that might be costly. | ||
| 124 | if iszero(comp) && !keep(t) | ||
| 125 | continue | ||
| 126 | end | ||
| 127 | if k <= n1 | ||
| 128 | DataStructures.enqueue!(buffer, get1(i)) | ||
| 129 | set(k, t) | ||
| 130 | else | ||
| 131 | push(t) | ||
| 132 | n1 += 1 | ||
| 133 | end | ||
| 134 | i += 1 | ||
| 135 | k += 1 | ||
| 136 | end | ||
| 137 | end | ||
| 138 | if buffer !== nothing && !isempty(buffer) | ||
| 139 | @assert j == n2 + 1 | ||
| 140 | @assert i == k | ||
| 141 | n = length(buffer) | ||
| 142 | resize(n1 + n) | ||
| 143 | for k in n1:-1:i | ||
| 144 | set(k + n, get1(k)) | ||
| 145 | end | ||
| 146 | for k in i:(i + n - 1) | ||
| 147 | set(k, DataStructures.dequeue!(buffer)) | ||
| 148 | end | ||
| 149 | n1 += n | ||
| 150 | else | ||
| 151 | len = (k - 1) + (n2 - (j - 1)) + (n1 - (i - 1)) | ||
| 152 | if n1 < len | ||
| 153 | resize(len) | ||
| 154 | end | ||
| 155 | while j <= n2 | ||
| 156 | set(k, get2(j)) | ||
| 157 | j += 1 | ||
| 158 | k += 1 | ||
| 159 | end | ||
| 160 | @assert j == n2 + 1 | ||
| 161 | while i <= n1 | ||
| 162 | set(k, get1(i)) | ||
| 163 | i += 1 | ||
| 164 | k += 1 | ||
| 165 | end | ||
| 166 | if len < n1 | ||
| 167 | resize(len) | ||
| 168 | end | ||
| 169 | @assert i == n1 + 1 | ||
| 170 | @assert k == len + 1 | ||
| 171 | end | ||
| 172 | return | ||
| 173 | end | ||
| 174 | |||
| 175 | |||
| 176 | #MA.mutable_operate!(op::Union{typeof(+), typeof(-)}, p::AbstractPolynomial, q::AbstractPolynomial) = MA.mutable_operate_to!(p, op, p, q) | ||
| 177 | MA.mutable_operate!(op::Union{typeof(+), typeof(-)}, p::AbstractPolynomial, q::AbstractPolynomialLike) = MA.mutable_operate!(op, p, polynomial(q)) | ||
| 178 | |||
| 179 | function mul_to_terms!(ts::Vector{<:AbstractTerm}, p1::APL, p2::APL) | ||
| 180 | for t1 in terms(p1) | ||
| 181 | for t2 in terms(p2) | ||
| 182 | 135 (16 %) |
2 (1 %)
samples spent calling
iterate
push!(ts, t1 * t2)
1 (1 %) samples spent calling * 86 (64 %) samples spent calling push! 46 (34 %) samples spent calling push! |
|
| 183 | end | ||
| 184 | end | ||
| 185 | return ts | ||
| 186 | end | ||
| 187 |
833 (100 %)
samples spent in *
function Base.:*(p::AbstractPolynomial, q::AbstractPolynomial)
3 (0 %) (ex.), 783 (94 %) (incl.) when called from power_by_squaring line 228 50 (6 %) (incl.) when called from power_by_squaring line 226 |
||
| 188 | 3 (0 %) | 833 (100 %) |
694 (84 %)
samples spent calling
polynomial!
polynomial!(mul_to_terms!(MA.promote_operation(*, termtype(p), termtype(q))[], p, q))
1 (0 %) samples spent calling mul_to_terms! 135 (16 %) samples spent calling mul_to_terms! |
| 189 | end | ||
| 190 | |||
| 191 | Base.isapprox(p::APL, α; kwargs...) = isapprox(promote(p, α)...; kwargs...) | ||
| 192 | Base.isapprox(α, p::APL; kwargs...) = isapprox(promote(p, α)...; kwargs...) | ||
| 193 | |||
| 194 | # `MA.operate(-, p)` redirects to `-p` as it assumes that `-p` can be modified | ||
| 195 | # through the MA API without modifying `p`. We should either copy the monomial | ||
| 196 | # here or implement a `MA.operate(-, p)` that copies it. We choose the first | ||
| 197 | # option. | ||
| 198 | Base.:-(m::AbstractMonomialLike) = (-1) * MA.copy_if_mutable(m) | ||
| 199 | Base.:-(t::AbstractTermLike) = MA.operate(-, coefficient(t)) * MA.copy_if_mutable(monomial(t)) | ||
| 200 | Base.:-(p::APL) = polynomial!((-).(terms(p))) | ||
| 201 | Base.:+(p::Union{APL, RationalPoly}) = p | ||
| 202 | Base.:*(p::Union{APL, RationalPoly}) = p | ||
| 203 | |||
| 204 | # Avoid adding a zero constant that might artificially increase the Newton polytope | ||
| 205 | # Need to add polynomial conversion for type stability | ||
| 206 | plusconstant(p::APL{S}, α::T) where {S, T} = iszero(α) ? polynomial( p, MA.promote_operation(+, S, T)) : p + constantterm(α, p) | ||
| 207 | plusconstant(α::S, p::APL{T}) where {S, T} = iszero(α) ? polynomial( p, MA.promote_operation(+, S, T)) : constantterm(α, p) + p | ||
| 208 | function MA.mutable_operate!(::typeof(plusconstant), p::APL, α) | ||
| 209 | if !iszero(α) | ||
| 210 | MA.mutable_operate!(+, p, constantterm(α, p)) | ||
| 211 | end | ||
| 212 | return p | ||
| 213 | end | ||
| 214 | minusconstant(p::APL{S}, α::T) where {S, T} = iszero(α) ? polynomial( p, MA.promote_operation(-, S, T)) : p - constantterm(α, p) | ||
| 215 | minusconstant(α::S, p::APL{T}) where {S, T} = iszero(α) ? polynomial(-p, MA.promote_operation(-, S, T)) : constantterm(α, p) - p | ||
| 216 | function MA.mutable_operate!(::typeof(minusconstant), p::APL, α) | ||
| 217 | if !iszero(α) | ||
| 218 | MA.mutable_operate!(-, p, constantterm(α, p)) | ||
| 219 | end | ||
| 220 | return p | ||
| 221 | end | ||
| 222 | |||
| 223 | # Coefficients and variables commute | ||
| 224 | multconstant(α, v::AbstractVariable) = multconstant(α, monomial(v)) # TODO linear term | ||
| 225 | multconstant(m::AbstractMonomialLike, α) = multconstant(α, m) | ||
| 226 | |||
| 227 | _multconstant(α, f, t::AbstractTermLike) = mapcoefficientsnz(f, t) | ||
| 228 | function _multconstant(α::T, f, p::AbstractPolynomial{S}) where {S, T} | ||
| 229 | if iszero(α) | ||
| 230 | zero(polynomialtype(p, MA.promote_operation(*, T, S))) | ||
| 231 | else | ||
| 232 | mapcoefficientsnz(f, p) | ||
| 233 | end | ||
| 234 | end | ||
| 235 | _multconstant(α, f, p::AbstractPolynomialLike) = _multconstant(α, f, polynomial(p)) | ||
| 236 | |||
| 237 | multconstant(α, p::AbstractPolynomialLike) = _multconstant(α, β -> α*β, p) | ||
| 238 | multconstant(p::AbstractPolynomialLike, α) = _multconstant(α, β -> β*α, p) | ||
| 239 | |||
| 240 | function mapcoefficientsnz_to! end | ||
| 241 | |||
| 242 | function _multconstant_to!(output, α, f, p) | ||
| 243 | if iszero(α) | ||
| 244 | MA.mutable_operate!(zero, output) | ||
| 245 | else | ||
| 246 | mapcoefficientsnz_to!(output, f, p) | ||
| 247 | end | ||
| 248 | end | ||
| 249 | function MA.mutable_operate_to!(output, ::typeof(multconstant), p::APL, α) | ||
| 250 | _multconstant_to!(output, α, β -> β*α, p) | ||
| 251 | end | ||
| 252 | function MA.mutable_operate_to!(output, ::typeof(multconstant), α, p::APL) | ||
| 253 | _multconstant_to!(output, α, β -> α*β, p) | ||
| 254 | end | ||
| 255 | |||
| 256 | MA.mutable_operate_to!(output::AbstractMonomial, ::typeof(*), m1::AbstractMonomialLike, m2::AbstractMonomialLike) = mapexponents_to!(output, +, m1, m2) | ||
| 257 | MA.mutable_operate!(::typeof(*), m1::AbstractMonomial, m2::AbstractMonomialLike) = mapexponents!(+, m1, m2) | ||
| 258 | 1 (0 %) |
1 (100 %)
samples spent calling
mapexponents
Base.:*(m1::AbstractMonomialLike, m2::AbstractMonomialLike) = mapexponents(+, m1, m2)
|
|
| 259 | #Base.:*(m1::AbstractMonomialLike, m2::AbstractMonomialLike) = *(monomial(m1), monomial(m2)) | ||
| 260 | |||
| 261 | Base.:*(m::AbstractMonomialLike, t::AbstractTermLike) = coefficient(t) * (m * monomial(t)) | ||
| 262 | Base.:*(t::AbstractTermLike, m::AbstractMonomialLike) = coefficient(t) * (monomial(t) * m) | ||
| 263 | 1 (0 %) |
1 (100 %)
samples spent calling
*
Base.:*(t1::AbstractTermLike, t2::AbstractTermLike) = (coefficient(t1) * coefficient(t2)) * (monomial(t1) * monomial(t2))
|
|
| 264 | |||
| 265 | Base.:*(t::AbstractTermLike, p::APL) = polynomial!(map(te -> t * te, terms(p))) | ||
| 266 | Base.:*(p::APL, t::AbstractTermLike) = polynomial!(map(te -> te * t, terms(p))) | ||
| 267 | Base.:*(p::APL, q::APL) = polynomial(p) * polynomial(q) | ||
| 268 | |||
| 269 | # guaranteed that monomial(t1) > monomial(t2) | ||
| 270 | function _polynomial_2terms(t1::TT, t2::TT, ::Type{T}) where {TT<:AbstractTerm, T} | ||
| 271 | if iszero(t1) | ||
| 272 | polynomial(t2, T) | ||
| 273 | elseif iszero(t2) | ||
| 274 | polynomial(t1, T) | ||
| 275 | else | ||
| 276 | # not `polynomial!` because we `t1` and `t2` cannot be modified | ||
| 277 | polynomial(termtype(TT, T)[t1, t2], SortedUniqState()) | ||
| 278 | end | ||
| 279 | end | ||
| 280 | for op in [:+, :-] | ||
| 281 | @eval begin | ||
| 282 | Base.$op(t1::AbstractTermLike, t2::AbstractTermLike) = $op(term(t1), term(t2)) | ||
| 283 | Base.$op(t1::AbstractTerm, t2::AbstractTerm) = $op(_promote_terms(t1, t2)...) | ||
| 284 | function Base.$op(t1::TT, t2::TT) where {T, TT <: AbstractTerm{T}} | ||
| 285 | S = MA.promote_operation($op, T, T) | ||
| 286 | # t1 > t2 would compare the coefficient in case the monomials are equal | ||
| 287 | # and it will throw a MethodError in case the coefficients are not comparable | ||
| 288 | if monomial(t1) == monomial(t2) | ||
| 289 | polynomial($op(coefficient(t1), coefficient(t2)) * monomial(t1), S) | ||
| 290 | elseif monomial(t1) > monomial(t2) | ||
| 291 | ts = _polynomial_2terms(t1, $op(t2), S) | ||
| 292 | else | ||
| 293 | ts = _polynomial_2terms($op(t2), t1, S) | ||
| 294 | end | ||
| 295 | end | ||
| 296 | end | ||
| 297 | end | ||
| 298 | _promote_terms(t1, t2) = promote(t1, t2) | ||
| 299 | # Promotion between `I` and `1` is `Any`. | ||
| 300 | _promote_terms(t1::AbstractTerm, t2::AbstractTerm{<:LinearAlgebra.UniformScaling}) = _promote_terms(t1, coefficient(t2).λ * monomial(t2)) | ||
| 301 | _promote_terms(t1::AbstractTerm{<:LinearAlgebra.UniformScaling}, t2::AbstractTerm) = _promote_terms(coefficient(t1).λ * monomial(t1), t2) | ||
| 302 | # Promotion between `I` and `2I` is `UniformScaling`, not `UniformScaling{Int}`. | ||
| 303 | function _promote_terms(t1::AbstractTerm{LinearAlgebra.UniformScaling{S}}, t2::AbstractTerm{LinearAlgebra.UniformScaling{T}}) where {S<:Number, T<:Number} | ||
| 304 | U = LinearAlgebra.UniformScaling{promote_type(S, T)} | ||
| 305 | _promote_terms(MA.scaling_convert(U, coefficient(t1)) * monomial(t1), MA.scaling_convert(U, coefficient(t2)) * monomial(t2)) | ||
| 306 | end | ||
| 307 | function _promote_terms(t1::AbstractTerm{LinearAlgebra.UniformScaling{T}}, t2::AbstractTerm{LinearAlgebra.UniformScaling{T}}) where T<:Number | ||
| 308 | return promote(t1, t2) | ||
| 309 | end | ||
| 310 | |||
| 311 | LinearAlgebra.adjoint(v::AbstractVariable) = v | ||
| 312 | LinearAlgebra.adjoint(m::AbstractMonomial) = m | ||
| 313 | LinearAlgebra.adjoint(t::AbstractTerm) = LinearAlgebra.adjoint(coefficient(t)) * monomial(t) | ||
| 314 | LinearAlgebra.adjoint(p::AbstractPolynomialLike) = polynomial(map(LinearAlgebra.adjoint, terms(p))) | ||
| 315 | LinearAlgebra.adjoint(r::RationalPoly) = adjoint(numerator(r)) / adjoint(denominator(r)) | ||
| 316 | |||
| 317 | LinearAlgebra.transpose(v::AbstractVariable) = v | ||
| 318 | LinearAlgebra.transpose(m::AbstractMonomial) = m | ||
| 319 | LinearAlgebra.transpose(t::AbstractTerm) = LinearAlgebra.transpose(coefficient(t)) * monomial(t) | ||
| 320 | LinearAlgebra.transpose(p::AbstractPolynomialLike) = polynomial(map(LinearAlgebra.transpose, terms(p))) | ||
| 321 | LinearAlgebra.transpose(r::RationalPoly) = transpose(numerator(r)) / transpose(denominator(r)) | ||
| 322 | |||
| 323 | LinearAlgebra.dot(p1::AbstractPolynomialLike, p2::AbstractPolynomialLike) = p1' * p2 | ||
| 324 | LinearAlgebra.dot(x, p::AbstractPolynomialLike) = x' * p | ||
| 325 | LinearAlgebra.dot(p::AbstractPolynomialLike, x) = p' * x | ||
| 326 | |||
| 327 | LinearAlgebra.symmetric_type(PT::Type{<:APL}) = PT | ||
| 328 | LinearAlgebra.symmetric(p::APL, ::Symbol) = p | ||
| 329 | |||
| 330 | # Amazingly, this works! Thanks, StaticArrays.jl! | ||
| 331 | """ | ||
| 332 | Convert a tuple of variables into a static vector to allow array-like usage. | ||
| 333 | The element type of the vector will be Monomial{vars, length(vars)}. | ||
| 334 | """ | ||
| 335 | Base.vec(vars::Tuple{Vararg{AbstractVariable}}) = [vars...] | ||
| 336 | # vec(vars::Tuple{Vararg{<:TypedVariable}}) = SVector(vars) | ||
| 337 | |||
| 338 | # https://github.com/JuliaLang/julia/pull/23332 | ||
| 339 | 833 (100 %) |
833 (100 %)
samples spent calling
power_by_squaring
Base.:^(x::AbstractPolynomialLike, p::Integer) = Base.power_by_squaring(x, p)
|
|
| 340 | |||
| 341 | function MA.mutable_operate_to!(output::AbstractPolynomial, op::MA.AddSubMul, x, args::Vararg{Any, N}) where N | ||
| 342 | return MA.mutable_operate_to!(output, MA.add_sub_op(op), x, *(args...)) | ||
| 343 | end | ||
| 344 | function MA.mutable_operate!(op::MA.AddSubMul, x, y, z, args::Vararg{Any, N}) where N | ||
| 345 | return MA.mutable_operate!(MA.add_sub_op(op), x, *(y, z, args...)) | ||
| 346 | end | ||
| 347 | MA.buffer_for(::MA.AddSubMul, ::Type{<:AbstractPolynomial}, args::Vararg{Type, N}) where {N} = zero(MA.promote_operation(*, args...)) | ||
| 348 | function MA.mutable_buffered_operate_to!(buffer::AbstractPolynomial, output::AbstractPolynomial, op::MA.AddSubMul, x, y, z, args::Vararg{Any, N}) where N | ||
| 349 | product = MA.operate_to!(buffer, *, y, z, args...) | ||
| 350 | return MA.mutable_operate_to!(output, MA.add_sub_op(op), x, product) | ||
| 351 | end | ||
| 352 | function MA.mutable_buffered_operate!(buffer::AbstractPolynomial, op::MA.AddSubMul, x, y, z, args::Vararg{Any, N}) where N | ||
| 353 | product = MA.operate_to!(buffer, *, y, z, args...) | ||
| 354 | return MA.mutable_operate!(MA.add_sub_op(op), x, product) | ||
| 355 | end |