Calculate the norm of a 3D array using pagefun - MATLAB - matlab

Is it possible to use pagefun to calculate the norm (and execute other built-in GPU functions - http://www.mathworks.co.uk/help/distcomp/run-built-in-functions-on-a-gpu.html) on multiple pages simultaneously?
For example, I need to calculate the norms of a 3D array.
N = 10000
Sig = gpuArray(2,2000,N) % This is just to get an idea of the dimensions. Its populated elsewhere
% This is what I am currently doing.
for k = 1:N
TNorm(k,:) = [norm(Sig(1,:,k),2) norm(Sig(2,:,k),2)];
end
Is there a way to execute it in one go rather than iterate through the 3rd dimension to calculate the norm each time? Can something like this be done?
pagefun(norm(Sig,2)) % This gives the error: Input data must be a double or single vector or 2D matrix.
Thanks in advance! :)

Say you have a 3D array, in your case lets call it E:
E = rand(3,3,4) % This is a 3D array with size 3x3x4
if you want to calculate the norm of page so from 1 to 4 in this case, you can use:
norm_E = arrayfun(#(idx) norm(E(:,:,idx)), 1:size(E,3))
The output will be:
norm_E = [(norm array 1),(norm array 2),(norm array 3),(norm array 4)]
You are also not limited to the 2-norm you can use any norm you please for example if you wanted to use the infinity norm you could put:
norm_E = arrayfun(#(idx) norm(E(:,:,idx),inf), 1:size(E,3))
Using arrayfun is faster than a for loop. I apologize if you are restricted to using pagefun I do not have that feature with my version of Matlab.

Related

3D Convolution in MATLAB

I'm currently trying to use convolution to average blocks in my data and turn my current, 336x264x25x27, grid into a new, 100x100x27, grid.
To achieve this I've been trying to use convolution.
In a 2-D setting I've been able to convert a 336x264 matrix to 100x100 using the conv2 function in matlab. I'm now trying to use convn to accomplish a similar task in 4-D.
As stated, currently I'm using the convn function. I'm trying to average out cells over the first two dimensions so that I end up with a 100x100x27 matrix. My code is as follows:
A = rand(336,264,25,27); % Sample Data
A = A(:,:,13,:); % This line and the following line eliminate the third dimension (time) which will be constant throughout my output. Now "A" is 336x264x27 after using "squeeze".
A = squeeze(A);
B = ones(100,100,27); % This is the size of matrix that I would like to achieve. I was under the impression that "B" was the size matrix that you want to inevitably end up with but I believe I am mistaken.
C = convn(A,B); % C would hopefully by my 100x100x27 matrix.
Currently, this is resulting in a 435x363x53 matrix. If you could help me with my logic and show me how I might turn "A" into a 100x100x27 matrix using convolution it would be much appreciated!

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

Duplicating a 2d matrix in matlab along a 3rd axis MANY times

I'm looking to duplication a 784x784 matrix in matlab along a 3rd axis. The following code seems to work:
mat = reshape(repmat(mat, 1,10000),784,784,10000);
Unfortunately, it takes so long to run it's worthless (changing the 10,000s to 1000 makes it take a few minutes, and using 10,000 makes my whole machine freeze up practically). is there a faster way to do this?
For reference, I'm looking to use mvnpdf on 10,000 vectors each of length 784, using the same covariance matrix for each. So my final call looks like
mvnpdf(X,mu,mat)
%size(X) = (10000,784), size(mu) = (10000,784), size(mat) = 784,784,10000
If there's a way to do this that's not repeating the covariance matrix 10,000 times, that'd be helpful too. Thanks!
For replication in more than 2 dimensions, you need to supply the replication counts as an array:
out = repmat(mat,[1,1,10000])
Creating a 784x784 matrix 10,000 times isn't going to take advantage of the vectorization in MATLAB, which is going to be more useful for small arrays. Avoiding a for loop also won't help too much, given the following:
The main speedup you can gain here is by computing the inverse of the covariance matrix once, and then computing the pdf yourself. The inverse of sigma takes O(n^3), and you are needlessly doing that 10,000 times. (Also, the square root determinant can be precomputed.) For reference, the PDF of the multivariate normal distribution is computed as follows:
http://en.wikipedia.org/wiki/Multivariate_normal_distribution#Properties
Better to just compute the inverse once, and then compute z = x - mu for each value, then doing z'Sz for each pdf value, and applying a simple function and a constant. But wait! You can vectorize that, too.
I don't have MATLAB in front of me, but this is basically what you need to do, and it'll run in an instant.
s = inv(sigma);
c = -0.5*log(det(s)) - (k/2)*log(2*pi);
z = x - mu; % 10000 x 784 matrix
ans = exp( c - 0.5 .* dot(z*s, z, 2) ); % 10000 x 1 vector

apply matrix randomisation without for loop in 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{:});

Matlab Isolating 2D Array from 3D Matrix

I have a 3D matrix called M of size <100x100x100>, so basically coordinates.
I am trying to get the array of at specific values of y. However using M(:,1,:) I get a <100x1x100> matrix whereas finding I can use M(:,:,1) and get a <100x100> matrix.
Is there an easy way to turn the <100x1x100> into a <100x100> by either isolating it a different way or using a short translation?
Thanks,
Does squeeze do what you want?
a = ones(100, 1, 100);
b = squeeze(a);
size(b) % 100x100