Flow Control Statements
Six forms of flow control
Execution statements such as assignment statements, SOLVE statements
or data management statements are normally executed in their order of
appearance in the body of a procedure. However, the presence of control
flow statements can redirect the flow of execution as the need arises.
AIMMS provides six forms of flow control:
the
IF-THEN-ELSEstatement for conditional execution,the
WHILEstatement for repetitive conditional execution,the
REPEATstatement for repetitive unconditional execution,the
FORstatement for repetitive domain-driven execution,the
SWITCHstatement for branching on set and integer values,the
HALTandRETURNstatement for terminating the current execution,the
SKIPandBREAKstatements for terminating the current repetitive execution, andthe
BLOCKstatement for visually grouping together multiple statements.
Syntax
flow-control-statement:
Flow control statements and special numbers
In the condition of flow control statements such as IF-THEN-ELSE,
WHILE and REPEAT it is needed to know whether the result is
equal to 0.0 or not in order to take the appropriate branch of
execution. The special number NA has the interpretation “not yet
available” thus it is also not yet known whether it is equal to 0.0 or
not. The special number UNDF is the result of an illegal operation,
so its value cannot be known. Therefor, AIMMS will issue an error
message if the result of a condition in these statements evaluates to
NA or UNDF. Special numbers and their interpretation as logical
values are discussed in full detail in Real Values and Arithmetic Extensions and
Logical Expressions.
The IF-THEN-ELSE Statement
The conditional IF-THEN-ELSE statement is used to choose between the
execution of several groups of statements depending on the outcome of
one or more logical conditions. The syntax of the IF-THEN-ELSE
statement is given in the following diagram.
Syntax
if-then-else-statement:
AIMMS will evaluate all logical conditions in succession and stops at
the first condition that is satisfied. The statements associated with
that particular branch are executed. If none of the conditions is
satisfied, the statements of the ELSE branch, if present, will be
executed.
Example
The following code illustrates the use of the IF-THEN-ELSE
statement.
if ( not SupplyDepot ) then
DialogMessage( "Select a supply depot before solving the model" );
elseif ( Exists[ p, Supply(SupplyDepot,p) < Sum( i, Demand(i,p) ) ] ) then
DialogMessage( "The selected supply depot has insufficient capacity" );
else
solve TransportModel ;
endif ;
Note that in this particular example the evaluation of the ELSEIF
condition only makes sense when a SupplyDepot exists. This is
automatically enforced because the IF condition is not satisfied.
Similarly, successful execution of the ELSE branch apparently
depends on the failure of both the IF and ELSEIF conditions.
The WHILE and REPEAT Statements
The WHILE and REPEAT statements group a series of execution
statements and execute them repeatedly. The execution of the repetitive
loop can be terminated by a logical condition that is part of the
WHILE statement, or by means of a BREAK statement from within
both the WHILE and REPEAT statements.
Syntax
while-statement:
repeat-statement:
Loop strings are discussed in Advanced Use of WHILE and REPEAT.
Termination by WHILE condition
The execution of a WHILE statement is subject to a logical condition
that is verified each time the statements in the loop are executed. If
the condition is false initially, the statements in the loop will never
be executed. In case the WHILE loop does not contain a BREAK,
HALT or RETURN statement, the statements inside the loop must in
some way influence the outcome of the logical condition for the loop to
terminate.
Termination by a BREAK statement
An alternative way to terminate a WHILE or REPEAT statement is
the use of a BREAK statement inside the loop. BREAK statements
make it possible to abort the execution at any position inside the loop.
This freedom allows you to formulate more natural termination conditions
than would otherwise be possible with just the logical condition in the
WHILE statement. After aborting the loop, AIMMS will continue with
the first statement following it.
Skipping the remainder of a loop
In addition to the BREAK statement, AIMMS also offers a SKIP
statement. With it you instruct AIMMS to skip the remaining statements
inside the current iteration of the loop, and immediately return to the
top of the WHILE or REPEAT statement to execute the next
iteration. The SKIP statement is an elegant alternative to placing
the statements inside the loop following the SKIP statement in a
conditional IF statement.
Syntax
skip-break-statement:
The WHEN clause
By adding a WHEN clause to either a BREAK or SKIP statement,
you make its execution conditional to a logical expression. In practice,
the execution of a BREAK or SKIP statement is almost always
subject to some condition.
Example WHILE statement
This example computes the machine epsilon, which is the smallest
number that, when added to 1.0, gives a value different from 1.0. It is
a measure of the accuracy of the floating point arithmetic, and it is
machine dependent. We assume that meps is a scalar parameter, and
that the numeric comparison tolerances are set to zero (see also
Numerical Comparison).
meps := 1.0;
while (1.0 + meps/2 > 1.0) do
meps /= 2;
endwhile;
Since the parameter meps is determined iteratively, and the loop
condition will eventually be satisfied, this example illustrates an
appropriate use of the WHILE loop.
Example REPEAT statement
By applying a BREAK statement, the machine epsilon can be computed
equivalently using the following REPEAT statement.
meps := 1.0;
repeat
break when (1.0 + meps/2 = 1.0) ;
meps /= 2;
endrepeat;
The BREAK statement could also have been formulated in an equivalent
but less elegant manner without a WHEN clause:
if (1.0 + meps/2 = 1.0) then
break;
endif;
Advanced Use of WHILE and REPEAT
Advanced uses
Next to the common use of the WHILE and REPEAT statements
described in the previous section, AIMMS offers some special constructs
that help you
keep track of the number executed iterations automatically, and
control nested arrangements of
WHILEandREPEATstatements.
Nonconvergent loops
There are practical examples in which the terminating condition of a repetitive statement may not be met at all or at least not within a reasonable amount of work or time. A good example of this behavior are solution algorithms for which convergence is likely but not guaranteed. In these cases, it is common practice to terminate the execution of the loop when the total number of iterations exceeds a certain limit.
In AIMMS, such conditions can be formulated easily without the need to
introduce an additional parameter,
add a statement to initialize it, and
increase the parameter every iteration of the loop.
Each repetitive statement keeps track of its iteration count
automatically and makes the number of times the loop is entered
available by means of the predefined operator LoopCount. Upon
entering a repetitive statement AIMMS will set its value to 1, and will
increase it by 1 at the end of every iteration.
Example
Whether the following sequence will converge depends on the initial
value of x. In the case where there is no convergence or if
convergence is too slow, the loop in the following example will
terminate after 100 iterations.
while ( Abs(x-OldValue) >= Tolerance and LoopCount <= 100 ) do
OldValue := x ;
x := x^2 - x ;
endwhile ;
Naming nested loops
So far, we have considered single loops. However, in practice it is quite common that repetitive statements appear in nested arrangements. To provide finer control over the flow of execution in such situations, AIMMS allows you to label a particular repetitive statement with a loop string.
Use of loop strings
Using a loop string in conjunction with the BREAK and SKIP
statements, it is possible to break out from several nested repetitive
statements with a single BREAK statement. The loop string argument
can also be supplied to the LoopCount operator so the break can be
conditional on the number of iterations of any loop. Without specifying
a loop string, BREAK, SKIP and LoopCount refer to the
current loop by default.
Example
The following example illustrates the use of loop strings and the
LoopCount operator in nested repetitive statements. It outlines an
algorithm in which the domain of definition of a particular problem is
extended in every loop based on the current solution, after which the
new problem is solved by means of a sequential solution process.
repeat "OuterLoop"
... ! Determine initial settings for sequential solution process
while( Abs( Solution - OldSolution ) <= Tolerance ) do
OldSolution := Solution ;
... ! Set up and solve next sequential step ...
! ... but terminate algorithm when convergence is too slow
break "OuterLoop" when LoopCount >= LoopCount("OuterLoop")^2 ;
endwhile;
... ! Extend the domain of definition based on current solution,
! or break from the loop when no extension is possible anymore.
endrepeat;
The FOR Statement
The FOR statement is related to the use of iterative operators in
expressions. An iterative operator such as SUM or MIN applies a
particular operation to all expressions defined over a particular
domain. Similarly, the FOR statement executes a group of execution
statements for all elements in its domain. The syntax of the FOR
statement is given in the following diagram.
Syntax
for-statement:
Execution is sequential
The binding domain of a FOR statement can only contain free indices,
which are then bound by the statement. All statements inside a FOR
statement are executed in sequence for the specific elements in the
binding domain. Unless specified otherwise, the ordering of elements in
the binding domain, and hence the execution order of the FOR
statement, is the same as the order of the corresponding binding set(s).
Integer domains
FOR statements with an integer domain in the form of an enumerated
set behave in a similar manner as the FOR statement in programming
languages like C or Pascal. Like the example below, FOR
statements of this type are mostly of an algorithmic nature, and the
indices bound by the FOR statement typically serve as an iteration
count.
Example
for ( n in { 1 .. MaxPriority } ) do
x.NonVar( i | x.Priority(i) < n ) := 1;
x.Relax ( i | x.Priority(i) = n ) := 0;
x.Relax ( i | x.Priority(i) > n ) := 1;
Solve IntegerModel;
endfor;
This example tries to solve a mixed-integer mathematical program heuristically in stages. The algorithm first only solves for those integer variables that have a particular integer priority, and then changes them to non-variables before going on to the next priority. The suffices used in this example are discussed in Variable Declaration and Attributes.
Non-integer domains
FOR statements with non-integer binding domains are typically used
to process the data of a model for all elements in a data-related
domain. The use of a FOR statement in such a situation is only
necessary if the statements inside it form a unit, for which sequential
execution for each element in the domain of the entire group of
statements is essential. An example follows.
Example
for ( i in Cities ) do
SmallestTransportCity := ArgMin( j, Transport(i,j) ) ;
DiscardedTransports += Transport( i, SmallestTransportCity ) ;
Transport( i, SmallestTransportCity ) := 0 ;
endfor;
In this example the three assignments form an inseparable unit. For each
particular value of i, the second and third assignment depend on the
correct value of SmallestTransport in the first assignment.
Use FOR only when needed
If you are familiar with programming language like PASCAL and C,
then the use of FOR statements will seem quite natural. In AIMMS,
however, FOR statements are often not needed, especially in the
context of indexed assignments. Indexed assignments bind the free
indices in their domain implicitly, resulting in sequential execution of
that particular assignment for all elements in its domain. In general,
such an index binding assignment is executed much more efficiently than
the same assignment placed inside an equivalent FOR statement. In
general, you should use FOR statements only when really necessary.
AIMMS issues a warning
AIMMS will provide a warning when it detects unnecessary FOR
statements in your model. Typically FOR statement are not required
when the loop only contains assignments that do not refer to scalar
identifiers (either numeric or element-valued) to which assignments have
been made inside the loop as well. For instance, in the last example the
FOR statement is essential, because the assignment and use of the
element parameter LargestTransportCity is inside the loop.
Example
The following example shows an unnecessary use of the FOR statement.
solve OptimizationModel;
! Mark variables with large marginal values
for (i) do
if ( Abs[x.Marginal(i)] > HighPrice ) then
Mark(i) := x.Marginal(i);
else
Mark(i) := 0.0;
endif;
endfor;
While this statement may seem very natural to C or Pascal
programmers, in a sparse execution language like AIMMS it should
preferably be written by the following simpler, and faster, assignment
statement.
Mark(i) := x.Marginal(i) OnlyIf ( Abs[x.Marginal(i)] > HighPrice );
The SPARSE, ORDERED and UNORDERED keywords
With the optional keywords SPARSE, ORDERED and UNORDERED you
can indicate that AIMMS should follow one of three possible strategies
to execute the FOR statement. If you do not explicitly specify a
strategy, AIMMS will follow the SPARSE strategy by default, and
issue a warning when this strategy leads to severe inefficiencies. You
can find an explanation of each of the strategies, as well as a
description of the cases in which you may want to choose a specific
strategy in Ordered Sets and the Condition of a FOR Statement.
FOR as a repetitive statement
Like the WHILE and the REPEAT statements, FOR is a
repetitive statement. Thus, you can use the SKIP and BREAK
statements and the LoopCount operator. In addition, you can identify
a FOR statement with a loop string thereby controlling execution in
nested arrangements as discussed in the previous section.
Use of SKIP and BREAK
The SKIP statement skips the remaining statements in the FOR
loop and continues to execute the loop for the next element in the
binding domain. The BREAK statement will abort the execution of the
FOR statement all together.
The SWITCH Statement
The SWITCH statement
The SWITCH statement is used to choose between the execution of
different groups of statements depending on the value of a scalar
parameter reference. The syntax of the SWITCH statement is given in
the following two diagrams.
Syntax
switch-statement:
selector:
Integers and set element
The SWITCH statement can switch on two types of scalar parameter
references: set element-valued or integer-valued. When you try to switch
on references to string-valued or non-integer numerical parameters,
AIMMS will issue a compile time error
Switch selectors
Each selector in a SWITCH statement must be a comma-separated list
of values or value ranges, matching the type of the selecting scalar
parameter. Expressions and ranges used in a SWITCH statement must
only contain constant integers and set elements. Set elements used in a
switch selector must be known at compile time, i.e. the data
initialization of the corresponding set must be a part of the model
description.
The DEFAULT selector last
The optional DEFAULT selector matches every reference. Since AIMMS
executes only those statements associated with the first selector
matching the value of the scalar reference, it is clear that the
DEFAULT selector should be placed last.
Example
The following SWITCH statement takes different actions based on the
model status returned by a SOLVE statement.
solve OptimizationModel;
switch OptimizationModel.ProgramStatus do
'Optimal', 'LocallyOptimal' :
ObservedModelStatus := 'Solved' ;
'Unbounded', 'Infeasible', 'IntegerInfeasible', 'LocallyInfeasible' :
ObservedModelStatus := 'Infeasible' ;
'IntermediateInfeasible', 'IntermediateNonInteger', 'IntermediateNonOptimal' :
ObservedModelStatus := 'Interrupted' ;
default :
ObservedModelStatus := 'Not solved' ;
endswitch ;
The HALT Statement
Terminating execution
With a HALT statement you can stop the current execution. You can
use it, for example, if your model has run into an unrecoverable error
condition during its execution, or if you simply want to skip the
remaining statements because they are no longer relevant in a particular
situation.
Compare to RETURN
Instead of the HALT statement you can also use the RETURN
statement (see also Internal Procedures) to terminate the current
execution. The HALT statement directly jumps back to the user
interface, but a RETURN statement in a procedure only passes back
control to the calling procedure and continues execution from there.
Syntax
The syntax of the HALT statement follows.
halt-statement:
Printing a message
You can optionally specify a string in the HALT statement that will
be printed in a message dialog box when execution has stopped. This is
useful, for instance, to pass on an appropriate message to the user when
a particular error condition has occurred.
The WHEN clause
You can make the execution of the HALT statement conditional on a
WHEN clause. If present, the current run will only be aborted if the
condition after the WHEN clause is satisfied.
Example
The following example terminates the current run if the SOLVE
statement does not solve to optimality. When aborting, the user will be
notified with an explanatory message.
solve LinearOptimizationModel;
halt with "Execution aborted: model not solved to optimality"
when OptimizationModel.ProgramStatus <> 'Optimal' ;
Alternative
Note that the type of model termination initiated by calling the
HALT statement cannot be guarded against using AIMMS’ error handling
facilities (see Raising and Handling Warnings and Errors). An alternative to the HALT
statement, which enables error handling, is the RAISE statement
discussed in Raising Errors and Warnings. When you want to let the
HALT act as a RAISE statement, you can switch the option
halt_acts_as_raise_error on.
The BLOCK Statement
The BLOCK statement
A sequence of statements can be grouped together into a single statement
using the BLOCK statement, possibly serving one or more of the
following purposes:
to emphasize the logical structure of the model,
to execute a group of statements with different option settings, or
to permit error handling on a group of statements (see Raising and Handling Warnings and Errors).
The syntax of the BLOCK statement is as follows.
Syntax
block-statement:
Emphasizing logical structure in the model
Consider the following BLOCK statement containing a group of
statements.
block ! Initialize measured compositions as observable.
CompositionObservable(nmf,c in MeasuredComponents(nmf)) := 1;
CompositionObservable(mf,mc) := 0;
if ( not CheckComputableFlows ) then
UnobservableComposition(nmf,c) := 1$(not CompositionObservable(nmf,c));
return 0;
endif;
CompositionCount(pu,c) :=
Count((f,g) | Admissable(pu,c,f,g) and CompositionObservable(g,c));
NewCount := Card ( CompositionObservable );
endblock ;
In the AIMMS syntax editor, a block can be displayed in either a
collapsed or an expanded state. When collapsed, the block will be
displayed as follows, using a single line comment following the
BLOCK keyword as its description.

When in a collapsed state, AIMMS will show the contents of the block in a tooltip if the mouse pointer is placed over the collapsed block, as illustrated in the figure below.

Executing with different option settings
During the execution of a block statement, the options in the WHERE
clause will have the specified values set at the beginning of the block
statement, and the old values restored at the end of the block
statement. More on the format of option names and value settings can be
found in The OPTION and PROPERTY Statements. The example below prints various
parameters using various settings of the option
Listing_number_precision.
! The default value of the option Listing_number_precision is 3.
block ! Start printing numbers using 6 decimals.
where Listing_number_precision := 6 ;
display A, B ;
block ! Start printing numbers without decimals.
where Listing_number_precision := 0 ;
display C, D ; ! The output looks as if C and D are integers.
endblock ;
display E, F ; ! Back to printing numbers using 6 decimals.
endblock ;
display G, H ; ! Back to printing numbers using 3 decimals.
In the above example, a nested block statement is used to set the scope of option settings; the inner block statement temporarily overrides the option setting of the outer block statement, which overrides the global option settings.
The OnError clause
The OnError clause is one of the means of handling runtime errors in
AIMMS. It is discussed in detail in Handling Errors.