Skip to content

Commit 2c0c3c7

Browse files
Some work on supporting non-standard indexing (#44)
* some work on supporting non-standard indexing * Update test/nonstandard_indices.jl Co-authored-by: Hendrik Ranocha <ranocha@users.noreply.github.com> * getindex probably ready? * check compat with EllipsisNotation only on Julia 1.5+ * fix version check? * bump version Co-authored-by: Hendrik Ranocha <ranocha@users.noreply.github.com>
1 parent 73ca826 commit 2c0c3c7

File tree

4 files changed

+107
-64
lines changed

4 files changed

+107
-64
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "HybridArrays"
22
uuid = "1baab800-613f-4b0a-84e4-9cd3431bfbb9"
33
authors = ["Mateusz Baran <mateuszbaran89@gmail.com>"]
4-
version = "0.4.6"
4+
version = "0.4.7"
55

66
[deps]
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

src/HybridArrays.jl

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import Base: convert,
2222
import Base.Array
2323

2424
using StaticArrays
25-
using StaticArrays: Dynamic
25+
using StaticArrays: Dynamic, StaticIndexing
2626
import StaticArrays: _setindex!_scalar, Size
2727

2828
using LinearAlgebra
@@ -55,11 +55,10 @@ end
5555

5656
destatizing = (inds[i] <: AbstractArray && !(
5757
inds[i] <: StaticArray ||
58-
inds[i] <: Base.Slice{<:StaticArray} ||
59-
inds[i] <: SOneTo ||
60-
inds[i] <: Base.Slice{<:SOneTo}))
58+
inds[i] <: Base.Slice ||
59+
inds[i] <: SOneTo))
6160

62-
nonstatizing = inds[i] == Colon || destatizing
61+
nonstatizing = inds[i] == Colon || inds[i] <: Base.Slice || destatizing
6362

6463
if destatizing || (isa(param, Dynamic) && nonstatizing)
6564
all_fixed = false
@@ -75,6 +74,23 @@ end
7574
end
7675
end
7776

77+
function has_dynamic(::Type{Size}) where Size<:Tuple
78+
for param in Size.parameters
79+
if isa(param, Dynamic)
80+
return true
81+
end
82+
end
83+
return false
84+
end
85+
86+
@generated function all_dynamic_fixed_val(::Type{Size}, inds::Union{Colon, Base.Slice}) where Size<:Tuple
87+
if has_dynamic(Size)
88+
return Val(:dynamic_fixed_false)
89+
else
90+
return Val(:dynamic_fixed_true)
91+
end
92+
end
93+
7894
@generated function tuple_nodynamic_prod(::Type{Size}) where Size<:Tuple
7995
i = 1
8096
for s Size.parameters

src/indexing.jl

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
@inline function getindex(sa::HybridArray{S}, ::Colon) where S
2-
return HybridArray{S}(getindex(sa.data, :))
3-
end
4-
51
Base.@propagate_inbounds function getindex(sa::HybridArray{S}, inds::Int...) where S
62
return getindex(sa.data, inds...)
73
end
@@ -10,39 +6,52 @@ Base.@propagate_inbounds function getindex(sa::HybridArray{S}, inds::Union{Int,
106
_getindex(all_dynamic_fixed_val(S, inds...), sa, inds...)
117
end
128

13-
Base.@propagate_inbounds function _getindex(::Val{:dynamic_fixed_true}, sa::HybridArray, inds::Union{Int, StaticArray{<:Tuple, Int}, Colon}...)
9+
# This plugs into a deeper level of indexing in base to catch custom
10+
# indexing schemes based on `to_indices`.
11+
# A minor version Julia release could potentially break this (though it seems unlikely).
12+
Base.@propagate_inbounds function Base._getindex(l::IndexLinear, sa::HybridArray{S}, inds::Int...) where S
13+
return Base._getindex(l, sa.data, inds...)
14+
end
15+
Base.@propagate_inbounds function Base._getindex(::IndexLinear, sa::HybridArray{S}, inds::Union{Int, StaticVector, Colon, Base.Slice}...) where S
16+
_getindex(all_dynamic_fixed_val(S, inds...), sa, inds...)
17+
end
18+
19+
Base.@propagate_inbounds function _getindex(::Val{:dynamic_fixed_true}, sa::HybridArray, inds::Union{Int, StaticVector, Colon, Base.Slice}...)
1420
return _getindex_all_static(sa, inds...)
1521
end
1622

1723
function _get_indices(i::Tuple{}, j::Int)
1824
return ()
1925
end
2026

21-
function _get_indices(i::Tuple, j::Int, i1::Type{Int}, inds...)
27+
function _get_indices(i::Tuple, j::Int, ::Type{Int}, inds...)
2228
return (:(inds[$j]), _get_indices(i, j+1, inds...)...)
2329
end
2430

25-
function _get_indices(i::Tuple, j::Int, i1::Type{T}, inds...) where T<:StaticArray{<:Tuple, Int}
31+
function _get_indices(i::Tuple, j::Int, ::Type{T}, inds...) where T<:StaticVector
2632
return (:(inds[$j][$(i[1])]), _get_indices(i[2:end], j+1, inds...)...)
2733
end
2834

29-
function _get_indices(i::Tuple, j::Int, i1::Type{Colon}, inds...)
35+
function _get_indices(i::Tuple, j::Int, ::Type{<:Union{Colon, Base.Slice}}, inds...)
3036
return (i[1], _get_indices(i[2:end], j+1, inds...)...)
3137
end
3238

3339
_totally_linear() = true
3440
_totally_linear(inds...) = false
3541
_totally_linear(inds::Type{Int}...) = true
42+
_totally_linear(inds::Type{<:Base.Slice}...) = true
3643
_totally_linear(inds::Type{Colon}...) = true
37-
_totally_linear(i1::Type{Colon}, inds...) = _totally_linear(inds...)
44+
_totally_linear(::Type{<:Base.Slice}, inds...) = _totally_linear(inds...)
45+
_totally_linear(::Type{Colon}, inds...) = _totally_linear(inds...)
3846

3947
function new_out_size_nongen(::Type{Size}, inds...) where Size
4048
os = []
49+
@assert length(Size.parameters) == length(inds)
4150
map(Size.parameters, inds) do s, i
4251
if i == Int
4352
elseif i <: StaticVector
4453
push!(os, length(i))
45-
elseif i == Colon
54+
elseif i == Colon || i <: Base.Slice
4655
push!(os, s)
4756
else
4857
error("Unknown index type: $i")
@@ -51,6 +60,14 @@ function new_out_size_nongen(::Type{Size}, inds...) where Size
5160
return tuple(os...)
5261
end
5362

63+
function new_out_size_nongen(::Type{Size}, i::Type{<:Union{Colon, Base.Slice}}) where Size
64+
if has_dynamic(Size)
65+
return (Dynamic(),)
66+
else
67+
return (tuple_nodynamic_prod(Size),)
68+
end
69+
end
70+
5471
"""
5572
_get_linear_inds(S, inds...)
5673
@@ -108,7 +125,7 @@ function _get_linear_inds(S, inds...)
108125
end
109126
end
110127

111-
@generated function _getindex_all_static(sa::HybridArray{S,T}, inds::Union{Int, StaticArray{<:Tuple, Int}, Colon}...) where {S,T}
128+
@generated function _getindex_all_static(sa::HybridArray{S,T}, inds::Union{Int, StaticIndexing, Base.Slice, Colon, StaticArray}...) where {S,T}
112129
newsize = new_out_size_nongen(S, inds...)
113130
exprs = Vector{Expr}(undef, length(newsize))
114131

@@ -138,17 +155,13 @@ end
138155
end
139156
end
140157

141-
function new_out_size(S::Type{Size}, inds::StaticArrays.StaticIndexing...) where Size
142-
return new_out_size(S, map(StaticArrays.unwrap, inds)...)
143-
end
144-
145-
146158
# _get_static_vector_length is used in a generated function so using a generic function
147159
# may not be a good idea
148160
_get_static_vector_length(::Type{<:StaticVector{N}}) where {N} = N
149161

150162
@generated function new_out_size(::Type{Size}, inds...) where Size
151163
os = []
164+
@assert length(Size.parameters) === length(inds)
152165
map(Size.parameters, inds) do s, i
153166
if i == Int
154167
elseif i <: StaticVector
@@ -166,9 +179,21 @@ _get_static_vector_length(::Type{<:StaticVector{N}}) where {N} = N
166179
return Tuple{os...}
167180
end
168181

169-
@inline function _getindex(::Val{:dynamic_fixed_false}, sa::HybridArray{S}, inds::Union{Int, StaticArray{<:Tuple, Int}, Colon}...) where S
170-
newsize = new_out_size(S, inds...)
171-
return HybridArray{newsize}(getindex(sa.data, inds...))
182+
@generated function new_out_size(::Type{Size}, ::Union{Colon, Base.Slice}) where Size
183+
if has_dynamic(Size)
184+
return Tuple{Dynamic()}
185+
else
186+
return Tuple{tuple_nodynamic_prod(Size)}
187+
end
188+
end
189+
190+
maybe_unwrap(i) = i
191+
maybe_unwrap(i::StaticIndexing) = i.ind
192+
193+
@inline function _getindex(::Val{:dynamic_fixed_false}, sa::HybridArray{S}, inds::Union{Int, StaticIndexing, StaticVector, Base.Slice, Colon}...) where S
194+
uinds = map(maybe_unwrap, inds)
195+
newsize = new_out_size(S, uinds...)
196+
return HybridArray{newsize}(getindex(sa.data, uinds...))
172197
end
173198

174199
# setindex stuff

test/nonstandard_indices.jl

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,60 +6,62 @@ using HybridArrays
66
using Test
77

88

9-
@testset "Compatibility with EllipsisNotation" begin
10-
u_array = rand(2, 10)
11-
u_hybrid = HybridArray{Tuple{2, HybridArrays.Dynamic()}}(copy(u_array))
12-
@test u_hybrid u_array
13-
@test u_hybrid[1, ..] u_array[1, ..]
14-
@test u_hybrid[.., 1] u_array[.., 1]
15-
@test u_hybrid[..] u_array[..]
16-
17-
@test_broken typeof(u_hybrid[1, ..]) == typeof(u_hybrid[1, :])
18-
@test_broken typeof(u_hybrid[.., 1]) == typeof(u_hybrid[:, 1])
19-
@test_broken typeof(u_hybrid[..]) == typeof(u_hybrid[:])
20-
21-
@inferred u_hybrid[1, ..]
22-
@inferred u_hybrid[.., 1]
23-
@inferred u_hybrid[..]
24-
25-
let new_values = rand(10)
26-
u_array[1, ..] .= new_values
27-
u_hybrid[1, ..] .= new_values
9+
if VERSION >= v"1.5"
10+
@testset "Compatibility with EllipsisNotation" begin
11+
u_array = rand(2, 10)
12+
u_hybrid = HybridArray{Tuple{2, HybridArrays.Dynamic()}}(copy(u_array))
2813
@test u_hybrid u_array
2914
@test u_hybrid[1, ..] u_array[1, ..]
3015
@test u_hybrid[.., 1] u_array[.., 1]
3116
@test u_hybrid[..] u_array[..]
32-
end
3317

34-
let new_values = rand(2)
35-
u_array[.., 1] .= new_values
36-
u_hybrid[.., 1] .= new_values
37-
@test u_hybrid u_array
38-
@test u_hybrid[1, ..] u_array[1, ..]
39-
@test u_hybrid[.., 1] u_array[.., 1]
40-
@test u_hybrid[..] u_array[..]
41-
end
42-
43-
let new_values = rand(2, 10)
44-
u_array .= new_values
45-
u_hybrid .= new_values
46-
@test u_hybrid u_array
47-
@test u_hybrid[1, ..] u_array[1, ..]
48-
@test u_hybrid[.., 1] u_array[.., 1]
49-
@test u_hybrid[..] u_array[..]
18+
@test typeof(u_hybrid[1, ..]) == typeof(u_hybrid[1, :])
19+
@test typeof(u_hybrid[.., 1]) == typeof(u_hybrid[:, 1])
20+
@test typeof(u_hybrid[..]) == typeof(u_hybrid[:])
21+
@test typeof(u_hybrid[..,..]) == typeof(u_hybrid[:, :])
22+
23+
@inferred u_hybrid[1, ..]
24+
@inferred u_hybrid[.., 1]
25+
@inferred u_hybrid[..]
26+
27+
let new_values = rand(10)
28+
u_array[1, ..] .= new_values
29+
u_hybrid[1, ..] .= new_values
30+
@test u_hybrid u_array
31+
@test u_hybrid[1, ..] u_array[1, ..]
32+
@test u_hybrid[.., 1] u_array[.., 1]
33+
@test u_hybrid[..] u_array[..]
34+
end
35+
36+
let new_values = rand(2)
37+
u_array[.., 1] .= new_values
38+
u_hybrid[.., 1] .= new_values
39+
@test u_hybrid u_array
40+
@test u_hybrid[1, ..] u_array[1, ..]
41+
@test u_hybrid[.., 1] u_array[.., 1]
42+
@test u_hybrid[..] u_array[..]
43+
end
44+
45+
let new_values = rand(2, 10)
46+
u_array .= new_values
47+
u_hybrid .= new_values
48+
@test u_hybrid u_array
49+
@test u_hybrid[1, ..] u_array[1, ..]
50+
@test u_hybrid[.., 1] u_array[.., 1]
51+
@test u_hybrid[..] u_array[..]
52+
end
5053
end
5154
end
5255

53-
5456
@testset "Compatibility with Cartesian indices" begin
5557
u_array = rand(2, 3, 4)
5658
u_hybrid = HybridArray{Tuple{2, 3, 4}}(copy(u_array))
5759
@test u_hybrid u_array
5860
@test u_hybrid[CartesianIndex(1, 2), :] u_array[CartesianIndex(1, 2), :]
5961
@test u_hybrid[:, CartesianIndex(1, 2)] u_array[:, CartesianIndex(1, 2)]
6062

61-
@test_broken typeof(u_hybrid[CartesianIndex(1, 2), :]) == typeof(u_hybrid[1, 2, :])
62-
@test_broken typeof(u_hybrid[:, CartesianIndex(1, 2)]) == typeof(u_hybrid[:, 1, 2])
63+
@test typeof(u_hybrid[CartesianIndex(1, 2), :]) == typeof(u_hybrid[1, 2, :])
64+
@test typeof(u_hybrid[:, CartesianIndex(1, 2)]) == typeof(u_hybrid[:, 1, 2])
6365

6466
@inferred u_hybrid[CartesianIndex(1, 2), :]
6567
@inferred u_hybrid[:, CartesianIndex(1, 2)]

0 commit comments

Comments
 (0)