apply matrix randomisation without for loop in matlab - matlab

A question about matlab and randomisation of a 3d matrix respecting the rows and columns.
I have a n x n x s matrix M and I want to mess it up a bit, but with some control.
I can achieve my wish with a for loop
for j=1:size(M,3)
r=randperm(size(M,1));
random_M(:,:,j)=M(r,r,j);
end
Is there a way to perform this without having to loop over j? I need many randomisation iterations and could afford the benefits of indexing.
Cheers!
edit: Some more thoughts following Alexandrew's comments
I have created a function that randomises a squeezed version of M:
function randomMat=randomiseMat(Mat)
[rows,cols]=size(Mat);
r=randperm(rows);
randomMat=Mat(r,r);
then, using arrayfun I seem to get what I want:
randomM=arrayfun(#(x) randomiseMat(M(:,:,x)),1:size(M,3),'UniformOutput', false)
however, randomM is now a cell array of size (1,size(M,3)) with each cell containing randomised array.
Is there a way to make it in a 3d matrix just like the input M?

You can calculate all the values for r in one go, and then use arrayfun:
[nRows,nCols,nPages] = size(M);
[~,r]=sort(rand(nRows,nPages));
%# you should test on a realistic example whether a for-loop
%# isn't faster here
outCell = arrayfun(#(x) M(r(:,x),r(:,x),x), 1:nPages,'UniformOutput',false);
randomM = cat(3,outCell{:});

Related

Filling a matrix with vectors in loop and apply operation

I am working on matlab with a matrix. I would like to reproduce this matrix and apply sum for elements in rows.
I have two vectors defined by this code:
unitsvector=1:5;
reordervector=1:3;
Then, I create an empty matrix to store the values:
resultvec=zeros(size(unitsvector,2)*size(reordervector,2),3);
Finally, here is the loop I use but it is not working:
for a=1:length(resultvec)
for b=reordervector
for c=unitsvector
resultvec(a,1)=b;
resultvec(a,2)=c;
resultvec(a,3)=b+c;
end
end
end
How could I reproduce this matrix in matlab. Thanks for your help.
You can use meshgrid for this without a for loop.
[a,b] = meshgrid(1:5,1:3);
M = [a(:) b(:)];
M(:,3) = sum(M,2); % Create third column by summing first two
Why are you looping at all? sum actually has vector support; a simple resultvec = [a(:,1),a(:,2),sum(a,2)] would work.
As to your code: of course it doesn't work. What do you expect to be the contents of a? You create a as a loop index, which runs over the range 1:length(resultvec). Ergo, within each loop iteration a is a scalar. You try to call it like it is a three-element vector. Nor do you define b and c. This might be possible in R, judging where you're coming from, but not in MATLAB.

Multiplying multi-dimensional matrices efficiently

I'd love to know if there is a more efficient way to multiply specific elements of multi-dimensional matrices that doesn't require a 'for' loop.
I have a region * time matrix for an individual (say, 50 regions and 1000 timepoints) and I want to multiply each pair of regions at each timepoint to create a new matrix of the products of each region pair at each time point (50 x 50 x 1000). The way that I'm currently running it is:
for t = 1:1000
for i = 1:50
for j = 1:50
new(i,j,t) = old(i,t) .* old(j,t)
As I'm sure you can imagine, this is super slow. Any ideas on how i can fix it up so that it will run more quickly?
%some example data easy to trace
old=[1:5]'
old(:,2)=old*i
%multiplicatiion
a=permute(old,[1,3,2])
b=permute(old,[3,1,2])
bsxfun(#times,a,b)
permute is used to make 3d-matrices with dimensions n*1*m and 1*n*m out of the n*m input matrix. Changing the dimensions this way, new(i,j,k) can be calculated using new(i,j,k)=a(i,1,k)*b(1,j,k). Applying such operations element-by-element is what bsxfun was designed for.
Regarding bsxfun, try to understand simple 2d-examples like bsxfun(#times,[1:7],[1,10,100]') first

How can I calculate dyadics in matlab without using for loops?

I was wondering if someone could help me with my problem.
Let say that I have the coordinates of MxN vectors in a tensor r of dimensions [M,N,3]. I would like to save in a 3M-by-3N block matrix all dyadic products r_0'*r_0, where r_0 is the vector r_0 = r(m,n,:) for some m and n, and I would like to do this without using for loops.
If haven't explain myself correctly, here is an example code that shows what I would like to obtain (but using for loops, of course):
N=10;
M=5;
r=rand(M,N,3);
Dyadic=zeros(3*M,3*N);
for m=1:M
a1=3*m-2;
a2=3*m;
for n=1:N
b1=3*n-2;
b2=3*n;
aux(3)=r(m,n,3);
aux(2)=r(m,n,2);
aux(1)=r(m,n,1);
Dyadic(a1:a2,b1:b2)=transpose(aux)*aux
end
end
Thanks in advance!
You need to use bsxfun(#times and then re-arrange elements to have the desired output -
%// Get the multipliication result
mat_mult = bsxfun(#times,permute(r,[1 2 4 3]),r);
%// OR if you would like to keep mat_mult as 3D that could be potentially faster -
%// mat_mult = bsxfun(#times,reshape(r,[],3),permute(reshape(r,[],3),[1 3 2]));
%// Re-arrange elements to have them the way you are indexing in nested loops
Dyadic = reshape(permute(reshape(mat_mult,M,N,3,[]),[3 1 4 2]),M*3,N*3);
The major play about this solution is really the re-arrangement of elements after we have the multiplication result.
Quick runtime tests with the input r as 1000 x 1000 x 3 sized array, show that this bsxfun based approach gives over 20x speedup over the nested loop code listed in the question!

MATLAB: Efficient (vectorized) way to apply function on two matrices?

I have two matrices X and Y, both of order mxn. I want to create a new matrix O of order mxm such that each i,j th entry in this new matrix is computed by applying a function to ith and jth row of X and Y respectively. In my case m = 10000 and n = 500. I tried using a loop but it takes forever. Is there an efficient way to do it?
I am targeting two functions dot product -- dot(row_i, row_j) and exp(-1*norm(row_i-row_j)). But I was wondering if there is a general way so that I can plugin any function.
Solution #1
For the first case, it looks like you can simply use matrix multiplication after transposing Y -
X*Y'
If you are dealing with complex numbers -
conj(X*ctranspose(Y))
Solution #2
For the second case, you need to do a little more work. You need to use bsxfun with permute to re-arrange dimensions and employ the raw form of norm calculations and finally squeeze to get a 2D array output -
squeeze(exp(-1*sqrt(sum(bsxfun(#minus,X,permute(Y,[3 2 1])).^2,2)))
If you would like to avoid squeeze, you can use two permute's -
exp(-1*sqrt(sum(bsxfun(#minus,permute(X,[1 3 2]),permute(Y,[3 1 2])).^2,3)))
I would also advise you to look into this problem - Efficiently compute pairwise squared Euclidean distance in Matlab.
In conclusion, there isn't a common most efficient way that could be employed for every function to ith and jth row of X. If you are still hell bent on that, you can use anonymous function handles with bsxfun, but I am afraid it won't be the most efficient technique.
For the second part, you could also use pdist2:
result = exp(-pdist2(X,Y));

Matlab 3d-matrix

I have to create a very big 3D matrix (such as: 500000x60x60). Is there any way to do this in matlab?
When I try
omega = zeros(500000,60,60,'single');
I get an out-of-memory error.
The sparse function is no option since it is only meant for 2D matrices. So is there any alternative to that for higher dimensional matrices?
Matlab only has support for sparse matrices (2D). For 3D tensors/arrays, you'll have to use a workaround. I can think of two:
linear indexing
cell arrays
Linear indexing
You can create a sparse vector like so:
A = spalloc(500000*60*60, 1, 100);
where the last entry (100) refers to the amount of non-zeros eventually to be assigned to A. If you know this amount beforehand it makes memory usage for A more efficient. If you don't know it beforehand just use some number close to it, it'll still work, but A can consume more memory in the end than it strictly needs to.
Then you can refer to elements as if it is a 3D array like so:
A(sub2ind(size(A), i,j,k))
where i, j and k are the indices to the 1st, 2nd and 3rd dimension, respectively.
Cell arrays
Create each 2D page in the 3D tensor/array as a cell array:
a = cellfun(#(x) spalloc(500000, 60, 100), cell(60,1), 'UniformOutput', false);
The same story goes for this last entry into spalloc. Then concatenate in 3D like so:
A = cat(3, a{:});
then you can refer to individual elements like so:
A{i,j,k}
where i, j and k are the indices to the 1st, 2nd and 3rd dimension, respectively.
Since your matrix is sparse, try to use ndsparse (N-dimensional sparse arrays FEX)