Statements¶
Assignment statements¶
Assignments are used to assign values to variables. There are many kind of assignments in Hexaly Modeler depending on the type of the variable (local or global) and the operation you want to perform.
assignment_operator
: '='
| '<-'
| '+='
| '-='
| '/='
| '*='
| '%='
local_assignment_operator
: '='
| '<-'
assignment_statement
: identifier assignment_operator expression ';'
| identifier assignment_compositor_list assignment_operator expression ';'
;
assignment_compositor_list
: assignment_compositor
| assignment_compositor_list assignment_compositor
;
assignment_compositor
: '[' filter_iterator ']'
| '[' arithm_expression ']'
| '.' identifier
;
local_assignment_statement
: 'local' identifier local_assignment_operator expression ';'
| 'local' identifier assignment_compositor_list local_assignment_operator expression ';'
;
local_statement
: 'local' identifier ';'
;
Simple statements¶
The simple statement is the classical variable = value;
that assigns the
given value to the specified variable, overriding the previous value. The
variable can be local or global. If the variable was not previously declared as
local, the variable is considered as global by default and is implicitly
declared if necessary.
This statement cannot be used to add an LSExpression to the mathematical model. See Link statements for that.
Examples:
a = true; // a = 1
b = 9; // b = 9
c = a + b; // c = 10
c = a * b; // c = 9
c = (a == b); // c = 0
c = a < b; // c = 1
Coumpound assignment statements¶
A coumpound assignment (also called “augmented assignment”) is the combination of an arithmetic operation and an assignment. The arithmetic operations allowed with coumpound assignment statements are:
+
addition-
substraction*
multiplication/
division%
modulo
Thus, an assignment like a = a + b
can be rewritten as a += b;
with the
coumpound assignment syntax.
Exemples:
a += 2; // a = a + 2
a /= 2; // a = a/2
a *= b + c; // a = a*(b + c)
Link statements¶
The link statement is a particular assignment that only applies to LSExpression or values that can be casted to LSExpression. This statement is used to add expressions to the mathematical model. If the assigned value is not an LSExpression or cannot be cast to an LSExpression, an exception is thrown. Only integers, floating point numbers and in certain circonstances maps, can be casted to LS expressions.
Examples:
a = true; // a = 1
b = 9; // b = 9
c = a + b; // c = 10
c = a * b; // c = 9
c = a == b; // c = 0
c = a < b; // c = 1
x <- 1; // 1 is transformed to a constant LS expression and is added to the mathematical model
y <- bool(); // y is a new decision variable and is added to the mathematical model
z <- x+y; // z is an LS expression and is added to the mathematical model
// This assignment will throw an exception: strings cannot be casted to LSExpression.
z <- "a string";
Local assignment and local statements¶
Local statements local variable
, local variable = value
or local variable <- value
introduce a new local variable with the given
name and set its value to nil or to the specified value.
If a global variable with the same name was already defined, then the global variable will be masked while this local variable is visible. If a local variable with the same name was already defined an error is thrown. See Variables for the differences between local and global variables and for details on the visibility scopes of variables.
Examples:
local i; // Declare i as local and set its value to nil.
local j = 2; // Declare j as local and set its value to 2.
for[z in 0...10] {
// This assignment will throw an exception since z was already implicitly
// declared as local in the for loop.
local z = "foo";
}
Iterated assignment statements¶
a[v in V] = f(v);
is an iterated assignment which is strictly equivalent
to for [v in V] a[v] = f(v);
Similarly a[v in V] <- f(v);
is an
iterated assignment which is strictly equivalent to for [v in V] a[v] <- f(v);
These iterated assignment features will help you will help you to make your
models shorter and clearer. Similarly to the for statement introduced in
the previous section, iterations can be nested and filtered:
for[i in 0...10][j in i+1...10][k in j+2...10]
a[i][j][k] = i + j + k;
a[i in 0...10][j in i+1...10][k in j+2...10] = i + j + k; // compact
If statements¶
The if statement is used for conditional execution.
if_condition
: 'if' '(' expression ')'
;
if_else_statement
: if_condition statement
| if_condition statement 'else' statement
;
if the if_condition
is true (equals to 1), then the first statement is
executed. Otherwise, the second statement after the else is executed. Note
that the else branch is optional and a block of statements can be declared
instead of a sole statement by using the braces {}
.
If the expression is not an integer equal to 0 or 1 an error will be thrown.
Note
The conditional ternary operator ? :
can also be used as shortcut.
Examples:
if (0) c = "ok";
if (true) c = "ok";
if (2) c = "error"; // ERROR: invalid condition
For statements¶
The for statement is used to iterate over a range, the elements of a map or any other iterable value. The for statements can be nested with a compact syntax and can be filtered with an optional conditionnal statement.
for_statement
: 'for' for_compositor statement
;
for_compositor_list
: for_compositor
| for_compositor_list for_compositor
;
for_compositor
: '[' filter_iterator ']'
;
for_compositor
: '[' filter_iterator ']'
| for_compositor '[' filter_iterator ']'
;
filter_iterator
: identifier 'in' expression ':' expression
| identifier ',' identifier 'in' expression ':' expression
| identifier 'in' expression
| identifier ',' identifier 'in' expression
;
Simple for-loops¶
for [v in A] S;
is a simple for-loop where the statement S
is
iteratively executed with v
taking all values in A
, where A
can be a
range or an iterable value like a map. Variable v
is implicitly declared as
a local variable visible within this for statement and in nested blocks only.
When a range is given, it is declared with the from...to
syntax where to
is excluded.
When an iterable object is given, the iteration is performed on the sub-values of the object. The iteration order is specific to the type of the value. For the maps, the order is defined as follows:
Iterates on number keys (integer or floats) in ascending order.
Iterates on strings in ascending order.
Iterates on other types in an implementation specific way.
It is also possible to iterate on the pairs (key, value) using
the syntax for [k,v in M]
. In that case, k
and v
are both declared
implicitly as local variables and contain respectively the current key and
the current value of the loop.
Example:
s = { -44, 12, 14 };
for [i in 0...3] a[i] = i + 1; // a[0] = 1, a[1] = 2, a[2] = 3
for [v in s] println(v/2); // prints -22, 6 and 7.
for [k, v in s] println(k, ":", v); // prints 0:-44, 1:12 and 2:14
Filtered for-loops¶
for [v in V : C]
is a filtered iteration loop: v takes only the values in
V satisfying the condition C.
Compact for-loops¶
Filtered and non filtered for-loops can be nested in a compact way, as follows:
// Non compact
for[i in 0...10]
for [j in i+1...10 : j % 2 == 0]
for [k in j+2...10]
a[i][j][k] = i + j + k;
// Compact
for[i in 0...10][j in i+1...10 : j % 2 == 0][k in j+2...10]
a[i][j][k] = i + j + k;
The behavior of these 2 examples is similar except that the language considers the non-compact case as the association of 3 enclosing loops, while the compact example is solely a loop with 3 iterated indexes. This semantic difference is particularly important for break statement: a break statement stops the most inner loop, regardless of the number of iterated indexes.
For all kinds of loops, a block of statements can be declared instead of a sole
statement by using the braces {}
:
for[i in 0...10][j in i+1...10][k in j+2...10] {
a[i][j][k] = i + j + k;
b[i][j][k] = i * j * k;
}
for-loops can contain ‘break’ and ‘continue’ statement to respectively terminate the loop unconditionnaly or to go back to testing the condition of the loop whitout executing the rest of the statement suite.
While/do-while statements¶
while_statement
: 'while' '(' expression ')' statement
;
dowhile_statement
: 'do' statement 'while' '(' expression ')' ';'
;
While or do-while loops are used for repeating a section of code as long as an expression is true.
For the ‘while’ loop, the expression is tested first. If it is true, the statement is executed. The loop terminates otherwise.
For the ‘do-while’ loop, the statement is executed first, then the expression is tested. Contrary to the ‘while’ loop, the ‘do-while’ loop ensures that at least one statement suite is executed.
While or do-while loops can contain ‘break’ and ‘continue’ statement to respectively terminate the loop unconditionnaly or to go back to testing the condition of the loop whitout executing the rest of the statement.
Continue statement¶
continue_statement
: 'continue' ';'
;
The continue statement, when encountered, goes back to testing the condition of the nearest enclosing loop, whitout executing the rest of the block code.
Continue statements can only take place in for/while/do-while loops. Use of this statement outside of these loops will throw an error.
Break statement¶
break_statement
: 'break' ';'
;
Break statement terminates the nearest enclosing loop. Please note that for ‘for’ loops, the entire loop is stopped, regardless of the number of iterated indexes.
Break statements can only take place in for/while/do-while loops. Use of this statement outside of these loops will throw an error.
Try-catch statement¶
In the Hexaly modeling language, all errors, faults or exceptionnal situations are handled with an exception mechanism.
trycatch_statement
: 'try' statement
'catch' '(' identifier ')' statement
;
Throw statement¶
throw_statement
: 'throw' expression ';'
| 'throw' ';'
;
With statement¶
The with statement is used for resource management. It ensures that a system or a native object like files or solver instances are correctly freed at the end of the statement, whatever flow the code follows (return, exception).
The with statement in Hexaly Modeler is similar to the with statement in Python, the try-with-resource in Java or the using statement in C#.
with_statement
: 'with' '(' with_resource ')' statement
;
with_resource
: identifier
| identifier '=' expression
Examples:
// f is a local variable whose scope is limited to the "with" statement
with(f = io.openRead("/path/to/file")) {
local a = f.readInt();
...
}
// The file is automatically closed at the end of the "with" statement,
// even if an exception is thrown.
Constraint/minimize/maximize statements¶
modifier
: 'minimize'
| 'maximize'
| 'constraint'
;
modifier_statement
: modifier expression ';'
;
These statements modify expressions of the mathematical model (LSExpression) to add them to the constraints list or the objectives list. These statements can also be applied on integers or doubles.
constraint c;
adds the LSExpressionc
as a constraint to the mathematical model. An error will be thrown is c is not a boolean LSExpression (introduced with a logical or relational operator) or a constant equals to 0 or 1.minimize c;
adds the LSExpressionc
as an objective to be minimized by the mathematical model.maximize c;
adds the LS expressionc
as an objective to be maximized by the mathematical model.
Note
At least one objective must be defined in the mathematical model.
Pragma statements¶
pragma_list
: pragma_statement
| pragma_list pragma_statement
;
pragma_statement
: 'pragma' identifier ';'
| 'pragma' identifier identifier ';'
| 'pragma' identifier integer ';'
| 'pragma' identifier double ';'
;
Pragma statements are special instructions to put at the beginning of an HXM
file (before the use
section) to alter the behavior of the compiler or
the modeler virtual machine. The pragmas only affect the module in which they
are declared, without affecting the other modules.
For the moment, there is only one pragma: modelingset
whose behavior is
detailed below.
modelingset
pragma¶
As Hexaly Optimizer evolves, new modeling functions are added (like sum
,
bool
or list
). Any new addition can cause compatibility issues with
existing programs when there is a conflict between the name of the new
modeling operator and the name of a user variable or a user function.
The use of this pragma allows to freeze the list of modeling functions imported in the module to a specific version of Hexaly Optimizer.
For instance:
pragma modelingset 10.0;
function model() {
cover("Tiramisu");
maximize 1;
}
function cover(cake) {
println(cake + " is now covered with cocoa powder");
}
In this example, we have frozen the modeling functions to the ones present in
LocalSolver 10.0. The COVER
operator was introduced in LocalSolver 10.5.
Without the pragma instruction, the above model would not compile since the
user cannot redefine modeling functions.
You can specify any valid version of Hexaly Optimizer. You can also specify 0.0. In the latter case, no modeling functions will be imported.
usedeprecated
pragma¶
This pragma restores a number of global functions that were removed in version 12.5 of Hexaly Modeler. The functions in question were present in the very first versions of the language, but were deprecated several years ago in version 6.0 when the modules were added. Their use is now strongly discouraged. It is preferable to use modules and class functions instead.
The list of restored functions is shown in the table below. The equivalent using the modules is given in the second column.
Deprecated function |
Replacement |
---|---|
split(str, delimiter) |
str.split(delimiter) |
toString(value) |
“” + value |
toInt(str) |
str.toInt() |
toDouble(str) |
str.toDouble() |
trim(str) |
str.trim() |
length(str) |
str.length() |
substring(str, start [, length]) |
str.substring(start [, length]) |
startsWith(str, prefix) |
str.startsWith(prefix) |
endsWith(str, prefix) |
str.endsWith(prefix) |
lowerCase(str) |
str.toLowerCase() |
upperCase(str) |
str.toUpperCase() |
replace(str, search, replace) |
str.replace(search, replace) |
openRead(filename) |
io.openRead(filename) |
openWrite(filename) |
io.openWrite(filename) |
openAppend(filename) |
io.openAppend(filename) |
close(file) |
file.close() |
eof(file) |
file.eof() |
readInt(file) |
file.readInt() |
readDouble(file) |
file.readDouble() |
readString(file) |
file.readString() |
readln(file) |
file.readln() |
add(m, value) |
m.add(value) |
keys(m) |
m.keys() |
values(m) |
m.values() |
error(message) |
throw message |
getSolutionStatus() |
hxSolution.status |