Static arrays and type stability under map

Hello,

I’ve been surprised by the return type of the mapping of a type stable function on a couple of static arrays.

Here is a minimal example, first with regular arrays

julia> n = 4;

julia> bool = rand(Bool, n);

julia> pair = [rand(1:n, 2) for i in 1:n];

julia> map(pair) do ci 
           (&)(bool[ci]...)
       end;

julia> typeof(ans)
Array{Bool,1}

Let’s now use static arrays and look at the type of the result:

julia> using StaticArrays

julia> sbool = SVector(bool...);

julia> spair = SVector(pair...);

julia> map(spair) do ci 
           (&)(sbool[ci]...)
       end;

julia> typeof(ans)
StaticArrays.SArray{Tuple{4},Any,1,4}

I was surprised to see the element type on return to be Any instead of Bool in this case.

Is this expected? Or am I missing something? I’ve been scratching my head on this one and would be grateful for any feedback!

V.

What should the code actually do?

Thanks for your reply.

Well I would like the code (with static arrays) to return a StaticArrays.SArray{Tuple{4},Bool,1,4} (rather than StaticArrays.SArray{Tuple{4},Any,1,4}).

The following (adding ::Bool) works but I wonder why it’s necessary with static arrays, and not with regular arrays…

julia> map(spair) do ci
           (&)(sbool[ci]...)::Bool
       end;

julia> typeof(ans)
StaticArrays.SArray{Tuple{4},Bool,1,4}

My question was what does the (&)(...) stuff do, since it is not very comprehensible (to me). Can you give example inputs and outputs? It feels like there should be a better way to do this.

1 Like

Ok sorry my mistake, here is what I’m trying to do… I have a first array that contains booleans (bool), and a second with pairs of indices (pairs) pointing to the elements of bool.

For each pair, the code should test whether both elements are true. Here is a slight rewrite that hopefully will make it more understandable.

julia> using StaticArrays

julia> n = 10;

julia> m = 5;

julia> sbool = SVector(rand(Bool, n)...);

julia> spair = SVector([rand(1:n, 2) for i in 1:m]...);

julia> map(spair) do ci
           sbool[ci[1]] & sbool[ci[2]]
       end;

julia> typeof(ans)
StaticArrays.SArray{Tuple{5},Any,1,5}

This needs to be done very fast, which is why I’m insisting on stick to immutable static arrays. Thanks for your help!

(&)(sbool[ci]...) can be more idiomatically written as all(sbool[ci]) but that also results in type instability for SArrays.

Looking into StaticArrays.jl, it seems that the same trick (::Bool annotation) is used in the package:

https://github.com/JuliaArrays/StaticArrays.jl/blob/master/src/mapreduce.jl#L214

If anyone reading this topic has an idea of how the annotation could be alleviated I would be curious to learn more…

1 Like
julia> map(spair) do ci 
           (&)(sbool[ci]...)
       end
4-element SArray{Tuple{4},Any,1,4}:
  true
 false
 false
 false

To help see what’s going on, I wanted to use @code_warntype, which means wrapping it in a function. Suddenly, it’s type stable:

julia> foo(spair, sbool) = map(spair) do ci 
           (&)(sbool[ci]...)
       end
foo (generic function with 1 method)

julia> @code_warntype foo(spair, sbool)
Body::SArray{Tuple{4},Bool,1,4}
1 1 ─ %1  = new(getfield(Main, Symbol("##37#38")){SArray{Tuple{4},Bool,1,4}}, %%sbool)::getfield(Main, Symbol("##37#38")){SArray{Tuple{4},Bool,1,4}}       │     
  │   %2  = :(StaticArrays.Tuple)::Type{Tuple}                                                                                                             │╻╷╷╷  map
  │         Core.apply_type(%2, 4)                                                                                                                         ││┃│││  _map
  │   %4  = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││╻     macro expansion
  │   %5  = Base.getfield(%4, 1, false)::Array{Int64,1}                                                                                                    ││││╻     getindex
  │   %6  = invoke %1(%5::Array{Int64,1})::Bool                                                                                                            ││││  
  │   %7  = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││││╻     getproperty
  │   %8  = Base.getfield(%7, 2, false)::Array{Int64,1}                                                                                                    │││││╻     getindex
  │   %9  = invoke %1(%8::Array{Int64,1})::Bool                                                                                                            ││││  
  │   %10 = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││││╻     getproperty
  │   %11 = Base.getfield(%10, 3, false)::Array{Int64,1}                                                                                                   │││││╻     getindex
  │   %12 = invoke %1(%11::Array{Int64,1})::Bool                                                                                                           ││││  
  │   %13 = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││││╻     getproperty
  │   %14 = Base.getfield(%13, 4, false)::Array{Int64,1}                                                                                                   │││││╻     getindex
  │   %15 = invoke %1(%14::Array{Int64,1})::Bool                                                                                                           ││││  
  │   %16 = StaticArrays.tuple(%6, %9, %12, %15)::NTuple{4,Bool}                                                                                           ││││  
  │   %17 = new(SArray{Tuple{4},Bool,1,4}, %16)::SArray{Tuple{4},Bool,1,4}                                                                                 ││││╻     Type
  └──       goto 3                                                                                                                                         ││││  
  2 ─       unreachable                                                                                                                                    ││││  
  3 ─       goto 4                                                                                                                                         ││    
  4 ─       return %17                                                                                                                                     │     

julia> foo(spair, sbool)
4-element SArray{Tuple{4},Bool,1,4}:
  true
 false
 false
 false
2 Likes

That did the trick thanks a lot!