Skip to content

Commit ddb9e57

Browse files
authored
Fix iteration for SparsePolynomial (#405)
* simplify iterate * empty sparse polynomial Change firstindex and lastindex for sparse polynomials to span the entire range of indices * optimized length for SparsePolynomial * bump version to v3.1.0
1 parent f6b7258 commit ddb9e57

File tree

6 files changed

+87
-93
lines changed

6 files changed

+87
-93
lines changed

Project.toml

+1-1
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 = "3.0.1"
5+
version = "3.1.0"
66

77
[deps]
88
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

src/common.jl

+22-35
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,8 @@ Base.length(p::AbstractPolynomial) = length(coeffs(p))
460460
461461
Returns the size of the polynomials coefficients, along axis `i` if provided.
462462
"""
463-
Base.size(p::AbstractPolynomial) = size(coeffs(p))
464-
Base.size(p::AbstractPolynomial, i::Integer) = size(coeffs(p), i)
463+
Base.size(p::AbstractPolynomial) = (length(p),)
464+
Base.size(p::AbstractPolynomial, i::Integer) = i <= 1 ? size(p)[i] : 1
465465
Base.eltype(p::AbstractPolynomial{T}) where {T} = T
466466
# in analogy with polynomial as a Vector{T} with different operations defined.
467467
Base.eltype(::Type{<:AbstractPolynomial}) = Float64
@@ -470,7 +470,7 @@ _eltype(::Type{<:AbstractPolynomial}) = nothing
470470
_eltype(::Type{<:AbstractPolynomial{T}}) where {T} = T
471471
function _eltype(P::Type{<:AbstractPolynomial}, p::AbstractPolynomial)
472472
T′ = _eltype(P)
473-
T = T′ == nothing ? eltype(p) : T′
473+
T = T′ === nothing ? eltype(p) : T′
474474
T
475475
end
476476
Base.iszero(p::AbstractPolynomial) = all(iszero, p)
@@ -664,47 +664,34 @@ Iteration =#
664664
# `pairs(p)`: `i => pᵢ` possibly skipping over values of `i` with `pᵢ == 0` (SparsePolynomial)
665665
# and possibly non ordered (SparsePolynomial)
666666
# `monomials(p)`: iterates over pᵢ ⋅ basis(p, i) i ∈ keys(p)
667-
function Base.iterate(p::AbstractPolynomial, state=nothing)
668-
i = firstindex(p)
669-
if state == nothing
670-
return (p[i], i)
671-
else
672-
j = lastindex(p)
673-
if i <= state < j
674-
return (p[state+1], state+1)
675-
end
676-
return nothing
677-
end
667+
function _iterate(p, state)
668+
firstindex(p) <= state <= lastindex(p) || return nothing
669+
return p[state], state+1
678670
end
671+
Base.iterate(p::AbstractPolynomial, state = firstindex(p)) = _iterate(p, state)
679672

680673
# pairs map i -> aᵢ *possibly* skipping over ai == 0
681674
# cf. abstractdict.jl
682-
struct PolynomialKeys{P}
675+
struct PolynomialKeys{P} <: AbstractSet{Int}
683676
p::P
684677
end
685-
struct PolynomialValues{P}
678+
struct PolynomialValues{P, T} <: AbstractSet{T}
686679
p::P
680+
681+
PolynomialValues{P}(p::P) where {P} = new{P, eltype(p)}(p)
682+
PolynomialValues(p::P) where {P} = new{P, eltype(p)}(p)
687683
end
688684
Base.keys(p::AbstractPolynomial) = PolynomialKeys(p)
689685
Base.values(p::AbstractPolynomial) = PolynomialValues(p)
690686
Base.length(p::PolynomialValues) = length(p.p.coeffs)
691687
Base.length(p::PolynomialKeys) = length(p.p.coeffs)
692688
Base.size(p::Union{PolynomialValues, PolynomialKeys}) = (length(p),)
693-
function Base.iterate(v::PolynomialKeys, state=nothing)
694-
i = firstindex(v.p)
695-
state==nothing && return (i, i)
696-
j = lastindex(v.p)
697-
i <= state < j && return (state+1, state+1)
698-
return nothing
689+
function Base.iterate(v::PolynomialKeys, state = firstindex(v.p))
690+
firstindex(v.p) <= state <= lastindex(v.p) || return nothing
691+
return state, state+1
699692
end
700693

701-
function Base.iterate(v::PolynomialValues, state=nothing)
702-
i = firstindex(v.p)
703-
state==nothing && return (v.p[i], i)
704-
j = lastindex(v.p)
705-
i <= state < j && return (v.p[state+1], state+1)
706-
return nothing
707-
end
694+
Base.iterate(v::PolynomialValues, state = firstindex(v.p)) = _iterate(v.p, state)
708695

709696

710697
# iterate over monomials of the polynomial
@@ -719,7 +706,7 @@ Returns an iterator over the terms, `pᵢ⋅basis(p,i)`, of the polynomial for e
719706
monomials(p) = Monomials(p)
720707
function Base.iterate(v::Monomials, state...)
721708
y = iterate(pairs(v.p), state...)
722-
y == nothing && return nothing
709+
y === nothing && return nothing
723710
kv, s = y
724711
return (kv[2]*basis(v.p, kv[1]), s)
725712
end
@@ -736,18 +723,18 @@ _indeterminate(::Type{P}) where {P <: AbstractPolynomial} = nothing
736723
_indeterminate(::Type{P}) where {T, X, P <: AbstractPolynomial{T,X}} = X
737724
function indeterminate(::Type{P}) where {P <: AbstractPolynomial}
738725
X = _indeterminate(P)
739-
X == nothing ? :x : X
726+
X === nothing ? :x : X
740727
end
741728
indeterminate(p::P) where {P <: AbstractPolynomial} = _indeterminate(P)
742729
function indeterminate(PP::Type{P}, p::AbstractPolynomial{T,Y}) where {P <: AbstractPolynomial, T,Y}
743730
X = _indeterminate(PP)
744-
X == nothing && return Y
731+
X === nothing && return Y
745732
assert_same_variable(X,Y)
746733
return X
747-
#X = _indeterminate(PP) == nothing ? indeterminate(p) : _indeterminate(PP)
734+
#X = _indeterminate(PP) === nothing ? indeterminate(p) : _indeterminate(PP)
748735
end
749736
function indeterminate(PP::Type{P}, x::Symbol) where {P <: AbstractPolynomial}
750-
X = _indeterminate(PP) == nothing ? x : _indeterminate(PP)
737+
X = _indeterminate(PP) === nothing ? x : _indeterminate(PP)
751738
end
752739

753740
#=
@@ -774,7 +761,7 @@ Base.zero(p::P, var=indeterminate(p)) where {P <: AbstractPolynomial} = zero(P,
774761
Returns a representation of 1 as the given polynomial.
775762
"""
776763
Base.one(::Type{P}) where {P<:AbstractPolynomial} = throw(ArgumentError("No default method defined")) # no default method
777-
Base.one(::Type{P}, var::SymbolLike) where {P <: AbstractPolynomial} = one((P){eltype(P), Symbol(var == nothing ? :x : var)})
764+
Base.one(::Type{P}, var::SymbolLike) where {P <: AbstractPolynomial} = one((P){eltype(P), Symbol(var === nothing ? :x : var)})
778765
Base.one(p::P, var=indeterminate(p)) where {P <: AbstractPolynomial} = one(P, var)
779766

780767
Base.oneunit(::Type{P}, args...) where {P <: AbstractPolynomial} = one(P, args...)

src/polynomials/Poly.jl

+3-7
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,9 @@ _eltype(::Type{<:Poly{T}}) where {T} = T
4747
_eltype(::Type{Poly}) = Float64
4848

4949
# when interating over poly return monomials
50-
function Base.iterate(p::Poly, state=nothing)
51-
i = 0
52-
state == nothing && return (p[i]*one(p), i)
53-
j = degree(p)
54-
s = state + 1
55-
i <= state < j && return (p[s]*Polynomials.basis(p,s), s)
56-
return nothing
50+
function Base.iterate(p::Poly, state = firstindex(p))
51+
firstindex(p) <= state <= lastindex(p) || return nothing
52+
return p[state] * Polynomials.basis(p,state), state+1
5753
end
5854
Base.collect(p::Poly) = [pᵢ for pᵢ p]
5955

src/polynomials/SparsePolynomial.jl

+1-6
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,6 @@ function Base.setindex!(p::SparsePolynomial, value::Number, idx::Int)
127127
return p
128128
end
129129

130-
131-
Base.firstindex(p::SparsePolynomial) = sort(collect(keys(p.coeffs)), by=x->x[1])[1]
132-
Base.lastindex(p::SparsePolynomial) = sort(collect(keys(p.coeffs)), by=x->x[1])[end]
133-
Base.eachindex(p::SparsePolynomial) = sort(collect(keys(p.coeffs)), by=x->x[1])
134-
135130
# pairs iterates only over non-zero
136131
# inherits order for underlying dictionary
137132
function Base.iterate(v::PolynomialKeys{SparsePolynomial{T,X}}, state...) where {T,X}
@@ -146,7 +141,7 @@ function Base.iterate(v::PolynomialValues{SparsePolynomial{T,X}}, state...) wher
146141
return (y[1][2], y[2])
147142
end
148143

149-
144+
Base.length(S::SparsePolynomial) = isempty(S.coeffs) ? 0 : maximum(keys(S.coeffs)) + 1
150145

151146
##
152147
## ----

test/ChebyshevT.jl

+19-16
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
@testset "Construction" for coeff in [
2-
Int64[1, 1, 1, 1],
3-
Float32[1, -4, 2],
4-
ComplexF64[1 - 1im, 2 + 3im],
5-
[3 // 4, -2 // 1, 1 // 1]
6-
]
7-
p = ChebyshevT(coeff)
8-
@test p.coeffs == coeff
9-
@test coeffs(p) == coeff
10-
@test degree(p) == length(coeff) - 1
11-
@test Polynomials.indeterminate(p) == :x
12-
@test length(p) == length(coeff)
13-
@test size(p) == size(coeff)
14-
@test size(p, 1) == size(coeff, 1)
15-
@test typeof(p).parameters[1] == eltype(coeff)
16-
@test eltype(p) == eltype(coeff)
1+
@testset "Construction" begin
2+
@testset for coeff in Any[
3+
Int64[1, 1, 1, 1],
4+
Float32[1, -4, 2],
5+
ComplexF64[1 - 1im, 2 + 3im],
6+
[3 // 4, -2 // 1, 1 // 1]
7+
]
8+
9+
p = ChebyshevT(coeff)
10+
@test p.coeffs == coeff
11+
@test coeffs(p) == coeff
12+
@test degree(p) == length(coeff) - 1
13+
@test Polynomials.indeterminate(p) == :x
14+
@test length(p) == length(coeff)
15+
@test size(p) == size(coeff)
16+
@test size(p, 1) == size(coeff, 1)
17+
@test typeof(p).parameters[1] == eltype(coeff)
18+
@test eltype(p) == eltype(coeff)
19+
end
1720
end
1821

1922
@testset "Other Construction" begin

test/StandardBasis.jl

+41-28
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,39 @@ Ps = (ImmutablePolynomial, Polynomial, SparsePolynomial, LaurentPolynomial, Fact
2828
isimmutable(p::P) where {P} = P <: ImmutablePolynomial
2929
isimmutable(::Type{<:ImmutablePolynomial}) = true
3030

31-
@testset "Construction" for coeff in [
32-
Int64[1, 1, 1, 1],
33-
Float32[1, -4, 2],
34-
ComplexF64[1 - 1im, 2 + 3im],
35-
[3 // 4, -2 // 1, 1 // 1]
36-
]
31+
@testset "Construction" begin
32+
@testset for coeff in Any[
33+
Int64[1, 1, 1, 1],
34+
Float32[1, -4, 2],
35+
ComplexF64[1 - 1im, 2 + 3im],
36+
[3 // 4, -2 // 1, 1 // 1]
37+
]
38+
39+
@testset for P in Ps
40+
p = P(coeff)
41+
@test coeffs(p) ==ᵗ⁰ coeff
42+
@test degree(p) == length(coeff) - 1
43+
@test indeterminate(p) == :x
44+
P == Polynomial && @test length(p) == length(coeff)
45+
P == Polynomial && @test size(p) == size(coeff)
46+
P == Polynomial && @test size(p, 1) == size(coeff, 1)
47+
P == Polynomial && @test typeof(p).parameters[1] == eltype(coeff)
48+
if !(eltype(coeff) <: Real && P == FactoredPolynomial) # roots may be complex
49+
@test eltype(p) == eltype(coeff)
50+
end
51+
@test all([-200, -0.3, 1, 48.2] .∈ Polynomials.domain(p))
3752

38-
for P in Ps
39-
p = P(coeff)
40-
@test coeffs(p) ==ᵗ⁰ coeff
41-
@test degree(p) == length(coeff) - 1
42-
@test indeterminate(p) == :x
43-
P == Polynomial && @test length(p) == length(coeff)
44-
P == Polynomial && @test size(p) == size(coeff)
45-
P == Polynomial && @test size(p, 1) == size(coeff, 1)
46-
P == Polynomial && @test typeof(p).parameters[1] == eltype(coeff)
47-
@test eltype(p) == eltype(coeff)
48-
@test all([-200, -0.3, 1, 48.2] .∈ Polynomials.domain(p))
49-
50-
## issue #316
51-
@test_throws InexactError P{Int,:x}([1+im, 1])
52-
@test_throws InexactError P{Int}([1+im, 1], :x)
53-
@test_throws InexactError P{Int,:x}(1+im)
54-
@test_throws InexactError P{Int}(1+im)
55-
56-
## issue #395
57-
v = [1,2,3]
58-
@test P(v) == P(v,:x) == P(v,'x') == P(v,"x") == P(v, Polynomials.Var(:x))
59-
end
53+
## issue #316
54+
@test_throws InexactError P{Int,:x}([1+im, 1])
55+
@test_throws InexactError P{Int}([1+im, 1], :x)
56+
@test_throws InexactError P{Int,:x}(1+im)
57+
@test_throws InexactError P{Int}(1+im)
6058

59+
## issue #395
60+
v = [1,2,3]
61+
@test P(v) == P(v,:x) == P(v,'x') == P(v,"x") == P(v, Polynomials.Var(:x))
62+
end
63+
end
6164
end
6265

6366
@testset "Mapdomain" begin
@@ -1493,3 +1496,13 @@ end
14931496
@test c == MA.operate(*, A, b)
14941497
@test 0 == @allocated MA.buffered_operate!(buffer, MA.add_mul, c, A, b)
14951498
end
1499+
1500+
@testset "empty SparsePolynomial" begin
1501+
p = SparsePolynomial(Float64[0])
1502+
@test eltype(p) == Float64
1503+
@test eltype(keys(p)) == Int
1504+
@test eltype(values(p)) == Float64
1505+
@test collect(p) == Float64[]
1506+
@test collect(keys(p)) == Int[]
1507+
@test collect(values(p)) == Float64[]
1508+
end

0 commit comments

Comments
 (0)