From 8ce72d10df2dd2d95c10185e859f814bdb757f5d Mon Sep 17 00:00:00 2001 From: jishnub Date: Wed, 5 Aug 2020 19:25:19 +0530 Subject: [PATCH] Add SingleValuedRange --- .appveyor.yml | 3 +- Project.toml | 2 +- src/SphericalHarmonicModes.jl | 57 ++++++++++++++--- test/runtests.jl | 117 ++++++++++++++++++++++++++-------- 4 files changed, 140 insertions(+), 39 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index d83e9ae..0e2ba5d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,7 +1,6 @@ environment: matrix: - - julia_version: 1.3 - - julia_version: 1.2 + - julia_version: 1.0 - julia_version: 1 - julia_version: nightly diff --git a/Project.toml b/Project.toml index f6fa1d2..a0729b1 100644 --- a/Project.toml +++ b/Project.toml @@ -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" diff --git a/src/SphericalHarmonicModes.jl b/src/SphericalHarmonicModes.jl index f415fa2..25ce8bb 100644 --- a/src/SphericalHarmonicModes.jl +++ b/src/SphericalHarmonicModes.jl @@ -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)) @@ -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 """ @@ -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 @@ -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 @@ -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) @@ -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) @@ -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 @@ -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) diff --git a/test/runtests.jl b/test/runtests.jl index 496f718..68aa938 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 @@ -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 @@ -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] @@ -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)) @@ -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] @@ -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)) @@ -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] @@ -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)) @@ -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 @@ -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 @@ -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))