How to declare N nested Vectors?

HI,
I want to declare N nested Vectors in Julia.
e.g.

Vector{Int}
Vector{Vector{Int}}
...
Vector{Vector{....Vector{Int}...} # N dimensional nested Vector

In C++, it can be implemented by using recursive specialization template like below (please refer to c++ - n-dimensional vector - Stack Overflow)

template<size_t dimcount, typename T>
struct multidimensional_vector
{
    typedef std::vector< typename multidimensional_vector<dimcount-1, T>::type > type;
};

template<typename T>
struct multidimensional_vector<0,T>
{
    typedef T type;
};

multidimensional_vector<1, int>::type v;
multidimensional_vector<2, int>::type v2;
multidimensional_vector<3, int>::type v3;
multidimensional_vector<4, int>::type v4;

Is there a similar method in Julia to define N dimensional Vectors by recursive specialized parameters N.

Thanks.

Julia has native N dimensional arrays. Vector{Int} is just Array{Int,1}, Matrix{Int} is Array{Int,2}, but Julia support up to 511 dimensions.

Thanks Oscar. N dimensional array is one option. But difference is that array has the same size of each row, but vector is more dynamic. In my application, that is exactly different size with different row, like:
row 1: 1,2,3
row 2: 3,4,5,6,…, 20
…, etc.

In terms of terminology, that is not an N-dimensional array, but simply nested vectors. And Julia already have those. Perhaps you can explain what you mean be “implementing them”, seeing as they already exist?

You could use a macro to do this.

julia> macro NVector(N,T)
           e = T
           for i in 1:N
               e = :(Vector{$e})
           end
           return e
       end
@NVector (macro with 1 method)

julia> @NVector(0,Int)
Int64

julia> @NVector(1,Int)
Vector{Int64} (alias for Array{Int64, 1})

julia> @NVector(2,Int)
Vector{Vector{Int64}} (alias for Array{Array{Int64, 1}, 1})

julia> @NVector(3,Int)
Vector{Vector{Vector{Int64}}} (alias for Array{Array{Array{Int64, 1}, 1}, 1})

julia> @NVector(4,Int)
Vector{Vector{Vector{Vector{Int64}}}} (alias for Array{Array{Array{Array{Int64, 1}, 1}, 1}, 1})
4 Likes

See also

1 Like

The type may be constructed without macros as well

julia> foldl((T,_) -> Vector{T},  1:4, init=Int)
Vector{Vector{Vector{Vector{Int64}}}} (alias for Array{Array{Array{Array{Int64, 1}, 1}, 1}, 1})
4 Likes

Based on some of the answers here, it seems you are not really looking for how to implement the type, but perhaps for a convenient way to declare it? Is that right? Maybe you can update the thread title and OP?

Yes my question is how to declare N nested Vector. I have updated thread title.

Thanks mkitti and jishnub. That is exactly addressing my question.

Julia’s methods are themselves very much like C++'s templates. You can convert your recursive code to Julia in a 1:1 manner, too:

julia> nvec(::Val{n}, ::Type{T}) where {dimcount, T} = nvec(Val(dimcount-1), Vector{T})
nvec (generic function with 2 methods)

julia> nvec(::Val{0}, ::Type{T}) where {T} = T
nvec (generic function with 2 methods)

julia> nvec(Val(3), Int)
Vector{Vector{Vector{Int64}}} (alias for Array{Array{Array{Int64, 1}, 1}, 1})

The special Val type allows you to dispatch on the value of dimcount.

7 Likes

I have tried parametric Type with Val as such: struct NDVector{::Val{N}} and hit error.
Your method is using parametric method. Yes that is 1:1 manner with C++.
Thank you @mbauman

I’ve been looking for such a Julia equivalent of C++ recursive parametric templates for a while, thanks a lot !

Out of curiosity, is this only a pragmatic limit, as in a reasonable constraint for how many cases the compiler has to deal with, or is this also being stored as a nine-bit value in a tag somewhere?

9 bits. In Julia 1.11, though the number is bumped up to typemax(Int). (although actually constructing one with more than a couple thousand dimensions will likely crash the compiler)

2 Likes

At least printing starts to break at 491 dimensions in 1.10

julia> Array{Int, 491}(undef, ntuple(i -> 1, 491))

(...trimmed...)

Internal error: stack overflow in type inference of view(Array{Int64, 491}, Base.UnitRange{Int64}, Base.UnitRange{Int64}, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64).
This might be caused by recursion over very long tuples or argument lists.

And it breaks at 429 on 1.12 nightly

1 Like

The printing issue also occurs for deeply recursive types, as I discovered recently.

Deeply nested containers kind of have to overflow the stack, because it’s too late to add a depth counter to show, which at least the internal implementations could have used.

But type printing is internal*, and could use a stack with a loop to avoid this. Might be a good first issue, since the fix would be fairly self-contained.

[*] this isn’t 100% true, since it’s possible to overload show(::IO, ::Type{MyType}), however bad an idea that might be. But handling it for the builtin types would flatten the stack enough to avoid a blowup in many more cases.

2 Likes