Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking: fix cat, again #515

Merged
merged 6 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Dimensions/indexing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Convert a `Dimension` or `Selector` `I` to indices of `Int`, `AbstractArray` or
# Otherwise attempt to convert dims to indices
@inline function dims2indices(dims::DimTuple, I::DimTuple)
extradims = otherdims(I, dims)
length(extradims) > 0 && _warnextradims(extradims)
length(extradims) > 0 && _extradimswarn(extradims)
_dims2indices(lookup(dims), dims, sortdims(I, dims))
end

Expand Down
149 changes: 114 additions & 35 deletions src/Dimensions/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ julia> dimnum(A, Y)
```
"""
@inline function dimnum(x, q1, query...)
all(hasdim(x, q1, query...)) || _errorextradims()
all(hasdim(x, q1, query...)) || _extradimserror()
_call_primitive(_dimnum, MaybeFirst(), x, q1, query...)
end

Expand Down Expand Up @@ -466,62 +466,124 @@ cell step, sampling type and order.

const DimTupleOrEmpty = Union{DimTuple,Tuple{}}

struct _Throw end

"""
comparedims(A::AbstractDimArray...; kw...)
comparedims(A::Tuple...; kw...)
comparedims(a, b; kw...)
comparedims(A::Dimension...; kw...)
comparedims(::Type{Bool}, args...; kw...)

Check that dimensions or tuples of dimensions are the same,
and return the first valid dimension. If `AbstractDimArray`s
are passed as arguments their dimensions are compared.
Check that dimensions or tuples of dimensions passed as each
argument are the same, and return the first valid dimension.
If `AbstractDimArray`s are passed as arguments their dimensions are compared.

Empty tuples and `nothing` dimension values are ignored,
returning the `Dimension` value if it exists.

Passing `Bool` as the first argument means `true`/`false` will be returned,
rather than throwing an error.

# Keywords

These are all `Bool` flags:

- `type`: compare dimension type, `true` by default.
- `valtype`: compare wrapped value type, `false` by default.
- `valtype`: compare wrapped value type, `false` by default.
- `val`: compare wrapped values, `false` by default.
- `order`: compare order, `false` by default.
- `length`: compare lengths, `true` by default.
- `ignore_length_one`: ignore length `1` in comparisons, and return whichever
dimension is not length 1, if any. This is useful in e.g. broadcasting comparisons.
`false` by default.
- `warn`: a `String` or `nothing`. Used only for `Bool` methods,
to give a warning for `false` values and include `warn` in the warning text.
"""
function comparedims end
@inline comparedims(ds::Dimension...; kw...) = map(d -> comparedims(first(ds), d; kw...), ds)
@inline comparedims(x...; kw...) = comparedims(x; kw...)
@inline comparedims(A::Tuple; kw...) = comparedims(map(dims, A)...; kw...)
@inline comparedims(dims::Vararg{Tuple{Vararg{Dimension}}}; kw...) =
map(d -> comparedims(first(dims), d; kw...), dims) |> first

@inline comparedims(a::DimTupleOrEmpty, ::Nothing; kw...) = a
@inline comparedims(::Nothing, b::DimTupleOrEmpty; kw...) = b
@inline comparedims(::Nothing, ::Nothing; kw...) = nothing
# Cant use `map` here, tuples may not be the same length
@inline comparedims(a::DimTuple, b::DimTuple; kw...) =
(comparedims(first(a), first(b); kw...), comparedims(tail(a), tail(b); kw...)...)
@inline comparedims(a::DimTuple, b::Tuple{}; kw...) = a
@inline comparedims(a::Tuple{}, b::DimTuple; kw...) = b
@inline comparedims(a::Tuple{}, b::Tuple{}; kw...) = ()
@inline comparedims(a::AnonDim, b::AnonDim; kw...) = nothing
@inline comparedims(a::Dimension, b::AnonDim; kw...) = a
@inline comparedims(a::AnonDim, b::Dimension; kw...) = b
@inline function comparedims(a::Dimension, b::Dimension;
type=true, valtype=false, val=false, length=true, ignore_length_one=false,

@inline comparedims(args...; kw...) = _comparedims(_Throw, args...; kw...)
@inline comparedims(T::Type, args...; kw...) = _comparedims(T, args...; kw...)

@inline _comparedims(T::Type, ds::Dimension...; kw...) =
map(d -> _comparedims(T, first(ds), d; kw...), ds)
@inline _comparedims(T::Type, A::Tuple; kw...) = _comparedims(T, map(dims, A)...; kw...)
@inline _comparedims(T::Type, A...; kw...) = _comparedims(T, map(dims, A)...; kw...)
@inline _comparedims(T::Type, dims::Vararg{Tuple{Vararg{Dimension}}}; kw...) =
map(d -> _comparedims(T, first(dims), d; kw...), dims)

@inline _comparedims(T::Type{_Throw}, a::DimTuple, b::DimTuple; kw...) =
(_comparedims(T, first(a), first(b); kw...), _comparedims(T, tail(a), tail(b); kw...)...)
@inline _comparedims(::Type{_Throw}, a::DimTupleOrEmpty, ::Nothing; kw...) = a
@inline _comparedims(::Type{_Throw}, ::Nothing, b::DimTupleOrEmpty; kw...) = b
@inline _comparedims(::Type{_Throw}, a::DimTuple, b::Tuple{}; kw...) = a
@inline _comparedims(::Type{_Throw}, a::Tuple{}, b::DimTuple; kw...) = b
@inline _comparedims(::Type{_Throw}, a::Tuple{}, b::Tuple{}; kw...) = ()
@inline _comparedims(::Type{_Throw}, ::Nothing, ::Nothing; kw...) = nothing
@inline _comparedims(::Type{_Throw}, a::AnonDim, b::AnonDim; kw...) = nothing
@inline _comparedims(::Type{_Throw}, a::Dimension, b::AnonDim; kw...) = a
@inline _comparedims(::Type{_Throw}, a::AnonDim, b::Dimension; kw...) = b
@inline function _comparedims(::Type{_Throw}, a::Dimension, b::Dimension;
type=true, valtype=false, val=false, length=true, order=false, ignore_length_one=false,
)
type && basetypeof(a) != basetypeof(b) && _dimsmismatcherror(a, b)
valtype && typeof(parent(a)) != typeof(parent(b)) && _valtypeerror(a, b)
val && parent(a) != parent(b) && _valerror(a, b)
order && order(a) != order(b) && _ordererror(a, b)
if ignore_length_one && (Base.length(a) == 1 || Base.length(b) == 1)
return Base.length(b) == 1 ? a : b
end
length && Base.length(a) != Base.length(b) && _dimsizeerror(a, b)
return a
end

@inline _comparedims(T::Type{Bool}, ds::Dimension...; kw...) =
all(map(d -> _comparedims(T, first(ds), d; kw...), ds))
@inline _comparedims(T::Type{Bool}, dims::Vararg{Tuple{Vararg{Dimension}}}; kw...) =
all(map(d -> _comparedims(T, first(dims), d; kw...), dims))
@inline _comparedims(T::Type{Bool}, a::DimTuple, b::DimTuple; kw...) =
all((_comparedims(T, first(a), first(b); kw...), _comparedims(T, tail(a), tail(b); kw...)...))
@inline _comparedims(T::Type{Bool}, a::DimTupleOrEmpty, ::Nothing; kw...) = true
@inline _comparedims(T::Type{Bool}, ::Nothing, b::DimTupleOrEmpty; kw...) = true
@inline _comparedims(T::Type{Bool}, ::Nothing, ::Nothing; kw...) = true
@inline _comparedims(T::Type{Bool}, a::DimTuple, b::Tuple{}; kw...) = true
@inline _comparedims(T::Type{Bool}, a::Tuple{}, b::DimTuple; kw...) = true
@inline _comparedims(T::Type{Bool}, a::Tuple{}, b::Tuple{}; kw...) = true
@inline _comparedims(T::Type{Bool}, a::AnonDim, b::AnonDim; kw...) = true
@inline _comparedims(T::Type{Bool}, a::Dimension, b::AnonDim; kw...) = true
@inline _comparedims(T::Type{Bool}, a::AnonDim, b::Dimension; kw...) = true
@inline function _comparedims(::Type{Bool}, a::Dimension, b::Dimension;
type=true, lookuptype=false, valtype=false, val=false, length=true, order=false, ignore_length_one=false, warn=nothing,
)
if type && basetypeof(a) != basetypeof(b)
isnothing(warn) || _dimsmismatchwarn(a, b, warn)
return false
end
if lookuptype && basetypeof(lookup(a)) != basetypeof(lookup(b))
isnothing(warn) || _typewarn(lookup(a), lookup(b), warn)
return false
end
if valtype && typeof(parent(a)) != typeof(parent(b))
isnothing(warn) || _valtypewarn(a, b, warn)
return false
end
if val && parent(a) != parent(b)
isnothing(warn) || _valwarn(a, b, warn)
return false
end
if order && LookupArrays.order(a) != LookupArrays.order(b)
isnothing(warn) || _orderwarn(a, b, warn)
return false
end
if ignore_length_one && (Base.length(a) == 1 || Base.length(b) == 1)
return true
end
if length && Base.length(a) != Base.length(b)
isnothing(warn) || _dimsizewarn(a, b, warn)
return false
end
return true
end

"""
combinedims(xs; check=true)

Expand Down Expand Up @@ -592,7 +654,7 @@ struct AlwaysTuple end
@inline _call_primitive1(f, t, op::Function, x, query) = _call_primitive1(f, t, op, dims(x), query)
@inline _call_primitive1(f, t, op::Function, x::Nothing) = _dimsnotdefinederror()
@inline _call_primitive1(f, t, op::Function, x::Nothing, query) = _dimsnotdefinederror()
@inline function _call_primitive1(f, t, op::Function, d::Tuple, query)
@inline function _call_primitive1(f, t, op::Function, d::Tuple, query)
ds = dims(query)
isnothing(ds) && _dims_are_not_dims()
_call_primitive1(f, t, op, d, ds)
Expand Down Expand Up @@ -649,13 +711,30 @@ _maybefirst(::Tuple{}) = nothing
_astuple(t::Tuple) = t
_astuple(x) = (x,)

# Error methods. @noinline to avoid allocations.

# Warnings and Error methods.
_dimsmismatchmsg(a, b) = "$(basetypeof(a)) and $(basetypeof(b)) dims on the same axis."
_valmsg(a, b) = "Lookup values for $(basetypeof(a)) of $(parent(a)) and $(parent(b)) do not match."
_dimsizemsg(a, b) = "Found both lengths $(length(a)) and $(length(b)) for $(basetypeof(a))."
_valtypemsg(a, b) = "Lookup for $(basetypeof(a)) of $(lookup(a)) and $(lookup(b)) do not match."
_extradimsmsg(extradims) = "$(map(basetypeof, extradims)) dims were not found in object."
_extradimsmsg(::Tuple{}) = "Some dims were not found in object."
_metadatamsg(a, b) = "Metadata $(metadata(a)) and $(metadata(b)) do not match."
_dimordermsg(a, b) = "Lookups do not all have the same order: $(order(a)), $(order(b))."

# Warning: @noinline to avoid allocations when it isn't used
@noinline _dimsmismatchwarn(a, b, msg="") = @warn _dimsmismatchmsg(a, b) * msg
@noinline _valwarn(a, b, msg="") = @warn _valmsg(a, b) * msg
@noinline _dimsizewarn(a, b, msg="") = @warn _dimsizemsg(a, b) * msg
@noinline _valtypewarn(a, b, msg="") = @warn _valtypemsg(a, b) * msg
@noinline _extradimswarn(dims, msg="") = @warn _extradimsmsg(dims) * msg

# Error
@noinline _dimsmismatcherror(a, b) = throw(DimensionMismatch(_dimsmismatchmsg(a, b)))
@noinline _dimordererror(a, b) = throw(DimensionMismatch(_dimsizemsg(a, b)))
@noinline _dimsizeerror(a, b) = throw(DimensionMismatch(_dimsizemsg(a, b)))
@noinline _valtypeerror(a, b) = throw(DimensionMismatch(_valtypemsg(a, b)))
@noinline _valerror(a, b) = throw(DimensionMismatch(_valmsg(a, b)))
@noinline _metadataerror(a, b) = throw(DimensionMismatch(_metadatamsg(a, b)))
@noinline _extradimserror(args...) = throw(ArgumentError(_extradimsmsg(args)))
@noinline _dimsnotdefinederror() = throw(ArgumentError("Object does not define a `dims` method"))
@noinline _dimsmismatcherror(a, b) = throw(DimensionMismatch("$(basetypeof(a)) and $(basetypeof(b)) for dims on the same axis"))
@noinline _dimsizeerror(a, b) = throw(DimensionMismatch("Found both lengths $(length(a)) and $(length(b)) for $(basetypeof(a))"))
@noinline _valtypeerror(a, b) = throw(DimensionMismatch("Mode $((a)) and $(lookup(b)) do not match"))
@noinline _metadataerror(a, b) = throw(DimensionMismatch("Metadata $(metadata(a)) and $(metadata(b)) do not match"))
@noinline _valerror(a, b) = throw(DimensionMismatch("Dimension index $(parent(a)) and $(parent(b)) do not match"))
@noinline _warnextradims(extradims) = @warn "$(map(basetypeof, extradims)) dims were not found in object"
@noinline _errorextradims() = throw(ArgumentError("Some dims were not found in object"))
Loading