Skip to content

Commit 1570dfc

Browse files
authored
Merge pull request #251 from hurak/master
Added roots() for LaurentPolynomial type.
2 parents ad67b84 + 7e52c57 commit 1570dfc

File tree

3 files changed

+60
-24
lines changed

3 files changed

+60
-24
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 = "1.1.4"
5+
version = "1.1.5"
66

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

src/polynomials/LaurentPolynomial.jl

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A [Laurent](https://en.wikipedia.org/wiki/Laurent_polynomial) polynomial is of t
88
The `coeffs` specify `a_{m}, a_{m-1}, ..., a_{n}`. The range specified is of the form `m:n`, if left empty, `0:length(coeffs)-1` is used (i.e., the coefficients refer to the standard basis). Alternatively, the coefficients can be specified using an `OffsetVector` from the `OffsetArrays` package.
99
1010
Laurent polynomials and standard basis polynomials promote to Laurent polynomials. Laurent polynomials may be converted to a standard basis polynomial when `m >= 0`
11-
.
11+
.
1212
1313
Integration will fail if there is a `x⁻¹` term in the polynomial.
1414
@@ -203,7 +203,7 @@ function Base.setindex!(p::LaurentPolynomial{T}, value::Number, idx::Int) where
203203
p.coeffs[i] = value
204204

205205
return p
206-
206+
207207
end
208208

209209
Base.firstindex(p::LaurentPolynomial) = p.m[]
@@ -215,7 +215,7 @@ Base.eachindex(p::LaurentPolynomial) = range(p)
215215
function chop!(p::P;
216216
rtol::Real = Base.rtoldefault(real(T)),
217217
atol::Real = 0,) where {T, P <: LaurentPolynomial{T}}
218-
218+
219219
m0,n0 = m,n = extrema(p)
220220
for k in n:-1:m
221221
if isapprox(p[k], zero(T); rtol = rtol, atol = atol)
@@ -246,7 +246,7 @@ end
246246
function truncate!(p::LaurentPolynomial{T};
247247
rtol::Real = Base.rtoldefault(real(T)),
248248
atol::Real = 0,) where {T}
249-
249+
250250
max_coeff = maximum(abs, coeffs(p))
251251
thresh = max_coeff * rtol + atol
252252

@@ -259,7 +259,7 @@ function truncate!(p::LaurentPolynomial{T};
259259
chop!(p)
260260

261261
return p
262-
262+
263263
end
264264

265265
# use unicode exponents. XXX modify printexponent to always use these?
@@ -314,7 +314,7 @@ end
314314
315315
[cf.](https://ccrma.stanford.edu/~jos/filters/Paraunitary_FiltersC_3.html)
316316
317-
Call `p̂ = paraconj(p)` and `p̄` = conj(p)`, then this satisfies
317+
Call `p̂ = paraconj(p)` and `p̄` = conj(p)`, then this satisfies
318318
`conj(p(z)) = p̂(1/conj(z))` or `p̂(z) = p̄(1/z) = (conj ∘ p ∘ conj ∘ inf)(z)`.
319319
320320
Examples:
@@ -364,7 +364,7 @@ julia> s = 2im
364364
julia> p = LaurentPolynomial([im,-1, -im, 1], 1:2, :s)
365365
LaurentPolynomial(im*s - s² - im*s³ + s⁴)
366366
367-
julia> Polynomials.cconj(p)(s) ≈ conj(p(s))
367+
julia> Polynomials.cconj(p)(s) ≈ conj(p(s))
368368
true
369369
370370
julia> a = LaurentPolynomial([-0.12, -0.29, 1],:s)
@@ -415,8 +415,8 @@ function (p::LaurentPolynomial{T})(x::S) where {T,S}
415415
l + r - mid
416416
end
417417
end
418-
419-
418+
419+
420420

421421
# scalar operattoinis
422422
Base.:-(p::P) where {P <: LaurentPolynomial} = P(-coeffs(p), range(p), p.var)
@@ -447,7 +447,7 @@ function Base.:+(p1::P1, p2::P2) where {T,P1<:LaurentPolynomial{T}, S, P2<:Laure
447447
elseif isconstant(p2)
448448
p2 = P2(p2.coeffs, range(p2), p1.var)
449449
end
450-
450+
451451
p1.var != p2.var && error("LaurentPolynomials must have same variable")
452452

453453
R = promote_type(T,S)
@@ -465,7 +465,7 @@ function Base.:+(p1::P1, p2::P2) where {T,P1<:LaurentPolynomial{T}, S, P2<:Laure
465465
chop!(q)
466466

467467
return q
468-
468+
469469
end
470470

471471
function Base.:*(p1::LaurentPolynomial{T}, p2::LaurentPolynomial{S}) where {T,S}
@@ -495,11 +495,45 @@ function Base.:*(p1::LaurentPolynomial{T}, p2::LaurentPolynomial{S}) where {T,S}
495495
return p
496496
end
497497

498+
##
499+
## roots
500+
##
501+
"""
502+
roots(p)
503+
504+
Compute the roots of the Laurent polynomial `p`.
505+
506+
The roots of a function (Laurent polynomial in this case) `a(z)` are the values of `z` for which the function vanishes. A Laurent polynomial ``a(z) = a_m z^m + a_{m+1} z^{m+1} + ... + a_{-1} z^{-1} + a_0 + a_1 z + ... + a_{n-1} z^{n-1} + a_n z^n`` can equivalently be viewed as a rational function with a multiple singularity (pole) at the origin. The roots are then the roots of the numerator polynomial. For example, ``a(z) = 1/z + 2 + z`` can be written as ``a(z) = (1+2z+z^2) / z`` and the roots of `a` are the roots of ``1+2z+z^2``.
507+
508+
# Example
509+
510+
```julia
511+
julia> p = LaurentPolynomial([24,10,-15,0,1],-2:1,:z)
512+
LaurentPolynomial(24*z⁻² + 10*z⁻¹ - 15 + z²)
513+
514+
julia> roots(a)
515+
4-element Array{Float64,1}:
516+
-3.999999999999999
517+
-0.9999999999999994
518+
1.9999999999999998
519+
2.9999999999999982
520+
```
521+
"""
522+
function roots(p::P; kwargs...) where {T, P <: LaurentPolynomial{T}}
523+
c = coeffs(p)
524+
r = range(p)
525+
d = r[end]-min(0,r[1])+1 # Length of the coefficient vector, taking into consideration the case when the lower degree is strictly positive (like p=3z^2).
526+
z = zeros(T,d) # Reserves space for the coefficient vector.
527+
z[max(0,r[1])+1:end] = c # Leaves the coeffs of the lower powers as zeros.
528+
a = Polynomial(z,p.var) # The root is then the root of the numerator polynomial.
529+
return roots(a; kwargs...)
530+
end
531+
498532
##
499533
## d/dx, ∫
500534
##
501535
function derivative(p::P, order::Integer = 1) where {T, P<:LaurentPolynomial{T}}
502-
536+
503537
order < 0 && error("Order of derivative must be non-negative")
504538
order == 0 && return p
505539

@@ -518,9 +552,9 @@ function derivative(p::P, order::Integer = 1) where {T, P<:LaurentPolynomial{T}}
518552
as[idx] = reduce(*, (k - order + 1):k, init = p[k])
519553
end
520554
end
521-
555+
522556
chop!(LaurentPolynomial(as, m:n, p.var))
523-
557+
524558
end
525559

526560

@@ -546,15 +580,15 @@ function integrate(p::P, k::S) where {T, P<: LaurentPolynomial{T}, S<:Number}
546580
m = 0
547581
end
548582
as = zeros(R, length(m:n))
549-
583+
550584
for k in eachindex(p)
551585
as[1 + k+1-m] = p[k]/(k+1)
552586
end
553587

554588
as[1-m] = k
555589

556590
return (P)(as, m:n, p.var)
557-
591+
558592
end
559593

560594

@@ -568,7 +602,7 @@ function Base.gcd(p::LaurentPolynomial{T}, q::LaurentPolynomial{T}, args...; kwa
568602
degree(p) == 0 && return iszero(p) ? q : one(q)
569603
degree(q) == 0 && return iszero(q) ? p : one(p)
570604
check_same_variable(p,q) || throw(ArgumentError("p and q have different symbols"))
571-
605+
572606
pp, qq = convert(Polynomial, p), convert(Polynomial, q)
573607
u = gcd(pp, qq, args..., kwargs...)
574608
return LaurentPolynomial(coeffs(u), p.var)

test/StandardBasis.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ end
247247
@test (P([NaN]) NaN) == (false)
248248
@test (P([Inf]) P([Inf])) == ([Inf] [Inf]) # true
249249
@test (P([Inf]) Inf) == (true)
250-
@test (P([1,Inf]) P([0,Inf])) == ([1,Inf] [0,Inf]) # false
251-
@test (P([1,NaN,Inf]) P([0,NaN, Inf])) == ([1,NaN,Inf] [0,NaN, Inf]) #false
250+
@test (P([1,Inf]) P([0,Inf])) == ([1,Inf] [0,Inf]) # false
251+
@test (P([1,NaN,Inf]) P([0,NaN, Inf])) == ([1,NaN,Inf] [0,NaN, Inf]) #false
252252
@test (P([eps(), eps()]) P([0,0])) == ([eps(), eps()] [0,0]) # false
253253
@test (P([1,eps(), 1]) P([1,0,1])) == ([1,eps(), 1] [1,0,1]) # true
254254
@test (P([1,2]) P([1,2,eps()])) == ([1,2,0] [1,2,eps()])
@@ -431,6 +431,8 @@ end
431431
@test roots(pNULL) == []
432432
@test sort(roots(pR)) == [1 // 2, 3 // 2]
433433

434+
@test sort(roots(LaurentPolynomial([24,10,-15,0,1],-2:1,:z)))[-4.0,-1.0,2.0,3.0]
435+
434436
A = [1 0; 0 1]
435437
@test fromroots(A) == Polynomial(Float64[1, -2, 1])
436438
p = fromroots(P, A)
@@ -511,7 +513,7 @@ end
511513
q = [3, p1]
512514
if P != ImmutablePolynomial
513515
@test q isa Vector{typeof(p1)}
514-
@test p isa Vector{typeof(p2)}
516+
@test p isa Vector{typeof(p2)}
515517
else
516518
@test q isa Vector{P{eltype(p1)}} # ImmutablePolynomial{Int64,N} where {N}, different Ns
517519
@test p isa Vector{P{eltype(p2)}} # ImmutablePolynomial{Int64,N} where {N}, different Ns
@@ -651,7 +653,7 @@ end
651653
end
652654

653655
@test eltype(p1) == Int
654-
for P in Ps
656+
for P in Ps
655657
p1 = P([1,2,0,3])
656658
@test eltype(collect(p1)) <: P{Int}
657659
@test eltype(collect(P{Float64}, p1)) <: P{Float64}
@@ -754,7 +756,7 @@ end
754756
for n in (2,5,10,20,50, 100)
755757
p = (x-1)^n * (x-2)^n * (x-3)
756758
q = (x-1) * (x-2) * (x-4)
757-
@test degree(gcd(p,q, method=:numerical)) == 2
759+
@test degree(gcd(p,q, method=:numerical)) == 2
758760
end
759761
end
760762
end
@@ -889,6 +891,6 @@ end
889891
@test typeof(p₁) == P{T,5} # conversion works
890892
@test_throws InexactError P{T}(rand(5))
891893
end
892-
end
894+
end
893895

894896
end

0 commit comments

Comments
 (0)