Ideally, I’d like to create a container vector v of 10 elements, where each element of v is a vector of length 3, holding Float64 numbers (undefined value at the outset). After this, I’d like to fill in vectors with values a for loop. [Sizes 10 and 3 are just an example.]
Method 1
It seems like I can create something like this via the construct:
v = Vector{Vector{Float64}}(undef,10)
This creates a vector v of 10 undefined elements, where each element must be a vector of Float64 elements. However, the length of each element may vary, so it is not quite what I want. So I assume that I haven’t specified exactly the memory layout of v.
The next step is to fill in elements, say, in a for loop, e.g.:
for i in 1:10
v[i] = rand(0:9.,3)
end
Method 2
An alternative approach is to push! elements into the vector, e.g.:
v = Vector{Vector{Float64}}(undef,0)
for i in 1:10
push!(v,rand(0:9.,3))
end
Questions:
Which of these methods is most efficient? [I’d assume Method 1, since Method 2 must iteratively expand the size of `v`. However, also in Method 1, the length of each vector that I insert is unknown at the outset??]
Is there a more precise and efficient way to specify at the outset (i.e., before the loop insert) that vector v should have 10 elements where each element is a vector of exactly 3 elements of type Float64?
Final comment: the size is just to make the question concrete. Also, I’m not filling in random numbers, but results of a more elaborate computation.
Your method 1 and 2 are basically the same thing. Standard vectors do not have their size encoded in their type information, so nothing about your nested vectors being length 3 is going to matter. You could instead consider your vector of vectors as a Matrix so that the second dimension is required to be uniform.
If your “second” dimension is small in your actual application (less than 10 or so?) you should definitely consider StaticArrays.jl and then you can have a Vector of SVectors. They have an example in the README I believe. This approach should be much more performant and does check off your requirements.
Thanks for suggestion. The “only” problem with a Matrix is that I only have a single for loop. But I guess I can use the idea of Cartesian indexing (is that it?), i.e., use a single index.
If you know the sizes in advance, you can just use map, collect (see the [ ... ] syntax).
Also, I think you are chasing “efficiency” in the wrong direction. Both versions of your code allocate quite a bit, so almost all alternatives (using a Matrix, a Vector of SVector or MVectors, reorganizing your code) are more efficient.
Incidentally, when learning the language I think many new users of Julia focus too much on micro-efficiency issues like this. There is usually a clean, elegant and fast solution to these problems, but it is impossible to help without context. Things like
are a strong signal that some context would help a lot.
To be more concrete, I solve a boundary value problem with 2 inputs (x: x1, x2), and 2 outputs (y: y1, y2) of interest [I only care about the boundary values; not the internal values]. Thus, a 2 element vector produces a 2 element vector.
Next, I want to compute the solution for each of the two inputs in a range, i.e., x1 in a range of Nx1 values and x2 in a range of Nx2 values. For each combination of (x1,x2), I find a solution (y1,y2). Thus, I should have an Nx1 by Nx2 matrix where each element in the matrix is a vector of two outputs (y1,y2).
The result is a Nx1 by Nx2 matrix with a 2-vector in each element. Or an Nx1*Nx2 vector with 2-vector elements.
I guess that your proposal is that I instead operate with a matrix of dimension Nx1*Nx2 by 2, and reshape it to the form I want in post processing.