Skip to content

Commit

Permalink
Add SingleValuedRange
Browse files Browse the repository at this point in the history
  • Loading branch information
jishnub committed Aug 5, 2020
1 parent 244a3b8 commit 8ce72d1
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 39 deletions.
3 changes: 1 addition & 2 deletions .appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
environment:
matrix:
- julia_version: 1.3
- julia_version: 1.2
- julia_version: 1.0
- julia_version: 1
- julia_version: nightly

Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "SphericalHarmonicModes"
uuid = "0e9554e2-b38b-11e9-16d7-9d9abfec665a"
version = "0.4.3"
version = "0.4.4"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
57 changes: 49 additions & 8 deletions src/SphericalHarmonicModes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module SphericalHarmonicModes
import Base: @propagate_inbounds

export LM, ML, L2L1Triangle
export FullRange, ZeroTo, ToZero
export FullRange, ZeroTo, ToZero, SingleValuedRange
export modeindex, l_range, m_range, l2_range, l1_range

firstlast(r::AbstractRange) = (first(r), last(r))
Expand All @@ -13,6 +13,8 @@ firstlast(r::AbstractRange) = (first(r), last(r))
Abstract type whose subtypes are iterators over combinations of spherical harmonic modes.
This is the topmost node in the type hierarchy defined in this package.
Direct subtypes of `ModeRange` are [`SHModeRange`](@ref) and [`L2L1Triangle`](@ref).
"""
abstract type ModeRange end
"""
Expand All @@ -36,8 +38,8 @@ runs over all valid values of `m` for each `l`.
Neither `l_range` nor `m_range` may be empty.
Optionally `m_range` may be provided implicitly using the range specifiers
[`FullRange`](@ref), [`ZeroTo`](@ref) and [`ToZero`](@ref).
Additionally, `l_range` may be of type [`ZeroTo`](@ref).
[`FullRange`](@ref), [`ZeroTo`](@ref) and [`ToZero`](@ref), or as a [`SingleValuedRange`](@ref) type.
Additionally, `l_range` may be of type [`ZeroTo`](@ref) or [`SingleValuedRange`](@ref).
Iterators constructed using these special types would often permit optimizations.
!!! warning
Expand Down Expand Up @@ -90,8 +92,8 @@ runs over all valid values of `m` for each `l`.
Neither `l_range` nor `m_range` may be empty.
Optionally `m_range` may be provided implicitly using the range specifiers
[`FullRange`](@ref), [`ZeroTo`](@ref) and [`ToZero`](@ref).
Additionally `l_range` may be of type [`ZeroTo`](@ref).
[`FullRange`](@ref), [`ZeroTo`](@ref) and [`ToZero`](@ref), or as a [`SingleValuedRange`](@ref) type.
Additionally `l_range` may be of type [`ZeroTo`](@ref) or [`SingleValuedRange`](@ref).
Iterators constructed using these special types would often permit optimizations.
!!! warning
Expand Down Expand Up @@ -285,6 +287,20 @@ abstract type PartiallySpecifiedRange <: AbstractUnitRange{Int} end
abstract type ZeroClampedRange <: PartiallySpecifiedRange end
Base.isempty(::PartiallySpecifiedRange) = false

"""
SingleValuedRange(n::Int)
The range `n:n`.
"""
struct SingleValuedRange <: AbstractUnitRange{Int}
n :: Int
end
Base.isempty(::SingleValuedRange) = false
Base.first(x::SingleValuedRange) = x.n
Base.last(x::SingleValuedRange) = x.n
Base.length(x::SingleValuedRange) = 1
Base.show(io::IO, x::SingleValuedRange) = print(io, repr(x.n), ":", repr(x.n))

"""
ZeroTo(l::Int)
Expand All @@ -303,7 +319,6 @@ Base.first(::ZeroTo) = 0
Base.last(r::ZeroTo) = r.l
Base.show(io::IO, r::ZeroTo) = print(io, "ZeroTo(", repr(r.l), ")")


"""
ToZero(l::Int)
Expand Down Expand Up @@ -794,6 +809,20 @@ end
nskip(mr, l) + m - first(m_range(mr,l)) + 1
end

# Special methods if one of the ranges is single valued
function modeindex(mr::Union{LM{<:AbstractUnitRange, SingleValuedRange}, ML{<:AbstractUnitRange, SingleValuedRange}}, l::Integer, m::Integer)
@boundscheck check_if_mode_present(mr, l, m)
l - first(l_range(mr)) + 1
end
function modeindex(mr::Union{LM{SingleValuedRange, <:AbstractUnitRange}, ML{SingleValuedRange, <:AbstractUnitRange}}, l::Integer, m::Integer)
@boundscheck check_if_mode_present(mr, l, m)
m - first(m_range(mr)) + 1
end
function modeindex(mr::Union{LM{SingleValuedRange, SingleValuedRange}, ML{SingleValuedRange, SingleValuedRange}}, l::Integer, m::Integer)
@boundscheck check_if_mode_present(mr, l, m)
1
end

@inline function nskip(mr::L2L1Triangle, l1)

Nskip = 0
Expand Down Expand Up @@ -839,17 +868,29 @@ end
Base.length(mr::ModeRange) = @inbounds modeindex(mr, last(mr))

# Optimized definition
function Base.length(mr::Union{LM{<:AbstractUnitRange{Int}, FullRange}, ML{<:AbstractUnitRange{Int}, FullRange}})
function Base.length(mr::Union{LM{<:AbstractUnitRange, FullRange}, ML{<:AbstractUnitRange, FullRange}})
# 2l + 1 m's for each l
l_min, l_max = firstlast(l_range(mr))
(l_max + 1)^2 - l_min^2
end
function Base.length(mr::Union{LM{<:AbstractUnitRange{Int}, <:ZeroClampedRange}, ML{<:AbstractUnitRange{Int}, <:ZeroClampedRange}})
function Base.length(mr::Union{LM{<:AbstractUnitRange, <:ZeroClampedRange}, ML{<:AbstractUnitRange, <:ZeroClampedRange}})
# l + 1 m's for each l
l_min, l_max = firstlast(l_range(mr))
div((1 + l_max - l_min)*(2 + l_max + l_min),2)
end

# Special methods for single-valued ranges
for DT in [:(<:AbstractUnitRange), :(<:ZeroClampedRange), :FullRange]
@eval function Base.length(mr::Union{LM{$DT, SingleValuedRange}, ML{$DT, SingleValuedRange}})
length(l_range(mr))
end
@eval function Base.length(mr::Union{LM{SingleValuedRange, $DT}, ML{SingleValuedRange, $DT}})
length(m_range(mr))
end
end

Base.length(mr::Union{LM{SingleValuedRange, SingleValuedRange}, ML{SingleValuedRange, SingleValuedRange}}) = 1

Base.firstindex(mr::ModeRange) = 1
Base.lastindex(mr::ModeRange) = length(mr)

Expand Down
117 changes: 89 additions & 28 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ import SphericalHarmonicModes: flip
@test last(r) == 0
@test !isempty(r)
end
@testset "SingleValuedRange" begin
n = 3
r = SingleValuedRange(n)
@test r == n:n
@test first(r) == n
@test last(r) == n
@test length(r) == 1
@test !isempty(r)
end
end

@testset "LM" begin
Expand Down Expand Up @@ -143,7 +152,7 @@ end

function testlength(iterated_length, mr)
@test begin
res = length(mr) == iterated_length(mr)
res = length(mr) == iterated_length(mr) == length(collect(mr))
if !res
@show mr
end
Expand All @@ -158,7 +167,6 @@ end
x->sum(length(l_range(x,m)) for m in m_range(x)))

@testset "ML" begin

for l_min=0:l_cutoff, l_max=l_min:l_cutoff

for T in [FullRange, ZeroTo, ToZero]
Expand All @@ -167,8 +175,20 @@ end

mr = ML(ZeroTo(l_max), T)
testlength(iterated_length, mr)

mr = ML(SingleValuedRange(l_min), T)
testlength(iterated_length, mr)
end

mr = ML(l_min:l_min, SingleValuedRange(l_min))
testlength(iterated_length, mr)

mr = ML(ZeroTo(l_min), SingleValuedRange(l_min))
testlength(iterated_length, mr)

mr = ML(SingleValuedRange(l_min), SingleValuedRange(l_min))
testlength(iterated_length, mr)

for m_min=-l_max:l_max, m_max=m_min:l_max

l_min_trimmed = max(l_min,max(m_min,-m_max))
Expand All @@ -180,7 +200,6 @@ end
end

@testset "LM" begin

for l_min=0:l_cutoff, l_max=l_min:l_cutoff

for T in [FullRange, ZeroTo, ToZero]
Expand All @@ -189,8 +208,20 @@ end

mr = LM(ZeroTo(l_max), T)
testlength(iterated_length, mr)

mr = LM(SingleValuedRange(l_min), T)
testlength(iterated_length, mr)
end

mr = LM(l_min:l_min, SingleValuedRange(l_min))
testlength(iterated_length, mr)

mr = LM(ZeroTo(l_min), SingleValuedRange(l_min))
testlength(iterated_length, mr)

mr = LM(SingleValuedRange(l_min), SingleValuedRange(l_min))
testlength(iterated_length, mr)

for m_min=-l_max:l_max, m_max=m_min:l_max

l_min_trimmed = max(l_min,max(m_min,-m_max))
Expand All @@ -201,7 +232,7 @@ end
end
end

@testset "LM==ML" begin
@testset "LM == ML" begin
for l_min=0:l_cutoff, l_max=l_min:l_cutoff

for T in [FullRange, ZeroTo, ToZero]
Expand All @@ -212,8 +243,24 @@ end
m1 = LM(ZeroTo(l_max), T)
m2 = ML(ZeroTo(l_max), T)
@test length(m1) == length(m2)

m1 = LM(SingleValuedRange(l_min), T)
m2 = ML(SingleValuedRange(l_min), T)
@test length(m1) == length(m2)
end

m1 = LM(l_min:l_min, SingleValuedRange(l_min))
m2 = ML(l_min:l_min, SingleValuedRange(l_min))
@test length(m1) == length(m2)

m1 = LM(ZeroTo(l_min), SingleValuedRange(l_min))
m1 = ML(ZeroTo(l_min), SingleValuedRange(l_min))
@test length(m1) == length(m2)

m1 = LM(SingleValuedRange(l_min), SingleValuedRange(l_min))
m2 = ML(SingleValuedRange(l_min), SingleValuedRange(l_min))
@test length(m1) == length(m2)

for m_min=-l_max:l_max,m_max=m_min:l_max

l_min_trimmed = max(l_min,max(m_min,-m_max))
Expand Down Expand Up @@ -353,32 +400,42 @@ end

modeindex2(m::SHModeRange,(s,t)::Tuple) = modeindex(m,s,t)

function testmodeindex(mr)
for (ind,(s,t)) in enumerate(mr)
@test modeindex(mr,(s,t)) == modeindex(mr,s,t) == modeindex2(mr,s,t) == ind
end
end

l_cutoff = 5
@testset "LM" begin
for l_min=0:l_cutoff, l_max=l_min:l_cutoff

for MT in [ZeroTo, ToZero, FullRange]
mr = LM(l_min:l_max, MT)

for (ind,(s,t)) in enumerate(mr)
@test modeindex(mr,(s,t)) == modeindex(mr,s,t) == modeindex2(mr,s,t) == ind
end
testmodeindex(mr)

mr = LM(ZeroTo(l_max), MT)
for (ind,(s,t)) in enumerate(mr)
@test modeindex(mr,(s,t)) == modeindex(mr,s,t) == modeindex2(mr,s,t) == ind
end
testmodeindex(mr)

mr = LM(SingleValuedRange(l_min), MT)
testmodeindex(mr)
end

mr = LM(l_min:l_max, SingleValuedRange(l_min))
testmodeindex(mr)

mr = LM(ZeroTo(l_max), SingleValuedRange(l_min))
testmodeindex(mr)

mr = LM(SingleValuedRange(l_min), SingleValuedRange(l_min))
testmodeindex(mr)

for m_min=-l_max:l_max, m_max=m_min:l_max

l_min_trimmed = max(l_min,max(m_min,-m_max))

mr = LM(l_min_trimmed:l_max, m_min:m_max)

for (ind,(s,t)) in enumerate(mr)
@test modeindex(mr,(s,t)) == modeindex(mr,s,t) == modeindex2(mr,s,t) == ind
end
testmodeindex(mr)
end
end
end
Expand All @@ -388,26 +445,30 @@ end

for MT in [ZeroTo, ToZero, FullRange]
mr = ML(l_min:l_max, MT)

for (ind,(s,t)) in enumerate(mr)
@test modeindex(mr,(s,t)) == modeindex(mr,s,t) == modeindex2(mr,s,t) == ind
end
testmodeindex(mr)

mr = ML(ZeroTo(l_max), MT)
for (ind,(s,t)) in enumerate(mr)
@test modeindex(mr,(s,t)) == modeindex(mr,s,t) == modeindex2(mr,s,t) == ind
end
testmodeindex(mr)

mr = ML(SingleValuedRange(l_min), MT)
testmodeindex(mr)
end

mr = ML(l_min:l_max, SingleValuedRange(l_min))
testmodeindex(mr)

mr = ML(ZeroTo(l_max), SingleValuedRange(l_min))
testmodeindex(mr)

mr = ML(SingleValuedRange(l_min), SingleValuedRange(l_min))
testmodeindex(mr)

for m_min=-l_max:l_max, m_max=m_min:l_max

l_min_trimmed = max(l_min,max(m_min,-m_max))

m2 = ML(l_min_trimmed:l_max, m_min:m_max)

for (ind,(s,t)) in enumerate(m2)
@test modeindex(m2,(s,t)) == modeindex(m2,s,t) == modeindex2(m2,s,t) == ind
end
mr = ML(l_min_trimmed:l_max, m_min:m_max)
testmodeindex(mr)
end
end
end
Expand Down Expand Up @@ -604,10 +665,10 @@ end
show(io, MIME"text/plain"(), x)
end

testshow(io, SingleValuedRange(2))
testshow(io, ZeroTo(2))
testshow(io, ToZero(2))
testshow(io, FullRange(2))
testshow(io, ZeroTo(2))

testshow(io, LM(1:2,1:1))
testshow(io, ML(1:2,1:1))
Expand Down

2 comments on commit 8ce72d1

@jishnub
Copy link
Owner Author

@jishnub jishnub commented on 8ce72d1 Aug 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/19025

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.4.4 -m "<description of version>" 8ce72d10df2dd2d95c10185e859f814bdb757f5d
git push origin v0.4.4

Please sign in to comment.