Swap cols/rows of a matrix


I need to swap columns or rows of a matrix. I have two ideas:
(1). Create a new matrix and copy cols/rows to the new matrix. We may use @view.
(2). Multiply by a permutation matrix. Of course, we also need to create that permutation matrix (but it’s sparse)

To my understanding, multiplication takes O(n^3) given a square matrix, but not sure how multiplying with a sparse matrix will make a difference or not.
I’m wondering which way is more preferable in terms of performance?

Your help is appreciated in advance!

I think it depends also on what you have to do with yr matrix later… With @view you have no allocations but you also have a structure where the data is no longer contigue, so if you have to use it “often” is penalising…

I am not sure if I understand what you want, but for swapping cols, for example, I would do this, which does not allocate anything:

julia> function swapcol!(x,i,j)
         for k in axes(x,1) # edited according to next answer
           idata = x[k,i]
           x[k,i] = x[k,j]
           x[k,j] = idata
swapcol! (generic function with 1 method)

julia> x = [ 1 2 3 ; 4 5 6 ]
2×3 Array{Int64,2}:
 1  2  3
 4  5  6

julia> swapcol!(x,1,2)

julia> x
2×3 Array{Int64,2}:
 2  1  3
 5  4  6

julia> @btime swapcol!($x,1,2)
  4.634 ns (0 allocations: 0 bytes)

agreed, thanks!

Interesting. Why don’t we need to allocate memory for this local temp variable "idate’'?

You don’t need the temporary variable:

function _swapcol!(x,i,j)
    for k in axes(x, 1)  # <- give dimension as input to axes function
        x[k, i], x[k, j] = x[k, j], x[k, i]

If you are reshuffling the columns (or rows) you can supply a vector of indices:

julia> x = collect(reshape(1:12, 3, 4))
3×4 Array{Int64,2}:
 1  4  7  10
 2  5  8  11
 3  6  9  12

julia> x[:, [2,3,1,4]]
3×4 Array{Int64,2}:
 4  7  1  10
 5  8  2  11
 6  9  3  12

idata is just a scalar value, so doesn’t need any memory allocation, unlike an array. But as I said, you don’t really need to create that variable explicitly (though, of course, x, y = y, x will do that ‘under the hood’.)

1 Like

This is a good question which I do not completely understand. Because the scope of that scalar is only local to the loop, I guess it is only created in a local memory during computation that does not count as an allocation. I do not know the details of that.

As DNF pointed out, in his version in which this scalar is not created explicitly, it is created anyway. Effectively the @code_typed of the two versions are identical and, if I understand what is there (not sure, though), this part shows that %16 creates a copy of element i, which is then used in the third line of instruction %17.

│    %16 = Base.arrayref(false, x, %14, i)::Int64
│    %17 = Base.arrayref(false, x, %14, j)::Int64
│          Base.arrayset(false, x, %17, %14, i)::Array{Int64,2}
│          Base.arrayset(false, x, %16, %14, j)::Array{Int64,2}

ps: I think that the counter of the loop must be created in a similar way and does not count as an allocation either.

1 Like