Skip to content

Commit bb96500

Browse files
authored
WIP: version 1 (#198)
This PR does: * close #187; close #199; close #201 * require use of using Polynomials.PolyCompat for Poly and Pade types. * Add ImmutablePolynomial type to take advantage of evalpoly function. Backed by an NTuple, not an array * Add SparsePolynomial type backed by a dictionary, not an array * Add evalpoly(x, p) method to enable broadcast for arrays of polynomials. Close #209 * refactor code from Polynomial.jl into standard-basis.jl to reuse amongst ImmutablePolynomial and Poly.jl * add StandardBasisPolynomial abstract type * modify + and * for immutable and sparse polynomials to close #206 * extend tests for Polynomial to cover ImmutablePolynomial, SparsePolynomial, rename test file borrow constructorof from ConstructionBase.jl * modify one, add oneunit * remove restriction that T <: Number in AbstractPolynomial type. This is enforceable by subtypes * Close #201 (issue with `round`)
1 parent 9fd5149 commit bb96500

File tree

19 files changed

+1594
-764
lines changed

19 files changed

+1594
-764
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name = "Polynomials"
22
uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
33
license = "MIT"
44
author = "JuliaMath"
5-
version = "0.8.0"
5+
version = "1.0.0"
66

77
[deps]
88
Intervals = "d8418881-c3e1-53bb-8760-2df7ec849ed5"

README.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Basic arithmetic, integration, differentiation, evaluation, and root finding ove
1111
## Installation
1212

1313
```julia
14-
(v1.2) pkg> add Polynomials
14+
(v1.4) pkg> add Polynomials
1515

1616
julia> using Polynomials
1717
```
@@ -21,6 +21,8 @@ julia> using Polynomials
2121
#### Available Polynomials
2222

2323
* `Polynomial` - Standard polynomials
24+
* `ImmutablePolynomial` - Standard polynomial backed by a tuple for faster evaluation of values
25+
* `SparsePolynomial` - Standard basis polynomial backed by a dictionary to hold sparse high-degree polynomials
2426
* `ChebyshevT` - Chebyshev polynomials of the first kind
2527

2628
#### Construction and Evaluation
@@ -186,14 +188,24 @@ Polynomial objects also have other methods:
186188

187189
## Related Packages
188190

191+
* [StaticUnivariatePolynomials.jl](https://github.com/tkoolen/StaticUnivariatePolynomials.jl) Fixed-size univariate polynomials backed by a Tuple
192+
189193
* [MultiPoly.jl](https://github.com/daviddelaat/MultiPoly.jl) for sparse multivariate polynomials
190194

191-
* [MultivariatePolynomials.jl](https://github.com/blegat/MultivariatePolynomials.jl) for multivariate polynomials and moments of commutative or non-commutative variables
195+
* [DynamicPolynomals.jl](https://github.com/JuliaAlgebra/DynamicPolynomials.jl) Multivariate polynomials implementation of commutative and non-commutative variables
196+
197+
* [MultivariatePolynomials.jl](https://github.com/JuliaAlgebra/MultivariatePolynomials.jl) for multivariate polynomials and moments of commutative or non-commutative variables
198+
199+
* [PolynomialRings](https://github.com/tkluck/PolynomialRings.jl) A library for arithmetic and algebra with multi-variable polynomials.
200+
201+
* [AbstractAlgebra.jl](https://github.com/wbhart/AbstractAlgebra.jl) and [Nemo.jl](https://github.com/wbhart/Nemo.jl) for generic polynomial rings, matrix spaces, fraction fields, residue rings, power series
202+
203+
* [PolynomialRoots.jl](https://github.com/giordano/PolynomialRoots.jl) for a fast complex polynomial root finder. For larger degree problems, also [FastPolynomialRoots](https://github.com/andreasnoack/FastPolynomialRoots.jl) and [AMRVW](https://github.com/jverzani/AMRVW.jl).
192204

193-
* [Nemo.jl](https://github.com/wbhart/Nemo.jl) for generic polynomial rings, matrix spaces, fraction fields, residue rings, power series
194205

195-
* [PolynomialRoots.jl](https://github.com/giordano/PolynomialRoots.jl) for a fast complex polynomial root finder
206+
## Legacy code
196207

208+
As of v0.7, the internals of this package were greatly generalized and new types and method names were introduced. For compatability purposes, legacy code can be run after issuing `using Polynomials.PolyCompat`.
197209

198210
## Contributing
199211

docs/src/index.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ resulting polynomial is one lower than the degree of `p`.
120120

121121
```jldoctest
122122
julia> derivative(Polynomial([1, 3, -1]))
123-
Polynomial(3.0 - 2.0*x)
123+
Polynomial(3 - 2*x)
124124
```
125125

126126
### Root-finding
@@ -211,13 +211,21 @@ julia> as[3], p[2], collect(p)[3]
211211

212212
## Related Packages
213213

214+
* [StaticUnivariatePolynomials.jl](https://github.com/tkoolen/StaticUnivariatePolynomials.jl) Fixed-size univariate polynomials backed by a Tuple
215+
214216
* [MultiPoly.jl](https://github.com/daviddelaat/MultiPoly.jl) for sparse multivariate polynomials
215217

216-
* [MultivariatePolynomials.jl](https://github.com/blegat/MultivariatePolynomials.jl) for multivariate polynomials and moments of commutative or non-commutative variables
218+
* [DynamicPolynomals.jl](https://github.com/JuliaAlgebra/DynamicPolynomials.jl) Multivariate polynomials implementation of commutative and non-commutative variables
219+
220+
* [MultivariatePolynomials.jl](https://github.com/JuliaAlgebra/MultivariatePolynomials.jl) for multivariate polynomials and moments of commutative or non-commutative variables
221+
222+
* [PolynomialRings](https://github.com/tkluck/PolynomialRings.jl) A library for arithmetic and algebra with multi-variable polynomials.
223+
224+
* [AbstractAlgebra.jl](https://github.com/wbhart/AbstractAlgebra.jl) and [Nemo.jl](https://github.com/wbhart/Nemo.jl) for generic polynomial rings, matrix spaces, fraction fields, residue rings, power series
225+
226+
* [PolynomialRoots.jl](https://github.com/giordano/PolynomialRoots.jl) for a fast complex polynomial root finder. For larger degree problems, also [FastPolynomialRoots](https://github.com/andreasnoack/FastPolynomialRoots.jl) and [AMRVW](https://github.com/jverzani/AMRVW.jl).
217227

218-
* [AbstractAlgeebra.jl](https://github.com/wbhart/AbstractAlgebra.jl) for generic polynomial rings, matrix spaces, fraction fields, residue rings, power series.
219228

220-
* [PolynomialRoots.jl](https://github.com/giordano/PolynomialRoots.jl) for a fast complex polynomial root finder
221229

222230
## Contributing
223231

docs/src/polynomials/polynomial.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ Polynomial
1111
```
1212

1313
```@docs
14-
Pade
15-
Pade(x)
14+
ImmutablePolynomial
15+
SparsePolynomial
1616
```
1717

18+

src/Polynomials.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ include("common.jl")
1313

1414

1515
# Polynomials
16+
include("polynomials/standard-basis.jl")
1617
include("polynomials/Polynomial.jl")
18+
include("polynomials/ImmutablePolynomial.jl")
19+
include("polynomials/SparsePolynomial.jl")
20+
1721
include("polynomials/ChebyshevT.jl")
1822

19-
# to be deprecated, then removed
20-
include("compat.jl")
23+
# compat; opt-in with `using Polynomials.PolyCompat`
24+
include("polynomials/Poly.jl")
2125

2226
end # module

src/abstract.jl

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,62 @@ macro register(name)
3535
poly = esc(name)
3636
quote
3737
Base.convert(::Type{P}, p::P) where {P<:$poly} = p
38-
Base.convert(P::Type{<:$poly}, p::$poly) where {T} = P(coeffs(p), p.var)
38+
Base.convert(P::Type{<:$poly}, p::$poly{T}) where {T} = P(coeffs(p), p.var)
3939
Base.promote_rule(::Type{$poly{T}}, ::Type{$poly{S}}) where {T,S} =
4040
$poly{promote_type(T, S)}
4141
Base.promote_rule(::Type{$poly{T}}, ::Type{S}) where {T,S<:Number} =
4242
$poly{promote_type(T, S)}
4343
$poly(coeffs::AbstractVector{T}, var::SymbolLike = :x) where {T} =
4444
$poly{T}(coeffs, Symbol(var))
45-
$poly{T}(x::AbstractVector{S}, var = :x) where {T,S<:Number} = $poly(T.(x), var)
46-
$poly{T}(n::Number, var = :x) where {T} = $poly(T(n), var)
45+
$poly{T}(x::AbstractVector{S}, var = :x) where {T,S<:Number} =
46+
$poly(T.(x), var)
47+
$poly{T}(n::S, var = :x) where {T, S<:Number} =
48+
$poly(T[n], var)
4749
$poly(n::Number, var = :x) = $poly([n], var)
48-
$poly{T}(var=:x) where {T} = variable($poly{T}, var)
49-
$poly(var=:x) = variable($poly, var)
50+
$poly{T}(var::SymbolLike=:x) where {T} = variable($poly{T}, Symbol(var))
51+
$poly(var::SymbolLike=:x) = variable($poly, Symbol(var))
5052
end
5153
end
54+
55+
56+
# Macros to register POLY{α, T} and POLY{α, β, T}
57+
macro register1(name)
58+
poly = esc(name)
59+
quote
60+
Base.convert(::Type{P}, p::P) where {P<:$poly} = p
61+
Base.promote_rule(::Type{$poly{α,T}}, ::Type{$poly{α,S}}) where {α,T,S} =
62+
$poly{α,promote_type(T, S)}
63+
Base.promote_rule(::Type{$poly{α,T}}, ::Type{S}) where {α,T,S<:Number} =
64+
$poly{α,promote_type(T,S)}
65+
function $poly{α,T}(x::AbstractVector{S}, var::Polynomials.SymbolLike = :x) where {α,T,S}
66+
$poly{α,T}(T.(x), Symbol(var))
67+
end
68+
$poly{α}(coeffs::AbstractVector{T}, var::Polynomials.SymbolLike=:x) where {α,T} =
69+
$poly{α,T}(coeffs, Symbol(var))
70+
$poly{α,T}(n::Number, var::Polynomials.SymbolLike = :x) where {α,T} = n*one($poly{α,T}, Symbol(var))
71+
$poly{α}(n::Number, var::Polynomials.SymbolLike = :x) where {α} = n*one($poly{α}, Symbol(var))
72+
$poly{α,T}(var::Polynomials.SymbolLike=:x) where {α, T} = variable($poly{α,T}, Symbol(var))
73+
$poly{α}(var::Polynomials.SymbolLike=:x) where {α} = variable($poly{α}, Symbol(var))
74+
end
75+
end
76+
77+
78+
# Macro to register POLY{α, β, T}
79+
macro register2(name)
80+
poly = esc(name)
81+
quote
82+
Base.convert(::Type{P}, p::P) where {P<:$poly} = p
83+
Base.promote_rule(::Type{$poly{α,β,T}}, ::Type{$poly{α,β,S}}) where {α,β,T,S} =
84+
$poly{α,β,promote_type(T, S)}
85+
Base.promote_rule(::Type{$poly{α,β,T}}, ::Type{S}) where {α,β,T,S<:Number} =
86+
$poly{α,β,promote_type(T, S)}
87+
$poly{α,β}(coeffs::AbstractVector{T}, var::Polynomials.SymbolLike = :x) where {α,β,T} =
88+
$poly{α,β,T}(coeffs, Symbol(var))
89+
$poly{α,β,T}(x::AbstractVector{S}, var = :x) where {α,β,T,S<:Number} = $poly{α,β,T}(T.(x), var)
90+
$poly{α,β,T}(n::Number, var = :x) where {α,β,T} = n*one($poly{α,β,T}, var)
91+
$poly{α,β}(n::Number, var = :x) where {α,β} = n*one($poly{α,β}, var)
92+
$poly{α,β,T}(var=:x) where {α,β, T} = variable($poly{α,β,T}, var)
93+
$poly{α,β}(var=:x) where {α,β} = variable($poly{α,β}, var)
94+
end
95+
end
96+

src/common.jl

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ Returns the roots of the given polynomial. This is calculated via the eigenvalue
110110
111111
!!! note
112112
113-
The [PolynomialRoots.jl](https://github.com/giordano/PolynomialRoots.jl) package provides an alternative that is a bit faster and abit more accurate; the [AMRVW.jl](https://github.com/jverzani/AMRVW.jl) package provides an alternative for high-degree polynomials.
113+
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.
114114
115115
"""
116116
function roots(q::AbstractPolynomial{T}; kwargs...) where {T <: Number}
@@ -364,8 +364,8 @@ Base.broadcastable(p::AbstractPolynomial) = Ref(p)
364364

365365
# basis
366366
# return the kth basis polynomial for the given polynomial type, e.g. x^k for Polynomial{T}
367-
function basis(p::P, k::Int) where {P<:AbstractPolynomial}
368-
basis(P, k)
367+
function basis(p::P, k::Int; var=:x) where {P<:AbstractPolynomial}
368+
basis(P, k, var=var)
369369
end
370370

371371
function basis(::Type{P}, k::Int; var=:x) where {P <: AbstractPolynomial}
@@ -384,14 +384,6 @@ end
384384

385385
Base.collect(p::P) where {P <: AbstractPolynomial} = collect(P, p)
386386

387-
function Base.getproperty(p::AbstractPolynomial, nm::Symbol)
388-
if nm == :a
389-
throw(ArgumentError("AbstractPolynomial.a is not supported, use coeffs(AbstractPolynomial) instead."))
390-
end
391-
return getfield(p, nm)
392-
end
393-
394-
395387
# getindex
396388
function Base.getindex(p::AbstractPolynomial{T}, idx::Int) where {T <: Number}
397389
idx < 0 && throw(BoundsError(p, idx))
@@ -434,6 +426,7 @@ Base.hash(p::AbstractPolynomial, h::UInt) = hash(p.var, hash(coeffs(p), h))
434426
435427
Returns a representation of 0 as the given polynomial.
436428
"""
429+
Base.zero(::Type{P}, var=:x) where {T, P <: AbstractPolynomial{T}} = P(zeros(T, 1), var)
437430
Base.zero(::Type{P}, var=:x) where {P <: AbstractPolynomial} = P(zeros(1), var)
438431
Base.zero(p::P) where {P <: AbstractPolynomial} = zero(P, p.var)
439432
"""
@@ -442,9 +435,11 @@ Base.zero(p::P) where {P <: AbstractPolynomial} = zero(P, p.var)
442435
443436
Returns a representation of 1 as the given polynomial.
444437
"""
438+
Base.one(::Type{P}, var=:x) where {T, P <: AbstractPolynomial{T}} = P(ones(T, 1), var)
445439
Base.one(::Type{P}, var=:x) where {P <: AbstractPolynomial} = P(ones(1), var)
446440
Base.one(p::P) where {P <: AbstractPolynomial} = one(P, p.var)
447-
441+
Base.oneunit(p::P, args...) where {P <: AbstractPolynomial} = one(p, args...)
442+
Base.oneunit(::Type{P}, args...) where {P <: AbstractPolynomial} = one(P, args...)
448443
#=
449444
arithmetic =#
450445
Base.:-(p::P) where {P <: AbstractPolynomial} = P(-coeffs(p), p.var)
@@ -457,10 +452,12 @@ function Base.:*(p::P, c::S) where {P <: AbstractPolynomial,S}
457452
T = promote_type(P, S)
458453
return T(coeffs(p) .* c, p.var)
459454
end
455+
460456
function Base.:/(p::P, c::S) where {T,P <: AbstractPolynomial{T},S}
461457
R = promote_type(P, eltype(one(T) / one(S)))
462458
return R(coeffs(p) ./ c, p.var)
463459
end
460+
464461
Base.:-(p1::AbstractPolynomial, p2::AbstractPolynomial) = +(p1, -p2)
465462

466463
function Base.:+(p::P, n::Number) where {P <: AbstractPolynomial}
@@ -530,7 +527,7 @@ Comparisons =#
530527
Base.isequal(p1::P, p2::P) where {P <: AbstractPolynomial} = hash(p1) == hash(p2)
531528
Base.:(==)(p1::AbstractPolynomial, p2::AbstractPolynomial) =
532529
(p1.var == p2.var) && (coeffs(p1) == coeffs(p2))
533-
Base.:(==)(p::AbstractPolynomial, n::Number) = coeffs(p) == [n]
530+
Base.:(==)(p::AbstractPolynomial, n::Number) = degree(p) <= 0 && p[0] == n
534531
Base.:(==)(n::Number, p::AbstractPolynomial) = p == n
535532

536533
function Base.isapprox(p1::AbstractPolynomial{T},
@@ -550,9 +547,9 @@ function Base.isapprox(p1::AbstractPolynomial{T},
550547
end
551548

552549
function Base.isapprox(p1::AbstractPolynomial{T},
553-
n::S;
554-
rtol::Real = (Base.rtoldefault(T, S, 0)),
555-
atol::Real = 0,) where {T,S}
550+
n::S;
551+
rtol::Real = (Base.rtoldefault(T, S, 0)),
552+
atol::Real = 0,) where {T,S}
556553
p1t = truncate(p1, rtol = rtol, atol = atol)
557554
if length(p1t) != 1
558555
return false

src/contrib.jl

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ end
3131
## Code from Julia 1.4 (https://github.com/JuliaLang/julia/blob/master/base/math.jl#L101 on 4/8/20)
3232
## cf. https://github.com/JuliaLang/julia/pull/32753
3333
## Slight modification when `x` is a matrix
34-
## Remove once dependencies for Julia 1.0.0
34+
## Remove once dependencies for Julia 1.0.0 are dropped
3535
function evalpoly(x::S, p::Tuple) where {S}
3636
if @generated
3737
N = length(p.parameters)
38-
ex = :(p[end])
38+
ex = :(p[end]*_one(S))
3939
for i in N-1:-1:1
4040
ex = :(_muladd(x, $ex, p[$i]))
4141
end
@@ -49,7 +49,7 @@ evalpoly(x, p::AbstractVector) = _evalpoly(x, p)
4949

5050
function _evalpoly(x::S, p) where {S}
5151
N = length(p)
52-
ex = p[end]
52+
ex = p[end]*_one(x)
5353
for i in N-1:-1:1
5454
ex = _muladd(x, ex, p[i])
5555
end
@@ -59,8 +59,8 @@ end
5959
function evalpoly(z::Complex, p::Tuple)
6060
if @generated
6161
N = length(p.parameters)
62-
a = :(p[end])
63-
b = :(p[end-1])
62+
a = :(p[end]*_one(z))
63+
b = :(p[end-1]*_one(z))
6464
as = []
6565
for i in N-2:-1:1
6666
ai = Symbol("a", i)
@@ -81,16 +81,16 @@ function evalpoly(z::Complex, p::Tuple)
8181
_evalpoly(z, p)
8282
end
8383
end
84-
evalpoly(z::Complex, p::Tuple{<:Any}) = p[1]
84+
evalpoly(z::Complex, p::Tuple{<:Any}) = p[1]*_one(z)
8585

8686

8787
evalpoly(z::Complex, p::AbstractVector) = _evalpoly(z, p)
8888

8989
function _evalpoly(z::Complex, p)
90-
length(p) == 1 && return p[1]
90+
length(p) == 1 && return p[1]*_one(z)
9191
N = length(p)
92-
a = p[end]
93-
b = p[end-1]
92+
a = p[end]*_one(z)
93+
b = p[end-1]*_one(z)
9494

9595
x = real(z)
9696
y = imag(z)
@@ -105,7 +105,25 @@ function _evalpoly(z::Complex, p)
105105
muladd(ai, z, b)
106106
end
107107

108-
## modify muladd for matrices
108+
## modify muladd, as needed
109109
_muladd(a,b,c) = muladd(a,b,c)
110+
_muladd(a::Vector, b, c) = a.*b .+ c
110111
_muladd(a::Matrix, b, c) = a*(b*I) + c*I
111112

113+
# try to get y = P(c::T)(x::S) = P{T}(c)(x::S) to
114+
# have y == one(T)*one(S)*x
115+
_one(P::Type{<:Matrix}) = one(eltype(P))*I
116+
_one(x::Matrix) = one(eltype(x))*I
117+
_one(x) = one(x)
118+
119+
## get type of parametric composite type without type parameters
120+
## this is needed when the underlying type changes, e.g. with integration
121+
## where T=Int might become T=Float64
122+
##
123+
## trick from [ConstructionBase.jl](https://github.com/JuliaObjects/ConstructionBase.jl/blob/b5686b755bd3bee29b181b3cb18fe2effa0f10a2/src/ConstructionBase.jl#L25)
124+
## as noted in https://discourse.julialang.org/t/get-new-type-with-different-parameter/37253/4
125+
##
126+
@generated function constructorof(::Type{T}) where T
127+
getfield(parentmodule(T), nameof(T))
128+
end
129+

src/pade.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ struct Pade{T <: Number,S <: Number}
2727
q::Union{Poly{S}, Polynomial{S}}
2828
var::Symbol
2929
function Pade{T,S}(p::Union{Poly{T}, Polynomial{T}}, q::Union{Poly{S}, Polynomial{S}}) where {T,S}
30-
Base.depwarn("Use of `Pade` from v1.0 forward will require `using Polynomials.PolyCompat`", :Pade)
3130
if p.var != q.var error("Polynomials must have same variable") end
3231
new{T,S}(p, q, p.var)
3332
end
@@ -89,7 +88,7 @@ Evaluate the Pade approximant at the given point.
8988
9089
# Examples
9190
```jldoctest pade
92-
julia> using SpecialFunctions, Polynomials
91+
julia> using Polynomials, Polynomials.PolyCompat, SpecialFunctions
9392
9493
9594
julia> p = Polynomial(@.(1 // BigInt(gamma(1:17))));

0 commit comments

Comments
 (0)