Line | Exclusive | Inclusive | Code |
---|---|---|---|
1 | export polynomial, polynomial!, polynomialtype, terms, nterms, coefficients, monomials | ||
2 | export coefficienttype, monomialtype | ||
3 | export mindegree, maxdegree, extdegree, effective_variables | ||
4 | export leadingterm, leadingcoefficient, leadingmonomial | ||
5 | export removeleadingterm, removemonomials, monic | ||
6 | |||
7 | LinearAlgebra.norm(p::AbstractPolynomialLike, r::Int=2) = LinearAlgebra.norm(coefficients(p), r) | ||
8 | |||
9 | changecoefficienttype(::Type{TT}, ::Type{T}) where {TT<:AbstractTermLike, T} = termtype(TT, T) | ||
10 | changecoefficienttype(::Type{PT}, ::Type{T}) where {PT<:AbstractPolynomial, T} = polynomialtype(PT, T) | ||
11 | |||
12 | changecoefficienttype(p::PT, ::Type{T}) where {PT<:APL, T} = convert(changecoefficienttype(PT, T), p) | ||
13 | |||
14 | abstract type ListState end | ||
15 | abstract type UnsortedState <: ListState end | ||
16 | struct MessyState <: UnsortedState end | ||
17 | # No duplicates or zeros | ||
18 | struct UniqState <: UnsortedState end | ||
19 | sortstate(::MessyState) = SortedState() | ||
20 | sortstate(::UniqState) = SortedUniqState() | ||
21 | struct SortedState <: ListState end | ||
22 | struct SortedUniqState <: ListState end | ||
23 | |||
24 | """ | ||
25 | polynomial(p::AbstractPolynomialLike) | ||
26 | |||
27 | Converts `p` to a value with polynomial type. | ||
28 | |||
29 | polynomial(p::AbstractPolynomialLike, ::Type{T}) where T | ||
30 | |||
31 | Converts `p` to a value with polynomial type with coefficient type `T`. | ||
32 | |||
33 | polynomial(a::AbstractVector, mv::AbstractVector{<:AbstractMonomialLike}) | ||
34 | |||
35 | Creates a polynomial equal to `dot(a, mv)`. | ||
36 | |||
37 | polynomial(terms::AbstractVector{<:AbstractTerm}, s::ListState=MessyState()) | ||
38 | |||
39 | Creates a polynomial equal to `sum(terms)` where `terms` are guaranteed to be in state `s`. | ||
40 | |||
41 | polynomial(f::Function, mv::AbstractVector{<:AbstractMonomialLike}) | ||
42 | |||
43 | Creates a polynomial equal to `sum(f(i) * mv[i] for i in 1:length(mv))`. | ||
44 | |||
45 | ### Examples | ||
46 | |||
47 | Calling `polynomial([2, 4, 1], [x, x^2*y, x*y])` should return ``4x^2y + xy + 2x``. | ||
48 | """ | ||
49 | polynomial(p::AbstractPolynomial) = p | ||
50 | polynomial(p::APL{T}, ::Type{T}) where T = polynomial(terms(p)) | ||
51 | polynomial(p::APL{T}) where T = polynomial(p, T) | ||
52 | function polynomial(Q::AbstractMatrix, mv::AbstractVector) | ||
53 | LinearAlgebra.dot(mv, Q * mv) | ||
54 | end | ||
55 | function polynomial(Q::AbstractMatrix, mv::AbstractVector, ::Type{T}) where T | ||
56 | polynomial(polynomial(Q, mv), T) | ||
57 | end | ||
58 | |||
59 | polynomial(f::Function, mv::AbstractVector{<:AbstractMonomialLike}) = polynomial!([f(i) * mv[i] for i in 1:length(mv)]) | ||
60 | |||
61 | polynomial(a::AbstractVector, x::AbstractVector, s::ListState=MessyState()) = polynomial([α * m for (α, m) in zip(a, x)], s) | ||
62 | |||
63 | polynomial(ts::AbstractVector, s::ListState=MessyState()) = sum(ts) | ||
64 | polynomial!(ts::AbstractVector, s::ListState=MessyState()) = sum(ts) | ||
65 | |||
66 | polynomial!(ts::AbstractVector{<:AbstractTerm}, s::SortedUniqState) = polynomial(coefficient.(ts), monomial.(ts), s) | ||
67 | |||
68 | function polynomial!(ts::AbstractVector{<:AbstractTerm}, s::SortedState) | ||
69 | 92 (11 %) | polynomial!(uniqterms!(ts), SortedUniqState()) | |
70 | end | ||
71 | function polynomial!(ts::AbstractVector{<:AbstractTerm}, s::UnsortedState=MessyState()) | ||
72 | 1388 (166 %) |
1388 (166 %)
samples spent in polynomial!
694 (50 %) (incl.) when called from polynomial! line 72 694 (50 %) (incl.) when called from * line 188
602 (43 %)
samples spent calling
sort!##kw
polynomial!(sort!(ts, lt=(>)), sortstate(s))
92 (7 %) samples spent calling polynomial! 694 (50 %) samples spent calling polynomial! |
|
73 | end | ||
74 | |||
75 | _collect(v::Vector) = v | ||
76 | _collect(v::AbstractVector) = collect(v) | ||
77 | polynomial(ts::AbstractVector{<:AbstractTerm}, args::Vararg{ListState, N}) where {N} = polynomial!(MA.mutable_copy(_collect(ts)), args...) | ||
78 | |||
79 | """ | ||
80 | polynomialtype(p::AbstractPolynomialLike) | ||
81 | |||
82 | Returns the type that `p` would have if it was converted into a polynomial. | ||
83 | |||
84 | polynomialtype(::Type{PT}) where PT<:AbstractPolynomialLike | ||
85 | |||
86 | Returns the same as `polynomialtype(::PT)`. | ||
87 | |||
88 | polynomialtype(p::AbstractPolynomialLike, ::Type{T}) where T | ||
89 | |||
90 | Returns the type that `p` would have if it was converted into a polynomial of coefficient type `T`. | ||
91 | |||
92 | polynomialtype(::Type{PT}, ::Type{T}) where {PT<:AbstractPolynomialLike, T} | ||
93 | |||
94 | Returns the same as `polynomialtype(::PT, ::Type{T})`. | ||
95 | """ | ||
96 | polynomialtype(::Union{P, Type{P}}) where P <: APL = Base.promote_op(polynomial, P) | ||
97 | polynomialtype(::Union{P, Type{P}}) where P <: AbstractPolynomial = P | ||
98 | polynomialtype(::Union{M, Type{M}}) where M<:AbstractMonomialLike = polynomialtype(termtype(M)) | ||
99 | polynomialtype(::Union{M, Type{M}}, ::Type{T}) where {M<:AbstractMonomialLike, T} = polynomialtype(termtype(M, T)) | ||
100 | polynomialtype(::Union{P, Type{P}}, ::Type{T}) where {P <: APL, T} = polynomialtype(polynomialtype(P), T) | ||
101 | polynomialtype(::Union{AbstractVector{PT}, Type{<:AbstractVector{PT}}}) where PT <: APL = polynomialtype(PT) | ||
102 | polynomialtype(::Union{AbstractVector{PT}, Type{<:AbstractVector{PT}}}, ::Type{T}) where {PT <: APL, T} = polynomialtype(PT, T) | ||
103 | |||
104 | function uniqterms!(ts::AbstractVector{<: AbstractTerm}) | ||
105 | i = firstindex(ts) | ||
106 | for j in Iterators.drop(eachindex(ts), 1) | ||
107 | 4 (0 %) |
4 (100 %)
samples spent calling
iszero
if !iszero(ts[j])
|
|
108 | 7 (1 %) | if monomial(ts[i]) == monomial(ts[j]) | |
109 | 1 (0 %) |
1 (100 %)
samples spent calling
setindex!
ts[i] = MA.add!(coefficient(ts[i]), coefficient(ts[j])) * monomial(ts[i])
|
|
110 | else | ||
111 | if !iszero(ts[i]) | ||
112 | i += 1 | ||
113 | end | ||
114 | 1 (0 %) |
1 (100 %)
samples spent calling
setindex!
ts[i] = MA.copy_if_mutable(ts[j])
|
|
115 | end | ||
116 | end | ||
117 | end | ||
118 | if i < length(ts) | ||
119 | if iszero(ts[i]) | ||
120 | i -= 1 | ||
121 | end | ||
122 | resize!(ts, i) | ||
123 | end | ||
124 | ts | ||
125 | end | ||
126 | |||
127 | """ | ||
128 | terms(p::AbstractPolynomialLike) | ||
129 | |||
130 | Returns an iterator over the nonzero terms of the polynomial `p` sorted in the decreasing monomial order. | ||
131 | |||
132 | ### Examples | ||
133 | |||
134 | Calling `terms` on ``4x^2y + xy + 2x`` should return an iterator of ``[4x^2y, xy, 2x]``. | ||
135 | """ | ||
136 | terms(t::AbstractTermLike) = iszero(t) ? termtype(t)[] : [term(t)] | ||
137 | terms(p::AbstractPolynomialLike) = terms(polynomial(p)) | ||
138 | |||
139 | """ | ||
140 | nterms(p::AbstractPolynomialLike) | ||
141 | |||
142 | Returns the number of nonzero terms in `p`, i.e. `length(terms(p))`. | ||
143 | |||
144 | ### Examples | ||
145 | |||
146 | Calling `nterms` on ``4x^2y + xy + 2x`` should return 3. | ||
147 | """ | ||
148 | function nterms end | ||
149 | |||
150 | nterms(t::AbstractTermLike) = iszero(t) ? 0 : 1 | ||
151 | nterms(p::AbstractPolynomialLike) = length(terms(p)) | ||
152 | |||
153 | """ | ||
154 | coefficients(p::AbstractPolynomialLike) | ||
155 | |||
156 | Returns an iterator over the coefficients of `p` of the nonzero terms of the polynomial sorted in the decreasing monomial order. | ||
157 | |||
158 | coefficients(p::AbstractPolynomialLike, X::AbstractVector) | ||
159 | |||
160 | Returns an iterator over the coefficients of the monomials of `X` in `p` where `X` is a monomial vector not necessarily sorted but with no duplicate entry. | ||
161 | |||
162 | ### Examples | ||
163 | |||
164 | Calling `coefficients` on ``4x^2y + xy + 2x`` should return an iterator of ``[4, 1, 2]``. | ||
165 | Calling `coefficients(4x^2*y + x*y + 2x + 3, [x, 1, x*y, y])` should return an iterator of ``[2, 3, 1, 0]``. | ||
166 | """ | ||
167 | coefficients(p::APL) = coefficient.(terms(p)) | ||
168 | function coefficients(p::APL{T}, X::AbstractVector) where T | ||
169 | σ, mv = sortmonovec(X) | ||
170 | @assert length(mv) == length(X) # no duplicate in X | ||
171 | c = zeros(T, length(mv)) | ||
172 | i = 1 | ||
173 | for t in terms(p) | ||
174 | m = monomial(t) | ||
175 | while i <= length(mv) && mv[i] > m | ||
176 | c[σ[i]] = zero(T) | ||
177 | i += 1 | ||
178 | end | ||
179 | if i <= length(mv) && mv[i] == m | ||
180 | c[σ[i]] = coefficient(t) | ||
181 | i += 1 | ||
182 | end | ||
183 | end | ||
184 | c | ||
185 | end | ||
186 | |||
187 | """ | ||
188 | monomials(p::AbstractPolynomialLike) | ||
189 | |||
190 | Returns an iterator over the monomials of `p` of the nonzero terms of the polynomial sorted in the decreasing order. | ||
191 | |||
192 | monomials(vars::Tuple, degs::AbstractVector{Int}, filter::Function = m -> true) | ||
193 | |||
194 | Builds the vector of all the monovec `m` with variables `vars` such that the degree `degree(m)` is in `degs` and `filter(m)` is `true`. | ||
195 | |||
196 | ### Examples | ||
197 | |||
198 | Calling `monomials` on ``4x^2y + xy + 2x`` should return an iterator of ``[x^2y, xy, x]``. | ||
199 | |||
200 | 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. | ||
201 | """ | ||
202 | monomials(p::APL) = monovec(monomial.(terms(p))) | ||
203 | |||
204 | #$(SIGNATURES) | ||
205 | """ | ||
206 | mindegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}) | ||
207 | |||
208 | Returns the minimal total degree of the monomials of `p`, i.e. `minimum(degree, terms(p))`. | ||
209 | |||
210 | mindegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, v::AbstractVariable) | ||
211 | |||
212 | Returns the minimal degree of the monomials of `p` in the variable `v`, i.e. `minimum(degree.(terms(p), v))`. | ||
213 | |||
214 | ### Examples | ||
215 | Calling `mindegree` on on ``4x^2y + xy + 2x`` should return 1, `mindegree(4x^2y + xy + 2x, x)` should return 1 and `mindegree(4x^2y + xy + 2x, y)` should return 0. | ||
216 | """ | ||
217 | function mindegree(X::AbstractVector{<:AbstractTermLike}, args...) | ||
218 | isempty(X) ? 0 : minimum(t -> degree(t, args...), X) | ||
219 | end | ||
220 | function mindegree(p::AbstractPolynomialLike, args...) | ||
221 | mindegree(terms(p), args...) | ||
222 | end | ||
223 | |||
224 | #$(SIGNATURES) | ||
225 | """ | ||
226 | maxdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}) | ||
227 | |||
228 | Returns the maximal total degree of the monomials of `p`, i.e. `maximum(degree, terms(p))`. | ||
229 | |||
230 | maxdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, v::AbstractVariable) | ||
231 | |||
232 | Returns the maximal degree of the monomials of `p` in the variable `v`, i.e. `maximum(degree.(terms(p), v))`. | ||
233 | |||
234 | ### Examples | ||
235 | Calling `maxdegree` on on ``4x^2y + xy + 2x`` should return 3, `maxdegree(4x^2y + xy + 2x, x)` should return 2 and `maxdegree(4x^2y + xy + 2x, y)` should return 1. | ||
236 | """ | ||
237 | function maxdegree(X::AbstractVector{<:AbstractTermLike}, args...) | ||
238 | isempty(X) ? 0 : maximum(t -> degree(t, args...), X) | ||
239 | end | ||
240 | function maxdegree(p::AbstractPolynomialLike, args...) | ||
241 | maxdegree(terms(p), args...) | ||
242 | end | ||
243 | |||
244 | #$(SIGNATURES) | ||
245 | """ | ||
246 | extdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}) | ||
247 | |||
248 | Returns the extremal total degrees of the monomials of `p`, i.e. `(mindegree(p), maxdegree(p))`. | ||
249 | |||
250 | extdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, v::AbstractVariable) | ||
251 | |||
252 | Returns the extremal degrees of the monomials of `p` in the variable `v`, i.e. `(mindegree(p, v), maxdegree(p, v))`. | ||
253 | |||
254 | ### Examples | ||
255 | Calling `extdegree` on on ``4x^2y + xy + 2x`` should return `(1, 3)`, `extdegree(4x^2y + xy + 2x, x)` should return `(1, 2)` and `maxdegree(4x^2y + xy + 2x, y)` should return `(0, 1)`. | ||
256 | """ | ||
257 | function extdegree(p::Union{AbstractPolynomialLike, AbstractVector{<:AbstractTermLike}}, args...) | ||
258 | (mindegree(p, args...), maxdegree(p, args...)) | ||
259 | end | ||
260 | |||
261 | """ | ||
262 | effective_variables(p::AbstractPolynomialLike) | ||
263 | |||
264 | Return a vector of `eltype` `variable_union_type(p)` (see [`variable_union_type`](@ref)), | ||
265 | containing all the variables that has nonzero degree in at least one term. | ||
266 | That is, return all the variables `v` such that `maxdegree(p, v)` is not zero. | ||
267 | """ | ||
268 | function effective_variables(p::AbstractPolynomialLike) | ||
269 | VT = variable_union_type(p) | ||
270 | return VT[v for v in variables(p) if !iszero(maxdegree(p, v))] | ||
271 | end | ||
272 | |||
273 | """ | ||
274 | leadingterm(p::AbstractPolynomialLike) | ||
275 | |||
276 | Returns the coefficient of the leading term, i.e. `first(terms(p))`. | ||
277 | |||
278 | ### Examples | ||
279 | |||
280 | Calling `leadingterm` on ``4x^2y + xy + 2x`` should return ``4x^2y``. | ||
281 | """ | ||
282 | function leadingterm(p::AbstractPolynomialLike) | ||
283 | if iszero(p) | ||
284 | zeroterm(p) | ||
285 | else | ||
286 | first(terms(p)) | ||
287 | end | ||
288 | end | ||
289 | leadingterm(t::AbstractTermLike) = term(t) | ||
290 | |||
291 | #$(SIGNATURES) | ||
292 | """ | ||
293 | leadingcoefficient(p::AbstractPolynomialLike) | ||
294 | |||
295 | Returns the coefficient of the leading term of `p`, i.e. `coefficient(leadingterm(p))`. | ||
296 | |||
297 | ### Examples | ||
298 | |||
299 | Calling `leadingcoefficient` on ``4x^2y + xy + 2x`` should return ``4`` and calling it on ``0`` should return ``0``. | ||
300 | """ | ||
301 | function leadingcoefficient(p::AbstractPolynomialLike) | ||
302 | coefficient(leadingterm(p)) | ||
303 | end | ||
304 | |||
305 | #$(SIGNATURES) | ||
306 | """ | ||
307 | leadingmonomial(p::AbstractPolynomialLike) | ||
308 | |||
309 | Returns the monomial of the leading term of `p`, i.e. `monomial(leadingterm(p))` or `first(monomials(p))`. | ||
310 | |||
311 | ### Examples | ||
312 | |||
313 | Calling `leadingmonomial` on ``4x^2y + xy + 2x`` should return ``x^2y``. | ||
314 | """ | ||
315 | function leadingmonomial(p::AbstractPolynomialLike) | ||
316 | # first(monomials(p)) would be more efficient for DynamicPolynomials but | ||
317 | # monomial(leadingterm(p)) is more efficient for TypedPolynomials and is better if p is a term | ||
318 | monomial(leadingterm(p)) | ||
319 | end | ||
320 | |||
321 | #$(SIGNATURES) | ||
322 | """ | ||
323 | removeleadingterm(p::AbstractPolynomialLike) | ||
324 | |||
325 | Returns a polynomial with the leading term removed in the polynomial `p`. | ||
326 | |||
327 | ### Examples | ||
328 | |||
329 | Calling `removeleadingterm` on ``4x^2y + xy + 2x`` should return ``xy + 2x``. | ||
330 | """ | ||
331 | function removeleadingterm(p::AbstractPolynomialLike) | ||
332 | # Iterators.drop returns an Interators.Drop which is not an AbstractVector | ||
333 | polynomial(terms(p)[2:end], SortedUniqState()) | ||
334 | end | ||
335 | |||
336 | #$(SIGNATURES) | ||
337 | """ | ||
338 | |||
339 | Returns a polynomial with the terms having their monomial in the monomial vector `mv` removed in the polynomial `p`. | ||
340 | |||
341 | ### Examples | ||
342 | |||
343 | Calling `removemonomials(4x^2*y + x*y + 2x, [x*y])` should return ``4x^2*y + 2x``. | ||
344 | """ | ||
345 | function removemonomials(p::AbstractPolynomialLike, mv::AbstractVector{MT}) where {MT <: AbstractMonomialLike} | ||
346 | smv = monovec(mv) # Make sure it is sorted | ||
347 | i = 1 | ||
348 | q = zero(p) | ||
349 | for t in terms(p) | ||
350 | m = monomial(t) | ||
351 | while i <= length(smv) && smv[i] > m | ||
352 | i += 1 | ||
353 | end | ||
354 | if i > length(smv) || smv[i] != m | ||
355 | q += t | ||
356 | end | ||
357 | end | ||
358 | q | ||
359 | end | ||
360 | |||
361 | """ | ||
362 | monic(p::AbstractPolynomialLike) | ||
363 | |||
364 | Returns `p / leadingcoefficient(p)` where the leading coefficient of the returned polynomials is made sure to be exactly one to avoid rounding error. | ||
365 | """ | ||
366 | function monic(p::APL) | ||
367 | α = leadingcoefficient(p) | ||
368 | polynomial!(_divtoone.(terms(p), α)) | ||
369 | end | ||
370 | monic(m::AbstractMonomialLike) = m | ||
371 | monic(t::AbstractTermLike{T}) where T = one(T) * monomial(t) | ||
372 | |||
373 | function _divtoone(t::AbstractTermLike{T}, α::S) where {T, S} | ||
374 | U = Base.promote_op(/, T, S) | ||
375 | β = coefficient(t) | ||
376 | if β == α | ||
377 | one(U) * monomial(t) | ||
378 | else | ||
379 | (β / α) * monomial(t) | ||
380 | end | ||
381 | end | ||
382 | |||
383 | """ | ||
384 | mapcoefficientsnz(f::Function, p::AbstractPolynomialLike) | ||
385 | |||
386 | Returns the polynomial obtained by applying `f` to each coefficients where `f` is a function such that `f(x)` is nonzero if `x` is nonzero. | ||
387 | |||
388 | ### Examples | ||
389 | |||
390 | Calling `mapcoefficientsnz(α -> α^2, 2x*y + 3x + 1)` should return `4x*y + 9x + 1`. | ||
391 | """ | ||
392 | function mapcoefficientsnz(f::Function, p::AbstractPolynomialLike) | ||
393 | # Invariant: p has only nonzero coefficient | ||
394 | # therefore f(α) will be nonzero for every coefficient α of p | ||
395 | # hence we can use Uniq | ||
396 | polynomial!(mapcoefficientsnz.(f, terms(p)), SortedUniqState()) | ||
397 | end | ||
398 | mapcoefficientsnz(f::Function, t::AbstractTermLike) = f(coefficient(t)) * monomial(t) | ||
399 | |||
400 | Base.round(t::AbstractTermLike; args...) = round(coefficient(t); args...) * monomial(t) | ||
401 | function Base.round(p::AbstractPolynomialLike; args...) | ||
402 | # round(0.1) is zero so we cannot use SortedUniqState | ||
403 | polynomial!(round.(terms(p); args...), SortedState()) | ||
404 | end | ||
405 | |||
406 | Base.ndims(::Type{<:AbstractPolynomialLike}) = 0 | ||
407 | Base.broadcastable(p::AbstractPolynomialLike) = Ref(p) |