Matlab : Vectorize technics in 3 dimensions matrix - matlab

I actually vectorizing one of my code and I have some issues.
This is my initial code:
CoordVorBd = random(N+1,3)
CoordCP = random(N,3)
v = random(1,3)
for i = 1 : N
for j = 1 : N
ri1j = (-CoordVorBd (i,:) + CoordCP(j,:));
vij(i,j,:) = cross(v,ri1j))/(norm(ri1j)
end
end
I have start to vectorize that creating some matrix that contains 3*1 Vectors. My size of matrix is N*N*3.
CoordVorBd1(1:N,:) = CoordVorBd(2:N+1,:);
CoordCP_x= CoordCP(:,1);
CoordCP_y= CoordCP(:,2);
CoordCP_z= CoordCP(:,3);
CoordVorBd_x = CoordVorBd([1:N],1);
CoordVorBd_y = CoordVorBd([1:N],2);
CoordVorBd_z = CoordVorBd([1:N],3);
CoordVorBd1_x = CoordVorBd1(:,1);
CoordVorBd1_y = CoordVorBd1(:,2);
CoordVorBd1_z = CoordVorBd1(:,3);
[X,Y] = meshgrid (1:N);
ri1j_x = (-CoordVorBd_x(X) + CoordCP_x(Y));
ri1j_y = (-CoordVorBd_y(X) + CoordCP_y(Y));
ri1j_z = (-CoordVorBd_z(X) + CoordCP_z(Y));
ri1jmat(:,:,1) = ri1j_x(:,:);
ri1jmat(:,:,2) = ri1j_y(:,:);
ri1jmat(:,:,3) = ri1j_z(:,:);
vmat(:,:,1) = ones(N)*v(1);
vmat(:,:,2) = ones(N)*v(2);
vmat(:,:,3) = ones(N)*v(3);
This code works but is heavy in terms of variable creation. I did'nt achieve to apply the vectorization to all the matrix in one time.
The formule like
ri1jmat(X,Y,1:3) = (-CoordVorBd (X,:) + CoordCP(Y,:));
doesn't work...
If someone have some ideas to have something cleaner.
At this point I have a N*N*3 matrix ri1jmat with all my vectors.
I want to compute the N*N rij1norm matrix that is the norm of the vectors
rij1norm(i,j) = norm(ri1jmat(i,j,1:3))
to be able to vectorize the vij matrix.
vij(:,:,1:3) = (cross(vmat(:,:,1:3),ri1jmat(:,:,1:3))/(ri1jmatnorm(:,:));
The cross product works.
I tried numbers of method without achieve to have this rij1norm matrix without doing a double loop.
If someone have some tricks, thanks by advance.

Here's a vectorized version. Note your original loop didn't include the last column of CoordVorBd, so if that was intentional you need to remove it from the below code as well. I assumed it was a mistake.
CoordVorBd = rand(N+1,3);
CoordCP = rand(N,3);
v = rand(1,3);
repCoordVor=kron(CoordVorBd', ones(1,size(CoordCP,1)))'; %based on http://stackoverflow.com/questions/16266804/matlab-repeat-every-column-sequentially-n-times
repCoordCP=repmat(CoordCP, size(CoordVorBd,1),1); %repeat matrix
V2=-repCoordVor + repCoordCP; %your ri1j
nrm123=sqrt(sum(V2.^2,2)); %vectorized norm for each row
vij_unformatted=cat(3,(v(:,2).*V2(:,3) - V2(:,2).*v(:,3))./nrm123,(v(:,3).*V2(:,1) - V2(:,3).*v(:,1))./nrm123,(v(:,1).*V2(:,2) - V2(:,1).*v(:,2))./nrm123); % cross product, expanded, and each term divided by norm, could use bsxfun(#rdivide,cr123,nrm123) instead, if cr123 is same without divisions
vij=permute(reshape( vij_unformatted,N,N+1,3),[2,1,3]); %reformat to match your vij

Here is another way to do it using arrayfun
% Define a meshgrid of indices to run over
[I, J] = meshgrid(1:N, 1:(N+1));
% Calculate ril for each index
rilj = arrayfun(#(x, y) -CoordVorBd (y,:) + CoordCP(x,:), I, J, 'UniformOutput', false);
%Calculate vij for each point
temp_vij1 = arrayfun(#(x, y) cross(v, rilj{x, y}) / norm(rilj{x, y}), J, I, 'UniformOutput', false);
%Reshape the matrix into desired format
temp_vij2 = cell2mat(temp_vij1);
vij = cat(3, temp_vij2(:, 1:3:end), temp_vij2(:, 2:3:end), temp_vij2(:, 3:3:end));

Related

average bins along a dimension of a nd array in matlab

To compute the mean of every bins along a dimension of a nd array in matlab, for example, average every 10 elements along dim 4 of a 4d array
x = reshape(1:30*30*20*300,30,30,20,300);
n = 10;
m = size(x,4)/10;
y = nan(30,30,20,m);
for ii = 1 : m
y(:,:,:,ii) = mean(x(:,:,:,(1:n)+(ii-1)*n),4);
end
It looks a bit silly. I think there must be better ways to average the bins?
Besides, is it possible to make the script applicable to general cases, namely, arbitray ndims of array and along an arbitray dim to average?
For the second part of your question you can use this:
x = reshape(1:30*30*20*300,30,30,20,300);
dim = 4;
n = 10;
m = size(x,dim)/10;
y = nan(30,30,20,m);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
for ii = 1 : m
idx1{dim} = ii;
idx2{dim} = (1:n)+(ii-1)*n;
y(idx1{:}) = mean(x(idx2{:}),dim);
end
For the first part of the question here is an alternative using cumsum and diff, but it may not be better then the loop solution:
function y = slicedmean(x,slice_size,dim)
s = cumsum(x,dim);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
idx1{dim} = slice_size;
idx2{dim} = slice_size:slice_size:size(x,dim);
y = cat(dim,s(idx1{:}),diff(s(idx2{:}),[],dim))/slice_size;
end
Here is a generic solution, using the accumarray function. I haven't tested how fast it is. There might be some room for improvement though.
Basically, accumarray groups the value in x following a matrix of customized index for your question
x = reshape(1:30*30*20*300,30,30,20,300);
s = size(x);
% parameters for averaging
dimAv = 4;
n = 10;
% get linear index
ix = (1:numel(x))';
% transform them to a matrix of index per dimension
% this is a customized version of ind2sub
pcum = [1 cumprod(s(1:end-1))];
sub = zeros(numel(ix),numel(s));
for i = numel(s):-1:1,
ixtmp = rem(ix-1, pcum(i)) + 1;
sub(:,i) = (ix - ixtmp)/pcum(i) + 1;
ix = ixtmp;
end
% correct index for the given dimension
sub(:,dimAv) = floor((sub(:,dimAv)-1)/n)+1;
% run the accumarray to compute the average
sout = s;
sout(dimAv) = ceil(sout(dimAv)/n);
y = accumarray(sub,x(:), sout, #mean);
If you need a faster and memory efficient operation, you'll have to write your own mex function. It shouldn't be so difficult, I think !

How would you remove the loop from this matlab code

Given that we have:
x is 2d matrix with size [numSamples x numFeatures]
A is 2d square matrix with size [numFeatures x numFeatures]
B is a 1d vector with size [1 x numFeatures]
I would like to evaluate the following code without a loop: (or in a way faster way)
out = zeros(1,numSamples);
for i = 1:numSamples
res = sum(repmat(B - x(i,:), numSamples, 1)*A.*(x - repmat(x(i,:), numSamples, 1)), 2).^2;
out(i) = var(res);
end
If you have other suggestions on a faster improvement of the above, it is also more than welcome.
You can bsxfun those piece-by-piece for a vectorized solution -
P1 = bsxfun(#minus,B,x)*A;
P2 = bsxfun(#minus,x,permute(x,[3 2 1]));
out = var(squeeze(sum(bsxfun(#times,P1,P2),2)).^2.');
Partially vectorized approach -
P = (bsxfun(#minus,B,x)*A).'; %//'
out = zeros(1,numSamples);
for i = 1:numSamples
out(i) = var((bsxfun(#minus,x,x(i,:))*P(:,i)).^2);
end

Optimized matrix operation of matrix with repeating elements in Matlab

I want to get exp() of a large matrix (A) with values that repeat at different indices. To speed-up the exp() operation I only perform it on the unique values of A and then reassemble the matrix. However the reassembly of the matrix is quite slow. The following code provides a working example:
% defintion of a grid
gridSp = 5:5:35*5;
X = repmat(gridSp,35,1);
Z = repmat(gridSp',1,35);
% calculation of the distances
locMat = [X(:) Z(:)];
dist=sqrt(bsxfun(#minus,locMat(:,1),locMat(:,1)').^2 +...
bsxfun(#minus,locMat(:,2),locMat(:,2)').^2);
sizeDist = size(dist);
uniqueDist = unique(dist,'stable');
[~, Locb] = ismember(dist,uniqueDist);
nn_A = exp(1i*2*pi*rand(sizeDist(1),100));
H_A = zeros(size(nn_A));
freq = linspace(10^-3,10,100);
psdA = 4096*length(freq).*10.*4.*22.6./((1 + 6.*freq*22.6).^(5/3));
for jj=1:100
b = exp(-8.8*uniqueDist*sqrt((freq(jj)/15).^2 + 10^-7));
b = b.*psdA(jj);
A = b(Locb);
droptol = max(A(:))*10^-10;
if min(A(:))<droptol
A = sparse(A);
HH_A = ichol(A,struct('type','ict','shape','lower','droptol',droptol));
else
HH_A = chol(A,'lower');
end
H_A(:,jj) = HH_A*nn_A(:,jj);
end
Especially the reassembly of the matrix
A = b(Locb);
and the conversion of the matrix to sparse
A = sparse(A);
in the last for-loop take up a lot of time. Is there a quicker way to do this? Interestingly:
B = A + A;
is much faster than
A = b(Locb);
I have to perfom these operations far more often than the 100 iterations in the example.
Here a condensed version of the code up on request (below).
% defintion of a grid
gridSp = 5:5:28*5;
X = repmat(gridSp,35,1);
Z = repmat(gridSp',1,35);
% calculation of the distances
locMat = [X(:) Z(:)];
dist=sqrt(bsxfun(#minus,locMat(:,1),locMat(:,1)').^2 +bsxfun(#minus,locMat(:,2),locMat(:,2)').^2);
uniqueDist = unique(dist,'stable');
[~, Locb] = ismember(dist,uniqueDist);
for jj=1:100
b = exp(jj.*uniqueDist);
A = b(Locb);
end
In your example, the dimension of dist is just 980 x 980 in which case you would be better off to just perform a dense matrix operation, i.e.
for jj=1:100
A=exp(jj*dist);
end
which is 2 times faster than
for jj=1:100
b = exp(jj.*uniqueDist);
A = b(Locb);
end
for your given example.

Get index of current element of a 2-D matrix within arrayfun

Let M be some matrix:
M = rand(1000, 2000);
Consider the following code example:
A = zeros(size(M));
for row = 1:1000
for col = 1:2000
A(row, col) = M(row,col)*(row + col);
end
end
How to compute the matrix A without for loops?
There is arrayfun function, but I don't know how get the index of the current element:
A = arrayfun(#(x)(x*(index(1) + index(2))), M); %// but how to get index???
Perhaps there are other solutions (and without extra loops)?
You can do something simple like as follows to get a matrix that will represent row+col and then multiply that by M
M = rand(1000, 2000);
rowPlusCol = bsxfun(#plus,(1:size(M,1)).',1:size(M,2));
A = M.*rowPlusCol;
From my experience bsxfun is an extremely powerful function and can definitely save some run time, and this is a perfect example of that.
Here's an alternative solution, boasting another fancy one-liner, for the sake of diversity:
A = M .* hankel(2:size(M, 1) + 1, size(M, 1) + 1:sum(size(M)));
I don't think it's possible with arrayfun.
You can get row and column numbers using meshgrid and then do some simple matrix math.
M = rand(1000, 2000);
[cols,rows] = meshgrid(1:size(M,2), 1:size(M,1));
A = M .* (cols + rows);

Calculate a tricky sum in Matlab

With the following variables:
m = 1:4; n = 1:32;
phi = linspace(0, 2*pi, 100);
theta = linspace(-pi, pi, 50);
S_mn = <a 4x32 coefficient matrix, corresponding to m and n>;
how do I compute the sum over m and n of S_mn*exp(1i*(m*theta + n*phi)), i.e.
I've thought of things like
[m, n] = meshgrid(m,n);
[theta, phi] = meshgrid(theta,phi);
r_mn = S_mn.*exp(1i*(m.*theta + n.*phi));
thesum = sum(r_mn(:));
but that requires theta and phi to have the same number of elements as m and n, and it gives me just one element in return - I want a matrix the the size of meshgrid(theta,phi), regardless of the sizes of theta and phi (i.e. I want to be able to evaluate the sum as a function of theta and phi).
How do I do this calculation in matlab?
Since I don't know what S is...
S = randn(4,32);
[m,n] = ndgrid(1:4,1:32);
fun = #(theta,phi) sum(sum(S.*exp(sqrt(-1)*(m*theta + n*phi))));
Works fine for me.
fun(pi,3*pi/2)
ans =
-15.8643373238676 - 1.45785698818839i
If you now wish to do this for a large set of values phi and theta, a pair of loops now are the trivial solution. Or, you can do it all in one computation, although the arrays will get larger. Still not hard. WTP?
You do realize that both meshgrid and ndgrid take more than just two arguments? So it is time to learn how to use bsxfun, and then squeeze.
[m,n,theta,phi] = ndgrid(1:4,1:32,linspace(-pi, pi, 50),linspace(0, 2*pi, 100));
res = bsxfun(#times,S,exp(sqrt(-1)*(m.*theta + n.*phi)));
res = squeeze(sum(sum(res,1),2));
Or do this, which will be a bit faster. The previous computation took my machine .07 seconds. This last one took .05, so some savings by using bsxfun heavily.
m = (1:4)';
n = 1:32;
[theta,phi] = ndgrid(linspace(-pi, pi, 50),linspace(0, 2*pi, 100));
theta = reshape(theta,[1,1,size(theta)]);
phi = reshape(phi,size(theta));
res = bsxfun(#plus,bsxfun(#times,m,theta*sqrt(-1)),bsxfun(#times,n,phi*sqrt(-1)));
res = bsxfun(#times,S,exp(res));
res = squeeze(sum(sum(res,1),2));
If you need to do the above 2000 times, so it should take 100 seconds to do. WTP? Get some coffee and relax.
First save the size of each variable:
size_m = size(m);
size_n = size(n);
size_theta = size(theta);
size_phi = size(phi);
Use ngrid function like this:
[theta, phi, m, n] = ngrid(theta, phi, m, n)
This will give you an array with 4 dimensions (one for each of your variables: theta, phi, m, n). Now you can calculate this:
m.*theta + n.*phi
Now you need to make S_mn have 4 dimensions with sizes size_theta, size_phi, size_m, size_n like this:
S_tpmn = repmat(S_mn, [size_theta size_phi size_m size_n]);
Now you can calculate your sum like this:
aux_sum = S_tpmn.*exp(1i*(m.*theta + n.*phi));
Finally you can sum along the last 2 dimensions (m and n) to get an array with 2 dimensions with size size_theta by size_phi:
final_sum = sum(sum(aux_sum, 4), 3);
Note: I don't have access to Matlab right now, so I can't test if this actually works.
There are several ways you could go about this.
One way is to create a function(-handle) that returns the sum as a function of theta and phi, and then use arrayfun to do the sums. Another is to fully vectorize the computation, though that will use more memory.
The arrayfun version:
[m, n] = meshgrid(m,n);
sumHandle = #(theta,phi)sum(reshape(...
S_mn.*exp(1i(m*theta + n*phi)),...
[],1))
[theta, phi] = meshgrid(theta,phi);
sumAsFunOfThetaPhi = arrayfun(sumHandle,theta,phi);
The vectorized version:
[m, n] = meshgrid(m,n);
m = permute(m(:),[2 4 1 3]); %# vector along dim 3
n = permute(n(:),[2 3 4 1]); %# vector along dim 4
S_mn = repmat( permute(S_mn,[3 4 1 2]), length(theta),length(phi));
theta = theta(:); %# vector along dim 1 (phi is along dim 2 b/c of linspace)
fullSum = S_mn.* exp( 1i*(...
bsxfun(#plus,...
bsxfun(#times, m, theta),...
bsxfun(#times, n, phi),...
)));
sumAsFunOfThetaPhi = sum(sum( fullSum, 3),4);