I am trying to integrate all the 2x2 matrices A(i-1:1,j-1:j) in Matlab without using a loop. Right now I am doing in a loop but it is extremely slow. The code is shown below:
A=rand(100)
t=linespace(0,1,100);
for i=2:length(A)
for j=2:length(A)
A_minor=A(i-1:i,j-1:j);
B(i,j)=trapz(t(j-1:j),trapz(t(i-1:i),A_minor));
end
end
I'd like to do this without using loops to speed up computation.
If you have the Matlab Image Processing Toolbox, you may be able to use blockproc to do what you want.
http://www.mathworks.com/help/images/ref/blockproc.html
To use blockproc, you will need to define a function that does what you want to be executed on each position in the matrix. Note that the way you are using trapz makes things a little trickier (passing the x-values in - if you can get away without them, you can simplify the code) - here I run trapz without them and scale the results.
% Data
foo = rand(100);
t = linspace(0,1,100);
% Execute blockproc on the indexes
fooproc = blockproc(foo, [2, 2], #(x) trapz(trapz(x.data)));
fooproc = fooproc * (t(2)-t(1))^2; % re-scale by the square of the step size
If you need to pass the x values to trapz, the solution gets a bit trickier.
As trapz is a simple function (especially on a 2x2 matrix), you can just compute the result directly, without calling a function:
t = linspace(0,1,100); % Note that this is a step size of 0.010101
A = rand(100);
B = nan(size(A));
Atmp = (A(1:end-1,:) + A(2:end,:))/2;
Atmp = (Atmp(:,1:end-1) + Atmp(:,2:end))/2;
B(2:end,2:end) = Atmp * (t(2)-t(1))^2;
This should give you the exact same result as your for loop, but much faster.
Related
I have to construct the following function in MATLAB and am having trouble.
Consider the function s(t) defined for t in [0,4) by
{ sin(pi*t/2) , for t in [0,1)
s(t) = { -(t-2)^3 , for t in [1,3)*
{ sin(pi*t/2) , for t in [3,4)
(i) Generate a column vector s consisting of 512 uniform
samples of this function over the interval [0,4). (This
is best done by concatenating three vectors.)
I know it has to be something of the form.
N = 512;
s = sin(5*t/N).' ;
But I need s to be the piecewise function, can someone provide assistance with this?
If I understand correctly, you're trying to create 3 vectors which calculate the specific function outputs for all t, then take slices of each and concatenate them depending on the actual value of t. This is inefficient as you're initialising 3 times as many vectors as you actually want (memory), and also making 3 times as many calculations (CPU), most of which will just be thrown away. To top it off, it'll be a bit tricky to use concatenate if your t is ever not as you expect (i.e. monotonically increasing). It might be an unlikely situation, but better to be general.
Here are two alternatives, the first is imho the nice Matlab way, the second is the more conventional way (you might be more used to that if you're coming from C++ or something, I was for a long time).
function example()
t = linspace(0,4,513); % generate your time-trajectory
t = t(1:end-1); % exclude final value which is 4
tic
traj1 = myFunc(t);
toc
tic
traj2 = classicStyle(t);
toc
end
function trajectory = myFunc(t)
trajectory = zeros(size(t)); % since you know the size of your output, generate it at the beginning. More efficient than dynamically growing this.
% you could put an assert for t>0 and t<3, otherwise you could end up with 0s wherever t is outside your expected range
% find the indices for each piecewise segment you care about
idx1 = find(t<1);
idx2 = find(t>=1 & t<3);
idx3 = find(t>=3 & t<4);
% now calculate each entry apprioriately
trajectory(idx1) = sin(pi.*t(idx1)./2);
trajectory(idx2) = -(t(idx2)-2).^3;
trajectory(idx3) = sin(pi.*t(idx3)./2);
end
function trajectory = classicStyle(t)
trajectory = zeros(size(t));
% conventional way: loop over each t, and differentiate with if-else
% works, but a lot more code and ugly
for i=1:numel(t)
if t(i)<1
trajectory(i) = sin(pi*t(i)/2);
elseif t(i)>=1 & t(i)<3
trajectory(i) = -(t(i)-2)^3;
elseif t(i)>=3 & t(i)<4
trajectory(i) = sin(pi*t(i)/2);
else
error('t is beyond bounds!')
end
end
end
Note that when I tried it, the 'conventional way' is sometimes faster for the sampling size you're working on, although the first way (myFunc) is definitely faster as you scale up really a lot. In anycase I recommend the first approach, as it is much easier to read.
I need the "for" loop in the following representative section of code to run as efficiently as possible. The mean function in the code is acting as a representative placeholder for my own function.
x = linspace(-1,1,15);
y = linspace(2,4,15);
[xgrid, ygrid] = meshgrid(x,y);
mc = rand(100000,1);
z=zeros(size(xgrid));
for i=1:length(xgrid)
for j=1:length(ygrid)
z(i,j) = mean(xgrid(i,j) + ygrid(i,j) + xgrid(i,j)*ygrid(i,j)*mc);
end
end
I have vectorized the code and improved its speed by about 2.5 times by building a matrix in which mc is replicated for each grid point. My implementation results in a very large matrix (3 x 22500000) filled with repeated data. I've mitigated the memory penalty of this approach by converting the matrix to single precision, but it seems like there should be a more efficient way to do what I want that avoids replicating so much data.
You could use bsxfun with few reshapes -
A = bsxfun(#times,y,x.'); %//'
B = bsxfun(#plus,y,x.'); %//'
C = mean(bsxfun(#plus,bsxfun(#times,mc,reshape(A,1,[])) , reshape(B,1,[])),1);
z_out = reshape(C,numel(x),[]).';
I'm using a Matlab program that has a very long loop, inside this loop is the following code:
...
H = fspecial('gaussian', 6*sig(i), sig(i));
img_out = imfilter(img{i},H,'same');
...
Where 'sig' is a list of Gaussian widths, and 'img' is a cell array of images.
I need to make this code more efficient and perhaps those two points will allow for something more clever:
The filter is always Gaussian - just different sigma.
The image inside 'img{i}' is a grayscale sparse matrix.
I found a wonderful solution to the problem:
http://blog.ivank.net/fastest-gaussian-blur.html
There is a quick implementation in Matlab Help files:
intImage = integralImage(I);
avgH = integralKernel([1 1 7 7], 1/49);
J = integralFilter(intImage, avgH);
So 3 passes of that should approximate a Gaussian!
I tried to batch process images of same size and sigma, stacking them:
%problem generator
%real number is 10^6
num_of_images=10^4;
%i assume squared images
image_size=randi([60,100],num_of_images,1);
sig=randi([2,4],num_of_images,1);
img=cell(num_of_images,1);
ratio_nnz=.02;
for idx=1:num_of_images
ti=rand(image_size(idx))/ratio_nnz;
ti(ti>1)=0;
img{idx}=ti;
end
%existing approac
tic;
for idx=1:num_of_images
H = fspecial('gaussian', 6*sig(idx), sig(idx));
img_out = imfilter(img{idx},H,'same');
end
toc;
%idea: Match images of same sigma and
tic
%calculate all filters offline
[sig_unique,~,sig_index]=unique(sig);
H=cell(numel(sig_unique),1);
for idx=1:numel(sig_unique)
H{idx}= fspecial('gaussian', 6*sig_unique(idx), sig_unique(idx));
end
%find instances of same size and sigma
[x,y]=cellfun(#size,img);
[a,b,c]=unique([sig_index,x,y],'rows');
img_out=cell(size(img));
for didx=1:numel(b)
%img{c==didx} contains images of same sigma and size, process them at
%once
iH=H{a(didx,1)};
timg=cat(3,img{c==didx});
timg_out=imfilter(timg,iH,'same');
img_out(c==didx)=num2cell(timg_out,[1,2]);
end
toc
The result surprised me, actually calling imfilter with less but larger matrices was slower with the data I generated. Nevertheless try it with your data and or the faster gaussian filter you are planning to implement. It might be faster then.
I'm coding a solution for Poisson equation on a 2d rectangle using finite elements. In order to simplify the code I store handles to the basis functions in an array and then loop over these basis functions to create my matrix and right hand side. The problem with this is that even for very coarse grids it is prohibitively slow. For a 9x9 grid (using Dirichlet BC, there are 49 nodes to solve for) it takes around 20 seconds. Using the profile I've noticed that around half the time is spent accessing (not executing) my basis functions.
The profiler says matrix_assembly>#(x,y)bilinearBasisFunction(x,y,xc(k-1),xc(k),xc(k+1),yc(j-1),yc(j),yc(j+1)) (156800 calls, 11.558 sec), the self time (not executing the bilinear basis code) is over 9 seconds. Any ideas as to why this might be so slow?
Here's some of the code, I can post more if needed:
%% setting up the basis functions, storing them in cell array
basisFunctions = cell(nu, 1); %nu is #unknowns
i = 1;
for j = 2:length(yc) - 1
for k = 2:length(xc) - 1
basisFunctions{i} = #(x,y) bilinearBasisFunction(x,y, xc(k-1), xc(k),...
xc(k+1), yc(j-1), yc(j), yc(j+1)); %my code for bilinear basis functions
i = i+1;
end
end
%% Assemble matrices and RHS
M = zeros(nu,nu);
S = zeros(nu,nu);
F = zeros(nu, 1);
for iE = 1:ne
for iBF = 1:nu
[z1, dx1, dy1] = basisFunctions{iBF}(qx(iE), qy(iE));
F(iBF) = F(iBF) + z1*forcing_handle(qx(iE),qy(iE))/ae(iE);
for jBF = 1:nu
[z2, dx2, dy2] = basisFunctions{jBF}(qx(iE), qy(iE));
%M(iBF,jBF) = M(iBF,jBF) + z1*z2/ae(iE);
S(iBF,jBF) = S(iBF, jBF) + (dx1*dx2 + dy1*dy2)/ae(iE);
end
end
end
Try to change basisFunctions from being a cell array to being a regular array.
You can also try to inline the direct call to bilinearBasisFunctionwithin your jBF loop, rather than using basisFunctions. Creating and later using anonymous functions in Matlab is always slower than directly using the target function. The code may be slightly more verbose this way, but will be faster.
I am using interp1 to inteprolate some data:
temp = 4 + (30-4).*rand(365,10);
depth = 1:10;
dz = 0.5; %define new depth interval
bthD = min(depth):dz:max(depth); %new depth vector
for i = 1:length(temp);
i_temp(i,:) = interp1(depth,temp(i,:),bthD);
end
Here, I am increasing the resolution of my measurements by interpolating the measurements from 1 m increments to 0.5 m increments. This code works fine i.e. it gives me the matrix I was looking for. However, when I apply this to my actual data, it takes a long time to run, primarily as I am running an additional loop which runs through various cells. Is there a way of achieving what is described above without using the loop, in other words, is there a faster method?
Replace your for loop with:
i_temp = interp1(depth,temp',bthD)';
You can get rid of the transposes if you change the way that temp is defined, and if you are OK with i_temp being a 19x365 array instead of 365x19.
BTW, the documentation for interp1 is very clear that you can pass in an array as the second argument.