Get the iteration number inside a MATLAB for loop - matlab

Say I have a for loop in MATLAB:
scales = 5:5:95;
for scale = scales
do stuff
end
How can I get the iteration number inside a MATLAB for loop as concisely as possible?
In Python for example I would use:
for idx, item in enumerate(scales):
where idx is the iteration number.
I know that in MATLAB (like in any other language) I could create a count variable:
scales = 5:5:95;
scale_count = 0;
for scale = scales
scale_count = scale_count + 1;
do stuff
end
I could otherwise use find:
scales = 5:5:95;
for scale = scales
scale_count = find(scales == scale);
do stuff
end
But I'm curious to know whether there exists a more concise way to do it, e.g. like in the Python example.

Maybe you can use the following:
scales = 5:5:95;
for iter = 1:length(scales)
scale=scales(iter); % "iter" is the iteration number.
do stuff
end

Since for iterates over the columns of whatever you give it, another way of approximating multiple loop variables would be to use an appropriately constructed matrix:
for scale=[5:5:95; 1:19]
% do stuff with scale(1) or scale(2) as appropriate
end
(my personal preference is to loop over the indices as per Parag's answer and just refer to data(index) directly within the loop, without an intermediate. Matlab's syntax isn't very concise at the best of times - you just get used to it)

The MATLAB way is probably doing it with vectors.
For example suppose you want to find in a vector if there is a value that is equal to its position. You would generally do this:
a = [10 20 1 3 5];
found = 0;
for index = 1:length(a)
if a(index) == index
found = 1;
break;
end
end
Instead you can do:
found = any(a == 1:length(a));
In general
for i=1:length(a)
dostuff(a(i), i);
end
can be replaced with:
dostuff(a(i), 1:length(a))
it dostuff can be vectorized or
arrayfun(#dostuff, a, 1:length(a))
otherwise.

Related

A conditional statement during a for loop in MATLAB

This is my attempt of a simple example (it seems pointless) but the idea is bigger than this simple code.
During a for loop, if something happens, I want to skip this step of the for loop and then add an extra step on the end.
I am trying to create a list of numbers, that do not include the number 8.
If the code creates an 8, this will mean that exitflag is equal to 1.
Can I adapt this program so that if the exitflag=1, the it will remove that result and add another loop.
The code:
for i = 1:1000
j = 1+round(rand*10)
if j == 8
exitflag = 1
else
exitflag = 0
end
storeexit(i)=exitflag;
storej(i)=j;
end
sum(storeexit)
I would ideally like a list of numbers, 1000 long which does not contain an 8.
If what you want to do is 1000 iterations of the loop, but repeat a loop iteration if you don't like its result, instead of tagging the repeat at the end, what you can do is loop inside the for loop until you do like the result of that iteration:
stores = zeros(1000,1); % Note that it is important to preallocate arrays, even in toy examples :)
for i = 1:1000
success = false; % MATLAB has no do..while loop, this is slightly more awkward....
while ~success
j = 1+round(rand*10);
success = j ~= 8;
end
storej(i) = j; % j guaranteed to not be 8
end
No.
With for loop, the number of loops is determined on the loop start and it is not dynamic.
For doing what you want, you need to use a while loop.

How can I avoid this for-loop in spite of every element having to be checked individually?

Using Matlab R2019a, is there any way to avoid the for-loop in the following code in spite of the dimensions containing different element so that each element has to be checked? M is a vector with indices, and Inpts.payout is a 5D array with numerical data.
for m = 1:length(M)-1
for power = 1:noScenarios
for production = 1:noScenarios
for inflation = 1:noScenarios
for interest = 1:noScenarios
if Inpts.payout(M(m),power,production,inflation,interest)<0
Inpts.payout(M(m+1),power,production,inflation,interest)=...
Inpts.payout(M(m+1),power,production,inflation,interest)...
+Inpts.payout(M(m),power,production,inflation,interest);
Inpts.payout(M(m),power,production,inflation,interest)=0;
end
end
end
end
end
end
It is quite simple to remove the inner 4 loops. This will be more efficient unless you have a huge matrix Inpts.payout, as a new indexing matrix must be generated.
The following code extracts the two relevant 'planes' from the input data, does the logic on them, then writes them back:
for m = 1:length(M)-1
payout_m = Inpts.payout(M(m),:,:,:,:);
payout_m1 = Inpts.payout(M(m+1),:,:,:,:);
indx = payout_m < 0;
payout_m1(indx) = payout_m1(indx) + payout_m(indx);
payout_m(indx) = 0;
Inpts.payout(M(m),:,:,:,:) = payout_m;
Inpts.payout(M(m+1),:,:,:,:) = payout_m1;
end
It is possible to avoid extracting the 'planes' and writing them back by working directly with the input data matrix. However, this yields more complex code.
However, we can easily avoid some indexing operations this way:
payout_m = Inpts.payout(M(1),:,:,:,:);
for m = 1:length(M)-1
payout_m1 = Inpts.payout(M(m+1),:,:,:,:);
indx = payout_m < 0;
payout_m1(indx) = payout_m1(indx) + payout_m(indx);
payout_m(indx) = 0;
Inpts.payout(M(m),:,:,:,:) = payout_m;
payout_m = payout_m1;
end
Inpts.payout(M(m+1),:,:,:,:) = payout_m1;
It seems like there is not a way to avoid this. I am assuming that each for lop independently changes a variable parameter used in the main calculation. Thus, it is required to have this many for loops. My only suggestion is to turn your nested loops into a function if you're concerned about appearance. Not sure if this will help run-time.

Declaring a vector in matlab whose size we don't know

Suppose we are running an infinite for loop in MATLAB, and we want to store the iterative values in a vector. How can we declare the vector without knowing the size of it?
z=??
for i=1:inf
z(i,1)=i;
if(condition)%%condition is met then break out of the loop
break;
end;
end;
Please note first that this is bad practise, and you should preallocate where possible.
That being said, using the end keyword is the best option for extending arrays by a single element:
z = [];
for ii = 1:x
z(end+1, 1) = ii; % Index to the (end+1)th position, extending the array
end
You can also concatenate results from previous iterations, this tends to be slower since you have the assignment variable on both sides of the equals operator
z = [];
for ii = 1:x
z = [z; ii];
end
Sadar commented that directly indexing out of bounds (as other answers are suggesting) is depreciated by MathWorks, I'm not sure on a source for this.
If your condition computation is separate from the output computation, you could get the required size first
k = 0;
while ~condition
condition = true; % evaluate the condition here
k = k + 1;
end
z = zeros( k, 1 ); % now we can pre-allocate
for ii = 1:k
z(ii) = ii; % assign values
end
Depending on your use case you might not know the actual number of iterations and therefore vector elements, but you might know the maximum possible number of iterations. As said before, resizing a vector in each loop iteration could be a real performance bottleneck, you might consider something like this:
maxNumIterations = 12345;
myVector = zeros(maxNumIterations, 1);
for n = 1:maxNumIterations
myVector(n) = someFunctionReturningTheDesiredValue(n);
if(condition)
vecLength = n;
break;
end
end
% Resize the vector to the length that has actually been filled
myVector = myVector(1:vecLength);
By the way, I'd give you the advice to NOT getting used to use i as an index in Matlab programs as this will mask the imaginary unit i. I ran into some nasty bugs in complex calculations inside loops by doing so, so I would advise to just take n or any other letter of your choice as your go-to loop index variable name even if you are not dealing with complex values in your functions ;)
You can just declare an empty matrix with
z = []
This will create a 0x0 matrix which will resize when you write data to it.
In your case it will grow to a vector ix1.
Keep in mind that this is much slower than initializing your vector beforehand with the zeros(dim,dim) function.
So if there is any way to figure out the max value of i you should initialize it withz = zeros(i,1)
cheers,
Simon
You can initialize z to be an empty array, it'll expand automatically during looping ...something like:
z = [];
for i = 1:Inf
z(i) = i;
if (condition)
break;
end
end
However this looks nasty (and throws a warning: Warning: FOR loop index is too large. Truncating to 9223372036854775807), I would do here a while (true) or the condition itself and increment manually.
z = [];
i = 0;
while !condition
i=i+1;
z[i]=i;
end
And/or if your example is really what you need at the end, replace the re-creation of the array with something like:
while !condition
i=i+1;
end
z = 1:i;
As mentioned in various times in this thread the resizing of an array is very processing intensive, and could take a lot of time.
If processing time is not an issue:
Then something like #Wolfie mentioned would be good enough. In each iteration the array length will be increased and that is that:
z = [];
for ii = 1:x
%z = [z; ii];
z(end+1) = ii % Best way
end
If processing time is an issue:
If the processing time is a large factor, and you want it to run as smooth as possible, then you need to preallocating.If you have a rough idea of the maximum number of iterations that will run then you can use #PluginPenguin's suggestion. But there could still be a change of hitting that preset limit, which will break (or severely slow down) the program.
My suggestion:
If your loop is running infinitely until you stop it, you could do occasional resizing. Essentially extending the size as you go, but only doing it once in a while. For example every 100 loops:
z = zeros(100,1);
for i=1:inf
z(i,1)=i;
fprintf("%d,\t%d\n",i,length(z)); % See it working
if i+1 >= length(z) %The array as run out of space
%z = [z; zeros(100,1)]; % Extend this array (note the semi-colon)
z((length(z)+100),1) = 0; % Seems twice as fast as the commented method
end
if(condition)%%condition is met then break out of the loop
break;
end;
end
This means that the loop can run forever, the array will increase with it, but only every once in a while. This means that the processing time hit will be minimal.
Edit:
As #Cris kindly mentioned MATLAB already does what I proposed internally. This makes two of my comments completely wrong. So the best will be to follow what #Wolfie and #Cris said with:
z(end+1) = i
Hope this helps!

regionprops returning single result

I do not understand the function 'regionprops' properly. For example if I create a binary matrix with three different areas, it only gives me a single centerpoint as output:
a = zeros(100,100);
a(1:49,1:49) = 1;
a(1:25,75:100) = 1;
a(51:100,51:100)= 1;
spy(a)
regionprops(a,'Centroid')
But if I add the line
a=bwmorph(a,'erode',0);
which does absolutely nothing, I get three different center points as output, one for each area. Why do they give different outputs and is it really necesarry to add a useless line of code?
The input to regionprops should be a logical array. If it's not, then it's assumed that the input is a labels matrix, as such it's processed as if all of the 1 values are part of the same object.
You can fix this by explicitly converting it to a logical matrix
regionprops(logical(a), 'Centroid') % or regionprops(a == 1, 'Centroid')
The better option may be to make a a logical to begin with by using false rather than zeros to construct a.
a = false(100, 100);
a(1:49,1:49) = 1;
a(1:25,75:100) = 1;
a(51:100,51:100)= 1;
The reason why the no-op erode causes it to work, is that the output of bwmorph is a logical matrix.

MATLAB: Check for while loop divergence

I have piece of code with a while loop. This loop will diverge in certain conditions and thus will yield a infinite loop.
I want to check whether the loop is diverging and break the loop in an elegant and efficient procedure.
A solution to this is to check every output of the loop, save it, and compare it to the previously calculated loop output.
This is the code:
ai = 0;
ai_old = 100;
iteration = 0;
CDeff = 0;
while abs(ai - ai_old)>2*10^-1 % Get induced angle of attack
iteration = iteration +1;
ai_old = ai;
Cleff = (Clp * cosd(ai)^2 + CDeff * sind(ai) )/cosd(ai);
Veff = Vp/cosd(ai);
Re_eff = Reinf * Veff/Vinf * cp/c;
Meff = Mp/cosd(ai);
if iteration ==1
AFdata(:,2) = AFdata(:,2)/cosd(SweepQC);
end
[~,a_eff,CDeff] = obj.ConstantVortex(AFdata,[],Cleff,Meff);
ai = -a_eff + (AOA + Twists(zz))/cosd(SweepQC);
end
Here, ai is calculated with the function obj.ConstantVortex and compared with the previous calculated ai. The while loop is terminated when the difference is small enough.
However, it can occur that the difference between the initial ai and calculated ai is increasing with every iteration.
How can I check this? and break the loop accordingly?
Thank you
A typical solution for this situation is to store more previous values, and monitor the behaviour over time. So instead of ai_old, you would use:
ai = 0;
num_prev = 5; % how many previous results to check
ai_prev = zeros(1,num_prev);
iteration = 0;
while abs(ai - ai_prev(end))>2*10^-1
iteration = iteration+1;
% your loop code goes here
% now update the array of previous values
ai_prev = circshift(ai_prev,[0 -1]);
ai_prev(end) = ai;
if iteration > num_prev && all(sign(diff(ai_prev)))
% the slope of the previous five results is positive, so exit
break
end
end
You can change the number of previous results and use whatever function is appropriate to check for a break condition on the data in ai_prev for your computation. For example you might want to do some averaging on the previous results, or use a different function than diff().
One solution is to keep last differences or min differences and compare the current difference with that. For example you can have variable ai_older and use it. You can add
ai_older = 1000;
before your while loop and then have
ai_older = ai_old;
ai_old = ai;
and change while condition to
while abs(ai - ai_old)>2*10^-1 && abs(ai - ai_old) < abs(ai_old - ai_older)
Now you can avoid divergence. However, I'm not completely aware of your problem and not sure using abs is required or not.
As I previously mentioned, depending to your problem you may want to keep minimum difference till now and compare current one against that.