Command statements instruct the AMPL processor to perform some action
that may or may not make use of model entities
(see Statement Types).
There are many different commands that we can call in an AMPL program. Here
we are going to take a look on the basic commands, just enough to get you
started with AMPL programming. We have divided them into five categories:
The commands model, data and commands
do exactly the same thing that include does,
with the addition that they also specify how the statements in the file should be interpreted.
The AMPL processor has two modes: model and data. The model
and commands statements put the processor in model mode,
in which all the statements are interpreted either as declarations or as commands.
The data statement puts the processor in data mode,
in which all the statatements are interpreted as assignment statements.
We can also call model, data
and commands without a file path, in which case all the statement
does is to change the mode in which the processor currently is.
See the Single File Program example.
Solver Usage
AMPL is not a solver. The AMPL processor acts as an interface between us and the
actual solver. A solver is a program that solves optimization problems.
AMPL is not a solver.
We feed data into the AMPL processor, which processes the data and sends it to a solver.
The solver then returns some output to the AMPL processor, which in turn returns
this output to us, usually by printing it into the terminal. The figure below gives an
overview of what happens when we call solve;.
AMPL is not a solver.
optionsolver"CPLEX";
We use the option command to set a variety of options
that influence the behaviour of commands and solvers. The above command shows
how to select the solver that we want to use to solve our optimization problem.
solve;
The solve command invokes the currently selected solver (see above)
to solve the optimization problem that we have implemented. The AMPL processor sends to the solver
all the problem information that we have provided (formulation and data) and also a variety
of options that affect the solver's behaviour.
Print and Display
There are three main AMPL commands that we can use to print something in the terminal:
display, print and printf.
displayI, a;printa[1];
We use the display and print commands
to print out the value, expression or formula associated with our
model entities. The main difference between display and
print is that the first can have model entity collections
in the list of entities to be printed, while the second can only take a list of
individual model entities.
In the example above, suppose that I is a set declared with
set I = 1..10; and that a is a parameter collection declared with
param a{I} default 0;. With the display
statement we are able to print out both the set and the collection, while with the
print command we can only print the parameter indicated with the subscript [1].
printf"Objective: %f\n",z.val;
printf stands for print formatted. The first argument of the
statement is the format of the string, which is basically a template string with missing values.
The missing values are specified in the template with a % followed by a
character indicating the type of value that goes there. %f means that
the missing value is a real number (a floating point value),
%d means it is an integer, and %s
means it is a string. \n is a special sequence
of characters that places a line break at that point.
After the template string comes a comma separated list of template fillers, which are the
values with which the template will be filled. The first filler goes where the first missing
value is, the second filler goes where the second missing value is, and so on. In the above
example, supposing that z is an objective function,
the command will print out into the terminal the objective's current value, e.g. Objective: 348.000000,
followed by a line break.
All of the three statements above can take an indexing expression, as shown below:
print{i in I : i >= 5}a[i];
The indexing expression instructs the
the AMPL processor to iterate over all entries in the specified expression and
print out the listed entities (orange part).
The above statement is roughly equivalent to the for-loop below, but the code below
will print each entry in a new line:
for {i in I : i >= 5} {
print a[i];
}
Modifying Model Entities
Sometimes we want to modify our problem data and/or formulation during the execution,
in which case we can use the following commands.
reset;resetdata;
The reset command discards something from our program, essentially
undoing something that we have done previously.
A common use case of the reset command is when we want to solve a given
problem with different formulations and/or with multiple data instances.
In order to read a different problem formulation, e.g. model "model-v2.md",
we need to discard the current model with all its declarations. Otherwise, they could conflict
with the declarations of the new model. In this case, we use the first form of the reset
command shown above, which causes the AMPL processor to forget all the previous declarations,
as well as the data associated with it.
But sometimes we want to keep the current model with all its declarations
and only provide a new data instance of the problem. In this case, we only
need to reset the assignments that we've made, which we do by calling
reset data;. This will cause the AMPL processor
to discard all the values assigned to the model entities that we have declared,
after which we are able to load new data by calling e.g. data "data-v2.md";.
updatedata;
The update data; command is similar to the reset data;
command in that both allow us to make new data assignments to our model entities. But the
update command will not discard the current values associated
with them. Instead, all it does is to allow us to make reassignments in a new data section,
in which we can assign new values to either all model entities or only part of it.
letalpha:=0.9;
The let command is an alternative way to update the data assigned
to a model entity. While with the update data command we have to provide
the new data in a data section, the let is an assignment
statement itself, in which we specify the model entity that will have its value updated
with a new value.
The let statement is not equivalent to the
assignment statements in a data section.
For instance, we cannot provide data in a
list-like or table-like fashion, but can only assign a specific expression to a
specific model entity. Use it when convenient.
We can also use indexing expression to update multiple values:
let{i in I}beta[i]:=beta[i] + 0.05;
In the example above, supposing that beta is a parameter collection declared with
param beta{I};, the indexing expression instructs the compiler
to iterate over all items in set I and update the value of the associated beta
parameter with the value in orange, thus incrementing it by 0.05.
This statement is equivalent to the for-loop below.
for {i in I} {
let beta[i] := beta[i] + 0.05;
}
Program Flow Control
The main mechanisms that we use to control the flow of our program are
conditional statements and loop statements.
We can instruct the AMPL processor to only execute a body of statements if certain conditions are met using
if ... then ... else blocks, whose structure and syntax looks like this:
ifsolve_result_num < 0then{
print "Problem not solved.";
} ifsolve_result_num < 0then{
print "Problem not solved.";
}else{
print "Problem solved.";
} ifsolve_result_num = 200then{
print "Problem is infeasible.";
}else ifsolve_result_num = 300then{
print "Problem is unbounded.";
}
As for loop statements, there are different ways in which we can repeat a sequence of instructions.
for{i in 1..10}{
solve;
printf "Result: %f\n", z.val;
let alpha := alpha - 0.05; }
A for-loop statement instructs the AMPL processor to repeat a body of statements
(in orange) for each index that satisfies the indexing expression (in yellow).
Once the body of statements have been executed for each index, it stops.
The repeat-while and repeat-until statements instructs the AMPL processor to repeat a body of statements
(in orange) while/until the condition in yellow is satisfied. In the while form, it continues to repeat
while the condition is true. In the until form, it continues to repeat until the condition is true.
When using the repeat command, we can also put the stop conditions
after the body of instructions, in which case the condition is checked after each iteration
rather than before each iteration:
repeat {
solve;
printf "Result: %f\n", z.val;
let alpha := alpha - 0.05;
} until alpha < 0.05;
Aside from specifying a loop stop condition in the loop statement, it is also possible to
interrupt a loop using a break; command within the body of statements.
For instance, we can have the following condition within the for-loop example above,
which would interrupt the loop after 10 minutes:
if _ampl_elapsed_time > 600 then {
break;
}
It is also possible to skip over an iteration using the continue;
command within the body of statements. It does not interrupt the loop, but rather, goes directly
to the next iteration without execution the remaining statements.
For instance, we can have the following condition within the for-loop example above,
which would skip over the iterations in which i is not in set S: