Assignment Statements
Assignment
Assignment statements are used to set or change the values of sets, parameters and variables during the execution of a procedure or a function. The syntax of an assignment statement is straightforward.
Syntax
assignment-statement:
data-selection:
Assignment operators
AIMMS offers several assignment operators. The standard replacement
assignment operator :=
replaces the value of all elements specified
on the left hand side with the value of the expression on the right hand
side. The arithmetic assignment operators +=
, -=
, *=
,
/=
and ^=
combine an assignment with an arithmetic operation.
Thus, the assignments
a += b
,a -= b
,a *= b
,a /= b
,a ^= b
form a shorthand notation for the assignments
a := a + b
,a := a - b
,a := a * b
,a := a / b
,a := a ^ b
.
Index binding
Assignment is an index binding statement. AIMMS also binds unbound indices in (nested) references to element-valued parameters that are used for indexing the left-hand side. AIMMS will execute the assignment repeatedly for all elements in the binding domain, and in the order as specified by the declaration(s) of the binding set(s). The precise rules for index binding are explained in Binding Rules.
Allowed binding domains
In contrast to the binding domain of iterative operators and the FOR
statements, the binding domain of an indexed assignment can contain the
full range of element expressions:
references to unbound indices, which will be bound by the assignment,
references to scalar element parameters and bound indices,
references to indexed element parameters, for which any nested unbound index will be bound as well,
calls to element-valued functions, and
element-valued iterative operators.
If the element expression inside the binding domain of an indexed assignment is too lengthy, it may be better to use an intermediate element parameter to improve readability.
Conditional assignments
Like any binding domain, the binding domain of an indexed assignment can be subject to a logical condition. Such an assignment is referred to as a conditional assignment, and is only executed for those elements in the binding domain that satisfy the logical condition.
Domain checking
In addition, if the identifier on the left-hand side of the assignment has its own domain restriction, then the assignment is limited to those elements of the binding domain that satisfy this restriction. Assignments to elements outside the restricted domain are not considered.
Example
The following five examples illustrate some simple assignment
statements. In all examples we assume that i
and j
are unbound
indices into a set Cities
, and that LargestCity
is an element
parameter into Cities
.
The first example illustrates a simple scalar assignment.
TotalTransportCost := sum[(i,j), UnitTransportCost(i,j)*Transport(i,j)];
The value of the scalar identifier on the left-hand side is replaced with the value of the expression on the right-hand side.
The second example illustrates an index binding assignment.
UnitTransportCost(i,j) *= CostWeightFactor(i,j) ;
For all cities
i
andj
in the index domain ofUnitTransportCost
, the old values of the identifierUnitTransportCost(i,j)
are multiplied with the values of the identifierCostWeightFactor(i,j)
and then used to replace the old values.The third example illustrates a conditional assignment.
Transport((i,j) | UnitTransportCost(i,j) > 100) := 0;
The zero assignment to
Transport
is made to only those citiesi
andj
for which theUnitTransportCost
is too high.The fourth example illustrates a sliced assignment, i.e. an assignment that only changes the values of a lower-dimensional subspace of the index domain of the left-hand side identifier.
Transport(LargestCity,j) := 0;
The sliced assignment in this example binds only the index
j
. The values of the parameterTransport
are set to zero from the cityLargestCity
to every cityj
, but the values from every other city i to all citiesj
remain unchanged.The fifth example illustrates a nested index binding statement.
PreviousCity( NextCity(i) ) := i;
The index
i
is bound, because it is used in the nested reference of the element parameterNextCity(i)
, which in turn is used for indexing the identifierPreviousCity
. Note that, in a tour, cityi
by definition is the previous city of the specific (next) city it is linked with.
Sequential execution
Indexed assignments are executed in a sequential manner, i.e. as if it
was replaced by a sequence of individual assignments to every element in
the binding domain. Thus, if Periods
is the integer set {0 .. 3}
with index t
, then the indexed assignment
Stock( t | t > 0 ) := Stock(t-1) + Supply(t) - Demand(t);
is executed (conceptually) as the sequence of individual statements
Stock(1) := Stock(0) + Supply(1) - Demand(1);
Stock(2) := Stock(1) + Supply(2) - Demand(2);
Stock(3) := Stock(2) + Supply(3) - Demand(3);
Therefore, in the right hand side expression it is possible to refer to elements of the identifier on the left which have received their value prior to the execution of the current individual assignment. This type of behavior is typically observed and wanted in stock balance type applications which use lag references as shown above. The same argument also applies to assignments that use element parameters for indexing on either the left- or right-hand side of the assignment.
Indexed assignment versus FOR
In addition to the indexed assignment, AIMMS also possesses a more
general FOR
statement which repeatedly executes a group of
statements for all elements in its binding domain (see also
The FOR Statement). If you are familiar with programming
languages like C
or PASCAL you might be tempted to embed every
indexed assignment into one or more FOR
statements with the proper
domain. Although this will conceptually produce the same results, we
strongly recommend against it for two reasons.
By omitting the
FOR
statements you improve to the readability and maintainability of your model code.By including the
FOR
statement unnecessarily you are effectively degrading the performance of your model, because AIMMS can execute an indexed assignment much more efficiently than the equivalentFOR
statement.
Whenever you use a FOR
statement unnecessarily, AIMMS will produce a
compile time warning to tell you that the code would be more efficient
by removing the FOR
statement.
Example
Consider the indexed assignment
Transport((i,j) | UnitTransportCost(i,j) > 100) := 0;
and the equivalent FOR
statement
for ((i,j) | UnitTransportCost(i,j) > 100) do
Transport(i,j) := 0;
endfor;
Notice that the indexed assignment is more compact than the FOR
statement and is easier to read. In this example AIMMS will warn against
this use of the FOR
statement, because it can be removed without any
change in semantics, and will lead to more efficient execution.
Undefined left-hand references
When there are undefined references with lag and lead operators on the left-hand side of an assignment (i.e. references that evaluate to the empty element), the corresponding assignments will be skipped. The same is true if the identifier on the left contains undefined references to element parameters. Notice that this behavior is different from the behavior of a reference containing undefined lag and lead expressions on the right-hand side of an assignment. These evaluate to zero.
Example
Consider the assignment to the parameter Stock
above. It could also
have been written as
Stock(t+1) := Stock(t) + Supply(t+1) - Demand(t+1);
In this case, there is no need to add a condition to the assignment for
t
\({}=3\). The reference to t+1
is undefined, and hence
the assignment will be skipped. Similarly, the assignment
PreviousCity( NextCity(i) ) := i;
will only be executed for those cities i
for which NextCity(i)
is defined.