How to use push!() in a two-dimensional matrix

Can someone explain what a 2 rows 0 column 2D array is?
I have trouble visualizing a Matrix with 2 rows and 0 columns

it’s just an empty Matrix

1 Like

It was initialized with no column (instead of a column of zeros) because push!() (which I (wrongly) thought could work) does not replace the existing values, and thus the first column would be having zeros.

array1 = [1; 1; 1]
array2 = [2 3; 2 3; 2 3]

array3 = hcat((array1,array2))

However, what I get is a tuple, which is immutable. Is there any way to get a matrix, because the process happens in a loop.

julia> array3 = hcat(array1,array2)
3×3 Matrix{Int64}:
 1  2  3
 1  2  3
 1  2  3
1 Like

Oh, I had enclosed the arrays in double parenthesis. Thanks a lot jling. I have a long way to go in learning this language. It’s not gonna be a cakewalk like MATLAB, it seems.

Finally, I’ve been able to get the thing done!

y=zeros(Float16,2,1)
# or y=Array{Float16}(undef,2,1)

for i1 in 1:1:10

     global y #without this the code threw a scoping error

     y =  hcat(y,[f(i1); g(i1)]) #some functions of i1    

end

Thanks all.

Usually it’s best to skip the “growing” step and create the matrix with the correct size right away and only modify its contents - e.g. in this case, with

f(x) = 2x
g(x) = 3x

function build_mat()
    y = zeros(Float16, 2, 11)
    for i1 in 1:10
        y[:,i1+1] .= f(i1), g(i1)
    end
    return y
end

This will be much faster than repeatedly growing/reallocating your matrix:

julia> function build_mat_hcat()                              
           y = zeros(Float16, 2, 1)                           
           for i1 in 1:1:10                                   
               y = hcat(y, [f(i1), g(i1)])                    
           end                                                
           return y                                           
       end                                                    
build_mat_hcat (generic function with 1 method)               
                                                              
julia> using BenchmarkTools                                   
                                                              
julia> @btime build_mat()                                    
  65.542 ns (1 allocation: 96 bytes)                         
2×11 Matrix{Float16}:                                        
 0.0  2.0  4.0  6.0   8.0  10.0  12.0  14.0  16.0  18.0  20.0
 0.0  3.0  6.0  9.0  12.0  15.0  18.0  21.0  24.0  27.0  30.0
                                                              
julia> @btime build_mat_hcat()                                
  744.828 ns (21 allocations: 1.62 KiB)                       
2×11 Matrix{Float16}:                                         
 0.0  2.0  4.0  6.0   8.0  10.0  12.0  14.0  16.0  18.0  20.0 
 0.0  3.0  6.0  9.0  12.0  15.0  18.0  21.0  24.0  27.0  30.0 

This is on the order of nanoseconds (“just” a factor 10x) here because the loop in the function is super tiny, but with longer loops it will be much more (I’m passing in the requested size as a parameter to make it easier to run):

julia> function build_mat_hcat(n)                
           y = zeros(Float16, 2, 1)              
           for i1 in 1:1:n                       
               y = hcat(y, [f(i1), g(i1)])       
           end                                   
           return y                              
       end                                       
build_mat_hcat (generic function with 2 methods) 
                                                 
julia> function build_mat(n)                     
           y = zeros(Float16, 2, n+1)            
           for i1 in 1:n                         
               y[:,i1+1] .= f(i1), g(i1)         
           end                                   
           return y                              
       end                                       
build_mat (generic function with 2 methods)      
                                                 
julia> @btime build_mat_hcat(100);               
  8.367 Îźs (201 allocations: 33.67 KiB)          
                                                 
julia> @btime build_mat(100);                    
  357.075 ns (1 allocation: 496 bytes)           
                                                 
julia> build_mat(100) ≈ build_mat_hcat(100)      
true                                             

As you can see, the repeatedly allocating function is ~23x slower, for the same result. This will become worse with even longer loops, because allocating is a comparatively expensive operation:

julia> @btime build_mat_hcat(1000);        
  340.100 Îźs (2001 allocations: 2.11 MiB)  
                                           
julia> @btime build_mat_hcat(10_000);      
  21.113 ms (25906 allocations: 192.58 MiB)
                                           
julia> @btime build_mat(10_000);           
  32.300 Îźs (2 allocations: 39.17 KiB)     

That’s ~653 times slower! That ratio is going to continue to get worse.

Reducing or even eliminating allocations is a key idiom for achieving extremely fast julia code - it’s best to try to be conscious of this from the beginning, instead of trying to translate other languages one-to-one. What’s good in language A may not be good in language B, after all.


Additionally, putting your code into functions and passing arguments in explicitly will make sure you don’t have problems with scoping of variables (global variables are kind of an anti-pattern in julia - they can lead to huge losses in performance).

3 Likes

Also, that is not good in Matlab either, and will be very very slow. It is just that Matlab allows a simple syntax for the bad code.

2 Likes

Yes, now I’m gonna allocate a large memory and, after the completion of the filling of the array, will remove the trailing part. Thanks a lot for the exposition.

Yes, will do that. But in my VSC not declaring y as a global variable inside the for loop throws an error.