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

Base.getindex(x::AbstractArray{T,N}, mask::Trues{N, NTuple{N,Base.OneTo{Int}}}) is downstream chaos #162

Closed
ChrisRackauckas opened this issue Sep 30, 2021 · 5 comments · Fixed by #163

Comments

@ChrisRackauckas
Copy link
Member

Found in the ambiguity detection in SciMLBase after a FillArrays.jl dependency was added:

https://github.com/SciML/RecursiveArrayTools.jl/runs/3752855248#step:6:202

The issue stems from Base.getindex(x::AbstractArray{T,N}, mask::Trues{N, NTuple{N,Base.OneTo{Int}}}) types of definitions. The abstract array interface is defined as https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array . This has definitions getindex(A, I...) and setindex!(A, X, I...) specific to the array A, but possibly non-specific to the type I. To handle some ambiguities we distinguish over a decently sized set:

https://github.com/SciML/SciMLBase.jl/blob/master/src/solutions/solution_interface.jl#L31-L32

but adding FillArrays to the set still fails:

SciML/SciMLBase.jl#105

I think downstream packages defining any overloads of the form Base.getindex(x::AbstractArray{T,N}, I::T) where T is the new package thing will cause ambiguity issues and require all downstream array types to define the intersection method Base.getindex(x::A, I::T), which means the only way it can be handled correctly is for type package T to be a dependency of all downstream array packages A (😱). To me that doesn't sound like a tenable solution.

On top of that, I would presume that this definition, since it introduces ambiguities into all of these downstream "open indexing" definitions, it's also invalidating a ton of downstream getindex and setindex! operations as well. So it's likely not just an issue causing ambiguity errors but also hitting compile times.

Maybe @timholy or @mbauman have an opinion on whether these overloads are okay. I would think the right thing would be to remove them.

@dlfivefifty
Copy link
Member

This is a common case of a bad overload that causes problems. Perhaps for now we change it to only N = 1, N = 2, and N = 3?

@ChrisRackauckas
Copy link
Member Author

I think specializing on a few might work.

@timholy
Copy link
Member

timholy commented Oct 1, 2021

When specializing on the indices rather than the array, it's better to specialize to_index or to_indices. I would try deleting all of these methods:

function Base.setindex!(y::AbstractArray{T,N}, x, mask::Trues{N, NTuple{N,Base.OneTo{Int}}}) where {T,N}
if axes(x) isa NTuple{N,Base.OneTo{Int}} && axes(y) isa NTuple{N,Base.OneTo{Int}}
@boundscheck size(y) == size(mask) || throw(BoundsError(y, mask))
@boundscheck size(x) == size(mask) || throw(DimensionMismatch(
"tried to assign $(length(x)) elements to $(length(y)) destinations"))
@boundscheck checkbounds(y, mask)
return copyto!(y, x)
end
return setindex!(y, x, trues(size(mask))) # fall back on usual setindex!
end
# x[mask] when mask isa Trues (cf x[trues(size(x))] or x[:])
# Supported here only for arrays with standard OneTo axes.
function Base.getindex(x::AbstractArray{T,N}, mask::Trues{N, NTuple{N,Base.OneTo{Int}}}) where {T,N}
if axes(x) isa NTuple{N,Base.OneTo{Int}}
@boundscheck size(x) == size(mask) || throw(BoundsError(x, mask))
return vec(x)
end
return x[trues(size(x))] # else revert to usual getindex method
end
# https://github.com/JuliaArrays/FillArrays.jl/issues/148 and 150
function Base.getindex(
a::AbstractFill{T, N, Tuple{Vararg{Base.OneTo{Int}, N}}},
b::Trues{N, Tuple{Vararg{Base.OneTo{Int}, N}}},
) where {T, N}
@boundscheck size(a) == size(b) || throw(BoundsError(a, b))
return Fill(getindex_value(a), length(a))
end

and replacing them with to_indices(A, I) = LinearIndices(A) (with appropriate types on I).

@dlfivefifty
Copy link
Member

@timholy I don't see how that could work in this case as we need to do something special with y and x for a special type of indices. Doesn't setindex! have to be overloaded somewhere?

timholy added a commit that referenced this issue Oct 1, 2021
The `getindex` and `setindex!` methods for `Trues` are limited
while also risking  ambiguities. This replaces those definitions
with a specialization for `to_indices` that avoids such problems.

Closes #162
@timholy
Copy link
Member

timholy commented Oct 1, 2021

Tell that to CI 😄 (see #163)

timholy added a commit that referenced this issue Oct 1, 2021
The `getindex` and `setindex!` methods for `Trues` are limited
while also risking  ambiguities. This replaces those definitions
with a specialization for `to_indices` that avoids such problems.

Closes #162
timholy added a commit that referenced this issue Oct 1, 2021
The `getindex` and `setindex!` methods for `Trues` are limited
while also risking  ambiguities. This replaces those definitions
with a specialization for `to_indices` that avoids such problems.

Closes #162
dlfivefifty pushed a commit that referenced this issue Oct 29, 2021
The `getindex` and `setindex!` methods for `Trues` are limited
while also risking  ambiguities. This replaces those definitions
with a specialization for `to_indices` that avoids such problems.

Closes #162
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants