Using views with CartesianIndices: Is this a bug?

I want to do something analogous to the following snippet:

function reverse_first_dim(A::AbstractArray)
    B = similar(A)
    trailing = size(A)[2:end] |> CartesianIndices
    N = size(A, 1)
    @views B[N:-1:1, trailing] .= A[:, trailing]
    B
end

But, attempting to call this function results in the following error:

julia> reverse_first_dimension(reshape([1, 2, 3, 4], (2, 2)))
ERROR: ArgumentError: an array of type `CartesianIndices` shares memory with another argument and must
make a preventative copy of itself in order to maintain consistent semantics,
but `copy(A)` returns a new array of type `Array{CartesianIndex{1},1}`. To fix, implement:
    `Base.unaliascopy(A::CartesianIndices)::typeof(A)`
Stacktrace:
 [1] _unaliascopy(::CartesianIndices{1,Tuple{Base.OneTo{Int64}}}, ::Array{CartesianIndex{1},1}) at ./abstractarray.jl:1120
 [2] unaliascopy(::CartesianIndices{1,Tuple{Base.OneTo{Int64}}}) at ./abstractarray.jl:1118
 [3] map(::typeof(Base.unaliascopy), ::Tuple{Base.Slice{Base.OneTo{Int64}},CartesianIndices{1,Tuple{Base.OneTo{Int64}}}}) at ./tuple.jl:166
 [4] unaliascopy(::SubArray{Int64,2,Array{Int64,2},Tuple{Base.Slice{Base.OneTo{Int64}},CartesianIndices{1,Tuple{Base.OneTo{Int64}}}},false}) at ./subarray.jl:98
 [5] unalias at ./abstractarray.jl:1101 [inlined]
 [6] copyto!(::SubArray{Int64,2,Array{Int64,2},Tuple{StepRange{Int64,Int64},CartesianIndices{1,Tuple{Base.OneTo{Int64}}}},false}, ::SubArray{Int64,2,Array{Int64,2},Tuple{Base.Slice{Base.OneTo{Int64}},CartesianIndices{1,Tuple{Base.OneTo{Int64}}}},false}) at ./multidimensional.jl:875
 [7] copyto! at ./broadcast.jl:838 [inlined]
 [8] copyto! at ./broadcast.jl:797 [inlined]
 [9] materialize!(::SubArray{Int64,2,Array{Int64,2},Tuple{StepRange{Int64,Int64},CartesianIndices{1,Tuple{Base.OneTo{Int64}}}},false}, ::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2},Nothing,typeof(identity),Tuple{SubArray{Int64,2,Array{Int64,2},Tuple{Base.Slice{Base.OneTo{Int64}},CartesianIndices{1,Tuple{Base.OneTo{Int64}}}},false}}}) at ./broadcast.jl:756
 [10] reverse_first_dimension(::Array{Int64,2}) at ./REPL[7]:5
 [11] top-level scope at none:0

This is a bit hard to parse and, it seems, not an issue with the user facing code. Should this be classed as a bug? I can write the same algorithm in another way, but the code is not as nice.

Here is an older topic with the issue: Error writing into a view of an array using CartesianIndices. It appears that the problem is views with CartesianIndices. I’m reposting for visibility as there were no replies to the original.

2 Likes

In 1.7.0-rc2 it seems to work:

julia> reverse_first_dim(reshape([1, 2, 3, 4], (2, 2)))
2×2 Matrix{Int64}:
 2  4
 1  3

Edit: damn I was tricked into replying to a 3 year old topic… @mbauman it seems someone changing the tags is enough to put the thread in the “latest” list?

I came across the same problem when dealing with discrete indexes.

using Test
a = rand(5)
b = similar(a)
I = CartesianIndex.([1,2])
a[I] = @views b[I] # work well
@test_throws MethodError a[I] .= @views b[I]
I = CartesianIndices(1:2)
a[I] .= @views b[I] # work well again

In some cases, we need the .= method, like

a[I] .+= @view(b[I]) # prefer
a[I] = @views a[I] + b[I] # rather than

Version info

Julia Version 1.8.0
Commit 5544a0fab76 (2022-08-17 13:38 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, icelake-client)
  Threads: 1 on 8 virtual cores

I wonder if it is a bug for @view since both a[I] and b[I] are vectors of equal length.

It looks like a missing method:

julia> CartesianIndex{1}(1)
CartesianIndex(1,)

julia> convert(CartesianIndex{1}, 1)
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type CartesianIndex{1}
Closest candidates are:
  convert(::Type{T}, ::T) where T at Base.jl:61
  CartesianIndex{N}(::Integer...) where N at multidimensional.jl:71
  CartesianIndex{N}(::Integer...) where N at multidimensional.jl:74
Stacktrace:
 [1] top-level scope
   @ REPL[40]:1

Perhaps the conversion should work as well.

1 Like