Reverse multidimensional array

Hey,

recently I wanted to reverse a multidimensional array. I didn’t find a built-in solution but only this post on StackOverflow.

I was wondering what’s a proper implementation. I came up with the following. reverse_2 is the the one from SO, but it’s horribly slow because it is copying everything for each reverse call again.
reverse_all and reverse_all_view are basically the same, except the @view.

function reverse_all(arr::AbstractArray, dims=1:ndims(arr))
    return arr[reverse_all_indices(arr, dims)...]
end

function reverse_all_view(arr::AbstractArray, dims=1:ndims(arr))
    return @view arr[reverse_all_indices(arr, dims)...]
end

function reverse_all_indices(arr, dims=1:ndims(arr))
    out = []
    for i in 1:ndims(arr)
        push!(out, 1:size(arr)[i])
    end 
        
    for d in dims
        out[d] = size(arr)[d]:-1:1
    end
    
    return out 
end

function reverse_2(arr)
    for d = 1:ndims(arr)
        arr = reverse(arr, dims=d)
    end 
    return arr 
end

I often find myself writing code to construct the range expressions. Is there a better way? I’m aware of CartesianIndices but often I don’t find them easier.

julia> x = randn((1000, 12, 122, 140));

julia> @time y = reverse_all(x);
  0.988784 seconds (14 allocations: 1.527 GiB, 0.67% gc time)

julia> @time y_v = reverse_all_view(x);
  0.000014 seconds (12 allocations: 528 bytes)

julia> @time y_2 = reverse_2(x);
 32.074907 seconds (817.91 M allocations: 18.302 GiB, 7.29% gc time)

julia> y == y_2 == y_v
true

Thanks,

Felix

reverse(A; dims=3) ?

Check a bit further down the page in the docs for reverse.

Hm, I don’t think that works.
According to the manual it only reverses the dims dimension, not all dimensions.

That’s the reason to use the loop in reverse_2.

Sure, you didnt say all dimensions until further down than I read :slight_smile:

With a matrix you can use rot180 for that.

Julia 1.6’s reverse allows defining a tuple with several dimensions to reverse.
Is this what you require:

julia> M=[i+j for i in -3:0, j in -1:1]
4×3 Matrix{Int64}:
 -4  -3  -2
 -3  -2  -1
 -2  -1   0
 -1   0   1

julia> reverse(M; dims=(1,2))
4×3 Matrix{Int64}:
  1   0  -1
  0  -1  -2
 -1  -2  -3
 -2  -3  -4
3 Likes

Nice, sounds good!

I’ll need to check whether it creates a view und what’s the performance in comparison to above implementation.

It doesn’t create a view, it makes a copy. You can also do reverse! to work in-place — this is the fastest option (that actually moves data) for large arrays.

Note also that the default is to reverse all the dimensions, so if that is what you want then you can simply do reverse(a) or reverse!(a).

julia> @btime reverse_all($x);
  1.152 s (14 allocations: 1.53 GiB)

julia> @btime reverse($x);
  1.171 s (3 allocations: 1.53 GiB)

julia> @btime reverse!($x);
  385.043 ms (0 allocations: 0 bytes)

PS. Implementing multidimensional reverse! is fun and surprisingly compact, heavily using CartesianIndices tricks and recursion over the dimension.

4 Likes