Shuffling the columns of a matrix independently

aliutkus: <p>hi, assume I have a <code>(m,n)</code> Tensor, say <code>mat</code>.<br>
I would like to have each of its <code>n</code> columns shuffled in a different manner.</p>
<p>a naive implementation would be, <code>mat</code> being the <code>(m,n)</code> Tensor:</p>
<pre><code class="lang-auto">res = mat.clone()
for i in range(res.shape[1]):
    ind = torch.randperm(res.shape[0], device=device)
    res[:, i] = res[ind, i]
</code></pre>
<p>can I do the same without the <code>for</code> loop in a vectorized manner ?</p>

tymokvo: <p>Hi!</p>
<p>One way would be to use advanced indexing and the stdlib function <code>random.shuffle</code>. I used it on a list rather than a call to <code>torch.arange</code> as <code>shuffle</code> seems to go against <code>torch</code>'s semantics under the hood.</p>
<pre><code class="lang-auto">mat = torch.linspace(1, 16, 16).view(4, 4)
</code></pre>
<pre><code class="lang-auto">tensor([[ 1.,  2.,  3.,  4.],
        [ 5.,  6.,  7.,  8.],
        [ 9., 10., 11., 12.],
        [13., 14., 15., 16.]])
</code></pre>
<pre><code class="lang-auto">col_idxs = list(range(mat.shape[1]))
random.shuffle(col_idxs)
mat = mat[:, torch.tensor(col_idxs)]
</code></pre>
<pre><code class="lang-auto">tensor([[ 2.,  1.,  4.,  1.],
        [ 6.,  5.,  8.,  5.],
        [10.,  9., 12.,  9.],
        [14., 13., 16., 13.]])
</code></pre>
<p>Hope that helps!</p>

KFrank: <p>Hi Antoine (and tymokvo)!</p>
<aside class="quote no-group">
<blockquote>
<p>hi, assume I have a (m,n) Tensor, say mat.<br>
I would like to have each of its n columns shuffled in a different manner.<br>
…<br>
can I do the same without the for loop in a vectorized manner ?</p>
</blockquote>
</aside>
<p>I can’t* think of a way to do this using built-in tensor<br>
functions without a loop.</p>
<p>*)  Well, actually I can, but with a cost in efficiency.</p>
<p>Try this (for <code>m = 4, n = 3</code>):</p>
<pre data-code-wrap="python"><code class="lang-python">import torch
mat = torch.tensor ([[11.0, 12, 13],[21, 22, 23],[31, 32, 33],[41, 42, 43]])
ind = torch.rand (4, 3).argsort (dim = 0)
res = torch.zeros (4, 3).scatter_ (0, ind, mat)
</code></pre>
<p>The computational time complexity of your task<br>
should be <code>m * n</code>.  (You have <code>n</code> columns and the<br>
cost of randomly permuting a length-<code>m</code> column<br>
is <code>m</code>.)</p>
<p>But the cost of <em>sorting</em> a length-<code>m</code> column<br>
is <code>m * log (m)</code>, so my scheme has cost<br>
<code>n * m * log (m)</code>.</p>
<p>The point is that I can’t figure out how to get<br>
the randomly permuted columns of indices<br>
without a loop or using the sort trick.</p>
<p>(Note that tymokvo’s approach is applying the same<br>
random permutation to each of the rows.  Antoine is<br>
asking for distinct random permutations for (in his<br>
case) each of the columns, as his loop-based solution<br>
does.  Also, for reasons I don’t understand – tymokvo’s<br>
code looks right for what it does – the final result in<br>
tymokvo’s post has a duplicated column, (1, 5, 9, 13),<br>
and a missing column (3, 7, 11, 15).)</p>
<p>Have fun!</p>
<p>K. Frank</p>

aliutkus: <p>thanks both,</p>
<p><a class="mention" href="/u/kfrank">@KFrank</a> your solution does work but indeed, on a <code>(1000, 10000)</code> problem, I get :</p>
<blockquote>
<p>scatter: 1.084769 ms</p>
</blockquote>
<p>and using naive with <code>for</code> loops:</p>
<blockquote>
<p>naive: 0.394080 ms</p>
</blockquote>