syntax error at "END LOOP;" part of WHILE loop in plpgsql - postgresql

I am getting a syntax error when trying to define a function in plpgsql that I cannot explain. For some reason, this code will always fail with syntax error at or nor "LOOP":
CREATE OR REPLACE FUNCTION ST_Dilate(
in_geom GEOMETRY,
scale_factor FLOAT,
tol FLOAT DEFAULT 0.001,
guess FLOAT DEFAULT 1,
safety INTEGER DEFAULT 1000
)
RETURNS GEOMETRY AS
$$
DECLARE
step FLOAT = guess/2;
current_area FLOAT = ST_Area(ST_Buffer(in_geom, buff));
desired_area FLOAT = ST_Area(in_geom)*scale_factor;
dev FLOAT = (current_area-desired_area)/desired_area;
old_dev FLOAT;
safety_counter INTEGER = 0;
BEGIN
WHILE ABS(dev) > tol LOOP
IF safety_counter > safety THEN /* Can't find suitable distance after many iterations, terminate the function to prevent extreme hangs. */
RAISE NOTICE 'Could not find suitable buffer distance when dilating geom % after % iterations, NULL geometry returned instead. Consider adjusting "guess" parameter value or initial step size.', geom, safety;
RETURN NULL;
END IF;
safety_counter = safety_counter + 1;
/* Save the old deviation to be compared later to the new one later, calculate the current area and the new deviation from the desired area. */
old_dev = dev;
current_area = ST_Area(ST_Buffer(in_geom, guess));
dev = (current_area - desired_area) / desired_area;
IF dev < 0 THEN /* Current area is smaller than desired area, increase the buffer distance by the step. */
guess = guess + step;
ELSE IF dev > 0 THEN /* Current area is larger than desired area, decrease the buffer distance by the step. */
guess = guess - step;
ELSE /* Technically shouldn't ever happen because then ABS(dev) is indeed lesser than tol but here just in case. */
EXIT;
END IF;
IF dev * old_dev < 0 THEN /* Negative value indicates difference of sign, which means we just overestimated the area after underestimating it or vice versa, need to half the step. */
step = step * 0.5;
END IF;
END LOOP; /* syntax error here */
RETURN ST_Buffer(in_geom, guess);
END
$$
LANGUAGE plpgsql;
I don't see the syntax error at all. What is more confusing is that if I comment out this block:
IF safety_counter > safety THEN /* Can't find suitable distance after many iterations, terminate the function to prevent extreme hangs. */
RAISE NOTICE 'Could not find suitable buffer distance when dilating geom % after % iterations, NULL geometry returned instead. Consider adjusting "guess" parameter value or initial step size.', geom, safety;
RETURN NULL;
END IF;
safety_counter = safety_counter + 1;
the code executes once successfully, but then immediately fails with the same error the second time around.
I'm completely confused about this. I have commented out every block one at a time as well as individual lines trying to find the problem and I cannot find a syntax error anywhere, and yet it keeps complaining.
I'm on postgresql 9.6.

Use ELSIF if you want to get a single IF statement:
IF dev < 0 THEN /* Current area is smaller than desired area, increase the buffer distance by the step. */
guess = guess + step;
ELSIF dev > 0 THEN /* Current area is larger than desired area, decrease the buffer distance by the step. */
guess = guess - step;
ELSE /* Technically shouldn't ever happen because then ABS(dev) is indeed lesser than tol but here just in case. */
EXIT;
END IF;
Read in the documentation about Control Structures.

You are misssing END IF before END LOOP. See it simplified and intended:
IF dev < 0 THEN
guess = guess + step;
ELSE
IF dev > 0 THEN
guess = guess - step;
ELSE
EXIT;
END IF;
END IF; -- this is missing

Related

I'm getting no output from this while loop I wrote

I've been trying to create a code that can do the bisection method to find roots of equations on MATLAB and so far I was not able to get it to run. Originally the if statement was outside of the while loop, but then I switched it out to see if it does anything. Instead, I've been getting no output from it in the command window. Any help and explanations for how this happens is much appreciated.
function [x_root] = bisection(func,x_low,x_high,accuracy)
x_root = (x_low+x_high)/2;
f_low=func(x_low);
f_high=func(x_high);
f_root=func(x_root);
if (f_low*f_high)<=0
x_root=(x_low+x_high)/2;
iterations = 0;
while abs(x_root) > accuracy
iterations = iterations + 1;
x_root = (x_low+x_high)/2;
if (f_low*f_root) < 0
x_high=x_root;
else
x_low=x_root;
end
end
else
error('There is no root')
end

Terminate recursion after reaching certain condition

I've been experimenting with a Sudoku solver using recursion. The problem I've encountered it that the recursive function after finding the right solution will not terminate, but goes on until every number is tested at every position.
How do I terminate such loop in Matlab? Error condition 'error('...') within the function can break the execution, but is by no means a good solution to my problem.
Here is a sample code for similar recursion, producing all possible 2 element vectors for numbers from 1 to 4. I would like it to stop when both numbers are equal to 2.
%possible moves at each position
moveMat = zeros([1,2])+3;
lineInput = zeros([1,2]);
%start recursion
recurNumbers(moveMat, 0, lineInput)
function recurNumbers(moveMat, position, lineVariable)
position = position + 1;
%if all numbers are equal to 2 then try to exit the function
if ~all(lineVariable == 2)
%if all numbers are not equal to 2, try other combination
if position < length(lineVariable)+1
for move = 0 : moveMat(position)
moveMat(position) = move;
lineVariable(position) = lineVariable(position) + 1;
recurNumbers(moveMat,position,lineVariable)
disp(lineVariable)
end
end
else
disp(lineVariable)
return
end
end
Now it will print the vector '[2 2]' twice, which indicates that it recognised the condition, but the 'return' will not do what I imagined it to do.
Although it is not very clear to me what you are trying to achieve, I assume that the following function meets your stopping criterion.
function exit_fl = recurNumbers(moveMat, position, lineVariable)
exit_fl = 0;
if (all(lineVariable == 2))
% Show the result and exit
disp(lineVariable)
exit_fl = 1;
else
position = position + 1;
%if all numbers are not equal to 2, try other combination
if position < length(lineVariable)+1
for move = 0 : moveMat(position)
moveMat(position) = move;
lineVariable(position) = lineVariable(position) + 1;
% Receive the exit status of your function
ex_fl = recurNumbers(moveMat,position,lineVariable);
if (ex_fl == 1)
% If the criterion was met, then stop
exit_fl = 1;
return
end
end
end
end
end

How to initialize a variable in a user defined MATLAB function(simulink)

I am trying to make a user defined function is simulink which looks something like this
function [minTR,maxTR,out] = Temperature(u)
[minTR] = min(u);
[maxTR] = max(u);
if((maxTR - minTR > 1.5))
out = 1;
else
out = 0;
end
end
Where u is a 1200x1 array for every sampling time , now the problem is I want is if this condition if((maxTR - minTR > 1.5)) holds true I want the out to remain 1 until the difference between maximum and minimum become less than lets say 0.7 , my plan was to set a flag which i would set in the condition to become 1 so my else code will look something like
else if((maxTR - minTR < 1.5) && flag == 0)
out = 0;
end
but the problem is initialization , I have to initialize this flag once but simulink run this whole function for every sampling time and the flag would be reinitialized again and again , does anyone know what can i do to achieve that ?
As percusse mentioned in the comments, you should look into already existing blocks (comparison + hysteresis) in the Simulink library.
In general, initialization of variables in Matlab/Simulink could be solved with persistent variables.
persistent flag
% If first run. Initialize flag.
if isempty(flag)
flag = false;
end
An alternative is to take in an output signal from previous sample period by using the delay block which can be initialized with a value.

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.

While Loop and Logical Operators

I want to use an OR statement in my while loop so that the loop terminates whenever either of the two conditions aren't satisfied.
The conditions: (1) i must be less or equal to nmax, and (2) absolute value of R(i,i)-R(i-1,i-1) is less than specified delta
Here's my code (everything seems to work fine except for the while condition as the function executes until nmax is reached every time):
function [R] = romberg(f,a,b,delta,nmax)
R=zeros(nmax,nmax);
R(1,1)=(f(a)+f(b))*(b-a)/2;
i=2;
while i<nmax || abs(R(i,i)-R(i-1,i-1))<delta
m=2^(i-1);
R(i,1)=trapez(f,a,b,m);
for j=2:i
R(i,j)=R(i,j-1)+(R(i,j-1)-R(i-1,j-1))/(4^j-1);
end
i=i+1;
end
R
Try this. There were a few issues with the abs(R(i,i)-R(i-1,i-1)) condition for the while loop.
function [R] = romberg(f,a,b,delta,nmax)
R=zeros(nmax,nmax);
R(1,1)=(f(a)+f(b))*(b-a)/2;
i=2;
E=2*delta;
while i<nmax && E>delta
m=2^(i-1);
R(i,1)=trapez(f,a,b,m);
for j=2:i
R(i,j)=R(i,j-1)+(R(i,j-1)-R(i-1,j-1))/(4^j-1);
end
E=abs(R(i,i)-R(i-1,i-1));
i=i+1;
end