How to generate independent vectors via for-loop?

At a point in my real code, I want to generate a number of independent vectors based on parameter N(which is calculated at a certain point in the code). For example, if N=3, then I need to generate the following vectors:

vector_1 = [0,0];
vector_2 = [0,0,0];
vector_3 = [0,0,0,0];

So, I tried to write something like below. However, there are two problems:
1- Syntax of vector_"$i" gives error . How to write the correct syntax?
2- Assuming the syntax is correct, an error is given at S = vector_"$i" since the vectors are not seen out of the for-loop scope. How to make the generated vectors seen out of for-loop scope?

function dd()
N =3;
for i = 1:N
    vector_"$i" = zeros(i+1); # gives `ERROR: UndefVarError: @vector__str not defined`
end
S = vector_1; # gives `ERROR: UndefVarError: vector_1 not defined`
end
dd()

Store these vectors in a dictionary. Don’t try to make them variables.

function dd()
    N = 3
    out Dict()
    for i = 1:N
        out[i] = zeros(i + 1)
    end
    return out
end

vector_dict = dd()
3 Likes

or a vector of vectors

julia> vectors = Vector{Int}[]
       for i in 1:3
           push!(vectors, zeros(i))
       end

julia> vectors
3-element Vector{Vector{Int64}}:
 [0]
 [0, 0]
 [0, 0, 0]

(also I would recommend typing the Dict, if the case Dict{Int,Vector{Int}}(():

julia> d = Dict{Int,Vector{Int}}()
Dict{Int64, Vector{Int64}}()

julia> for i in 1:3
           d[i] = zeros(i)
       end

julia> d
Dict{Int64, Vector{Int64}} with 3 entries:
  2 => [0, 0]
  3 => [0, 0, 0]
  1 => [0]
2 Likes

Or even as a tuple

julia> ntuple(i->zeros(i+1), 3)
([0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0])

You can dynamically generate variables using eval, but they will necessarily live in global scope. Don’t do it. Seriously. It leads to hard to read and reason about code, and a cluttered global namespace. Nevertheless:

julia> for i = 1:N
           var = Symbol("vector_$i")
           eval( :( $var = zeros($i+1) ));
       end

julia> vector_1
2-element Vector{Float64}:
 0.0
 0.0

julia> vector_2
3-element Vector{Float64}:
 0.0
 0.0
 0.0
1 Like

Thanks for your comment. The issue here is that I need to connect the generated vectors to other ones (which are changing in time-domain loop. For example, vectors[2] = @view(D[:]). However, a change in D will not be updated in vectors[2]/

D = [55,69];
vectors = Vector{Int}[]
       for i in 1:3
           push!(vectors, zeros(i))
       end

vectors[2] = @view(D[:]);
D[1] = 77;
julia> vectors
3-element Vector{Vector{Int64}}:
 [0]
 [55, 69]
 [0, 0, 0]

Thank you very much for your note. Can I restrict the generated vectors to be ONLY in scope of dd() function?

function dd()
N=3
for i = 1:N
    var = Symbol("vector_$i")
    eval( :( $var = zeros($i+1) ));
end
end

dd()

That’s because vectors is of type Vector{Vector{Int}} whereas @view(D[:]) is a subarray. Upon assignment vector[2] = @view(D[:]) a conversion to the former type, i.e. an implicit copy is made.
You could widen the container type

julia> vectors = AbstractVector{Int}[]

julia> for i in 1:3
           push!(vectors, zeros(i))
       end

julia> vectors
3-element Vector{AbstractVector{Int64}}:
 [0]
 [0, 0]
 [0, 0, 0]

julia> vectors[2] = @view(D[:]);

julia> vectors
3-element Vector{AbstractVector{Int64}}:
 [0]
 [55, 69]
 [0, 0, 0]

julia> vectors[2]
2-element view(::Vector{Int64}, :) with eltype Int64:
 55
 69

julia> D[1] = 77
77

julia> vectors
3-element Vector{AbstractVector{Int64}}:
 [0]
 [77, 69]
 [0, 0, 0]
3 Likes

No, that’s what I stated. eval evaluates an expression in the global scope of the module.

1 Like

IMHO this should just error.

1 Like

Thank you very much!
From a performance point of view, Do the values of vectors[2] are allocated in order in memory (column-major)? In other words, do working on the vector vector_2 is the same as on vectors[2]?

vector_1 = [0,0];
vector_2 = [0,0,0];
vector_3 = [0,0,0,0];

Performance-wise this will be quite bad in general, because you are handling a container with an abstract type.

If you need performance there you probably want something else, and this becomes a xy problem.

2 Likes

Yes, they form a concrete vector. Accessing vectors[2] is bad performance-wise because of the abstract container type. If you can live with that depends on the kind of work and how much indexing is done. Using a function barrier might be sufficient to make the overhead from the type instability due to indexing into vectors negligible.

2 Likes

Another option if you know how many you want at compile time:

sizes = (2,3,4)
Base.Cartesian.@nexprs 3 j->vector_j = zeros(sizes[j])

Now the variables vector_1, vector_2, and vector_3 exist with the sizes you have asked for.

3 Likes

I am sorry if I could not deliver my point clearly from the beginning.
Thanks for pointing this out. I will be more precise and accurate next times to make the discussion more productive.
From a performance point of view, can you recommend someting?

That depends a lot on the performance of what. It is not possible to answer that without further information.

1 Like