Skip to content

Commit 5a65a70

Browse files
authored
Issue 312a (#328)
* add some references; close issue #311 * close issue #312 * merge in master; better fix for issue #320 * update doctests for v1.6 change to print of Vectors
1 parent 9d51785 commit 5a65a70

File tree

11 files changed

+286
-37
lines changed

11 files changed

+286
-37
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,20 @@ Polynomial objects also have other methods:
206206

207207
* [MultivariatePolynomials.jl](https://github.com/JuliaAlgebra/MultivariatePolynomials.jl) for multivariate polynomials and moments of commutative or non-commutative variables
208208

209-
* [PolynomialRings](https://github.com/tkluck/PolynomialRings.jl) A library for arithmetic and algebra with multi-variable polynomials.
209+
* [PolynomialRings.jl](https://github.com/tkluck/PolynomialRings.jl) A library for arithmetic and algebra with multi-variable polynomials.
210210

211211
* [AbstractAlgebra.jl](https://github.com/wbhart/AbstractAlgebra.jl), [Nemo.jl](https://github.com/wbhart/Nemo.jl) for generic polynomial rings, matrix spaces, fraction fields, residue rings, power series, [Hecke.jl](https://github.com/thofma/Hecke.jl) for algebraic number theory.
212212

213-
* [CommutativeAlgebra](https://github.com/KlausC/CommutativeRings.jl) the start of a computer algebra system specialized to discrete calculations with support for polynomials.
213+
* [CommutativeAlgebra.jl](https://github.com/KlausC/CommutativeRings.jl) the start of a computer algebra system specialized to discrete calculations with support for polynomials.
214214

215215
* [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).
216216

217217

218+
* [SpecialPolynomials.jl](https://github.com/jverzani/SpecialPolynomials.jl) A package providing various polynomial types beyond the standard basis polynomials in `Polynomials.jl`. Includes interpolating polynomials, Bernstein polynomials, and classical orthogonal polynomials.
219+
220+
* [ClassicalOrthogonalPolynomials.jl](https://github.com/JuliaApproximation/ClassicalOrthogonalPolynomials.jl) A Julia package for classical orthogonal polynomials and expansions. Includes `chebyshevt`, `chebyshevu`, `legendrep`, `jacobip`, `ultrasphericalc`, `hermiteh`, and `laguerrel`. The same repository includes `FastGaussQuadrature.jl`, `FastTransforms.jl`, and the `ApproxFun` packages.
221+
222+
218223
## Legacy code
219224

220225
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`.

docs/src/extending.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ As always, if the default implementation does not work or there are more efficie
2525
| `domain` | x | Should return an [`AbstractInterval`](https://invenia.github.io/Intervals.jl/stable/#Intervals-1) |
2626
| `vander` | | Required for [`fit`](@ref) |
2727
| `companion` | | Required for [`roots`](@ref) |
28-
| `fromroots` | | By default, will form polynomials using `prod(variable(::P) - r)` for reach root `r`|
2928
| `*(::P, ::P)` | | Multiplication of polynomials |
3029
| `divrem` | | Required for [`gcd`](@ref)|
3130
| `one`| | Convenience to find constant in new basis |
@@ -141,7 +140,7 @@ Now `p` is treated as the vector `p.coeffs`, as regards broadcasting, so some th
141140

142141
```jldoctest AliasPolynomial
143142
julia> p .+ 2
144-
4-element Array{Int64,1}:
143+
4-element Vector{Int64}:
145144
5
146145
4
147146
5

docs/src/index.md

Lines changed: 126 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,17 @@ the returned roots may be real or complex.
144144

145145
```jldoctest
146146
julia> roots(Polynomial([1, 0, -1]))
147-
2-element Array{Float64,1}:
147+
2-element Vector{Float64}:
148148
-1.0
149149
1.0
150150
151151
julia> roots(Polynomial([1, 0, 1]))
152-
2-element Array{Complex{Float64},1}:
152+
2-element Vector{ComplexF64}:
153153
0.0 - 1.0im
154154
0.0 + 1.0im
155155
156156
julia> roots(Polynomial([0, 0, 1]))
157-
2-element Array{Float64,1}:
157+
2-element Vector{Float64}:
158158
0.0
159159
0.0
160160
```
@@ -227,7 +227,7 @@ The `pairs` iterator, iterates over the indices and coefficients, attempting to
227227

228228
```jldoctest
229229
julia> v = [1,2,0,4]
230-
4-element Array{Int64,1}:
230+
4-element Vector{Int64}:
231231
1
232232
2
233233
0
@@ -236,7 +236,7 @@ julia> v = [1,2,0,4]
236236
julia> p,ip,sp,lp = Polynomial(v), ImmutablePolynomial(v), SparsePolynomial(v), LaurentPolynomial(v, -1);
237237
238238
julia> collect(pairs(p))
239-
4-element Array{Pair{Int64,Int64},1}:
239+
4-element Vector{Pair{Int64, Int64}}:
240240
0 => 1
241241
1 => 2
242242
2 => 0
@@ -246,13 +246,13 @@ julia> collect(pairs(ip)) == collect(pairs(p))
246246
true
247247
248248
julia> collect(pairs(sp)) # unordered dictionary with only non-zero terms
249-
3-element Array{Pair{Int64,Int64},1}:
249+
3-element Vector{Pair{Int64, Int64}}:
250250
0 => 1
251251
3 => 4
252252
1 => 2
253253
254254
julia> collect(pairs(lp))
255-
4-element Array{Pair{Int64,Int64},1}:
255+
4-element Vector{Pair{Int64, Int64}}:
256256
-1 => 1
257257
0 => 2
258258
1 => 0
@@ -267,13 +267,126 @@ julia> p = Polynomial([1,2,0,4], :u)
267267
Polynomial(1 + 2*u + 4*u^3)
268268
269269
julia> collect(Polynomials.monomials(p))
270-
4-element Array{Any,1}:
270+
4-element Vector{Any}:
271271
Polynomial(1)
272272
Polynomial(2*u)
273273
Polynomial(0)
274274
Polynomial(4*u^3)
275275
```
276276

277+
## Relationship between the `T` and `P{T,X}`
278+
279+
The addition of a polynomial and a scalar, such as
280+
281+
```jldoctest natural_inclusion
282+
julia> using Polynomials
283+
284+
julia> p = Polynomial([1,2,3], :x)
285+
Polynomial(1 + 2*x + 3*x^2)
286+
287+
julia> p + 3
288+
Polynomial(4 + 2*x + 3*x^2)
289+
```
290+
291+
seems natural, but in `Julia`, as `3` is of type `Int` and `p` of type `Polynomial{Int,:x}` some addition must be defined. The basic idea is that `3` is promoted to the *constant* polynomial `3` with indeterminate `:x` as `3*one(p)` and then addition of `p + 3*one(p)` is performed.
292+
293+
This identification of a scalar with a constant polynomial can go both ways. If `q` is a *constant* polynomial of type `Polynomial{Int, :y}` then we should expect that `p+q` would be defined, as `p` plus the constant term of `q`. Indeed this is the case
294+
295+
```jldoctest natural_inclusion
296+
julia> q = Polynomial(3, :y)
297+
Polynomial(3)
298+
299+
julia> p + q
300+
Polynomial(4 + 2*x + 3*x^2)
301+
```
302+
303+
If `q` is non-constant, such as `variable(Polynomial, :y)`, then there would be an error due to the mismatched symbols. (The mathematical result would need a multivariate polynomial, not a univariate polynomial, as this package provides.)
304+
305+
The same conversion is done for polynomial multiplication: constant polynomials are treated as numbers; non-constant polynomials must have their symbols match.
306+
307+
There is an oddity though the following two computations look the same, they are technically different:
308+
309+
```jldoctest natural_inclusion
310+
julia> one(Polynomial, :x) + one(Polynomial, :y)
311+
Polynomial(2.0)
312+
313+
julia> one(Polynomial, :y) + one(Polynomial, :x)
314+
Polynomial(2.0)
315+
```
316+
317+
Both are constant polynomials over `Int`, but the first has the indeterminate `:y`, the second `:x`.
318+
319+
This technical difference causes no issues with polynomial addition or multiplication, as there constant polynomials are treated as numbers, but can be an issue when constant polynomials are used as array elements.
320+
321+
For arrays, the promotion of numbers to polynomials, allows natural constructions like:
322+
323+
```jldoctest natural_inclusion
324+
julia> p = Polynomial([1,2],:x)
325+
Polynomial(1 + 2*x)
326+
327+
julia> q = Polynomial([1,2],:y) # non-constant polynomials with different indeterminates
328+
Polynomial(1 + 2*y)
329+
330+
julia> [1 p]
331+
1×2 Matrix{Polynomial{Int64, :x}}:
332+
Polynomial(1) Polynomial(1 + 2*x)
333+
334+
julia> [1 one(q)]
335+
1×2 Matrix{Polynomial{Int64, :y}}:
336+
Polynomial(1) Polynomial(1)
337+
```
338+
339+
However, as there would be an ambiguous outcome of the following
340+
341+
```jldoctest natural_inclusion
342+
julia> [one(p) one(q)]
343+
ERROR: ArgumentError: Polynomials have different indeterminates
344+
[...]
345+
```
346+
347+
an error thrown.
348+
349+
In general, arrays with mixtures of non-constant polynomials with *different* indeterminates will error. By default, an error will occur when constant polynomials with different indeterminates are used as components. However, for *typed* arrays, conversion will allow such constructs to be used.
350+
351+
Using `one(q)` for a constant polynomial with indeterminate `:y` we have:
352+
353+
```jldoctest natural_inclusion
354+
julia> P = typeof(p)
355+
Polynomial{Int64, :x}
356+
357+
julia> P[one(p) one(q)]
358+
1×2 Matrix{Polynomial{Int64, :x}}:
359+
Polynomial(1) Polynomial(1)
360+
```
361+
362+
Of course, by not being explicit, there are sill gotchas. For example, we can construct this matrix without a specific types:
363+
364+
```jldoctest natural_inclusion
365+
julia> [one(p), one(q)+one(p)]
366+
2-element Vector{Polynomial{Int64, :x}}:
367+
Polynomial(1)
368+
Polynomial(2)
369+
```
370+
371+
but not this one:
372+
373+
```jldoctest natural_inclusion
374+
julia> [one(p), one(p) + one(q)]
375+
ERROR: ArgumentError: Polynomials have different indeterminates
376+
[...]
377+
```
378+
379+
Also, mixing types can result in unspecific symbols, as this example shows:
380+
381+
```jldoctest natural_inclusion
382+
julia> [1 p; p 1] + [1 2one(q); 3 4] # array{P{T,:x}} + array{P{T,:y}}
383+
2×2 Matrix{Polynomial{Int64, X} where X}:
384+
Polynomial(2) Polynomial(3 + 2*x)
385+
Polynomial(4 + 2*x) Polynomial(5)
386+
```
387+
388+
Though were a non-constant polynomial with indeterminate `y` replacing
389+
`2one(q)` above, that addition would throw an error.
277390

278391
## Related Packages
279392

@@ -285,14 +398,17 @@ julia> collect(Polynomials.monomials(p))
285398

286399
* [MultivariatePolynomials.jl](https://github.com/JuliaAlgebra/MultivariatePolynomials.jl) for multivariate polynomials and moments of commutative or non-commutative variables
287400

288-
* [PolynomialRings](https://github.com/tkluck/PolynomialRings.jl) A library for arithmetic and algebra with multi-variable polynomials.
401+
* [PolynomialRings.jl](https://github.com/tkluck/PolynomialRings.jl) A library for arithmetic and algebra with multi-variable polynomials.
289402

290403
* [AbstractAlgebra.jl](https://github.com/wbhart/AbstractAlgebra.jl), [Nemo.jl](https://github.com/wbhart/Nemo.jl) for generic polynomial rings, matrix spaces, fraction fields, residue rings, power series, [Hecke.jl](https://github.com/thofma/Hecke.jl) for algebraic number theory.
291404

292-
* [CommutativeAlgebra](https://github.com/KlausC/CommutativeRings.jl) the start of a computer algebra system specialized to discrete calculations with support for polynomials.
405+
* [CommutativeAlgebra.jl](https://github.com/KlausC/CommutativeRings.jl) the start of a computer algebra system specialized to discrete calculations with support for polynomials.
293406

294407
* [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).
295408

409+
* [SpecialPolynomials.jl](https://github.com/jverzani/SpecialPolynomials.jl) A package providing various polynomial types beyond the standard basis polynomials in `Polynomials.jl`. Includes interpolating polynomials, Bernstein polynomials, and classical orthogonal polynomials.
410+
411+
* [ClassicalOrthogonalPolynomials.jl](https://github.com/JuliaApproximation/ClassicalOrthogonalPolynomials.jl) A Julia package for classical orthogonal polynomials and expansions. Includes `chebyshevt`, `chebyshevu`, `legendrep`, `jacobip`, `ultrasphericalc`, `hermiteh`, and `laguerrel`. The same repository includes `FastGaussQuadrature.jl`, `FastTransforms.jl`, and the `ApproxFun` packages.
296412

297413

298414

src/abstract.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ abstract type AbstractPolynomial{T,X} end
2020
# (and for LaurentPolynomial the offset)
2121
_convert(p::P, as) where {T,X,P <: AbstractPolynomial{T,X}} = (P)(as, X)
2222

23+
2324
"""
2425
Polynomials.@register(name)
2526
@@ -41,7 +42,10 @@ macro register(name)
4142
poly = esc(name)
4243
quote
4344
Base.convert(::Type{P}, p::P) where {P<:$poly} = p
44-
Base.convert(P::Type{<:$poly}, p::$poly{T}) where {T} = constructorof(P){eltype(P), indeterminate(P,p)}(coeffs(p))
45+
function Base.convert(P::Type{<:$poly}, p::$poly{T,X}) where {T,X}
46+
isconstant(p) && return constructorof(P){eltype(P),indeterminate(P)}(constantterm(p))
47+
constructorof(P){eltype(P), indeterminate(P,p)}(coeffs(p))
48+
end
4549
Base.promote(p::P, q::Q) where {X, T, P <:$poly{T,X}, Q <: $poly{T,X}} = p,q
4650
Base.promote_rule(::Type{<:$poly{T,X}}, ::Type{<:$poly{S,X}}) where {T,S,X} = $poly{promote_type(T, S),X}
4751
Base.promote_rule(::Type{<:$poly{T,X}}, ::Type{S}) where {T,S<:Number,X} =

src/common.jl

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -413,17 +413,31 @@ end
413413
Returns the complex conjugate of the polynomial
414414
"""
415415
LinearAlgebra.conj(p::P) where {P <: AbstractPolynomial} = map(conj, p)
416-
LinearAlgebra.adjoint(p::P) where {P <: AbstractPolynomial} = map(adjoint, p)
416+
LinearAlgebra.adjoint(p::P) where {P <: AbstractPolynomial} = map(adjoint, p)
417+
LinearAlgebra.adjoint(A::VecOrMat{<:AbstractPolynomial}) = adjoint.(permutedims(A)) # default has indeterminate indeterminate
417418
LinearAlgebra.transpose(p::AbstractPolynomial) = p
418419
LinearAlgebra.transpose!(p::AbstractPolynomial) = p
419420

420421
#=
421422
Conversions =#
422423
Base.convert(::Type{P}, p::P) where {P <: AbstractPolynomial} = p
423424
Base.convert(P::Type{<:AbstractPolynomial}, x) = P(x)
425+
function Base.convert(::Type{S}, p::P) where {S <: Number,T, P<:Polynomials.AbstractPolynomial{T}}
426+
Polynomials.isconstant(p) && return convert(S, Polynomials.constantterm(p))
427+
throw(ArgumentError("Can't convert a nonconstant polynomial to type $S"))
428+
end
429+
430+
# Methods to ensure that matrices of polynomials behave as desired
424431
Base.promote_rule(::Type{<:AbstractPolynomial{T}},
425-
::Type{<:AbstractPolynomial{S}},
426-
) where {T,S} = Polynomial{promote_type(T, S)}
432+
::Type{<:AbstractPolynomial{S}},
433+
) where {T,S} = Polynomial{promote_type(T, S)}
434+
Base.promote_rule(::Type{P},::Type{Q}) where {T,X, P<:AbstractPolynomial{T,X},
435+
S, Q<:AbstractPolynomial{S,X}} =
436+
Polynomial{promote_type(T, S),X}
437+
Base.promote_rule(::Type{P},::Type{Q}) where {T,X, P<:AbstractPolynomial{T,X},
438+
S,Y, Q<:AbstractPolynomial{S,Y}} =
439+
assert_same_variable(X,Y)
440+
427441

428442
#=
429443
Inspection =#
@@ -453,6 +467,7 @@ function _eltype(P::Type{<:AbstractPolynomial}, p::AbstractPolynomial)
453467
end
454468
Base.iszero(p::AbstractPolynomial) = all(iszero, p)
455469

470+
456471
# See discussions in https://github.com/JuliaMath/Polynomials.jl/issues/258
457472
"""
458473
all(pred, poly::AbstractPolynomial)
@@ -543,9 +558,6 @@ return `p(0)`, the constant term in the standard basis
543558
"""
544559
constantterm(p::AbstractPolynomial{T}) where {T} = p(zero(T))
545560

546-
547-
548-
549561
"""
550562
degree(::AbstractPolynomial)
551563
@@ -720,8 +732,12 @@ function indeterminate(::Type{P}) where {P <: AbstractPolynomial}
720732
X == nothing ? :x : X
721733
end
722734
indeterminate(p::P) where {P <: AbstractPolynomial} = _indeterminate(P)
723-
function indeterminate(PP::Type{P}, p::AbstractPolynomial) where {P <: AbstractPolynomial}
724-
X = _indeterminate(PP) == nothing ? indeterminate(p) : _indeterminate(PP)
735+
function indeterminate(PP::Type{P}, p::AbstractPolynomial{T,Y}) where {P <: AbstractPolynomial, T,Y}
736+
X = _indeterminate(PP)
737+
X == nothing && return Y
738+
assert_same_variable(X,Y)
739+
return X
740+
#X = _indeterminate(PP) == nothing ? indeterminate(p) : _indeterminate(PP)
725741
end
726742
function indeterminate(PP::Type{P}, x::Symbol) where {P <: AbstractPolynomial}
727743
X = _indeterminate(PP) == nothing ? x : _indeterminate(PP)
@@ -776,7 +792,7 @@ julia> p = 100 + 24x - 3x^2
776792
Polynomial(100 + 24*x - 3*x^2)
777793
778794
julia> roots((x - 3) * (x + 2))
779-
2-element Array{Float64,1}:
795+
2-element Vector{Float64}:
780796
-2.0
781797
3.0
782798

src/pade.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,14 @@ julia> using Polynomials, Polynomials.PolyCompat, SpecialFunctions
9494
9595
9696
97+
9798
julia> p = Polynomial(@.(1 // BigInt(gamma(1:17))));
9899
99100
101+
100102
julia> pade = Pade(p, 8, 8);
101103
104+
102105
julia> pade(1.0) ≈ exp(1.0)
103106
true
104107

src/polynomials/ChebyshevT.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ julia> Polynomials.evalpoly(5.0, p, false) # bypasses the domain check done in p
3535
3636
The latter shows how to evaluate a `ChebyshevT` polynomial outside of its domain, which is `[-1,1]`. (For newer versions of `Julia`, `evalpoly` is an exported function from Base with methods extended in this package, so the module qualification is unnecessary.
3737
38+
!!! Note:
39+
The Chebyshev polynomials are also implemented in `ApproxFun`, `ClassicalOrthogonalPolynomials.jl`, `FastTransforms.jl`, and `SpecialPolynomials.jl`.
40+
3841
"""
3942
struct ChebyshevT{T <: Number, X} <: AbstractPolynomial{T, X}
4043
coeffs::Vector{T}
@@ -102,7 +105,7 @@ julia> c(0)
102105
1.5
103106
104107
julia> c.(-1:0.5:1)
105-
5-element Array{Float64,1}:
108+
5-element Vector{Float64}:
106109
2.0
107110
1.25
108111
1.5

src/polynomials/LaurentPolynomial.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,14 @@ function Base.convert(P::Type{<:Polynomial}, q::LaurentPolynomial)
135135
P([q[i] for i in 0:n], indeterminate(q))
136136
end
137137

138-
# save variable if specified in P
139-
Base.convert(::Type{P}, p::LaurentPolynomial) where {T, X, P<:LaurentPolynomial{T,X}} = P(p.coeffs, p.m[])
138+
# need to add p.m[], so abstract.jl method isn't sufficent
140139
function Base.convert(::Type{P}, p::LaurentPolynomial) where {P<:LaurentPolynomial}
141-
T = eltype(P)
142-
X = indeterminate(P,p)
143-
(P){T, X}(convert(Vector{T},p.coeffs), p.m[])
140+
S′ = _eltype(P)
141+
Y′ = _indeterminate(P)
142+
S = S′ == nothing ? eltype(p) : S′
143+
Y = Y′ == nothing ? indeterminate(p) : Y′
144+
isconstant(p) && return LaurentPolynomial{S,Y}(constantterm(p))
145+
LaurentPolynomial{S,Y}(p.coeffs, p.m[])
144146
end
145147

146148
function Base.convert(::Type{P}, q::StandardBasisPolynomial{S}) where {T, P <:LaurentPolynomial{T},S}

0 commit comments

Comments
 (0)