Ridiculously simple question, but I'd like to get it right: Working in MATLAB, I'm trying to take an NxN matrix and copy it N times to fill an NxNxN matrix. My code executes, but the variable "threeD" is left unchanged after the loop finishes. Also, I'm imagining a loop is not the best way to do this, although I have nothing against it in principle. Thanks in advance!
reps = 64;
gradient = (1:reps);
pattern = repmat(gradient,reps,1);
threeD = zeros(reps,reps,reps);
for c = reps
threeD(:,:,c) = pattern;
end
Method 1: Using Loops
The for-loop needed to loop from 1 to reps which is indicated by 1:reps.
reps = 64;
gradient = (1:reps);
pattern = repmat(gradient,reps,1);
threeD = zeros(reps,reps,reps);
for Layer = 1: reps
threeD(:,:,Layer) = pattern;
end
Method 2: Using Repmat to Replicate Along the Third Dimension
The second argument in repmat(), the array [1 1 reps] indicates how mnay times to replicate the array along the [row column layer]/[x y z] dimensions.
reps = 64;
gradient = (1:reps);
pattern = repmat(gradient,reps,1);
threeD = repmat(pattern,[1 1 reps]);
Using MATLAB version: R2019b
Related
This question already has answers here:
'for' loop vs vectorization in MATLAB
(5 answers)
Closed 3 years ago.
In Matlab, I am trying to vectorise my code to improve the simulation time. However, the result I got was that I deteriorated the overall efficiency.
To understand the phenomenon I created 3 distinct functions that does the same thing but with different approach :
The main file :
clc,
clear,
n = 10000;
Value = cumsum(ones(1,n));
NbLoop = 10000;
time01 = zeros(1,NbLoop);
time02 = zeros(1,NbLoop);
time03 = zeros(1,NbLoop);
for test = 1 : NbLoop
tic
vector1 = function01(n,Value);
time01(test) = toc ;
tic
vector2 = function02(n,Value);
time02(test) = toc ;
tic
vector3 = function03(n,Value);
time03(test) = toc ;
end
figure(1)
hold on
plot( time01, 'b')
plot( time02, 'g')
plot( time03, 'r')
The function 01:
function vector = function01(n,Value)
vector = zeros( 2*n,1);
for k = 1:n
vector(2*k -1) = Value(k);
vector(2*k) = Value(k);
end
end
The function 02:
function vector = function02(n,Value)
vector = zeros( 2*n,1);
vector(1:2:2*n) = Value;
vector(2:2:2*n) = Value;
end
The function 03:
function vector = function03(n,Value)
MatrixTmp = transpose([Value(:), Value(:)]);
vector = MatrixTmp (:);
end
The blue plot correspond to the for - loop.
n = 100:
n = 10000:
When I run the code with n = 100, the more efficient solution is the first function with the for loop.
When n = 10000 The first function become the less efficient.
Do you have a way to know how and when to properly replace a for-loop by a vectorised counterpart?
What is the impact of index searching with array of tremendous dimensions ?
Does Matlab compute in a different manner an array of dimensions 3 or higher than a array of dimension 1 or 2?
Is there a clever way to replace a while loop that use the result of an iteration for the next iteration?
Using MATLAB Online I see something different:
n 10000 100
function01 5.6248e-05 2.2246e-06
function02 1.7748e-05 1.9491e-06
function03 2.7748e-05 1.2278e-06
function04 1.1056e-05 7.3390e-07 (my version, see below)
Thus, the loop version is always slowest. Method #2 is faster for very large matrices, Method #3 is faster for very small matrices.
The reason is that method #3 makes 2 copies of the data (transpose or a matrix incurs a copy), and that is bad if there's a lot of data. Method #2 uses indexing, which is expensive, but not as expensive as copying lots of data twice.
I would suggest this function instead (Method #4), which transposes only vectors (which is essentially free). It is a simple modification of your Method #3:
function vector = function04(n,Value)
vector = [Value(:).'; Value(:).'];
vector = vector(:);
end
Do you have a way to know how and when to properly replace a for-loop by a vectorised counterpart?
In general, vectorized code is always faster if there are no large intermediate matrices. For small data you can vectorize more aggressively, for large data sometimes loops are more efficient because of the reduced memory pressure. It depends on what is needed for vectorization.
What is the impact of index searching with array of tremendous dimensions?
This refers to operations such as d = data(data==0). Much like everything else, this is efficient for small data and less so for large data, because data==0 is an intermediate array of the same size as data.
Does Matlab compute in a different manner an array of dimensions 3 or higher than a array of dimension 1 or 2?
No, not in general. Functions such as sum are implemented in a dimensionality-independent waycitation needed.
Is there a clever way to replace a while loop that use the result of an iteration for the next iteration?
It depends very much on what the operations are. Functions such as cumsum can often be used to vectorize this type of code, but not always.
This is my timing code, I hope it shows how to properly use timeit:
%n = 10000;
n = 100;
Value = cumsum(ones(1,n));
vector1 = function01(n,Value);
vector2 = function02(n,Value);
vector3 = function03(n,Value);
vector4 = function04(n,Value);
assert(isequal(vector1,vector2))
assert(isequal(vector1,vector3))
assert(isequal(vector1,vector4))
timeit(#()function01(n,Value))
timeit(#()function02(n,Value))
timeit(#()function03(n,Value))
timeit(#()function04(n,Value))
function vector = function01(n,Value)
vector = zeros(2*n,1);
for k = 1:n
vector(2*k-1) = Value(k);
vector(2*k) = Value(k);
end
end
function vector = function02(n,Value)
vector = zeros(2*n,1);
vector(1:2:2*n) = Value;
vector(2:2:2*n) = Value;
end
function vector = function03(n,Value)
MatrixTmp = transpose([Value(:), Value(:)]);
vector = MatrixTmp(:);
end
function vector = function04(n,Value)
vector = [Value(:).'; Value(:).'];
vector = vector(:);
end
I have two nested for-loops that are used to format data that I load it. The loops have the following construction:
data = magic(20000);
data = data(:,1:3);
for i=0:10
for j=0:10
data_tmp = data((1:100)+100*j+100*10*i,:);
vx(:, i+1,j+1) = data_tmp(:,1);
vy(:, i+1,j+1) = data_tmp(:,2);
vz(:, i+1,j+1) = data_tmp(:,3);
end
end
Arrays vx, vy and vz I do pre-allocate to their desired size. However, is there a way to vectorize the for-loops to increase the efficiency? I'm not convinced it is the case due to the first line in the second loop, data((1:100)+100*j+100*10*i,:), is there a better way to do this?
It turns out that you have repeated index in loop
at i=k, j=10 and i=k+1, j=0 for k<10
for example, you read 1:100 + 100*10 + 100*10*0 and then read 1:100 + 100*0 + 100*10*1 which are identical.
Reshape w/ Repeated index
If this was what you intended to do, then vectorization needs one more step (index generation).
Following is my suggestion (N=100, M=10 where N is the length of data_tmp and M is the maximum loop variable)
index = bsxfun(#plus,bsxfun(#plus,(1:N)',reshape(N*(0:M),1,1,M+1)),M*N*(0:M)); %index generation
vx = data(index);
vy = data(index + size(data,1));
vz = data(index + size(data,1)*2);
This is not that desirable, but it will work.
When I tested on my laptop, it is twice faster than your original code with pre-allocation. As I increase the size of data, the gap gets smaller and smaller.
Reshape w/o Repeated index
If not i.e., you want to reshape each column in the direction of 3rd dimension first, 2nd dimension last), then following would work.
Firstly, this is how I interpreted your code
data = magic(20000);
data = data(:,1:3);
N = 100; M = 10;
for i=0:(M-1)
for j=0:(M-1)
data_tmp = data((1:N)+M*j+N*M*i,:);
vx(:, i+1,j+1) = data_tmp(:,1);
vy(:, i+1,j+1) = data_tmp(:,2);
vz(:, i+1,j+1) = data_tmp(:,3);
end
end
Note that loop ended at (M-1).
Following is my suggestion.
vx = permute(reshape(dat(1:N*M*M,1), N, M, M),[1,3,2]);
vy = permute(reshape(dat(1:N*M*M,2), N, M, M),[1,3,2]);
vz = permute(reshape(dat(1:N*M*M,3), N, M, M),[1,3,2]);
In my laptop, it is 4 times faster than original code. As I increase the size, the gap approaches to 2.
Just in case you want to stick with the loop, here is a much faster way to do this:
data = randi(100,20000,3);
[vx,vy,vz] = deal(zeros(100,11,11));
[J,I] = ndgrid(1:11,1:11);
c = 1;
for k = 0:100:11000
vx(:,I(c),J(c)) = data((1:100)+k,1);
vy(:,I(c),J(c)) = data((1:100)+k,2);
vz(:,I(c),J(c)) = data((1:100)+k,3);
c = c+1;
end
My guess is that reshape from #Dohyun answer is what you looking for (and it's x10 faster than this, and x10000 faster than your code), but for next time you use loops, this may be useful.
And here is another option to do this without reshape, in a similar time to the reshape version:
[vx,vy,vz] = deal(zeros(100,10,11));
vx(:) = data(1:11000,1);
vy(:) = data(1:11000,2);
vz(:) = data(1:11000,3);
vx = permute(vx,[1 3 2]);
vy = permute(vy,[1 3 2]);
vz = permute(vz,[1 3 2]);
The idea is that you define the shape of [vx,vy,vz] while allocating them.
Here is the code
S = size(shape,3)
shape = 1 - shape;
for i = 2:S
SHAPE = prod(shape(:,:,1:i-1),3);
for c = 1:3
vision(:,:,c,i) = vision(:,:,c,i).*SHAPE;
end
end
output = sum(vision,4);
Maybe there is a way to vectorize it?
And by the way shape and SHAPE are arrays of zeros and ones so they may be somehow used as logicals.
Here's one more bsxfun solution -
S = size(shape,3);
shape = 1 - shape;
SHAPE = cumprod(shape(:,:,1:S-1),3);
vision(:,:,1:3,2:S) = bsxfun(#times,vision(:,:,1:3,2:S),permute(SHAPE,[1 2 4 3]));
output = sum(vision,4);
Tests
Since the code has vision(:,:,c,i) and the iterator c goes from c = 1:3, most likely the third dimension of vision might be 3. To verify that the proposed approach works either way, let's keep it as 5. Also, for proper benchmarking, let's have big numbers on other dimensions and let's have random numbers in them. For verification, at the end we would find the absolute maximum difference between the outputs from the proposed and original approaches.
Benchmarking and output verification code -
% Inputs
shape = rand(150,160,170);
vision = rand(150,160,5,170);
shape = 1 - shape;
S = size(shape,3);
%// Proposed solution :
disp('----------------------- With Proposed solution')
tic
V = vision; %// Make a copy for using with proposed solution
SHAPE = cumprod(shape(:,:,1:S-1),3);
V(:,:,1:3,2:S) = bsxfun(#times,V(:,:,1:3,2:S),permute(SHAPE,[1 2 4 3]));
out = sum(V,4);
toc
%// Original solution :
disp('----------------------- With Original solution')
tic
S = size(shape,3);
for i = 2:S
SHAPE = prod(shape(:,:,1:i-1),3);
for c = 1:3
vision(:,:,c,i) = vision(:,:,c,i).*SHAPE;
end
end
output = sum(vision,4);
toc
error_value = max(abs(output(:) - out(:)))
Command Output -
----------------------- With Proposed solution
Elapsed time is 0.802486 seconds.
----------------------- With Original solution
Elapsed time is 4.401897 seconds.
error_value =
0
This question is from chegg.com.
Given a vector a of N elements a_{n},n =1,2,...,N, the simple moving average of m sequential elements of this vector is defined as
mu(j) = mu(j-1) + (a(m+j-1)-a(j-1))/m for j = 2,3,...,(N-m+1)
where
mu(1) = sum(a(k))/m for k = 1,2,...,m
Write a script that computes these moving averages when a is given by a=5*(1+rand(N,1)), where rand generates uniformly distributed random numbers. Assume that N=100 and m=6. Plot the results using plot(j,mu(j)) for j=1,2,...,N-m+1.
My current code is below, but I'm not sure where to go from here or if it's even right.
close all
clear all
clc
N = 100;
m = 6;
a = 5*(1+rand(N,1));
mu = zeros(N-m+1,1);
mu(1) = sum(a(1:m));
for j=2
mu(j) = mu(j-1) + (a-a)/m
end
plot(1:N-m+1,mu)
I'll step you through the modifications.
Firstly, mu(1) was not fully defined. The equation given is slightly incorrect, but this is what it should be:
mu(1) = sum(a(1:m))/m;
then the for loop has to go from j=2 to j=N-m+1
for j=2:N-m+1
and at each step, mu(j) is given by this formula, the same as given in the question
mu(j) = mu(j-1) + (a(m+j-1)-a(j-1))/m
And that's all you need to change!
I'm looking for a way to speed up some simple two port matrix calculations. See the below code example for what I'm doing currently. In essence, I create a [Nx1] frequency vector first. I then loop through the frequency vector and create the [2x2] matrices H1 and H2 (all functions of f). A bit of simple matrix math including a matrix left division '\' later, and I got my result pb as a [Nx1] vector. The problem is the loop - it takes a long time to calculate and I'm looking for way to improve efficiency of the calculations. I tried assembling the problem using [2x2xN] transfer matrices, but the mtimes operation cannot handle 3-D multiplications.
Can anybody please give me an idea how I can approach such a calculation without the need for looping through f?
Many thanks: svenr
% calculate frequency and wave number vector
f = linspace(20,200,400);
w = 2.*pi.*f;
% calculation for each frequency w
for i=1:length(w)
H1(i,1) = {[1, rho*c*k(i)^2 / (crad*pi); 0,1]};
H2(i,1) = {[1, 1i.*w(i).*mp; 0, 1]};
HZin(i,1) = {H1{i,1}*H2{i,1}};
temp_mat = HZin{i,1}*[1; 0];
Zin(i,1) = temp_mat(1,1)/temp_mat(2,1);
temp_mat= H1{i,1}\[1; 1/Zin(i,1)];
pb(i,1) = temp_mat(1,1); Ub(i,:) = temp_mat(2,1);
end
Assuming that length(w) == length(k) returns true , rho , c, crad, mp are all scalars and in the last line is Ub(i,1) = temp_mat(2,1) instead of Ub(i,:) = temp_mat(2,1);
temp = repmat(eyes(2),[1 1 length(w)]);
temp1(1,2,:) = rho*c*(k.^2)/crad/pi;
temp2(1,2,:) = (1i.*w)*mp;
H1 = permute(num2cell(temp1,[1 2]),[3 2 1]);
H2 = permute(num2cell(temp2,[1 2]),[3 2 1]);
HZin = cellfun(#(a,b)(a*b),H1,H2,'UniformOutput',0);
temp_cell = cellfun(#(a,b)(a*b),H1,repmat({[1;0]},length(w),1),'UniformOutput',0);
Zin_cell = cellfun(#(a)(a(1,1)/a(2,1)),temp_cell,'UniformOutput',0);
Zin = cell2mat(Zin);
temp2_cell = cellfun(#(a)({[1;1/a]}),Zin_cell,'UniformOutput',0);
temp3_cell = cellfun(#(a,b)(pinv(a)*b),H1,temp2_cell);
temp4 = cell2mat(temp3_cell);
p(:,1) = temp4(1:2:end-1);
Ub(:,1) = temp4(2:2:end);