I have this toy example:
l = [1, 2, 3, 4, 5];
a = zeros(3, 1);
for p = 1:3
a(p) = sum(l(p:p+2));
end;
This example calculates sum of each 3 elements in 'l', which are close to each other and writes it in 'a'. Now, is it possible to rewrite the same code using vectorization and matrix operations, without 'for' loop? I have tried something similar to:
p = 1:3;
a(p) = sum(l(p:p+2));
The result was [6, 6, 6], it have calculated sum of the first three elements and wrote it in 'a' on each position. This is only a toy example, but i need to do something similar on 3d 256x256x128 array with 20x20x20 cube elements sum, using 'for' loops was VERY slow there. So, is there a fast way to solve this problem?
Edit :
Divakar have suggested me to use my actual case here. Well, here is the most important part of it, which i am trying to rewrite:
Edit2 :
Thank you for answer. Understood the idea, removed code above, rewriting it now.
You're describing a basic convolution:
a = conv(l,ones(1,3),'valid'); % a should have 3 elements
For 3d, use convn in a similar fashion with ones(20,20,20)
Related
I have some working code in matlab, and speed is vital. I have vectorized/optimized many parts of it, and the profiler now tells me that the most time is spent a short piece of code. For this,
I have some parameter sets for a multi-variate normal
distribution.
I then have to get the value from the corresponding PDF at some point
pos,
and multiply it by some other value stored in a vector.
I have produced a minimal working example below:
num_params = 1000;
prob_dist_params = repmat({ [1, 2], [10, 1; 1, 5] }, num_params, 1);
saved_nu = rand( num_params, 1 );
saved_pos = rand( num_params, 2 );
saved_total = 0;
tic()
for param_counter = 1:size(prob_dist_params)
% Evaluate the PDF at specified points
pdf_vals = mvnpdf( saved_pos(param_counter,:), prob_dist_params{param_counter,1}, prob_dist_params{param_counter, 2} );
saved_total = saved_total + saved_nu(param_counter)*pdf_vals;
end % End of looping over parameters
toc()
I am aware that prob_dist_params are all the same in this case, but in my code we have each element of this different depending on a few things upstream. I call this particular piece of code many tens of thousands of time in my full program, so am wondering if there is anything at all I can do to vectorize this loop, or failing that, speed it up at all? I do not know how to do so with the inclusion of a mvnpdf() function.
Yes you can, however, I don't think it will give you a huge performance boost. You will have to reshape your mu's and sigma's.
Checking the doc of mvnpdf(X,mu,sigma), you see that you will have to provide X and mu as n-by-d numeric matrix and sigma as d-by-d-by-n.
In your case, d is 2 and n is 1000. You have to split the cell array in two matrices, and reshape as follows:
prob_dist_mu = cell2mat(prob_dist_params(:,1));
prob_dist_sigma = cell2mat(permute(prob_dist_params(:,2),[3 2 1]));
With permute, I make the first dimension of the cell array the third dimension, so cell2mat will result in a 2-by-2-by-1000 matrix. Alternatively you can define them as follows,
prob_dist_mu = repmat([1 2], [num_params 1]);
prob_dist_sigma = repmat([10, 1; 1, 5], [1 1 num_params]);
Now call mvnpdf with
pdf_vals = mvnpdf(saved_pos, prob_dist_mu, prob_dist_sigma);
saved_total = saved_nu.'*pdf_vals; % simple dot product
I have a question that is related to this post: "Cloning" row or column vectors. I tried to work around the answers posted there, yet failed to apply them to my problem.
In my case, I'd like to "clone" each row row of a matrix by converting a matrix like
A = [1,2; 3, 4; 5, 6]
into the matrix
B = [1, 2
1, 2
3, 4
3, 4
5, 6
5, 6]
by repeating each row of A a number of times.
So far, I was able to work with repmat for a single row like
A = [1, 2];
B = repmat(A, 2, 1)
>> B = [1, 2
1, 2]
I was trying to build a loop using that formula, in order to obtain the matrix wanted. The loop looked like
T = 3; N = 2;
for t = 1:T
for I = 1:N
B = repmat(C, 21, 1)
end
end
Has anyone an idea how to correctly write the loop, or a better way to do this?
kron
There are a few ways you can do this. The shortest way would be to use the kron function as suggested by Adiel in the comments.
A = [1,2; 3, 4; 5, 6];
B = kron(A, [1;1]);
Note that the number of elements in the ones vector controls how many times each row is duplicated. For n times, use kron(A, ones(n,1)).
kron calculates the kronecker tensor product, which is not necessarily a fast process, nor is it intuitive to understand, but it does give the right result!
reshape and repmat
A more understandable process might involve a combination of reshape and repmat. The aim is to reshape the matrix into a row vector, repeat it the desired number of times, then reshape it again to regain the two-column matrix.
B = reshape(repmat(reshape(A, 1, []), 2, 1), [], 2);
Note that the 2 within the repmat function controls how many times each row is duplicated. For n times, use reshape(repmat(reshape(A, 1, []), n, 1), [], 2).
Speed
A quick benchmark can be written:
% Setup, using a large A
A = rand(1e5, 2);
f = #() kron(A, [1;1]);
g = #() reshape(repmat(reshape(A, 1, []), 2, 1), [], 2);
% timing
timeit(f);
timeit(g);
Output:
kron option: 0.0016622 secs
repmat/reshape option: 0.0012831 secs
Extended benchmark over different sizes:
Summary:
the reshape option is quicker (~25%) for just duplicating the rows once each, so you should go for this option if you want to end up with 2 of each row for a large matrix.
the reshape option appears to have complexity O(n) for the number of row repetitions. kron has some initial overhead, but is much quicker when you want many repetitions and hardly slows down because of them! Go for the kron method if you are doing more than a few repetitions.
I have a cell array A [1x80] in which each element is a cell array itself [9x2].
I have also a vector B representing a group of selected cells of A and I want to extract the element {2,2} of each selected cell.
I tried with a simple
A(1,B){2,2}
but of course it doesn't work....
Can you help me?
How about this:
A = {{1 2; 3 4}, {5 6;7 8}, {9 0; 1 2}; {3 4; 5 6}, {7 8; 9 0}, {11 22; 33 44}};
B = [2,3]
[cellfun(#(x)x(2,2), A){1, B}]
ans =
8 2
EDIT:
The above actually only works in octave. As #Amro points out, to modify it to work in Matlab you need to use a temporary variable:
temp = cellfun(#(x)x(2,2), A);
[temp{1, B}]
or in a one liner (also thanks to #Amro)
cellfun(#(c)c{2,2}, A(1,B))
This answer is the same as #Dan's, but using a simple for loop for performance improvement, if needed.
% If you know that the numel of elements returned by {2,2} will always be one:
nElem = numel(B);
ret(1,nElem)=0;
for k=1:nElem
ret(k) = A{1,B(k)}{2,2}
end
The following answer is wrong, it will only return the {2,2} index from the first element from B
subsref([A{1,B}],struct('type','{}','subs',{{2,2}}))
Which sounds more like what you are doing (and doesn't uses cellfun and arrayfun, that would be better if you are doing this operation on a loop, because they are slow)
See subsref documentation here.
A longer path would be:
temp = [A{1,B}]
temp{2,2}
How about arrayfun(#(x) A{1,x}{2,2}, B)
or (thanks #Amro) cellfun(#(c)c{2,2}, A(1,B))?
I have a school project about running a SOR algorithm on Octave, but mine is very inefficient. So I have this snippet of code:
for ii=1:n
r = 1/A(ii,ii);
for jj=1:n
if (ii!=jj)
A(ii,jj) = A(ii,jj)*r;
end;
end;
b(ii,1) = b(ii,1)*r;
x(ii,1) = b(ii,1);
end;
How can I vectorize this? My first attempt was this:
for ii=1:n
r = 1/A(ii,ii);
A(find(eye(length(A))!=1)) = A(find(eye(length(A))!=1))*r;
b(ii,1) = b(ii,1)*r;
x(ii,1) = b(ii,1);
end;
But I'm not sure it helped much. Is there a better and/or more efficient way to do it?
Thanks!
You can totally avoid loops I believe. You have to see that you are taking as the reciprocal of diagonal elements of A, then why do you want to use a loop. Do it directly. That's the first step. Remove Inf and now you want to multiply r to corresponding non-diagonal elements of corresponding rows, right?
Therefore, use repmat to construct such a matrix which will have replicated elements along columns, since you are multiplying same r to (1,2), (1,3), ... , (1,n). But R has non-zero diagonal elements. Therefore make them zero. Now you will get your A except that the diagonal elements will be zero. Therefore, you just have to add them back from original A. That can be done by A=A.*R+A.*eye(size(A,1)).
Vectorization comes from experience and most importantly analyzing your code. Think at each step whether you want to use the loop, if not replace that step with the equivalent command, other code will follow (for example, I constructed a matrix R, whereas you were constructing individual elements r. So I only thought about converting r -> R and then the rest of the code will just fall into place).
The code is as follows:
R=1./(A.*eye(size(A,1))); %assuming matrix A is square and it does not contain 0 on the main diagonal
R=R(~isinf(R));
R=R(:);
R1=R;
R=repmat(R,[1,size(A,2)]);
R=R.*(true(size(A,1))-eye(size(A,1)));
A=A.*R+A.*eye(size(A,1)); %code verified till here since A comes out to be the same
b = b.*R1;
x=b;
I suppose there are matrices:
A (NxN)
b (Nx1)
The code:
d = diag(A);
A = diag(1 ./ d) * A + diag(d - 1);
b = b ./ d;
x = b;
Randomly bumped onto this problem and at first glance looked interesting given the fact that the problem was tagged as a vectorization problem.
I was able to come up with a bsxfun based vectorized solution that also uses diagonal indexing. This solution seems to give me a 3-4x speedup over the loop code with decent sized inputs.
Assuming that you are still somewhat interested in seeing speedup improvement on this problem, I would be eager to know the kind of speedups you would be getting with it. Here's the code -
diag_ind = 1:size(A,1)+1:numel(A);
diag_A = A(diag_ind(:));
A = bsxfun(#rdivide,A,diag_A);
A(diag_ind) = diag_A;
b(:,1) = b(:,1)./diag_A;
x(:,1) = b(:,1);
Let me know!
I need to circularly shift individual columns of a matrix.
This is easy if you want to shift all the columns by the same amount, however, in my case I need to shift them all by a different amount.
Currently I'm using a loop and if possible I'd like to remove the loop and use a faster, vector based, approach.
My current code
A = randi(2, 4, 2);
B = A;
for i = 1:size( A,2 );
d = randi( size( A,1 ));
B(:,i) = circshift( A(:,i), [d, 0] );
end
Is is possible to remove the loop from this code?
Update I tested all three methods and compared them to the loop described in this question. I timed how long it would take to execute a column by column circular shift on a 1000x1000 matrix 100 times. I repeated this test several times.
Results:
My loop took more than 12 seconds
Pursuit's suggestion less than a seconds
Zroth's orginal answer took just over 2 seconds
Ansari's suggest was slower than the original loop
Edit
Pursuit is right: Using a for-loop and appropriate indexing seems to be the way to go here. Here's one way of doing it:
[m, n] = size(A);
D = randi([0, m - 1], [1, n]);
B = zeros(m, n);
for i = (1 : n)
B(:, i) = [A((m - D(i) + 1 : m), i); A((1 : m - D(i) ), i)];
end
Original answer
I've looked for something similar before, but I never came across a good solution. A modification of one of the algorithms used here gives a slight performance boost in my tests:
[m, n] = size(A);
mtxLinearIndices ...
= bsxfun(#plus, ...
mod(bsxfun(#minus, (0 : m - 1)', D), m), ...
(1 : m : m * n));
C = A(idxs);
Ugly? Definitely. Like I said, it seems to be slightly faster (2--3 times faster for me); but both algorithms are clocking in at under a second for m = 3000 and n = 1000 (on a rather old computer, too).
It might be worth noting that, for me, both algorithms seem to outperform the algorithm provided by Ansari, though his answer is certainly more straightforward. (Ansari's algorithm's output does not agree with the other two algorithms for me; but that could just be a discrepancy in how the shifts are being applied.) In general, arrayfun seems pretty slow when I've tried to use it. Cell arrays also seem slow to me. But my testing might be biased somehow.
Not sure how much faster this would be, but you could try this:
[nr, nc] = size(A);
B = arrayfun(#(i) circshift(A(:, i), randi(nr)), 1:nc, 'UniformOutput', false);
B = cell2mat(B);
You'll have to benchmark it, but using arrayfun may speed it up a little bit.
I suspect, your circular shifting, operations on the random integer matrix donot make it any more random since the numbers are uniformly distributed.
So I hope your question is using randi() for demonstration purposes only.