Skip to content

Commit d8111d0

Browse files
authored
Merge pull request #264 from jverzani/issue_258a
integrate in map, any, all, as possible
2 parents d17ec51 + b27f892 commit d8111d0

File tree

7 files changed

+65
-63
lines changed

7 files changed

+65
-63
lines changed

src/abstract.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ abstract type AbstractPolynomial{T} end
1717
# works for most cases
1818
(P::Type{<:AbstractPolynomial}) = constructorof(P)
1919

20+
# convert `as` into polynomial of type P based on instance, inheriting variable
21+
# (and for LaurentPolynomial the offset)
22+
_convert(p::P, as) where {P <: AbstractPolynomial} = (P)(as, p.var)
23+
2024
"""
2125
Polynomials.@register(name)
2226

src/common.jl

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Returns the roots of the given polynomial. This is calculated via the eigenvalue
120120
121121
!!! note
122122
123-
The [PolynomialRoots.jl](https://github.com/giordano/PolynomialRoots.jl) package provides an alternative that is a bit faster and a bit more accurate; the [FastPolynomialRoots](https://github.com/andreasnoack/FastPolynomialRoots.jl) provides an interface to FORTRAN code implementing an algorithm that can handle very large polynomials (it is `O(n^2)` not `O(n^3)`. the [AMRVW.jl](https://github.com/jverzani/AMRVW.jl) package implements the algorithm in Julia, allowing the use of other number types.
123+
The [PolynomialRoots.jl](https://github.com/giordano/PolynomialRoots.jl) package provides an alternative that is a bit faster and a bit more accurate; the [FastPolynomialRoots](https://github.com/andreasnoack/FastPolynomialRoots.jl) provides an interface to FORTRAN code implementing an algorithm that can handle very large polynomials (it is `O(n^2)` not `O(n^3)`. The [AMRVW.jl](https://github.com/jverzani/AMRVW.jl) package implements the algorithm in Julia, allowing the use of other number types.
124124
125125
"""
126126
function roots(q::AbstractPolynomial{T}; kwargs...) where {T <: Number}
@@ -210,8 +210,8 @@ In-place version of [`chop`](@ref)
210210
function chop!(p::AbstractPolynomial{T};
211211
rtol::Real = Base.rtoldefault(real(T)),
212212
atol::Real = 0,) where {T}
213-
isempty(coeffs(p)) && return p
214-
tol = norm(coeffs(p)) * rtol + atol
213+
isempty(values(p)) && return p
214+
tol = norm(p) * rtol + atol
215215
for i = lastindex(p):-1:0
216216
val = p[i]
217217
if abs(val) > tol #!isapprox(val, zero(T); rtol = rtol, atol = atol)
@@ -253,15 +253,19 @@ Linear Algebra =#
253253
254254
Calculates the p-norm of the polynomial's coefficients
255255
"""
256-
LinearAlgebra.norm(q::AbstractPolynomial, p::Real = 2) = norm(coeffs(q), p)
256+
function LinearAlgebra.norm(q::AbstractPolynomial, p::Real = 2)
257+
vs = values(q)
258+
isempty(vs) && return zero(q[0])
259+
return norm(vs, p)
260+
end
257261

258262
"""
259263
conj(::AbstractPolynomial)
260264
261265
Returns the complex conjugate of the polynomial
262266
"""
263-
LinearAlgebra.conj(p::P) where {P <: AbstractPolynomial} = (P)(conj(coeffs(p)), p.var)
264-
LinearAlgebra.adjoint(p::P) where {P <: AbstractPolynomial} = (P)(adjoint.(coeffs(p)), p.var)
267+
LinearAlgebra.conj(p::P) where {P <: AbstractPolynomial} = map(conj, p)
268+
LinearAlgebra.adjoint(p::P) where {P <: AbstractPolynomial} = map(adjoint, p)
265269
LinearAlgebra.transpose(p::AbstractPolynomial) = p
266270
LinearAlgebra.transpose!(p::AbstractPolynomial) = p
267271

@@ -293,12 +297,7 @@ Base.eltype(p::AbstractPolynomial{T}) where {T} = T
293297
Base.eltype(::Type{<:AbstractPolynomial}) = Float64
294298
Base.eltype(::Type{<:AbstractPolynomial{T}}) where {T} = T
295299
#Base.eltype(::Type{P}) where {P <: AbstractPolynomial} = P # changed in v1.1.0
296-
function Base.iszero(p::AbstractPolynomial)
297-
if length(p) == 0
298-
return true
299-
end
300-
return all(iszero.(coeffs(p))) && p[0] == 0
301-
end
300+
Base.iszero(p::AbstractPolynomial) = all(iszero, p)
302301

303302
# See discussions in https://github.com/JuliaMath/Polynomials.jl/issues/258
304303
"""
@@ -308,21 +307,25 @@ Test whether all coefficients of an `AbstractPolynomial` satisfy predicate `pred
308307
309308
You can implement `isreal`, etc., to a `Polynomial` by using `all`.
310309
"""
311-
Base.all(pred, poly::AbstractPolynomial) = all(pred, poly[:])
310+
Base.all(pred, p::AbstractPolynomial) = all(pred, values(p))
312311
"""
313312
any(pred, poly::AbstractPolynomial)
314313
315314
Test whether any coefficient of an `AbstractPolynomial` satisfies predicate `pred`.
316315
"""
317-
Base.any(pred, poly::AbstractPolynomial) = any(pred, poly[:])
316+
Base.any(pred, p::AbstractPolynomial) = any(pred, values(p))
317+
318+
319+
320+
318321
"""
319-
map(fn, p::AbstractPolynomial)
322+
map(fn, p::AbstractPolynomial, args...)
320323
321324
Transform coefficients of `p` by applying a function (or other callables) `fn` to each of them.
322325
323326
You can implement `real`, etc., to a `Polynomial` by using `map`.
324327
"""
325-
Base.map(fn, p::P) where {P<:AbstractPolynomial} = (P)(map(fn, coeffs(p)), p.var)
328+
Base.map(fn, p::P, args...) where {P<:AbstractPolynomial} = _convert(p, map(fn, coeffs(p), args...))
326329

327330
"""
328331
isreal(p::AbstractPolynomial)
@@ -371,7 +374,7 @@ coeffs(p::AbstractPolynomial) = p.coeffs
371374
Return the degree of the polynomial, i.e. the highest exponent in the polynomial that
372375
has a nonzero coefficient. The degree of the zero polynomial is defined to be -1.
373376
"""
374-
degree(p::AbstractPolynomial) = iszero(p) ? -1 : length(p) - 1
377+
degree(p::AbstractPolynomial) = iszero(p) ? -1 : lastindex(p)
375378

376379

377380
"""
@@ -384,7 +387,7 @@ isconstant(p::AbstractPolynomial) = degree(p) <= 0
384387

385388

386389

387-
hasnan(p::AbstractPolynomial) = any(isnan.(coeffs(p)))
390+
hasnan(p::AbstractPolynomial) = any(isnan, p)
388391

389392
"""
390393
domain(::Type{<:AbstractPolynomial})
@@ -427,6 +430,8 @@ Base.firstindex(p::AbstractPolynomial) = 0
427430
Base.lastindex(p::AbstractPolynomial) = length(p) - 1
428431
Base.eachindex(p::AbstractPolynomial) = 0:length(p) - 1
429432
Base.broadcastable(p::AbstractPolynomial) = Ref(p)
433+
# like coeffs, though possibly only non-zero values (e.g. SparsePolynomial)
434+
Base.values(p::AbstractPolynomial) = coeffs(p)
430435

431436
# iteration
432437
# iteration occurs over the basis polynomials
@@ -472,7 +477,7 @@ Base.setindex!(p::AbstractPolynomial, values, ::Colon) =
472477

473478
#=
474479
identity =#
475-
Base.copy(p::P) where {P <: AbstractPolynomial} = P(copy(coeffs(p)), p.var)
480+
Base.copy(p::P) where {P <: AbstractPolynomial} = _convert(p, copy(coeffs(p)))
476481
Base.hash(p::AbstractPolynomial, h::UInt) = hash(p.var, hash(coeffs(p), h))
477482

478483
#=
@@ -540,20 +545,18 @@ basis(p::P, k::Int, _var::SymbolLike=:x; var=_var) where {P<:AbstractPolynomial}
540545

541546
#=
542547
arithmetic =#
543-
Base.:-(p::P) where {P <: AbstractPolynomial} = P(-coeffs(p), p.var)
548+
Base.:-(p::P) where {P <: AbstractPolynomial} = _convert(p, -coeffs(p))
544549
Base.:+(c::Number, p::AbstractPolynomial) = +(p, c)
545550
Base.:-(p::AbstractPolynomial, c::Number) = +(p, -c)
546551
Base.:-(c::Number, p::AbstractPolynomial) = +(-p, c)
547552
Base.:*(c::Number, p::AbstractPolynomial) = *(p, c)
548553

549554
function Base.:*(p::P, c::S) where {P <: AbstractPolynomial,S}
550-
T = promote_type(P, S)
551-
return T(coeffs(p) .* c, p.var)
555+
_convert(p, coeffs(p) .* c)
552556
end
553557

554558
function Base.:/(p::P, c::S) where {T,P <: AbstractPolynomial{T},S}
555-
R = promote_type(P, eltype(one(T) / one(S)))
556-
return R(coeffs(p) ./ c, p.var)
559+
_convert(p, coeffs(p) ./ c)
557560
end
558561

559562
Base.:-(p1::AbstractPolynomial, p2::AbstractPolynomial) = +(p1, -p2)
@@ -644,7 +647,6 @@ function Base.isapprox(p1::AbstractPolynomial{T},
644647

645648
p1, p2 = promote(p1, p2)
646649
check_same_variable(p1, p2) || error("p1 and p2 must have same var")
647-
648650
# copy over from abstractarray.jl
649651
Δ = norm(p1-p2)
650652
if isfinite(Δ)
@@ -661,7 +663,7 @@ function Base.isapprox(p1::P,
661663
n::S;
662664
rtol::Real = (Base.rtoldefault(T, S, 0)),
663665
atol::Real = 0,) where {T,S, P<:AbstractPolynomial{T}}
664-
return isapprox(p1, (P){T}(n,p1.var))
666+
return isapprox(p1, _convert(p1, [n]))
665667
end
666668

667669
Base.isapprox(n::S,

src/polynomials/ImmutablePolynomial.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ function ImmutablePolynomial(coeffs::NTuple{M,T}, var::SymbolLike=:x) where {M,
9292
ImmutablePolynomial{T,N}(cs, var)
9393
end
9494

95+
9596
# Convenience; pass tuple to Polynomial
9697
# Not documented, not sure this is a good idea as P(...)::P is not true...
9798
Polynomial(coeffs::NTuple{N,T}, var::SymbolLike = :x) where{N,T} =
@@ -110,7 +111,6 @@ Base.collect(p::P) where {P <: ImmutablePolynomial} = [pᵢ for pᵢ ∈ p]
110111
Base.copy(p::P) where {P <: ImmutablePolynomial} = P(coeffs(p), p.var)
111112

112113
# catch q == 0 case
113-
LinearAlgebra.norm(q::ImmutablePolynomial{T}, p::Real = 2) where {T} = degree(q) == -1 ? zero(T) : norm(coeffs(q), p)
114114

115115
# zero, one, variable
116116
function Base.zero(P::Type{<:ImmutablePolynomial},var::SymbolLike=:x)

src/polynomials/LaurentPolynomial.jl

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ end
9393
@register LaurentPolynomial
9494

9595
## constructors
96-
function LaurentPolynomial{T}(coeffs::AbstractVector{S}, m::Int, var::Symbol=:x) where {
96+
function LaurentPolynomial{T}(coeffs::AbstractVector{S}, m::Int, var::SymbolLike=:x) where {
9797
T <: Number, S <: Number}
9898
LaurentPolynomial{T}(T.(coeffs), m, var)
9999
end
100100

101-
function LaurentPolynomial{T}(coeffs::AbstractVector{S}, var::Symbol=:x) where {
101+
function LaurentPolynomial{T}(coeffs::AbstractVector{S}, var::SymbolLike=:x) where {
102102
T <: Number, S <: Number}
103103
LaurentPolynomial{T}(T.(coeffs), 0, var)
104104
end
@@ -114,7 +114,8 @@ end
114114

115115

116116
# Add interface for OffsetArray
117-
function LaurentPolynomial{T}(coeffs::OffsetArray{S, 1, Array{S,1}}, var::SymbolLike=:x) where {T, S}
117+
function LaurentPolynomial{T}(coeffs::OffsetArray{S, 1, Array{S,1}}, var::SymbolLike=:x) where {
118+
T<:Number, S<:Number}
118119
m,n = axes(coeffs, 1)
119120
LaurentPolynomial{T}(T.(coeffs.parent), m:n, Symbol(var))
120121
end
@@ -177,21 +178,19 @@ function Base.range(p::LaurentPolynomial)
177178
end
178179

179180
function Base.inv(p::LaurentPolynomial)
180-
m,n = degreerange(p)
181+
m,n = (extremadegreerange)(p)
181182
m != n && throw(ArgumentError("Only monomials can be inverted"))
182183
LaurentPolynomial([1/p for p in p.coeffs], -m, p.var)
183184
end
184185

185186
##
186187
## changes to common.jl mostly as the range in the type is different
187188
##
188-
Base.copy(p::P) where {P <: LaurentPolynomial} = P(copy(coeffs(p)), degreerange(p), p.var)
189189
Base.:(==)(p1::LaurentPolynomial, p2::LaurentPolynomial) =
190190
check_same_variable(p1, p2) && (degreerange(p1) == degreerange(p2)) && (coeffs(p1) == coeffs(p2))
191191
Base.hash(p::LaurentPolynomial, h::UInt) = hash(p.var, hash(degreerange(p), hash(coeffs(p), h)))
192192

193-
degree(p::LaurentPolynomial) = p.n[]
194-
isconstant(p::LaurentPolynomial) = degreerange(p) == 0:0
193+
isconstant(p::LaurentPolynomial) = iszero(lastindex(p)) && iszero(firstindex(p))
195194
basis(P::Type{<:LaurentPolynomial{T}}, n::Int, var::SymbolLike=:x) where{T} = LaurentPolynomial(ones(T,1), n:n, var)
196195
basis(P::Type{LaurentPolynomial}, n::Int, var::SymbolLike=:x) = LaurentPolynomial(ones(Float64, 1), n:n, var)
197196

@@ -234,6 +233,8 @@ Base.lastindex(p::LaurentPolynomial) = p.n[]
234233
Base.eachindex(p::LaurentPolynomial) = degreerange(p)
235234
degreerange(p::LaurentPolynomial) = firstindex(p):lastindex(p)
236235

236+
_convert(p::P, as) where {P <: LaurentPolynomial} = (P)(as, firstindex(p), p.var)
237+
237238
## chop/truncation
238239
# trim from *both* ends
239240
function chop!(p::P;
@@ -326,11 +327,7 @@ julia> conj(p)(z) ≈ (conj ∘ p ∘ conj)(z)
326327
true
327328
```
328329
"""
329-
function LinearAlgebra.conj(p::P) where {P <: LaurentPolynomial}
330-
ps = coeffs(p)
331-
m = firstindex(p)
332-
(P)(conj(ps), m, p.var)
333-
end
330+
LinearAlgebra.conj(p::P) where {P <: LaurentPolynomial} = map(conj, p)
334331

335332

336333
"""
@@ -443,23 +440,8 @@ end
443440

444441

445442
# scalar operattoinis
446-
Base.:-(p::P) where {P <: LaurentPolynomial} = P(-coeffs(p), firstindex(p), p.var)
447-
448-
function Base.:+(p::LaurentPolynomial{T}, c::S) where {T, S <: Number}
449-
q = LaurentPolynomial([c], 0, p.var)
450-
p + q
451-
end
452-
453-
function Base.:*(p::P, c::S) where {T,P <: LaurentPolynomial, S <: Number}
454-
as = c * copy(coeffs(p))
455-
return (P)(as, firstindex(p), p.var)
456-
end
457-
458-
459-
function Base.:/(p::P, c::S) where {T,P <: LaurentPolynomial{T},S <: Number}
460-
R = promote_type(P, eltype(one(T) / one(S)))
461-
return R(coeffs(p) ./ c, firstindex(p), p.var)
462-
end
443+
# standard-basis defn. assumes basis 1, x, x², ...
444+
Base.:+(p::LaurentPolynomial{T}, c::S) where {T, S <: Number} = sum(promote(p,c))
463445

464446
##
465447
## Poly + and *

src/polynomials/SparsePolynomial.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ function (p::SparsePolynomial{T})(x::S) where {T,S}
182182

183183
end
184184

185+
# map: over values -- not keys
186+
function Base.map(fn, p::P, args...) where {P <: SparsePolynomial}
187+
ks, vs = keys(p.coeffs), values(p.coeffs)
188+
vs′ = map(fn, vs, args...)
189+
_convert(p, Dict(Pair.(ks, vs′)))
190+
end
191+
185192

186193

187194
function Base.:+(p1::SparsePolynomial{T}, p2::SparsePolynomial{S}) where {T, S}

src/polynomials/standard-basis.jl

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ isconstant(p::StandardBasisPolynomial) = degree(p) <= 0
2424

2525
Base.convert(P::Type{<:StandardBasisPolynomial}, q::StandardBasisPolynomial) = isa(q, P) ? q : P([q[i] for i in 0:degree(q)], q.var)
2626

27+
Base.values(p::StandardBasisPolynomial) = values(p.coeffs)
28+
2729
variable(::Type{P}, var::SymbolLike = :x) where {P <: StandardBasisPolynomial} = P([0, 1], var)
2830

2931
function fromroots(P::Type{<:StandardBasisPolynomial}, r::AbstractVector{T}; var::SymbolLike = :x) where {T <: Number}
@@ -45,7 +47,7 @@ function Base.:+(p::P, c::S) where {T, P <: StandardBasisPolynomial{T}, S<:Numbe
4547
R = promote_type(T,S)
4648
as = R[c for c in coeffs(p)]
4749
as[1] += c
48-
(P)(as, p.var)
50+
_convert(p, as)
4951
end
5052

5153

@@ -66,7 +68,7 @@ function derivative(p::P, order::Integer = 1) where {T, P <: StandardBasisPolyno
6668
@inbounds for i in order:n - 1
6769
a2[i - order + 1] = reduce(*, (i - order + 1):i, init = p[i])
6870
end
69-
return (P)(a2, p.var)
71+
return _convert(p, a2)
7072
end
7173

7274

@@ -82,7 +84,7 @@ function integrate(p::P, k::S) where {T, P <: StandardBasisPolynomial{T}, S<:Num
8284
@inbounds for i in 1:n
8385
a2[i + 1] = p[i - 1] / i
8486
end
85-
return (P)(a2, p.var)
87+
return _convert(p, a2)
8688
end
8789

8890

@@ -119,7 +121,7 @@ function Base.divrem(num::P, den::Q) where {T, P <: StandardBasisPolynomial{T},
119121
end
120122
resize!(r_coeff, min(length(r_coeff), m))
121123

122-
return (P)(q_coeff, var), (P)(r_coeff, var)
124+
return _convert(num, q_coeff), _convert(num, r_coeff)
123125

124126
end
125127

@@ -191,7 +193,7 @@ function gcd_noda_sasaki(p::P, q::Q;
191193
a, b = coeffs(p), coeffs(q)
192194
as = _gcd_noda_sasaki(a,b, atol=atol, rtol=rtol)
193195

194-
(P)(as, p.var)
196+
_convert(p, as)
195197
end
196198

197199
function _gcd_noda_sasaki(a::Vector{T}, b::Vector{S};
@@ -304,7 +306,7 @@ end
304306
compensated_horner(p::P, x)
305307
compensated_horner(ps, x)
306308
307-
Evaluate `p(x)` using a compensation scheme of S. Graillat, Ph. Langlois, N. Louve [Compensated Horner Scheme](https://cadxfem.org/cao/Compensation-horner.pdf). Either a `Polynomial` `p` or it coefficients may be passed in.
309+
Evaluate `p(x)` using a compensation scheme of S. Graillat, Ph. Langlois, N. Louve [Compensated Horner Scheme](https://cadxfem.org/cao/Compensation-horner.pdf). Either a `Polynomial` `p` or its coefficients may be passed in.
308310
309311
The Horner scheme has relative error given by
310312
@@ -379,7 +381,7 @@ end
379381
# rule of thumb: p̂ a compute value
380382
# |p(x) - p̃(x)|/|p(x)| ≤ α(n)⋅u ⋅ cond(p,x), where u = finite precision of compuation (2^-p)
381383
function LinearAlgebra.cond(p::P, x) where {P <: Polynomials.StandardBasisPolynomial}
382-
= Polynomials.:(P)(abs.(coeffs(p)), p.var)
384+
= map(abs, p)
383385
(abs(x))/ abs(p(x))
384386
end
385387

test/StandardBasis.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ end
155155
@test p+q == P([1+im, 2, 3])
156156
@test p*q == P(im*[1,2,3])
157157
end
158+
159+
# LaurentPolynomial has an inverse for monomials
160+
x = variable(LaurentPolynomial)
161+
@test Polynomials.isconstant(x * inv(x))
162+
@test_throws ArgumentError inv(x + x^2)
158163
end
159164

160165
@testset "Divrem" begin

0 commit comments

Comments
 (0)