Skip to content

Commit 5a285b9

Browse files
committed
fix == and isapprox
2 parents a03c44c + 6a6be69 commit 5a285b9

File tree

6 files changed

+173
-120
lines changed

6 files changed

+173
-120
lines changed

src/common.jl

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,6 @@ Calculates the p-norm of the polynomial's coefficients
407407
"""
408408
function LinearAlgebra.norm(q::AbstractPolynomial, p::Real = 2)
409409
vs = values(q)
410-
isempty(vs) && return zero(eltype(q))
411410
return norm(vs, p) # if vs=() must be handled in special type
412411
end
413412

@@ -572,7 +571,7 @@ constantterm(p::AbstractPolynomial{T}) where {T} = p(zero(T))
572571
Return the degree of the polynomial, i.e. the highest exponent in the polynomial that
573572
has a nonzero coefficient. The degree of the zero polynomial is defined to be -1. The default method assumes the basis polynomial, `βₖ` has degree `k`.
574573
"""
575-
degree(p::AbstractPolynomial) = iszero(p) ? -1 : lastindex(p)
574+
degree(p::AbstractPolynomial) = iszero(coeffs(p)) ? -1 : length(coeffs(p)) - 1 + min(0, minimumexponent(p))
576575

577576

578577
"""
@@ -613,10 +612,17 @@ mapdomain(::P, x::AbstractArray) where {P <: AbstractPolynomial} = mapdomain(P,
613612

614613
#=
615614
indexing =#
615+
# minimumexponent(p) returns min(0, minimum(keys(p)))
616+
# For most polynomial types, this is statically known to be zero
617+
# For polynomials that support negative indices, minimumexponent(typeof(p))
618+
# should return typemin(Int)
619+
minimumexponent(p::AbstractPolynomial) = minimumexponent(typeof(p))
620+
minimumexponent(::Type{<:AbstractPolynomial}) = 0
616621
Base.firstindex(p::AbstractPolynomial) = 0
617-
Base.lastindex(p::AbstractPolynomial) = length(p) - 1
618-
Base.eachindex(p::AbstractPolynomial) = 0:length(p) - 1
622+
Base.lastindex(p::AbstractPolynomial) = length(p) - 1 + firstindex(p)
623+
Base.eachindex(p::AbstractPolynomial) = firstindex(p):lastindex(p)
619624
Base.broadcastable(p::AbstractPolynomial) = Ref(p)
625+
degreerange(p::AbstractPolynomial) = firstindex(p):lastindex(p)
620626

621627
# getindex
622628
function Base.getindex(p::AbstractPolynomial{T}, idx::Int) where {T}
@@ -671,7 +677,7 @@ Base.iterate(p::AbstractPolynomial, state = firstindex(p)) = _iterate(p, state)
671677
struct PolynomialKeys{P} <: AbstractSet{Int}
672678
p::P
673679
end
674-
struct PolynomialValues{P, T} <: AbstractSet{T}
680+
struct PolynomialValues{P, T}
675681
p::P
676682

677683
PolynomialValues{P}(p::P) where {P} = new{P, eltype(p)}(p)
@@ -680,6 +686,7 @@ end
680686
Base.keys(p::AbstractPolynomial) = PolynomialKeys(p)
681687
Base.values(p::AbstractPolynomial) = PolynomialValues(p)
682688
Base.length(p::PolynomialValues) = length(p.p.coeffs)
689+
Base.eltype(p::PolynomialValues{<:Any,T}) where {T} = T
683690
Base.length(p::PolynomialKeys) = length(p.p.coeffs)
684691
Base.size(p::Union{PolynomialValues, PolynomialKeys}) = (length(p),)
685692
function Base.iterate(v::PolynomialKeys, state = firstindex(v.p))
@@ -1018,23 +1025,33 @@ Base.rem(n::AbstractPolynomial, d::AbstractPolynomial) = divrem(n, d)[2]
10181025
#=
10191026
Comparisons =#
10201027
Base.isequal(p1::P, p2::P) where {P <: AbstractPolynomial} = hash(p1) == hash(p2)
1028+
function Base.:(==)(p1::P, p2::P) where {P <: AbstractPolynomial}
1029+
iszero(p1) && iszero(p2) && return true
1030+
eachindex(p1) == eachindex(p2) || return false
1031+
# coeffs(p1) == coeffs(p2), but non-allocating
1032+
p1val = (p1[i] for i in eachindex(p1))
1033+
p2val = (p2[i] for i in eachindex(p2))
1034+
all(((a,b),) -> a == b, zip(p1val, p2val))
1035+
end
10211036
function Base.:(==)(p1::AbstractPolynomial, p2::AbstractPolynomial)
10221037
if isconstant(p1)
10231038
isconstant(p2) && return constantterm(p1) == constantterm(p2)
10241039
return false
1040+
elseif isconstant(p2)
1041+
return false # p1 is not constant
10251042
end
1026-
indeterminate(p1) == indeterminate(p2) || return false
1043+
check_same_variable(p1, p2) || return false
10271044
==(promote(p1,p2)...)
10281045
end
1029-
Base.:(==)(p1::P, p2::P) where {P <: AbstractPolynomial} =
1030-
check_same_variable(p1,p2) && (coeffs(p1) == coeffs(p2))
10311046
Base.:(==)(p::AbstractPolynomial, n::Number) = degree(p) <= 0 && constantterm(p) == n
10321047
Base.:(==)(n::Number, p::AbstractPolynomial) = p == n
10331048

10341049
function Base.isapprox(p1::AbstractPolynomial, p2::AbstractPolynomial; kwargs...)
10351050
if isconstant(p1)
10361051
isconstant(p2) && return constantterm(p1) == constantterm(p2)
10371052
return false
1053+
elseif isconstant(p2)
1054+
return false
10381055
end
10391056
isapprox(promote(p1, p2)...; kwargs...)
10401057
end

src/polynomials/LaurentPolynomial.jl

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ julia> integrate(q)
5151
LaurentPolynomial(1.0*x + 0.5*x² + 0.3333333333333333*x³)
5252
5353
julia> integrate(p) # x⁻¹ term is an issue
54-
ERROR: ArgumentError: Can't integrate Laurent polynomial with `x⁻¹` term
54+
ERROR: ArgumentError: Can't integrate Laurent polynomial with `x⁻¹` term
5555
5656
julia> integrate(P([1,1,1], -5))
5757
LaurentPolynomial(-0.25*x⁻⁴ - 0.3333333333333333*x⁻³ - 0.5*x⁻²)
@@ -69,7 +69,7 @@ julia> x^degree(p) * p(x⁻¹) # reverses coefficients
6969
LaurentPolynomial(3.0 + 2.0*x + 1.0*x²)
7070
```
7171
"""
72-
struct LaurentPolynomial{T, X} <: StandardBasisPolynomial{T, X}
72+
struct LaurentPolynomial{T, X} <: LaurentBasisPolynomial{T, X}
7373
coeffs::Vector{T}
7474
m::Base.RefValue{Int}
7575
n::Base.RefValue{Int}
@@ -126,12 +126,6 @@ Base.promote_rule(::Type{P},::Type{Q}) where {T, X, P <: LaurentPolynomial{T,X},
126126
Base.promote_rule(::Type{Q},::Type{P}) where {T, X, P <: LaurentPolynomial{T,X}, S, Q <: StandardBasisPolynomial{S,X}} =
127127
LaurentPolynomial{promote_type(T, S),X}
128128

129-
function Base.convert(P::Type{<:Polynomial}, q::LaurentPolynomial)
130-
m,n = (extremadegreerange)(q)
131-
m < 0 && throw(ArgumentError("Can't convert a Laurent polynomial with m < 0"))
132-
P([q[i] for i in 0:n], indeterminate(q))
133-
end
134-
135129
# need to add p.m[], so abstract.jl method isn't sufficent
136130
# XXX unlike abstract.jl, this uses Y variable in conversion; no error
137131
# Used in DSP.jl
@@ -154,7 +148,7 @@ function Base.convert(::Type{P}, q::StandardBasisPolynomial{S}) where {P <:Laure
154148

155149
T = _eltype(P, q)
156150
X = indeterminate(P, q)
157-
(P){T,X}([q[i] for i in 0:degree(q)], 0)
151+
(P){T,X}([q[i] for i in eachindex(q)], firstindex(q))
158152
end
159153

160154
function Base.convert(::Type{P}, q::AbstractPolynomial) where {P <:LaurentPolynomial}
@@ -215,10 +209,11 @@ function Base.setindex!(p::LaurentPolynomial{T}, value::Number, idx::Int) where
215209

216210
end
217211

218-
Base.firstindex(p::LaurentPolynomial) = p.m[]
219-
Base.lastindex(p::LaurentPolynomial) = p.n[]
220-
Base.eachindex(p::LaurentPolynomial) = degreerange(p)
221-
degreerange(p::LaurentPolynomial) = firstindex(p):lastindex(p)
212+
minimumexponent(::Type{<:LaurentPolynomial}) = typemin(Int)
213+
minimumexponent(p::LaurentPolynomial) = p.m[]
214+
Base.firstindex(p::LaurentPolynomial) = minimumexponent(p)
215+
degree(p::LaurentPolynomial) = p.n[]
216+
222217

223218
_convert(p::P, as) where {T,X,P <: LaurentPolynomial{T,X}} = (P)(as, firstindex(p), Var(X))
224219

@@ -517,12 +512,13 @@ function derivative(p::P, order::Integer = 1) where {T, X, P<:LaurentPolynomial{
517512
n = n - order
518513
as = zeros(T, length(m:n))
519514

520-
for k in eachindex(p)
515+
for (k, pₖ) in pairs(p)
516+
iszero(pₖ) && continue
521517
idx = 1 + k - order - m
522518
if 0 k order - 1
523519
as[idx] = zero(T)
524520
else
525-
as[idx] = reduce(*, (k - order + 1):k, init = p[k])
521+
as[idx] = reduce(*, (k - order + 1):k, init = pₖ)
526522
end
527523
end
528524

@@ -531,38 +527,6 @@ function derivative(p::P, order::Integer = 1) where {T, X, P<:LaurentPolynomial{
531527
end
532528

533529

534-
function integrate(p::P) where {T, X, P<: LaurentPolynomial{T, X}}
535-
536-
!iszero(p[-1]) && throw(ArgumentError("Can't integrate Laurent polynomial with `x⁻¹` term"))
537-
R = eltype(one(T)/1)
538-
Q = (P){R, X}
539-
540-
if hasnan(p)
541-
return Q([NaN],0)
542-
end
543-
544-
545-
m,n = (extrema degreerange)(p)
546-
if n < 0
547-
n = 0
548-
else
549-
n += 1
550-
end
551-
if m < 0
552-
m += 1
553-
else
554-
m = 0
555-
end
556-
as = zeros(R, length(m:n))
557-
558-
for k in eachindex(p)
559-
as[1 + k+1-m] = p[k]/(k+1)
560-
end
561-
562-
return Q(as, m)
563-
564-
end
565-
566530
function Base.gcd(p::LaurentPolynomial{T,X}, q::LaurentPolynomial{T,Y}, args...; kwargs...) where {T,X,Y}
567531
mp, Mp = (extrema degreerange)(p)
568532
mq, Mq = (extrema degreerange)(q)

src/polynomials/SparsePolynomial.jl

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ julia> p(1)
3838
```
3939
4040
"""
41-
struct SparsePolynomial{T, X} <: StandardBasisPolynomial{T, X}
41+
struct SparsePolynomial{T, X} <: LaurentBasisPolynomial{T, X}
4242
coeffs::Dict{Int, T}
4343
function SparsePolynomial{T, X}(coeffs::AbstractDict{Int, S}) where {T, X, S}
4444
c = Dict{Int, T}(coeffs)
@@ -73,18 +73,9 @@ function SparsePolynomial{T,X}(coeffs::AbstractVector{S}) where {T, X, S}
7373
return SparsePolynomial{T,X}(p)
7474
end
7575

76-
77-
78-
79-
# conversion
80-
function Base.convert(P::Type{<:Polynomial}, q::SparsePolynomial)
81-
(P)(coeffs(q), indeterminate(q))
82-
end
83-
84-
function Base.convert(P::Type{<:SparsePolynomial}, q::StandardBasisPolynomial{T}) where {T}
85-
R = promote_type(eltype(P), T)
86-
(P){R,indeterminate(P,q)}(coeffs(q))
87-
end
76+
minimumexponent(::Type{<:SparsePolynomial}) = typemin(Int)
77+
minimumexponent(p::SparsePolynomial) = isempty(p.coeffs) ? 0 : min(0, minimum(keys(p.coeffs)))
78+
Base.firstindex(p::SparsePolynomial) = minimumexponent(p)
8879

8980
## changes to common
9081
degree(p::SparsePolynomial) = isempty(p.coeffs) ? -1 : maximum(keys(p.coeffs))
@@ -94,6 +85,8 @@ function isconstant(p::SparsePolynomial)
9485
return true
9586
end
9687

88+
Base.convert(::Type{T}, p::StandardBasisPolynomial) where {T<:SparsePolynomial} = T(Dict(pairs(p)))
89+
9790
function basis(P::Type{<:SparsePolynomial}, n::Int)
9891
T,X = eltype(P), indeterminate(P)
9992
SparsePolynomial{T,X}(Dict(n=>one(T)))
@@ -104,9 +97,10 @@ end
10497
function coeffs(p::SparsePolynomial{T}) where {T}
10598

10699
n = degree(p)
107-
cs = zeros(T, n+1)
100+
cs = zeros(T, length(p))
101+
keymin = firstindex(p)
108102
for (k,v) in p.coeffs
109-
cs[k+1]=v
103+
cs[k - keymin + 1] = v
110104
end
111105
cs
112106

@@ -118,7 +112,6 @@ function Base.getindex(p::SparsePolynomial{T}, idx::Int) where {T}
118112
end
119113

120114
function Base.setindex!(p::SparsePolynomial, value::Number, idx::Int)
121-
idx < 0 && return p
122115
if iszero(value)
123116
haskey(p.coeffs, idx) && pop!(p.coeffs, idx)
124117
else
@@ -141,7 +134,10 @@ function Base.iterate(v::PolynomialValues{SparsePolynomial{T,X}}, state...) wher
141134
return (y[1][2], y[2])
142135
end
143136

144-
Base.length(S::SparsePolynomial) = isempty(S.coeffs) ? 0 : maximum(keys(S.coeffs)) + 1
137+
Base.length(S::SparsePolynomial) = isempty(S.coeffs) ? 0 : begin
138+
minkey, maxkey = extrema(keys(S.coeffs))
139+
maxkey - min(0, minkey) + 1
140+
end
145141

146142
##
147143
## ----
@@ -198,6 +194,8 @@ function Base.:+(p1::P1, p2::P2) where {T,X, P1<:SparsePolynomial{T,X},
198194

199195
end
200196

197+
Base.:-(a::SparsePolynomial) = typeof(a)(Dict(k=>-v for (k,v) in a.coeffs))
198+
201199
## Multiplication
202200
function scalar_mult(p::P, c::S) where {T, X, P <: SparsePolynomial{T,X}, S<:Number}
203201

@@ -225,7 +223,6 @@ function scalar_mult(c::S, p::P) where {T, X, P <: SparsePolynomial{T,X}, S<:Num
225223
return q
226224
end
227225

228-
229226
function Base.:*(p::P, q::Q) where {T,X,P<:SparsePolynomial{T,X},
230227
S, Q<:SparsePolynomial{S,X}}
231228
R = promote_type(T,S)
@@ -243,32 +240,12 @@ function derivative(p::SparsePolynomial{T,X}, order::Integer = 1) where {T,X}
243240
hasnan(p) && return P{R,X}(Dict(0 => R(NaN)))
244241

245242
n = degree(p)
246-
order > n && return zero(P{R,X})
247243

248244
dpn = zero(P{R,X})
249-
@inbounds for k in eachindex(p)
250-
dpn[k-order] = reduce(*, (k - order + 1):k, init = p[k])
245+
@inbounds for (k,v) in pairs(p)
246+
dpn[k-order] = reduce(*, (k - order + 1):k, init = v)
251247
end
252248

253249
return dpn
254250

255251
end
256-
257-
258-
function integrate(p::P) where {T, X, P<:SparsePolynomial{T,X}}
259-
260-
R = eltype(one(T)/1)
261-
Q = SparsePolynomial{R,X}
262-
263-
if hasnan(p)
264-
return Q(Dict(0 => NaN))
265-
end
266-
267-
∫p = zero(Q)
268-
for k in eachindex(p)
269-
∫p[k + 1] = p[k] / (k+1)
270-
end
271-
272-
return ∫p
273-
274-
end

src/polynomials/standard-basis.jl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
abstract type StandardBasisPolynomial{T,X} <: AbstractPolynomial{T,X} end
2+
abstract type LaurentBasisPolynomial{T,X} <: StandardBasisPolynomial{T,X} end
23

34
function showterm(io::IO, ::Type{<:StandardBasisPolynomial}, pj::T, var, j, first::Bool, mimetype) where {T}
45

@@ -49,9 +50,11 @@ function Base.convert(P::Type{<:StandardBasisPolynomial}, q::StandardBasisPolyno
4950
if isa(q, P)
5051
return q
5152
else
53+
minimumexponent(P) <= minimumexponent(q) ||
54+
throw(ArgumentError("a $P can not have a minimum exponent of $(minimumexponent(q))"))
5255
T = _eltype(P,q)
5356
X = indeterminate(P,q)
54-
return (P){T,X}([q[i] for i in 0:degree(q)])
57+
return (P){T,X}([q[i] for i in eachindex(q)])
5558
end
5659
end
5760

@@ -197,6 +200,22 @@ function integrate(p::P) where {T, X, P <: StandardBasisPolynomial{T, X}}
197200
return Q(as)
198201
end
199202

203+
function integrate(p::P) where {T, X, P <: LaurentBasisPolynomial{T, X}}
204+
R = typeof(constantterm(p)/1)
205+
Q = (P){R,X}
206+
207+
hasnan(p) && return Q([NaN])
208+
iszero(p) && return zero(Q)
209+
210+
∫p = zero(Q)
211+
for (k, pₖ) pairs(p)
212+
iszero(pₖ) && continue
213+
k == -1 && throw(ArgumentError("Can't integrate Laurent polynomial with `x⁻¹` term"))
214+
∫p[k+1] = pₖ/(k+1)
215+
end
216+
∫p
217+
end
218+
200219
function Base.divrem(num::P, den::Q) where {T, P <: StandardBasisPolynomial{T}, S, Q <: StandardBasisPolynomial{S}}
201220

202221
assert_same_variable(num, den)

0 commit comments

Comments
 (0)