The at operator¶
The operator “at” is a binary operator whose first operand is an array or a list and whose second operand is an integer (the index). Its value is the value of the element in the array at the given index.
Principles¶
Assume that we model the behavior of a machine whose status can be on (1) or off (0). This status is modeled as a binary variable. Now the operating cost of this machine can depend on its status what would be modeled straightforwardly in LocalSolver as:
status <- bool();
mcost <- status ? onCost : offCost;
Note that onCost
and offCost
are not necessarily constants.
They can depend on other expressions, for instance we may have an optional
special technology (say a binary variable specialTechno
) impacting the
cost as follows: onCost <- 100 + 20 * specialTechno
.
The at
operator can be seen as a generalization of this conditional
expression (c ? a : b
). Lets us generalize the above example to a machine
having several possible operating modes. Now the status is an integer variable
whose value is among {0, 1, 2, 3, 4, 5}
, and the operating cost depends on
the operating mode (the status
).
Operating mode Operating cost 0 Cost 0 1 Cost 1 2 Cost 2 3 Cost 3 4 Cost 4 5 Cost 5
Here again cost0, ... cost5
are not necessarily constants. They can depend
on other expressions or decisions. It would be possible to model the operating
cost of this machine with 5 nested conditional expressions but this approach
would be tedious and inappropriate in case of hundreds of operating modes.
Instead the at
operator allows us to have all costs in an array and to
define the operating cost by merely accessing this array with
the index status
:
status <- int(0,5);
costs = {cost0, cost1, cost2, cost3, cost4, cost5};
// In LSP the at operator is naturally written with the [] notation on a map
mcost <- costs[status];
In the APIs the equivalient of the above code requires creating an O_Array
expression. Such an expression has no numeric value (calling getValue
on it
is forbidden) but contains an array of expressions instead. Expressions in the
array can be constant or not.
LSExpression status = model.intVar(0, 5);
std::vector<int> vv { cost0,cost1,cost2,cost3,cost4,cost5 };
LSExpression lsv = model.array(vv.begin(), vv.end());
LSExpression mcost = lsvn[status];
Pitfalls¶
Indices start at zero¶
The indexing of the array starts at 0. Hence when creating an at
expression
in the LSP language, the map must have a value defined for index 0.
For example, the following code will throw an exception:
status <- int(1,3);
costs[1] = 10;
cost[2] = 18.2;
cost[3] = 20;
// will throw an exception because indices of map "costs" do not start at zero
mcost <- costs[status];
Indeed when creating such a expression through a map, an O_Array
expression
is implicitly created, hence indices in the map must be consecutive and
starting at 0, as in a regular array.
Beware of out of bounds exceptions¶
An at
expression must be valid for any assignment of values to variables,
not only for feasible assignments. Indeed LocalSolver may start from an
infeasible solution and during the search it may explore infeasible regions of
the search space (as a shortcut to promising regions).
For example, the following code will throw an exception during the search:
x <- int(-5, 15);
y <- int(-5, 15);
constraint x + y > 0;
c[i in 0..30] <- 3*i + 1;
// Will throw an exception during search, as soon as x + y is negative
z <- c[x + y];
What happens here is that at some point we encounter a solution where x + y
is negative, what make the at
operator invalid. The min
and max
operators should be used to avoid this kind of issue, as follows:
x <- int(-5, 15);
y <- int(-5, 15);
constraint x + y > 0;
c[i in 0..30] <- 3*i + 1;
// OK since the index is now between 0 and 30
z <- c[max(0, x + y)];
In the APIs, an operator of type O_Array
must be created¶
Altough the LSP language allows creating array
expressions from maps,
API functions require the explicit creation of an O_Array
expression.
See the C++ code below.
std::vector<int> vv { 10, 18, 20 };
LSExpression x = model.intVar(0, 2);
// Will not compile because vv is not an LSExpression
LSExpression wwx_bad = vv[x];
// OK since lsv is an LSExpression of type O_Array
LSExpression lsv = model.array(10, 18, 20);
LSExpression wwx_good = lsv[x];