Speedup 3D array multiplication - matlab

I have the following code snippet, which multiplies each 2D cross section of a 3D array by a vector:
A = zeros(N,M);
for k = 1:M
B = C(:,:,k);
A(:,k) = B * f(:,k);
end
When I profile the code I see that for N=200, M = 25 this can be quite slow (relative to other parts of my code); in particular the line:
B=C(:,:,k)
can take up a large fraction of the total runtime. Is there some way I could speed this up?

You can use bsxfun combined with permute to perform the required multiplications with singleton expansion, and then add along the appropriate dimension to compute the matrix multiplications.
Define example data:
N = 5;
M = 4;
C = rand(N,N,M);
f = rand(N,M);
Then the result is computed as
result = permute(sum(bsxfun(#times, C, permute(f,[3 1 2])),2), [1 3 2]);
As a check, compare with the result computed using the loop:
A = zeros(N,M);
for k = 1:M
B = C(:,:,k);
A(:,k) = B * f(:,k);
end
result./A
gives
ans =
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1

Related

How can I randomize two binary vectors that are similar while making sure all possible combinations are respected?

Been trying to solve this simple problem for a while but just couldn't find the solution for the life of me...
I'm programming an experiment in PsychToolbox but I'll spare you the details, basically I have two vectors A and B of equal size with the same number of ones and zeroes:
A = [0 0 1 1]
B = [0 0 1 1]
Both vectors A and B must be randomized independently but in such a way that one combination of items between the two vectors is never repeated. That is, I must end up with this
A = [1 1 0 0]
B = [1 0 0 1]
or this:
A = [0 0 1 1]
B = [0 1 0 1]
but I should never end up with this:
A = [1 1 0 0]
B = [1 1 0 0]
or this
A = [0 1 0 1]
B = [0 1 0 1]
One way to determine this is to check the sum of items between the two vectors A+B, which should always contain only one 2 or only one 0:
A = [1 1 0 0]
B = [1 0 0 1]
A+B = 2 1 0 1
Been trying to make this a condition within a 'while' loop (e.g. so long as the number of zeroes in the vector obtained by A+B is superior to 1, keep randomizing A and B), but either it still produces repeated combination or it just never stops looping. I know this is a trivial problem but I just can't get my head around it somehow. Anyone care to help?
This is a simplified version of the script I got:
A = [1 1 0 0];
B = A;
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
while nnz(~(A+B)) > 1
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
end
Still, I end up with repeated combinations.
% If you are only looking for an answer to this scenario the easiest way is
% as follows:
A = [0 0 1 1];
B = [0 0 1 1];
nn = length(A);
keepset = [0 0 1 1;0 1 0 1];
keepset = keepset(:,randperm(nn))
% If you want a more general solution for arbitrary A & B (for example)
A = [0 0 0 1 1 1 2 2 2];
B = [0 0 0 1 1 1 2 2 2];
nn = length(A);
Ai = A(randperm(nn));
Bi = B(randperm(nn));
% initialize keepset with the first combination of A & B
keepset = [Ai(1);Bi(1)];
loopcnt = 0;
while (size(keepset,2) < nn)
% randomize the elements in A and B independently
Ai = A(randperm(nn));
Bi = B(randperm(nn));
% test each combination of Ai and Bi to see if it is already in the
% keepset
for ii = 1:nn
tstcombo = [Ai(ii);Bi(ii)];
matchtest = bsxfun(#eq,tstcombo,keepset);
matchind = find((matchtest(1,:) & matchtest(2,:)));
if isempty(matchind)
keepset = [keepset tstcombo];
end
end
loopcnt = loopcnt + 1;
if loopcnt > 1000
disp('Execution halted after 1000 attempts')
break
elseif (size(keepset,2) >= nn)
disp(sprintf('Completed in %0.f iterations',loopcnt))
end
end
keepset
It's much more efficient to permute the combinations randomly than shuffling the arrays independently and handling the inevitable matching A/B elements.
There are lots of ways to generate all possible pairs, see
How to generate all pairs from two vectors in MATLAB using vectorised code?
For this example I'll use
allCombs = combvec([0,1],[0,1]);
% = [ 0 1 0 1
% 0 0 1 1 ]
Now you just want to select some amount of unique (non-repeating) columns from this array in a random order. In all of your examples you select all 4 columns. The randperm function is perfect for this, from the docs:
p = randperm(n,k) returns a row vector containing k unique integers selected randomly from 1 to n inclusive.
n = size(allCombs,2); % number of combinations (or columns) to choose from
k = 4; % number of columns to choose for output
AB = allCombs( :, randperm(n,k) ); % random selection of pairs
If you need this split into two variables then you have
A = AB(1,:);
B = AB(2,:);
Here's a possible solution:
A = [0 0 1 1];
B = [0 0 1 1];
% Randomize A and B independently
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
% Keep randomizing A and B until the condition is met
while sum(A+B) ~= 1 && sum(A+B) ~= length(A)
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
end
This solution checks if the sum of the elements in A+B is either 1 or the length of A, which indicates that only one element in A+B is either a 0 or a 2, respectively. If either of these conditions is not met, the vectors A and B are randomized again.

How to generate matrix in MATLAB where values decrease with increasing coordinates

I'm trying to generate an n x n matrix like
5 4 3 2 1
4 4 3 2 1
3 3 3 2 1
2 2 2 2 1
1 1 1 1 1
where n = 5 or n 50. I'm at an impasse and can only generate a portion of the matrix. It is Problem 2.14 from Numerical Methods using MATLAB 3rd Edition by Penny and Lindfield. This is the best I have so far:
n = 5;
m = n;
A = zeros(m,n);
for i = 1:m
for j = 1:n
A(i,j) = m;
end
m = m - 1;
end
Any feedback is appreciated.
That was a nice brain-teaser, here’s my solution:
[x,y] = meshgrid(5:-1:1);
out = min(x,y)
Output:
ans =
5 4 3 2 1
4 4 3 2 1
3 3 3 2 1
2 2 2 2 1
1 1 1 1 1
Here's one loop-based approach:
n = 5;
m = n;
A = zeros(m, n);
for r = 1:m
for c = 1:n
A(r, c) = n+1-max(r, c);
end
end
And here's a vectorized approach (probably not faster, just for fun):
n = 5;
A = repmat(n:-1:1, n, 1);
A = min(A, A.');
That's one of the matrices in Matlab's gallery, except that it needs a 180-degree rotation, which you can achieve with rot90:
n = 5;
A = rot90(gallery('minij', n), 2);

adding consecutive numbers in a vector in matlab

i have a vector and a scalar as input in a problem , the problem is to compute the largest product of n consecutive number of the vector and output the product and the index of the element of the vector that is the first term of the product.
E.g vector =[ 1 2 3 4 5 6] , n=3
I'm supposed to get '3' (i e n) consecutive number of vector whose product is the largest .
in this case it will be 4*5*6
so output will be 120 and 4 as the index.
now if vector has fewer than 'n' elements, the function outputon returns 0 and -1 as the output.
please i need ideas on how to achieve this
You can make a simple implementation with loops:
a = [1 2 3 4 5 6]
w = 3
n = length(a)
maximum = -1
for i = 1:n-w
p = prod(a(i:i+w))
if (p > maximum)
maximum = p
end
end
maximum
Or, you can use the nlfilter from the image processing palette.
a = []
w = 3
if (length(a) >= w)
products = nlfilter(a, [1 w], #(x) prod(x))
res = max(products)
else
res = -1
end
You can create the moving window from the answer of raryeng for the question Matrix with sliding window elements and then appyl cumprod on the columns and take the max with its index.
myvec = [1 2 2 1 3 1];
n = 3;
ind = bsxfun(#plus, 1:n, (0:1:length(myvec)-n).')';
M = cumprod(myvec(ind));
[val,its_ind] = max(M(end,:));
You can encapsulate this with an if condition checking whether the length of myvec is larger than
You can compute the element-wise logarithm of your vector, so multiplication becomes addition; and sliding addition is just convolution with a window of ones:
function [m, p] = f(v, n)
if numel(v) < n
m = -1;
p = 0;
else
c = conv(log(v(:)), ones(n,1), 'valid'); % convolution with vector of n ones
[~, m] = max(c); % starting index of maximizing window
p = prod(v(m+(0:n-1))); % corresponding product
end
Examples:
>> v = [1 2 3 4 5 6]; n = 3;
>> [m, p] = f(v,n)
m =
4
p =
120
>> v = [1 2 3 4 5 6]; n = 7;
>> [m, p] = f(v,n)
m =
-1
p =
0
>> v = [1 4 6 2 5 3]; n = 3;
>> [m, p] = f(v,n)
m =
3
p =
60

Index out column vector of matrix and then multiply corresponding matrix

I have heavy computation as follows:
L = ones(100000,200000);
for i = 1:10000
temp = f(i,...);
L = L .* temp(:, index );
end
where temp is a 100,000*3 matrix (values computed from f(i,...); I omit arguments here) and index is a 1*200,000 integer vector (1 to 3).
I have to do above many times in my algorithm. I feel Matlab wastes time creating 100000*200000 from temp(:, index ) in the iteration. But, it may not necessary; that is, we can just extract corresponding column and then multiply to corresponding column of L. Yet, I cannot find a way to do it efficiently...
Hope anyone can give advice on this. Thanks!
I give a small and hypothetical example:
function test
x = rand(5,3);
t = rand(10,1); % could be very long
point = 3;
index = [1 2 1 3 2 3 1 2;...
2 3 2 1 2 3 1 1;...
1 1 1 2 2 3 1 1;...
3 3 2 3 2 2 2 1;...
2 3 2 1 2 1 3 1]; % could be very long
L = ones(10,8);
for i = 1:5
temp = myfun(x(i,:),t,point);
L = L .* temp(:, index(i,:) );
end
function prob = myfun(x,t,point)
prob = ones(size(t,1),point);
for k = 2:point
prob(:,k) = exp( ((k-1).*x(1).*(t) + x(k) ));
end
de = sum(prob,2);
for k = 1:point
prob(:,k) = prob(:,k)./de;
end
end
end
I managed to save just some minor computation during each iteration, perhaps it makes a difference on your large matrices though. What I did was to change one line into prob(:,k) = exp( ((k-1).*x(i,1).*(t) + x(i,k) ));. Notice the elements in x. This saves some unnecessary computation. It is somewhat difficult to optimize this as I have no idea what this is, but here's my code:
x = rand(5,3);
t = rand(10,1);
point = 3;
index = [1 2 1 3 2 3 1 2;...
2 3 2 1 2 3 1 1;...
1 1 1 2 2 3 1 1;...
3 3 2 3 2 2 2 1;...
2 3 2 1 2 1 3 1];
L = ones(10,8);
for i = 1:5
prob = ones(size(t,1),point);
for k = 2:point
prob(:,k) = exp( ((k-1).*x(i,1).*(t) + x(i,k) ));
end
de = sum(prob,2);
for k = 1:point
prob(:,k) = prob(:,k)./de;
end
L = L .* prob(:, index(i,:) );
end
There are some dangerous operations I noticed, e.g. de = sum(prob,2);. Note that if you would change prob(:,k) = prob(:,k)./de; to prob(:,k) = prob(:,k)./sum(prob,2); you have a different result. Perhaps you're aware of this already, but it may be worth mentioning. Let me know if there is anything more I can do to help.

Execute formulas ignoring zero elements

I want to run fast Matlab algorithms over Matrices by ignoring zero-elements.
In the past I just worked with a very slow double-for-loop e.g.
for i = 1 : size(x,1)
for j = 1 : size(x,2)
if x(i,j) ~= 0
... do something with x(i,j)
end
end
end
But how can I make the matrix operation on the whole matrix x?
E.g. how can I run
x(i,j) = log(x(i,j)) if x>0 else 0 <-- pseudo code
in Matlab on the whole matrix without for loops?
Finally I want to rewrite lines like
result = sum(sum((V.*log(V./(W*H))) - V + W*H));
with ignoring zeros.
I just need to understand the concept.
In case of need I could also use NaN instead of zero, but I didn't find e.g. the function
nanlog()
x~=0 returns you the indices of the locations not equal to zero. Then, you can use them to index corresponding locations of x such as follows:
>> x = [1 0 2 3; 0 4 0 5]
x =
1 0 2 3
0 4 0 5
>> mean(x(:)) %#mean of all elements
ans =
1.8750
>> mean(x(x~=0)) %#mean of nonzero elements
ans =
3
>> x(x~=0) = x(x~=0) + 1
x =
2 0 3 4
0 5 0 6
You can use NaN as a temporary and make use of the fact that log(NaN) = NaN, like so:
x(x==0) = NaN;
y = log(x);
y(isnan(y)) = 0;
alternatively, you can use logical indexing:
x(x~=0) = log(x(x~=0));
or, if you want to preserve x,
y = x;
y(y~=0) = log(y(y~=0));
For the example you provide, you can just do
result = nansum(nansum((V.*log(V./(W*H))) - V + W*H));
assuming that V == 0 is the problem.