Skip to content

Commit

Permalink
add a custom exception for selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaqz committed Oct 27, 2023
1 parent 67394b2 commit d49157d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/DimensionalData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export AbstractDimStack, DimStack

export AbstractDimTable, DimTable

export DimIndices, DimKeys, DimPoints
export DimIndices, DimKeys, DimPoints, DimSelectors

# getter methods
export dims, refdims, metadata, name, lookup, bounds
Expand Down
2 changes: 1 addition & 1 deletion src/LookupArrays/lookup_arrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ for f in (:getindex, :view, :dotview)
end

function Adapt.adapt_structure(to, l::AbstractSampled)
rebuild(l; data=Adapt.adapt(to, parent(l)), metadata=NoMetadata())
rebuild(l; data=Adapt.adapt(to, parent(l)), metadata=NoMetadata(), span=Adapt.adapt(to, span(l)))
end

# bounds
Expand Down
4 changes: 4 additions & 0 deletions src/LookupArrays/lookup_traits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ abstract type Span <: LookupArrayTrait end

struct NoSpan <: Span end

Adapt.adapt_structure(to, s::Span) = s

"""
AutoSpan <: Span
Expand Down Expand Up @@ -255,6 +257,8 @@ Explicit() = Explicit(AutoBounds())
val(span::Explicit) = span.val
Base.:(==)(l1::Explicit, l2::Explicit) = val(l1) == val(l2)

Adapt.adapt_structure(to, s::Explicit) = Explicit(Adapt.adapt_structure(to, val(s)))

"""
AutoIndex
Expand Down
25 changes: 20 additions & 5 deletions src/LookupArrays/selector.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
struct SelectorError{L,S} <: Exception
lookup::L
selector::S
end

function Base.showerror(io::IO, ex::SelectorError)
if order(ex.lookup) isa Ordered
println(io, "SelectorError: attempt to select $(ex.selector) from lookup $(typeof(ex.lookup)) with bounds $(bounds(ex.lookup))")
else
println(io, "SelectorError: attempt to select $(ex.selector) from lookup $(typeof(ex.lookup)) with values $(ex.lookup)")
end
end
Base.showerror(io::IO, ex::SelectorError{<:Categorical}) =
println(io, "SelectorError: attempt to select $(ex.selector) from lookup $(typeof(ex.lookup)) with categories $(ex.lookup)")

"""
Selector
Expand Down Expand Up @@ -117,7 +132,7 @@ function at(lookup::NoLookup, sel::At; kw...)
at >= 0.5 && error("atol must be small than 0.5 for NoLookup")
isapprox(v, r; atol=at) || _selvalnotfound(lookup, v)
end
r in lookup || throw(BoundsError(lookup, r))
r in lookup || throw(SelectorError(lookup, sel))
return r
end
function at(lookup::LookupArray, sel::At; kw...)
Expand Down Expand Up @@ -293,7 +308,7 @@ selectindices(l::LookupArray, sel::Contains{<:AbstractVector}) = _selectvec(l, s

function contains(l::NoLookup, sel::Contains; kw...)
i = Int(val(sel))
i in l || throw(BoundsError(l, i))
i in l || throw(SelectorError(l, i))
return i
end
contains(l::LookupArray, sel::Contains; kw...) = contains(sampling(l), l, sel; kw...)
Expand All @@ -307,7 +322,7 @@ function contains(::Points, l::LookupArray, sel::Contains; kw...)
end
# Intervals -----------------------------------
function contains(sampling::Intervals, l::LookupArray, sel::Contains; err=_True())
_locus_checkbounds(locus(l), l, sel) || return _boundserror_or_nothing(err)
_locus_checkbounds(locus(l), l, sel) || return _boundserror_or_nothing(err, l, sel)
contains(order(l), span(l), sampling, locus(l), l, sel; err)
end
# Regular Intervals ---------------------------
Expand Down Expand Up @@ -377,8 +392,8 @@ function contains(
return i
end

_boundserror_or_nothing(err::_True) = throw(BoundsError())
_boundserror_or_nothing(err::_False) = nothing
_boundserror_or_nothing(err::_True, l, i) = throw(SelectorError(l, i))
_boundserror_or_nothing(err::_False, l, i) = nothing

_notcontained_or_nothing(err::_True, selval) = _notcontainederror(selval)
_notcontained_or_nothing(err::_False, selval) = nothing
Expand Down
86 changes: 63 additions & 23 deletions src/dimindices.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
abstract type AbstractDimIndices{T,N,D} <: AbstractDimArray{T,N,D,AbstractArray{T,N}} end

abstract type AbstractDimIndices{T,N} <: AbstractArray{T,N} end
# We need to be able to return a non-DimArray from `parent`
# to prevent stack overflows in dispatch. so we just wrap.
struct ParentWrapper{T,N,A<:AbstractDimIndices{T,N}} <: AbstractArray{T,N}
child::A
end

Base.size(A::ParentWrapper) = size(A.child)
Base.getindex(A::ParentWrapper, I::Int...) = getindex(A.child, I...)

dims(di::AbstractDimIndices) = di.dims
refdims(A::AbstractDimIndices) = ()
data(A::AbstractDimIndices) = ParentWrapper(A)
metadata(A::AbstractDimIndices) = NoMetadata()
name(A::AbstractDimIndices) = Symbol("")

rebuild(di::AbstractDimIndices; kw...) = rebuild(DimArray(di); kw...)
rebuild(di::AbstractDimIndices, args...) = rebuild(DimArray(di), args...)

Base.size(di::AbstractDimIndices) = map(length, dims(di))
Base.axes(di::AbstractDimIndices) = map(d -> axes(d, 1), dims(di))
Expand All @@ -27,7 +42,7 @@ function _format(dims::Tuple)
end

"""
DimIndices <: AbstractArray
DimIndices <: AbstractDimArray
DimIndices(x)
DimIndices(dims::Tuple)
Expand All @@ -40,7 +55,7 @@ This can be used to view/index into arbitrary dimensions over an array, and
is especially useful when combined with `otherdims`, to iterate over the
indices of unknown dimension.
"""
struct DimIndices{T,N,D<:Tuple{Vararg{Dimension}}} <: AbstractDimIndices{T,N}
struct DimIndices{T,N,D<:Tuple{Vararg{Dimension}}} <: AbstractDimIndices{T,N,D}
dims::D
# Manual inner constructor for ambiguity only
function DimIndices{T,N,D}(dims::Tuple{Vararg{Dimension}}) where {T,N,D<:Tuple{Vararg{Dimension}}}
Expand All @@ -53,6 +68,11 @@ function DimIndices(dims::D) where {D<:Tuple{Vararg{Dimension}}}
dims = N > 0 ? _format(dims) : dims
DimIndices{T,N,typeof(dims)}(dims)
end
function DimIndices(dims::NamedTuple{K}) where K
DimIndices(map((d, v) -> rebuild(d, v), key2dim(K), values(dims)))
end

name(A::DimIndices) = :indices

function Base.getindex(di::DimIndices, i1::Int, i2::Int, I::Int...)
map(dims(di), (i1, i2, I...)) do d, i
Expand All @@ -70,9 +90,8 @@ function Base.getindex(di::DimIndices{<:Any,N}, i::Int) where N
end
end


"""
DimPoints <: AbstractArray
DimPoints <: AbstractDimArray
DimPoints(x; order)
DimPoints(dims::Tuple; order)
Expand All @@ -89,7 +108,7 @@ Either a `Dimension`, a `Tuple` of `Dimension` or an object that defines a
- `order`: determines the order of the points, the same as the order of `dims` by default.
"""
struct DimPoints{T,N,D<:DimTuple,O} <: AbstractDimIndices{T,N}
struct DimPoints{T,N,D<:DimTuple,O} <: AbstractDimIndices{T,N,D}
dims::D
order::O
end
Expand All @@ -100,6 +119,11 @@ function DimPoints(dims::DimTuple; order=dims)
dims = N > 0 ? _format(dims) : dims
DimPoints{T,N,typeof(dims),typeof(order)}(dims, order)
end
function DimPoints(dims::NamedTuple{K}) where K
DimPoints(map((d, v) -> rebuild(d, v), key2dim(K), values(dims)))
end

name(A::DimPoints) = :points

function Base.getindex(dp::DimPoints, i1::Int, i2::Int, I::Int...)
# Get dim-wrapped point values at i1, I...
Expand All @@ -113,60 +137,76 @@ Base.getindex(di::DimPoints{<:Any,1}, i::Int) = (dims(di, 1)[i],)
Base.getindex(di::DimPoints, i::Int) = di[Tuple(CartesianIndices(di)[i])...]

"""
DimKeys <: AbstractArray
DimSelectors <: AbstractArray
DimKeys(x)
DimKeys(dims::Tuple)
DimKeys(dims::Dimension)
DimSelectors(x)
DimSelectors(dims::Tuple)
DimSelectors(dims::Dimension)
Like `CartesianIndices`, but for the lookup values of Dimensions. Behaves as an
`Array` of `Tuple` of `Dimension(At(lookupvalue))` for all combinations of the
lookup values of `dims`.
"""
struct DimKeys{T,N,D<:Tuple{Dimension,Vararg{Dimension}},S} <: AbstractDimIndices{T,N}
struct DimSelectors{T,N,D<:Tuple{Dimension,Vararg{Dimension}},S} <: AbstractDimIndices{T,N,D}
dims::D
selectors::S
end
function DimKeys(dims::DimTuple; atol=nothing, selectors=_selectors(dims, atol))
DimKeys(dims, selectors)
function DimSelectors(dims::DimTuple;
type::Type{<:LookupArrays.IntSelector}=At, atol=nothing, selectors=_selectors(dims, type, atol)
)
DimSelectors(dims, selectors)
end
function DimKeys(dims::DimTuple, selectors)
function DimSelectors(dims::DimTuple, selectors)
T = typeof(map(rebuild, dims, selectors))
N = length(dims)
dims = N > 0 ? _format(dims) : dims
DimKeys{T,N,typeof(dims),typeof(selectors)}(dims, selectors)
DimSelectors{T,N,typeof(dims),typeof(selectors)}(dims, selectors)
end
function DimSelectors(dims::NamedTuple{K}; kw...) where K
DimSelectors(map((d, v) -> rebuild(d, v), key2dim(K), values(dims)); kw...)
end

function _selectors(dims, atol)
name(A::DimSelectors) = :selectors

@deprecate DimKeys DimSelectors

function _selectors(dims, type, atol)
map(dims) do d
atol1 = _atol(eltype(d), atol)
At{eltype(d),typeof(atol1),Nothing}(first(d), atol1, nothing)
end
end
function _selectors(dims, atol::Tuple)
function _selectors(dims, type, atol::Tuple)
map(dims, atol) do d, a
atol1 = _atol(eltype(d), a)
At{eltype(d),typeof(atol1),Nothing}(first(d), atol1, nothing)
end
end
function _selectors(dims, atol::Nothing)
function _selectors(dims, type, atol::Nothing)
map(dims) do d
atolx = _atol(eltype(d), nothing)
atol = _atol(eltype(d), nothing)
v = first(val(d))
At{typeof(v),typeof(atolx),Nothing}(v, atolx, nothing)
_construct_selector(type, v, atol)
end
end

_construct_selector(::Type{At}, v, atol) =
At{typeof(v),typeof(atol),Nothing}(v, atol, nothing)
_construct_selector(::Type{Near}, v, atol) =
Near{typeof(v)}(v)
_construct_selector(::Type{Contains}, v, atol) =
Contains{typeof(v)}(v)

_atol(::Type, atol) = atol
_atol(T::Type{<:AbstractFloat}, atol::Nothing) = eps(T)

function Base.getindex(di::DimKeys, i1::Int, i2::Int, I::Int...)
function Base.getindex(di::DimSelectors, i1::Int, i2::Int, I::Int...)
map(dims(di), di.selectors, (i1, i2, I...)) do d, s, i
rebuild(d, rebuild(s; val=d[i])) # At selector with the value at i
end
end
function Base.getindex(di::DimKeys{<:Any,1}, i::Int)
function Base.getindex(di::DimSelectors{<:Any,1}, i::Int)
d = dims(di, 1)
(rebuild(d, rebuild(di.selectors[1]; val=d[i])),)
end
Base.getindex(di::DimKeys, i::Int) = di[Tuple(CartesianIndices(di)[i])...]
Base.getindex(di::DimSelectors, i::Int) = di[Tuple(CartesianIndices(di)[i])...]

0 comments on commit d49157d

Please sign in to comment.