# Output type of binary operations involving StaticArrays

**URL:** https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187
**Category:** Performance
**Created:** [January 10, 2020, 9:48am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187 "2020-01-10T09:48:36Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)
#### Post date: [January 10, 2020, 9:48am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/1 "2020-01-10T09:48:37Z")

</div>

binary operations on a `StaticArray` and `Array` give outputs with quite-inconsistent types. For example, intuitively `SVector .+ Vector` should give a `SVector` rather than a `Vector`?

```julia
julia> using StaticArrays

julia> typeof(SVector{3, Float64}(randn(3)) .+ randn(3) )
Array{Float64,1}

julia> typeof(SVector{3, Float64}(randn(3)) .* randn(3) )
Array{Float64,1}

julia> typeof(SVector{3, Float64}(randn(3)) .+ 1.0 )
SArray{Tuple{3},Float64,1,3}

julia> typeof(SVector{3, Float64}(randn(3)) .* 2.0 )
SArray{Tuple{3},Float64,1,3}

julia> typeof(SMatrix{3, 2, Float64}(randn(3, 2)) * randn(2) )
SArray{Tuple{3},Float64,1,3}

julia> typeof(randn(1, 3) * SMatrix{3, 2, Float64}(randn(3, 2)) )
Array{Float64,2}

```

how does the output type be determined? could I override them? thanks.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [January 10, 2020, 10:13am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/2 "2020-01-10T10:13:05Z")

</div>

> [@tomtom](#):
>
> intuitively `SVector .+ Vector` should give a `SVector` rather than a `Vector` ?

I am not sure about this — both options can be reasonable and consistent.

If you want static results, use statically _sized_ decorators (see the docs):

```julia
using StaticArrays
a = SVector{3, Float64}(randn(3))
b = SizedVector{3}(randn(3))
a .+ b # 3-element SArray{Tuple{3},Float64,1,3} with indices SOneTo(3)

```

---

<div class="post-metadata">

### Author: ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)
#### Post date: [January 10, 2020, 10:19am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/3 "2020-01-10T10:19:42Z")

</div>

thanks.  
what I mean by inconsistency is some `op.(::StaticArray, ::Array)` give a `StaticArray` while other `op().` gives `Array`.  
that means we have to test every `op().` before knowing the type it gives.

btw, does `SizedVector{N}()` cause any runtime?

actually, how could I **control** the output type of generally `op.(::A, ::B)`? is it about broadcasting? thanks.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [January 10, 2020, 10:41am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/4 "2020-01-10T10:41:23Z")

</div>

> [@tomtom](#):
>
> that means we have to test every `op().` before knowing the type it gives.

I am not sure I understand why you think this. It may be better to just write generic code, and control your inputs when it matters.

> [@tomtom](#):
>
> btw, does `SizedVector{N}()` cause any runtime?

Quite the opposite, the purpose is to aid inference. Again, please see the StaticArrays manual.

---

<div class="post-metadata">

### Author: ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)
#### Post date: [January 10, 2020, 11:10am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/5 "2020-01-10T11:10:59Z")

</div>

seems like I had some **confusions** about `x .+ y` vs. `x .+ 1.0`. To clarify, I try the following:

```julia
module Testing

import Base.size, Base.getindex
export A, B

struct A{T <: AbstractVector{Float64} } <: AbstractVector{Float64}
    data::T
end

struct B{T <: AbstractVector{Float64} } <: AbstractVector{Float64}
    data::T
end

Base.size(x::A) = size(x.data)
Base.size(x::B) = size(x.data)

Base.getindex(x::A, i) = x.data[i]
Base.getindex(x::B, i) = x.data[i]

end

using Main.Testing
a = A(randn(3) )
b = B(randn(3) )

julia> typeof(exp.(a) )
Array{Float64,1}
julia> typeof(a .+ a)
Array{Float64,1}
julia> typeof(a .+ b)
Array{Float64,1}
julia> typeof(b .+ a)
Array{Float64,1}
julia> typeof(a .+ 1.0)
Array{Float64,1}
julia> typeof(b .+ 1.0)
Array{Float64,1}

```

the last five `typeof()` results show that, **by default** , `exp.(::A)`, `.+(::A, ::B)` and `.+(::A, ::Float64)` **always** output a `Vector` (but not a `A` nor a `B`).

So now I know that `.+(::SVector, ::Vector)` giving a `Vector` is just the **default** behavior.

However, there’re important **exceptions** e.g. `exp.(::SVector)`, `.+(::SVector, ::Float64)` and `.+(::SVector, ::SizedVector)`: they output `SVector` but not the default `Vector` !

How could it be done 😵? An example making `exp.(::A)`, `.+(::A, ::B)` and `.+(::A, ::Float32)` outputting a `A` is highly appreciated, thanks.

---

<div class="post-metadata">

### Author: ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)
#### Post date: [January 10, 2020, 11:21am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/6 "2020-01-10T11:21:55Z")

</div>

> [@tomtom](#):
>
> For example, intuitively `SVector .+ Vector` should give a `SVector` rather than a `Vector` ?

I would agree with that… in an ideal world. The broadcasting machinery is sufficiently intricate that it might not be possible/reasonable though. File an issue?

---

<div class="post-metadata">

### Author: ![mateuszbaran](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mateuszbaran/32/221842_2.png) [@mateuszbaran](https://discourse.julialang.org/u/mateuszbaran)
#### Post date: [January 10, 2020, 11:58am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/7 "2020-01-10T11:58:07Z")

</div>

> [@Tamas\_Papp](#):
>
> > btw, does `SizedVector{N}()` cause any runtime?
> 
> Quite the opposite, the purpose is to aid inference. Again, please see the StaticArrays manual.

In some cases wrapping in `SizedVector` may slow things down since one additional allocation happens (at least in stable versions of Julia).

> [@cstjean](#):
>
> > For example, intuitively `SVector .+ Vector` should give a `SVector` rather than a `Vector` ?
> 
> I would agree with that… in an ideal world. The broadcasting machinery is sufficiently intricate that it might not be possible/reasonable though. File an issue?

This should be possible but at the cost of making custom broadcasting in `StaticArrays` more complicated. It’d have to figure out which arrays should have sizes statically checked and which should have run-time checks.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [January 10, 2020, 12:07pm UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/8 "2020-01-10T12:07:03Z")

</div>

> [@mateuszbaran](#):
>
> In some cases wrapping in `SizedVector` may slow things down since one additional allocation happens (at least in stable versions of Julia).

That’s interesting to know, can you please give an example?

---

<div class="post-metadata">

### Author: ![mateuszbaran](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mateuszbaran/32/221842_2.png) [@mateuszbaran](https://discourse.julialang.org/u/mateuszbaran)
#### Post date: [January 10, 2020, 12:41pm UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/9 "2020-01-10T12:41:29Z")

</div>

For example:

```julia
julia> f(n) = SizedVector{2}([n, n+1])
f (generic function with 1 method)

julia> g(n) = [n, n+1]
g (generic function with 1 method)

julia> @btime f(10)
  37.265 ns (2 allocations: 112 bytes)
2-element SizedArray{Tuple{2},Int64,1,1} with indices SOneTo(2):
 10
 11

julia> @btime g(10)
  28.605 ns (1 allocation: 96 bytes)
2-element Array{Int64,1}:
 10
 11

```

Although the worse performance is limited to very specific circumstances (lots of allocations that can’t be optimized away). `similar` for `SizedArray` was also surprisingly slow until a few months ago: [Faster similar for SizedArray by mateuszbaran · Pull Request #625 · JuliaArrays/StaticArrays.jl · GitHub](https://github.com/JuliaArrays/StaticArrays.jl/pull/625)

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [January 10, 2020, 12:45pm UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/10 "2020-01-10T12:45:52Z")

</div>

Thanks. My suggestion was more about the original example of @tomtom, something like

```julia
julia> using StaticArrays, BenchmarkTools

julia> f(x, n) = x .+ SizedVector{2}([n, n+1])
f (generic function with 1 method)

julia> g(x, n) = x .+ [n, n+1]
g (generic function with 1 method)

julia> x = SVector(3, 4)
2-element SArray{Tuple{2},Int64,1,2} with indices SOneTo(2):
 3
 4

julia> @btime f($x, $10)
  33.359 ns (1 allocation: 96 bytes)
2-element SArray{Tuple{2},Int64,1,2} with indices SOneTo(2):
 13
 15

julia> @btime g($x, $10)
  62.608 ns (2 allocations: 192 bytes)
2-element Array{Int64,1}:
 13
 15

```

---

<div class="post-metadata">

### Author: ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)
#### Post date: [January 11, 2020, 10:11am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/11 "2020-01-11T10:11:56Z")

</div>

more examples of “inconsistency”:

```julia
julia> exp.(SizedVector{3, Float64}(randn(3) ) )
3-element SArray{Tuple{3},Float64,1,3} with indices SOneTo(3):

julia> 1.0 .+ SizedVector{3, Float64}(randn(3) )
3-element SArray{Tuple{3},Float64,1,3} with indices SOneTo(3):

```

operations on `SizedVector` gives `SVector` rather than `SizedVector` !

but now `SizedVector` “loses” to `SVector` or `Vector`:

```julia
julia> SVector{3, Float64}(randn(3) ) .+ SizedVector{3, Float64}(randn(3) )
3-element SArray{Tuple{3},Float64,1,3} with indices SOneTo(3):

julia> (1.0:3.0) .+ SizedVector{3, Float64}(randn(3) )
3-element Array{Float64,1}:

```

that said, it’s hard to predict the type of output unless a testing is done.

---

<div class="post-metadata">

### Author: ![mateuszbaran](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mateuszbaran/32/221842_2.png) [@mateuszbaran](https://discourse.julialang.org/u/mateuszbaran)
#### Post date: [January 11, 2020, 10:38am UTC](https://discourse.julialang.org/t/output-type-of-binary-operations-involving-staticarrays/33187/12 "2020-01-11T10:38:21Z")

</div>

As far as I understand `StaticArrays` the general rule is that when all arguments to a broadcast are `StaticArray`s or scalars (or some wrapped `StaticArrays`), then `SArray` will be returned (because it’s the fastest thing). You’ll see it in many other places as well, `StaticArrays` operations usually return `SArray` when they can.
