MATLAB: Check for while loop divergence - matlab

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.

Related

How to jump to a particular place in the for loop if the if condition is satisfied?

I have some image files. I'm trying to perform some calculations using each file and if a certain condition is met, I'd like to go back to a particular line in the code and run it from there once more. But only just once again. Regardless of whether the if condition is satisfied or not satisfied the second time, I want to go to the next iteration. But, MATLAB doesn't seem to have a goto function and also, using goto implies bad programming so I thought I'd just iterate the for loop twice for a particular 'i' value which satisfies the if condition.
file = dir('*.jpg');
n = length(file);
for i = 1:n
*perform some operations on the 'i'th file*
if 'condition'
*run the for loop again for the 'i'th file instead of going to the 'i+1'th file*
i=i-1;
else
*go to next iteration*
end
end
I have tried to code this by changing the loop variable 'i' inside the loop to 'i-1' so that on the next iteration, the 'i'th loop will be repeated again but doing so is giving the wrong output, though I don't know if there is some other error in my code or if the changing of the loop variable internally is the cause of the problem. Any help on this is appreciated.
Replace the for loop with a while loop to have a bit more flexibility. The only difference is that you have to manually increment i, hence this also allows you to not increment i.
Given your new requirement, you can keep track of the number of attempts, and easily change this if needed:
file = dir('*.jpg');
n = length(file);
i = 1;
attempts = 1;
while i <= n
% perform code on i'th file
success = doSomething(); % set success true or false;
if success
% increment to go to next file
i = i + 1;
elseif ~success && attempts <= 2 % failed, but gave it only one try
% increment number of attempts, to prevent performing
attempts = attempts + 1;
else % failed, and max attempts reached, increment to go to next file
i = i + 1;
% reset number of attempts
attempts = 1;
end
end
Given the new requirement, added after rinkert's answer, the simplest approach becomes separating out code from your loop in a separate function:
function main_function
file = dir('*.jpg');
n = length(file);
for i = 1:n
some_operations(i);
if 'condition'
some_operations(i);
end
end
function some_operations(i)
% Here you can access file(i), since this function has access to the variables defined in main_function
*perform some operations on the 'i'th file*
end
end % This one is important, it makes some_operations part of main_function

How to restart a for loop iteration in MATLAB?

I am trying to force a for loop to restart if a condition is not stratified. I can do it with while since I want the loop to run for a certain number of iterations. I tried to set iter=iter-1 inside the if statement but it did not work. Any suggestions?
R=2*10^3;
lamda= 0.00001;
h=100;
a = 9.6117;
b = 0.1581;
for iter=1:10
M=poissrnd(lamda*R^2);
xx=R*rand(1,M);
yy=R*rand(1,M);
zz=ones(1,M)*h;
BS=[xx' yy' zz'];
user=[0,0, 0];
s=pdist2(BS(:,1:2),user(1,1:2));
anga=atand(h./s);
PL=1./(1+(a*exp(b*(a-anga))));
berRV=binornd(1,PL);
if berRV(1)==1
% do something
else
% repeat
end
end
You can accomplish this by using a while loop, with a comparison to whether the number of needed results have been identified. See the comment about saving your found values, as you hadn't specified what needs to be done when the condition you're searching for is satisfied.
R=2*10^3;
lamda= 0.00001;
h=100;
a = 9.6117;
b = 0.1581;
total_results_found = 0;
needed_results_found = 10;
while total_results_found < needed_results_found
M=poissrnd(lamda*R^2);
xx=R*rand(1,M);
yy=R*rand(1,M);
zz=ones(1,M)*h;
BS=[xx' yy' zz'];
user=[0,0, 0];
s=pdist2(BS(:,1:2),user(1,1:2));
anga=atand(h./s);
PL=1./(1+(a*exp(b*(a-anga))));
berRV=binornd(1,PL);
if berRV(1)==1
% save the result here
% iterate the counter
total_results_found = total_results_found + 1;
end
end
The simplest approach here would be with a while loop inside the for loop:
for iter=1:10
berRV(1) = 0
while berRV(1)~=1
% original loop code here
end
% do something
end
[Sadly, MATLAB does not have a do...while loop, it would make the above a little cleaner.]
While the other two solutions are perfectly valid, I wanted to give you another solution, which I think is the closest to the logic you provided in the question.
for iter=1:10
while 1
% loop code here
if berRV(1) == 1
break
end
end
end
The idea is similar to the one Cris presents, namely that you repeat the body of the for-loop until some condition is met. The difference lies solely in how you terminate the while loop,

MATLAB: Saving While loop data

I'm running a while loop and I am running in to some problems.
I have the following piece of code:
Angle_int = 0.5; % Initial interpolation angle of attack
Clmax2d(1,1:length(Yle_wing)) = 3; % Dummy value
diff = 0; % Dummy value
while sum(diff < 0) > fix(tol*length(Yle_wing))
Angle_int = Angle_int + 0.5; % Interpolation angle increases with 0.5 with every iteration
for j = 1:length(Yle_wing)
CL3d = interp1(Angle,[cl_matrix(1,j) cl_matrix(2,j) cl_matrix(3,j)],Angle_int,'linear');
CL_3DD(:,j) = CL3d;
end
diff = (Clmax2d - CL_3DD); % Difference between Cl2d and Cl3d
Angle_stall = Angle_int;
CL_3D = CL_3DD;
end
For some reason, CL_3D = CL_3DD; and Angle_stall = Angle_int; seem to disappear when the while loop finishes. Hence, I cannot use their converged values ahead of the while loop since these variables are not recognized. I get the following error:
Undefined function or variable "CL_3D".
Hence, does someone knows what I am doing wrong? or any tips would be welcome as well.
Thanks in advance,
cheers
The error message:
Undefined function or variable "CL_3D".
is always because you're trying to use a variable or function that you haven't initialized yet. Often this happens in loops where you want to increment a counter, or compare values etc.
A common error is doing something like this without writing ii = 0 in front of the loop:
while ii < some_num
ii = ii + 1;
some_function();
end
With your dummy variables, you never enter the loop (unless tol < 0, which seems like an odd choice). You probably want to initialize diff = Inf or something like that. However, using diff as a variable name is not a good idea, since it's a builtin function.
You probably try to use CL_3D, when it's not yet initialized (somewhere else in your code, not in the part you posted). MATLAB tells you which line the error appears in, you should try using it!
Maybe initializing it like zeros(size(Clmax2d)); could work (it will definitely remove your error, but it might not give the desired behavior).
PS!
Using i and j as variables are not recommended as they represent the imaginary unit in MATLAB.

Avoiding race conditions when using parfor in MATLAB

I'm looping in parallel and changing a variable if a condition is met. Super idiomatic code that I'm sure everyone has written a hundred times:
trials = 100;
greatest_so_far = 0;
best_result = 0;
for trial_i = 1:trials
[amount, result] = do_work();
if amount > greatest_so_far
greatest_so_far = amount;
best_result = result;
end
end
If I wanted to replace for by parfor, how can I ensure that there aren't race conditions when checking whether we should replace greatest_so_far? Is there a way to lock this variable outside of the check? Perhaps like:
trials = 100;
greatest_so_far = 0;
best_result = 0;
parfor trial_i = 1:trials
[amount, result] = do_work();
somehow_lock(greatest_so_far);
if amount > greatest_so_far
greatest_so_far = amount;
best_result = result;
end
somehow_unlock(greatest_so_far);
end
Skewed answer. It does not exactly solve your problem, but it might help you avoiding it.
If you can afford the memory to store the outputs of your do_work() in some vectors, then you could simply run your parfor on this function only, store the result, then do your scoring at the end (outside of the loop):
amount = zeros( trials , 1 ) ;
result = zeros( trials , 1 ) ;
parfor trial_i = 1:trials
[amount(i), result(i)] = do_work();
end
[ greatest_of_all , greatest_index ] = max(amount) ;
best_result = result(greatest_index) ;
Edit/comment : (wanted to put that in comment of your question but it was too long, sorry).
I am familiar with .net and understand completely your lock/unlock request. I myself tried many attempts to implement a kind of progress indicator for very long parfor loop ... to no avail.
If I understand Matlab classification of variable correctly, the mere fact that you assign greatest_so_far (in greatest_so_far=amount) make Matlab treat it as a temporary variable, which will be cleared and reinitialized at the beginning of every loop iteration (hence unusable for your purpose).
So an easy locked variable may not be a concept we can implement simply at the moment. Some convoluted class event or file writing/checking may do the trick but I am afraid the timing would suffer greatly. If each iteration takes a long time to execute, the overhead might be worth it, but if you use parfoor to accelerate a high number of short execution iterations, then the convoluted solutions would slow you down more than help ...
You can have a look at this stack exchange question, you may find something of interest for your case: Semaphores and locks in MATLAB
The solution from Hoki is the right way to solve the problem as stated. However, as you asked about race conditions and preventing them when loop iterations depend on each other you might want to investigate spmd and the various lab* functions.
You need to use SPMD to do this - SPMD allows communication between the workers. Something like this:
bestResult = -Inf;
bestIndex = NaN;
N = 97;
spmd
% we need to round up the loop range to ensure that each
% worker executes the same number of iterations
loopRange = numlabs * ceil(N / numlabs);
for idx = 1:numlabs:loopRange
if idx <= N
local_result = rand(); % obviously replace this with your actual function
else
local_result = -Inf;
end
% Work out which index has the best result - use a really simple approach
% by concatenating all the results this time from each worker
% allResultsThisTime will be 2-by-numlabs where the first row is all the
% the results this time, and the second row is all the values of idx from this time
allResultsThisTime = gcat([local_result; idx]);
% The best result this time - consider the first row
[bestResultThisTime, labOfBestResult] = max(allResultsThisTime(1, :));
if bestResultThisTime > bestResult
bestResult = bestResultThisTime;
bestIndex = allResultsThisTime(2, labOfBestResult);
end
end
end
disp(bestResult{1})
disp(bestIndex{1})

Get the iteration number inside a MATLAB for loop

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.