Avoid for-loop to increase performance - matlab

Is it possible to obtain the matrix A in a more efficient way than using a for loop?
a = 6; % constant
b = 2; % constant
s = 0.1; % possible to change
I = 12; % possible to change
A = zeros(a,I+1);
A(:,1) = rand(a,1); % some initial value
B = rand(b,I);
% possible to avoid for-loop to increase performance?
for i = 1:I
A(:,i+1) = fun(A(:,i),B(:,i), a, s);
end
The function fun is given as
function [AOut] = fun(AIn, B, a, s)
AOut = zeros(a,1);
AOut(1) = AIn(1) + AIn(4)*s*cos(AIn(3));
AOut(2) = AIn(2) + AIn(4)*s*sin(AIn(3));
AOut(3) = AIn(3) + AIn(4)*AIn(6)*s;
AOut(4) = AIn(4) + AIn(5)*s;
AOut(5) = AIn(5) + B(1);
AOut(6) = AIn(6) + B(2);
end

i dont think you can optimize the loop in regards to effiency as the last values are needed for calculating the next. #Tony Tannous showed you a nice way how to get rid of the loop in your code. For better performance at high values of I you can change fun() to:
function [AOut] = fun2(AIn, B, a, s)
AOut=AIn+ [AIn(4)*s*cos(AIn(3)) ;...
AIn(4)*s*sin(AIn(3)) ;...
AIn(4)*AIn(6)*s ;...
AIn(5)*s ; ...
B(1) ; ...
B(2)];
end

First of all, if a != n
You will get an error:
Subscripted assignment dimension mismatch.
So you must be cautious
And, to get rid of the loop you can do this:
EDIT:
Apparently i isn't getting modified on the right side. I'll try to fix it and reedit my answer.
anyway, to get rid of the loop you can still use A(:, i:1:I)
i = 1;
A(:, i:1:I) = fun(A(:,i),B(:,i), a, s);
If you have any further question, please ask!

Related

Indexing a vector within a bigger one in MATLAB

I'm trying to find the index position of the smaller vector inside a bigger one.
I've already solved this problem using strfind and bind2dec,
but I don't want to use strfind, I don't want to convert to string or to deciamls at all.
Given the longer vector
a=[1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];
I want to find the index of the smaller vector b inside a
b=[1,1,1,0,0,0];
I would expect to find as result:
result=[15,16,17,18,19,20];
Thank you
Here is as solution using 1D convolution. It may find multiple matches so start holds beginning indices of sub-vectors:
f = flip(b);
idx = conv(a,f,'same')==sum(b) & conv(~a,~f,'same')==sum(~b);
start = find(idx)-ceil(length(b)/2)+1;
result = start(1):start(1)+length(b)-1;
Solution with for loops:
a=[1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];
b=[1,1,1,0,0,0];
c = [];
b_len = length(b)
maxind0 = length(a) - b_len + 1 %no need to search higher indexes
for i=1:maxind0
found = 0;
for j=1:b_len
if a(i+j-1) == b(j)
found = found + 1;
else
break;
end
end
if found == b_len % if sequence is found fill c with indexes
for j=1:b_len
c(j)= i+j-1;
end
break
end
end
c %display c
Does it need to be computationally efficient?
A not very efficient but short solution would be this:
a=[1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];;
b=[1,1,1,0,0,0];
where = find(arrayfun(#(n) all(a(n+1:n+length(b))==b),0:length(a)-length(b)));
... gives you 15. Your result would be the vector where:where+length(b)-1.
edit: I tried it and I stand corrected. Here is a version with loops:
function where = find_sequence(a,b)
na = 0;
where = [];
while na < length(a)-length(b)
c = false;
for nb = 1:length(b)
if a(na+nb)~=b(nb)
na = na + 1; % + nb
c = true;
break
end
end
if ~c
where = [where,na+1];
na = na + 1;
end
end
Despite its loops and their bad reputation in Matlab, it's a lot faster:
a = round(rand(1e6,1));
b = round(rand(10,1));
tic;where1 = find(arrayfun(#(n) all(a(n+1:n+length(b))==b),0:length(a)-length(b)));toc;
tic;where2 = find_sequence(a,b);toc;
>> test_find_sequence
Elapsed time is 4.419223 seconds.
Elapsed time is 0.042969 seconds.
A neater method using for would look like this, there is no need for an inner checking loop as we can vectorize that with all...
a = [1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1];
b = [1,1,1,0,0,0];
idx = NaN( size(b) ); % Output NaNs if not found
nb = numel( b ); % Store this for re-use
for ii = 1:numel(a)-nb+1
if all( a(ii:ii+nb-1) == b )
% If matched, update the index and exit the loop
idx = ii:ii+nb-1;
break
end
end
Output:
idx = [15,16,17,18,19,20]
Note, I find this a bit easier to read that some of the nested solutions, but it's not necessarily faster, since the comparison is done on all elements in b each time.

Trying to get a MATLAB function to take an array of inputs

I'm trying to call a numerical integration function (namely one that uses the trapazoidal method) to compute a definite integral. However, I want to pass more than one value of 'n' to the following function,
function I = traprule(f, a, b, n)
if ~isa(f, 'function_handle')
error('Your first argument was not a function handle')
end
h = (b-a)./ n;
x = a:h:b;
S = 0;
for j = 2:n
S = S + f(x(j));
end
I = (h/2)*(f(a) + 2*S + f(b)); %computes indefinite integral
end
I'm using; f = #(x) 1/x, a = 1 and b = 2. I'm trying to pass n = 10.^(1:10) too, however, I get the following output for I when I do so,
I =
Columns 1 through 3
0.693771403175428 0.069377140317543 0.006937714031754
Columns 4 through 6
0.000693771403175 0.000069377140318 0.000006937714032
Columns 7 through 9
0.000000693771403 0.000000069377140 0.000000006937714
Column 10
0.000000000693771
Any ideas on how to get the function to take n = 10.^(1:10) so I get an output something like,
I = 0.693771403175428, 0.693153430481824, 0.693147243059937 ... and so on for increasing powers of 10?
In the script where you are calling this from, simply iterate over n
k = 3;
f = #(x)1./x;
a = 1; b = 2;
I = zeros(k,1);
for n = 1:k
I(n) = traprule(f, a, b, 10^n);
end
% output: I = 0.693771403175428
% 0.693153430481824
% 0.693147243059937
Then I will contain all of the outputs. Alternatively you can adapt your function to use the same logic to loop over the elements of n if it is passed
as a vector.
Note, you can improve the efficiency of your traprule code by removing the for loop:
% This loop operates on every element of x individually, and is inefficient
S = 0;
for j = 2:n
S = S + f(x(j));
end
% If you ensure you use element-wise equations like f=#(x)1./x instead of f=#(x)1/x
% Then you can use this alternative:
S = sum(f(x(2:n)));

how to sum a serie in MATLAB

I have a serie and I do not know how to sum the elements together in my for loop.
for j=1:50
E=a(j,1).*(x.^j)
(what should I do now)
end
Thanks in advance
Just for completeness I'll add the vectorized answer:
j = 1:50
E=sum(A.*(x.^j)) %//Assuming you have an n-by-1 vector of coefficients call A and x is a constant
This way you won't need a loop at all and is generally the preferred Matlab method. You should revisit this once you've understood the basics of Matlab .
You would have to:
1) store each element separately and then add them together, so that you don't overwrite their values as the loop goes on.
Here is a very simple example:
clear
clc
a = rand(50,1); % generate dummy values for the coefficients;
n = 50;
x = 3; % dummy x value
MySum = zeros(1,n);
for Counter = 1:n
CurrentValue = a(Counter,1)*(x^Counter); % Calculate the current value
MySum(Counter) = CurrentValue; % Store in an array
end
TotalSum = sum(MySum) ;% Once the loop is complete, sum all the values together.
This not the most efficient way. However it would allow you to access every individual sum calculated for each iteration, which could be somehow useful.
2) Alternatively, you could simply add each "Current Value" to the previous sum calculated, and then the final sum would be the last sum calculated in the loop.:
MySum = zeros(1,n);
CurrentSum = 0; % Initialize CurrentSum.
for Counter = 1:n
CurrentValue = a(Counter,1)*(x^Counter)
CurrentSum = CurrentSum + CurrentValue
end
TotalSum = CurrentSum
So basically your problem goes down to this:
E = E + a(j,1).*(x.^j)
That was a pretty long answer for a simple question sorry! Hope the principles of indexing and for loops is clearer for you now :)
E = 0;
for j=1:50
E= E +a(j,1).*(x.^j);
end

Extremely strange for loop behavior in Matlab

When attempting to write a for loop to increment from 1 to the end of an array, Matlab throws an error saying that I attempted to access the 0th array element, which doesn't exist. Here's the snippet in question:
function [adjMatNew] = delete(obj, adjMat)
[~, n] = size(adjMat);
adjMatNew = adjMat;
for i = 1:n
if adjMat(obj.number, i) ~= 0
adjList(i) = i
end
end
for j = 1:numel(adjList)
for k = 1:numel(adjList)
if j ~= k
adjMatNew(adjList(k), adjList(j)) = 1;
end
end
end
adjMatNew(obj.number, :) = 0;
adjMatNew(:, obj.number) = 0;
end
I can't think of any possible reason why, in the for loops above, the loop would start at 0. Changing the beginning increment variable from 1 to 2 fixed the issue and the function worked as intended, but it doesn't seem like the right fix. Does anybody have an explanation for this?
Without any help or comments, it's not very clear what you're actually attempting to do; however, in the line
adjMatNew(adjList(k), adjList(j)) = 1;
you implicitly assume that adjList is never zero. Since that is apparently the case, you get the error.
As suggested in other comments/answers, the issue is where you construct adjList.
for i = 1:n
if adjMat(obj.number, i) ~= 0
adjList(i) = i
end
end
Let's consider what happens in three different situations where n = 3:
1) If all of adjMat(obj.number, 1:3) are ~=0, each loop
adjList(1) = 1;
adjList(2) = 2;
adjList(3) = 3;
output: adjList = [1,2,3];
This case shouldn't throw an error.
2) If adjMat(obj.number, 3) is zero:
adjList(1) = 1;
adjList(2) = 2;
% adjList(3) not set
output: adjList = [1,2];
No error here, either.
3) If adjMat(obj.number, 2) is zero:
adjList(1) = 1;
% adjList(2) not set
adjList(3) = 3;
Since adjList(2) is not set, when calling adjList(3) = 3;, MATLAB needs there to be some value for adjList(2). By default, this is handled by filling with zeros to make the matrix the right size:
output: adjList = [1 0 3];
This causes the error.
As suggested in the comments by Notlikethat, adjList = find(adjMat(obj.number,:)) would be good way of replacing this loop. In this case, it only returns the positions of the non zero elements, so in the third case above, the output would be adjList = [1 3].

optimizing nested for loop in matlab

I'm trying to optimize the performance (e.g. speed) of my code. I 'm new to vectorization and tried myself to vectorize, but unsucessful ( also try bxsfun, parfor, some kind of vectorization, etc ). Can anyone help me optimize this code, and a short description of how to do this?
% for simplify, create dummy data
Z = rand(250,1)
z1 = rand(100,100)
z2 = rand(100,100)
%update missing param on the last updated, thanks #Bas Swinckels and #Daniel R
j = 2;
n = length(Z);
h = 0.4;
tic
[K1, K2] = size(z1);
result = zeros(K1,K2);
for l = 1 : K1
for m = 1: K2
result(l,m) = sum(K_h(h, z1(l,m), Z(j+1:n)).*K_h(h, z2(l,m), Z(1:n-j)));
end
end
result = result ./ (n-j);
toc
The K_h.m function is the boundary kernel and defined as (x is scalar and y can be vector)
function res = K_h(h, x,y)
res = 0;
if ( x >= 0 & x < h)
denominator = integral(#kernelFunc,-x./h,1);
res = 1./h.*kernelFunc((x-y)/h)/denominator;
elseif (x>=h & x <= 1-h)
res = 1./h*kernelFunc((x-y)/h);
elseif (x > 1 - h & x <= 1)
denominator = integral(#kernelFunc,-1,(1-x)./h);
res = 1./h.*kernelFunc((x-y)/h)/denominator;
else
fprintf('x is out of [0,1]');
return;
end
end
It takes a long time to obtain the results: \Elapsed time is 13.616413 seconds.
Thank you. Any comments are welcome.
P/S: Sorry for my lack of English
Some observations: it seems that Z(j+1:n)) and Z(1:n-j) are constant inside the loop, so do the indexing operation before the loop. Next, it seems that the loop is really simple, every result(l, m) depends on z1(l, m) and z2(l, m). This is an ideal case for the use of arrayfun. A solution might look something like this (untested):
tic
% do constant stuff outside of the loop
Zhigh = Z(j+1:n);
Zlow = Z(1:n-j);
result = arrayfun(#(zz1, zz2) sum(K_h(h, zz1, Zhigh).*K_h(h, zz2, Zlow)), z1, z2)
result = result ./ (n-j);
toc
I am not sure if this will be a lot faster, since I guess the running time will not be dominated by the for-loops, but by all the work done inside the K_h function.