Preferred type expression for a vector of vectors


#1

Among the expressions below, which one is preferred?

  1. x::Vector{Vector{T}} where T<:Real
  2. x::Vector{Vector{T} where T<:Real}
  3. x::Array{Array{T,1},1} where T<:Real
  4. x::Array{Array{T,1} where T<:Real,1}

#2

1 and 2, and, 3 and 4, are not the same so it depends what behaviour want which is preferable.


#3

If you’re storing vectors that all have the same eltype (eg. Vector{Int}), then options 1 and 3 are generally preferable for speed reasons, because a nested loop over all elements in all vectors will be type-stable. I would favor 1 over 3 because it’s shorter and cleaner.


#4

If we are talking about argument types for a function declaration, then the type declaration has nothing whatsoever to do with performance or type stability. You get exactly the same performance even if you declare no types at all, because the compiler specializes the function for the whatever argument types are passed.

The question is, what is the poster trying to accomplish by the type declaration here?


#5

Thank you for comments. As an example, in the program below, f1(x) works as expected. f1(y) fails, but f2(y) succeeds.
Question 1: Why does f1(y) fail?
Question 2: Why does f2(y) succeed?
Question 3: How can I create an array of type Vector{Vector{T}} where T<:Real?

When I do typeof(),
typeof(Vector{Vector{T} where T<:Real}) is DataType, and
typeof(Vector{Vector{T}} where T<:Real) is UnionAll.
Not sure of its implications, but the difference seems significant…

function f1(u::Vector{Vector{T} where T<:Real})
    u + 1
end

x = Vector{Vector{T} where T<:Real}(2)
x[1] = [1,2]; x[2] = [3,4];
f1(x)

y = Vector{Vector{Int64}}(2)
y[1] = [5,6]; y[2] = [7,8];
f1(y)

function f2(u::Vector{Vector{T}} where T<:Real)
    u + 1
end

f2(y)

Using juliapro 0.6.4.1

julia> versioninfo()
Julia Version 0.6.4
Commit 9d11f62bcb* (2018-07-09 19:09 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i7-2620M CPU @ 2.70GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Sandybridge MAX_THREADS=16)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.9.1 (ORCJIT, sandybridge)

#6

The discussion about covariance should help answer your first 2 questions.

If you want to have a method that is dispatched on vectors that contain vectors of real numbers, I would recommend something like

f(u::AbstractVector{<:AbstractVector{<:Real}}) = ...

Note the AbstractVector, which makes it works for all ranges, etc.

If you want to create a vector of vectors without automatic conversions, use

v = Vector{<:Real}[[1], [2.0]]

#7

Thanks :smiley: I’ve tried them as follows, and it seems to work well.

I wasn’t aware of the use of AbstractVector, and will have a closer look later.

f(u::AbstractVector{<:AbstractVector{<:Real}}) = u + 1
f (generic function with 1 method)
methods(f)

1 method for generic function f:

  • f(u::AbstractArray{#s1,1} where #s1<:(AbstractArray{#s2,1} where #s2<:Real)) at In[2]:1
v = Vector{<:Real}[[1], [2.0]]
2-element Array{Array{#s1,1} where #s1<:Real,1}:
 [1]  
 [2.0]
f(v)
2-element Array{Array{T,1} where T,1}:
 [2]  
 [3.0]
v1 = Vector{Vector{Real}}(2)
v1[1] = [5]; v1[2] = [6.7];
v1
2-element Array{Array{Real,1},1}:
 Real[5]  
 Real[6.7]
f(v1)
2-element Array{Array{T,1} where T,1}:
 [6]  
 [7.7]
v2 = Vector{Vector{Int}}(2)
v2[1] = [8]; v2[2] = [9];
v2
2-element Array{Array{Int64,1},1}:
 [8]
 [9]
f(v2)
2-element Array{Array{Int64,1},1}:
 [9] 
 [10]

#8

Note that if u is a vector of vectors, you will get deprecation warnings about broadcasting later in v0.7. If + 1 is your actual use case, see this topic.


#10

Looked at the topic, and made a julia0.7-rc2 version below, using your suggestion in the topic. It worked without error/warnings :slight_smile:

julia> bc(f) = (args...) -> f.(args...)
bc (generic function with 1 method)

julia> f(u::AbstractVector{<:AbstractVector{<:Real}}) = (bc(+)).(u, 1)
f (generic function with 1 method)

julia> x = Vector{Vector{T} where T<:Real}(undef,2)
2-element Array{Array{T,1} where T<:Real,1}:
 #undef
 #undef

julia> x[1] = [1,2]; x[2] = [3,4];

julia> f(x)
2-element Array{Array{Int64,1},1}:
 [2, 3]
 [4, 5]

julia> y = Vector{Vector{Int64}}(undef,2)
2-element Array{Array{Int64,1},1}:
 #undef
 #undef

julia> y[1] = [5,6]; y[2] = [7,8];

julia> f(y)
2-element Array{Array{Int64,1},1}:
 [6, 7]
 [8, 9]

julia>