2020for op in [:+ , :- , :(== )]
2121 @eval Base.$ op (p1:: APL , p2:: APL ) = $ op (promote (p1, p2)... )
2222end
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
2330Base. isapprox (p1:: APL , p2:: APL ; kwargs... ) = isapprox (promote (p1, p2)... ; kwargs... )
2431
2532# @eval $op(p::APL, α) = $op(promote(p, α)...) would be less efficient
2633for (op, fun) in [(:+ , :plusconstant ), (:- , :minusconstant ), (:* , :multconstant ), (:(== ), :eqconstant )]
2734 @eval Base.$ op (p:: APL , α) = $ fun (p, α)
2835 @eval Base.$ op (α, p:: APL ) = $ fun (α, p)
2936end
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+ constant_function (:: typeof (+ )) = plusconstant
41+ constant_function (:: typeof (- )) = minusconstant
42+ MA. mutable_operate! (op:: Union{typeof(+), typeof(-)} , p:: APL , α) = MA. mutable_operate! (constant_function (op), p, α)
43+ MA. mutable_operate_to! (output:: AbstractPolynomial , op:: typeof (* ), α, p:: APL ) = MA. mutable_operate_to! (output, multconstant, α, p)
44+ MA. mutable_operate_to! (output:: AbstractPolynomial , op:: typeof (* ), p:: APL , α) = MA. mutable_operate_to! (output, multconstant, p, α)
45+
46+ function polynomial_merge! (
47+ n1:: Int , n2:: Int , get1:: Function , get2:: Function ,
48+ set:: Function , push:: Function , compare_monomials:: Function ,
49+ combine:: Function , keep:: Function , resize:: Function )
50+ buffer = nothing
51+ i = j = k = 1
52+ # Invariant:
53+ # The terms p[0] -> p[k-1] are sorted and are smaller than the remaining terms.
54+ # The terms p[k] -> p[i-1] are garbage.
55+ # The terms p[i] -> p[end] are sorted and still need to be added.
56+ # The terms q[j] -> p[end] are sorted and still need to be added.
57+ # If `buffer` is not empty:
58+ # The terms in `buffer` are sorted and still need to be added.
59+ # Moreover, they are smaller than the terms p[i] -> p[end].
60+ while i <= n1 && j <= n2
61+ @assert buffer === nothing || isempty (buffer)
62+ comp = compare_monomials (i, j)
63+ if comp > 0
64+ if k == i
65+ t0 = get1 (i)
66+ if buffer === nothing
67+ buffer = DataStructures. Queue {typeof(t0)} ()
68+ end
69+ DataStructures. enqueue! (buffer, t0)
70+ i += 1
71+ end
72+ set (k, get2 (j))
73+ j += 1
74+ k += 1
75+ elseif iszero (comp)
76+ combine (i, j)
77+ if keep (i)
78+ if k != i
79+ @assert k < i
80+ set (k, get1 (i))
81+ end
82+ k += 1
83+ end
84+ i += 1
85+ j += 1
86+ else
87+ if k != i
88+ set (k, get1 (i))
89+ end
90+ i += 1
91+ k += 1
92+ end
93+ while buffer != = nothing && ! isempty (buffer) && j <= n2
94+ @assert i == k
95+ t = DataStructures. front (buffer)
96+ comp = compare_monomials (t, j)
97+ if comp >= 0
98+ if comp > 0
99+ t = get2 (j)
100+ else
101+ t = combine (t, j)
102+ end
103+ j += 1
104+ end
105+ if comp <= 0
106+ DataStructures. dequeue! (buffer)
107+ end
108+ # if `comp` is zero, we called `combine` so `t`
109+ # might not be kept. If `comp` is not zero, we
110+ # skip the `keep` call that might be costly.
111+ if iszero (comp) && ! keep (t)
112+ continue
113+ end
114+ if k <= n1
115+ DataStructures. enqueue! (buffer, get1 (i))
116+ set (k, t)
117+ else
118+ push (t)
119+ n1 += 1
120+ end
121+ i += 1
122+ k += 1
123+ end
124+ end
125+ if buffer != = nothing && ! isempty (buffer)
126+ @assert j == n2 + 1
127+ @assert i == k
128+ n = length (buffer)
129+ resize (n1 + n)
130+ for k in n1: - 1 : i
131+ set (k + n, get1 (k))
132+ end
133+ for k in i: (i + n - 1 )
134+ set (k, DataStructures. dequeue! (buffer))
135+ end
136+ n1 += n
137+ else
138+ len = (k - 1 ) + (n2 - (j - 1 )) + (n1 - (i - 1 ))
139+ if n1 < len
140+ resize (len)
141+ end
142+ while j <= n2
143+ set (k, get2 (j))
144+ j += 1
145+ k += 1
146+ end
147+ @assert j == n2 + 1
148+ while i <= n1
149+ set (k, get1 (i))
150+ i += 1
151+ k += 1
152+ end
153+ if len < n1
154+ resize (len)
155+ end
156+ @assert i == n1 + 1
157+ @assert k == len + 1
158+ end
159+ return
160+ end
161+
30162
31- MA. mutable_operate! (op:: Union{typeof(+), typeof(-)} , p:: AbstractPolynomial , q:: AbstractPolynomial ) = MA. mutable_operate_to! (p, op, p, q)
163+ # MA.mutable_operate!(op::Union{typeof(+), typeof(-)}, p::AbstractPolynomial, q::AbstractPolynomial) = MA.mutable_operate_to!(p, op, p, q)
32164MA. mutable_operate! (op:: Union{typeof(+), typeof(-)} , p:: AbstractPolynomial , q:: AbstractPolynomialLike ) = MA. mutable_operate! (op, p, polynomial (q))
33165
34166# Special case AbstractArrays of APLs
@@ -41,6 +173,18 @@ for op in [:+, :-, :*]
41173end
42174Base.:/ (A:: AbstractArray{<:APL} , p:: APL ) = map (f -> f / p, A)
43175
176+ function mul_to_terms! (ts:: Vector{<:AbstractTerm} , p1:: APL , p2:: APL )
177+ for t1 in terms (p1)
178+ for t2 in terms (p2)
179+ push! (ts, t1 * t2)
180+ end
181+ end
182+ return ts
183+ end
184+ function Base.:* (p:: AbstractPolynomial , q:: AbstractPolynomial )
185+ polynomial (mul_to_terms! (MA. promote_operation (* , termtype (p), termtype (q))[], p, q))
186+ end
187+
44188Base. isapprox (p:: APL , α; kwargs... ) = isapprox (promote (p, α)... ; kwargs... )
45189Base. isapprox (α, p:: APL ; kwargs... ) = isapprox (promote (p, α)... ; kwargs... )
46190
@@ -52,10 +196,16 @@ Base.:*(p::Union{APL, RationalPoly}) = p
52196
53197# Avoid adding a zero constant that might artificially increase the Newton polytope
54198# Need to add polynomial conversion for type stability
55- plusconstant (p:: APL{S} , α:: T ) where {S, T} = iszero (α) ? polynomial ( p, Base. promote_op (+ , S, T)) : p + constantterm (α, p)
56- plusconstant (α:: S , p:: APL{T} ) where {S, T} = iszero (α) ? polynomial ( p, Base. promote_op (+ , S, T)) : constantterm (α, p) + p
57- minusconstant (p:: APL{S} , α:: T ) where {S, T} = iszero (α) ? polynomial ( p, Base. promote_op (- , S, T)) : p - constantterm (α, p)
58- minusconstant (α:: S , p:: APL{T} ) where {S, T} = iszero (α) ? polynomial (- p, Base. promote_op (- , S, T)) : constantterm (α, p) - p
199+ plusconstant (p:: APL{S} , α:: T ) where {S, T} = iszero (α) ? polynomial ( p, MA. promote_operation (+ , S, T)) : p + constantterm (α, p)
200+ plusconstant (α:: S , p:: APL{T} ) where {S, T} = iszero (α) ? polynomial ( p, MA. promote_operation (+ , S, T)) : constantterm (α, p) + p
201+ function MA. mutable_operate! (:: typeof (plusconstant), p:: APL , α)
202+ if ! iszero (α)
203+ MA. mutable_operate! (+ , p, constantterm (α, p))
204+ end
205+ return p
206+ end
207+ minusconstant (p:: APL{S} , α:: T ) where {S, T} = iszero (α) ? polynomial ( p, MA. promote_operation (- , S, T)) : p - constantterm (α, p)
208+ minusconstant (α:: S , p:: APL{T} ) where {S, T} = iszero (α) ? polynomial (- p, MA. promote_operation (- , S, T)) : constantterm (α, p) - p
59209
60210# Coefficients and variables commute
61211multconstant (α, v:: AbstractVariable ) = multconstant (α, monomial (v)) # TODO linear term
@@ -64,7 +214,7 @@ multconstant(m::AbstractMonomialLike, α) = multconstant(α, m)
64214_multconstant (α, f, t:: AbstractTermLike ) = mapcoefficientsnz (f, t)
65215function _multconstant (α:: T , f, p:: AbstractPolynomial{S} ) where {S, T}
66216 if iszero (α)
67- zero (polynomialtype (p, Base . promote_op (* , T, S)))
217+ zero (polynomialtype (p, MA . promote_operation (* , T, S)))
68218 else
69219 mapcoefficientsnz (f, p)
70220 end
@@ -74,6 +224,22 @@ _multconstant(α, f, p::AbstractPolynomialLike) = _multconstant(α, f, polynomia
74224multconstant (α, p:: AbstractPolynomialLike ) = _multconstant (α, β -> α* β, p)
75225multconstant (p:: AbstractPolynomialLike , α) = _multconstant (α, β -> β* α, p)
76226
227+ function mapcoefficientsnz_to! end
228+
229+ function _multconstant_to! (output, α, f, p)
230+ if iszero (α)
231+ MA. mutable_operate! (zero, output)
232+ else
233+ mapcoefficientsnz_to! (output, f, p)
234+ end
235+ end
236+ function MA. mutable_operate_to! (output, :: typeof (multconstant), p:: APL , α)
237+ _multconstant_to! (output, α, β -> β* α, p)
238+ end
239+ function MA. mutable_operate_to! (output, :: typeof (multconstant), α, p:: APL )
240+ _multconstant_to! (output, α, β -> α* β, p)
241+ end
242+
77243MA. mutable_operate_to! (output:: AbstractMonomial , :: typeof (* ), m1:: AbstractMonomialLike , m2:: AbstractMonomialLike ) = mapexponents_to! (output, + , m1, m2)
78244MA. mutable_operate! (:: typeof (* ), m1:: AbstractMonomial , m2:: AbstractMonomialLike ) = mapexponents! (+ , m1, m2)
79245Base.:* (m1:: AbstractMonomialLike , m2:: AbstractMonomialLike ) = mapexponents (+ , m1, m2)
100266for op in [:+ , :- ]
101267 @eval begin
102268 Base.$ op (t1:: AbstractTermLike , t2:: AbstractTermLike ) = $ op (term (t1), term (t2))
103- Base.$ op (t1:: AbstractTerm , t2:: AbstractTerm ) = $ op (promote (t1, t2)... )
269+ Base.$ op (t1:: AbstractTerm , t2:: AbstractTerm ) = $ op (_promote_terms (t1, t2)... )
104270 function Base. $op (t1:: TT , t2:: TT ) where {T, TT <: AbstractTerm{T} }
105- S = Base . promote_op ($ op, T, T)
271+ S = MA . promote_operation ($ op, T, T)
106272 # t1 > t2 would compare the coefficient in case the monomials are equal
107273 # and it will throw a MethodError in case the coefficients are not comparable
108274 if monomial (t1) == monomial (t2)
@@ -115,6 +281,18 @@ for op in [:+, :-]
115281 end
116282 end
117283end
284+ _promote_terms (t1, t2) = promote (t1, t2)
285+ # Promotion between `I` and `1` is `Any`.
286+ _promote_terms (t1:: AbstractTerm , t2:: AbstractTerm{<:LinearAlgebra.UniformScaling} ) = _promote_terms (t1, coefficient (t2). λ * monomial (t2))
287+ _promote_terms (t1:: AbstractTerm{<:LinearAlgebra.UniformScaling} , t2:: AbstractTerm ) = _promote_terms (coefficient (t1). λ * monomial (t1), t2)
288+ # Promotion between `I` and `2I` is `UniformScaling`, not `UniformScaling{Int}`.
289+ function _promote_terms (t1:: AbstractTerm{LinearAlgebra.UniformScaling{S}} , t2:: AbstractTerm{LinearAlgebra.UniformScaling{T}} ) where {S<: Number , T<: Number }
290+ U = LinearAlgebra. UniformScaling{promote_type (S, T)}
291+ _promote_terms (MA. scaling_convert (U, coefficient (t1)) * monomial (t1), MA. scaling_convert (U, coefficient (t2)) * monomial (t2))
292+ end
293+ function _promote_terms (t1:: AbstractTerm{LinearAlgebra.UniformScaling{T}} , t2:: AbstractTerm{LinearAlgebra.UniformScaling{T}} ) where T<: Number
294+ return promote (t1, t2)
295+ end
118296
119297LinearAlgebra. adjoint (v:: AbstractVariable ) = v
120298LinearAlgebra. adjoint (m:: AbstractMonomial ) = m
@@ -132,6 +310,9 @@ LinearAlgebra.dot(p1::AbstractPolynomialLike, p2::AbstractPolynomialLike) = p1'
132310LinearAlgebra. dot (x, p:: AbstractPolynomialLike ) = x' * p
133311LinearAlgebra. dot (p:: AbstractPolynomialLike , x) = p' * x
134312
313+ LinearAlgebra. symmetric_type (PT:: Type{<:APL} ) = PT
314+ LinearAlgebra. symmetric (p:: APL , :: Symbol ) = p
315+
135316# Amazingly, this works! Thanks, StaticArrays.jl!
136317"""
137318Convert a tuple of variables into a static vector to allow array-like usage.
@@ -146,15 +327,15 @@ Base.:^(x::AbstractPolynomialLike, p::Integer) = Base.power_by_squaring(x, p)
146327function MA. mutable_operate_to! (output:: AbstractPolynomial , :: typeof (MA. add_mul), x, args:: Vararg{Any, N} ) where N
147328 return MA. mutable_operate_to! (output, + , x, * (args... ))
148329end
149- function MA. mutable_operate! (:: typeof (MA. add_mul), x, args:: Vararg{Any, N} ) where N
150- return MA. mutable_operate! (+ , x, * (args... ))
330+ function MA. mutable_operate! (:: typeof (MA. add_mul), x, y, z, args:: Vararg{Any, N} ) where N
331+ return MA. mutable_operate! (+ , x, * (y, z, args... ))
151332end
152- MA. buffer_for (:: typeof (MA. add_mul), :: Type{<:AbstractPolynomial} , args:: Vararg{Any , N} ) where {N} = MA. promote_operation (* , args... )
153- function MA. mutable_buffered_operate_to! (buffer:: AbstractPolynomial , output:: AbstractPolynomial , :: typeof (MA. add_mul), x, args:: Vararg{Any, N} ) where N
154- product = MA. operate_to! (buffer, * , args... )
333+ MA. buffer_for (:: typeof (MA. add_mul), :: Type{<:AbstractPolynomial} , args:: Vararg{Type , N} ) where {N} = zero ( MA. promote_operation (* , args... ) )
334+ function MA. mutable_buffered_operate_to! (buffer:: AbstractPolynomial , output:: AbstractPolynomial , :: typeof (MA. add_mul), x, y, z, args:: Vararg{Any, N} ) where N
335+ product = MA. operate_to! (buffer, * , y, z, args... )
155336 return MA. mutable_operate_to! (output, + , x, product)
156337end
157- function MA. mutable_buffered_operate! (buffer:: AbstractPolynomial , :: typeof (MA. add_mul), x, args:: Vararg{Any, N} ) where N
158- product = MA. operate_to! (buffer, * , args... )
338+ function MA. mutable_buffered_operate! (buffer:: AbstractPolynomial , :: typeof (MA. add_mul), x, y, z, args:: Vararg{Any, N} ) where N
339+ product = MA. operate_to! (buffer, * , y, z, args... )
159340 return MA. mutable_operate! (+ , x, product)
160341end
0 commit comments