Table of contents
- Objective function
- Constraints
- Variables and compound variables
- Data
- Blocks
- Domains
- Functions and tuples
- Other things
Objective function
The objective function can be one of either "min" or "max", after the keyword, you can define whichever
expression
you wish to optimize.
Once the model is compiled, it will be ran through the solver and find the optimal
solution
which fits the constraints.
Some solvers allow you to also work on finding a satisfiable solution, which is a solution that fits the
constraints, not caring about the objective function. in that case, instead of writing the min/max keyword and
objective function,
you can use the "solve" keyword.
Constraints
The formal model can follow a list of constraints, you can use one of <=, >=, =, <, > comparisons.
Some solvers like the simplex, do not allow for strict inequalities <, >.
The special keyword "for" can be used at the end of a constraint to create a constraint for each element that
you iterate over.
You can also give a name to constraints, so that you can more easily recognise them in the output.
To do that, just write the name of the constraint followed by a colon ":". (eg. "myConstraint: x + y = 2"),
you can also treat the constraint name as a compound variable and use the iteration syntax.
Variables and compound variables
The language employs two execution environments: a formal model and a compiled model. Within the formal model, you can define three types of variables:
- Compound variables: These variables have names determined after compilation.
- Constant variables: These variables have their values replaced during compilation (e.g., substituting a symbolic name with a concrete number).
- Standard variables: These variables retain their names and values after compilation.
Compound Variables
A compound variable is identified by an underscore (_
) in its name. The portion of the name
preceding the first underscore serves as a prefix. The remaining parts (split by underscores) are treated as an
expression that must evaluate to a string, number, or node.
For example, in the compound variable x_i
, x
is the prefix, and i
is a
variable whose value will be used during compilation to construct the final variable name. If i
has
the value 3
, the compiled variable name would be x_3
.
Compound variables are particularly useful when iterating over a list and dynamically generating variable names based on each element's value. See the example below for further clarification.
You can use curly braces {}
to enclose more complex expressions within the compound variable
name.
If the expression is a simple number or a single variable name, the curly braces can be omitted. For instance, data_{i + 1}
allows for more complex index calculations, while item_1
or value_count
are simpler examples.
Escaping Compound Variable Names
If you need to use a variable name that looks like a compound variable but should be treated literally,
you can escape the name using a backslash (\
). For example, \x_hello
will be
interpreted as the literal variable name x_hello
, preventing the evaluation of hello
.
Data
Following the constraint definitions, you can define data within the where
section. This data is
then available for use throughout your model.
The ROOC language supports various data types, including arrays, matrices, graphs, strings, numbers, and boolean
values. Furthermore, you can use expressions, function calls, and other computational constructs within the where
section.
To define a named constant, use the let
keyword followed by the constant's name and its value. For
example:
Expansion blocks
Expansion blocks are a special type of expression macro, used to preprocess other expressions. A common example
is the avg
block, which takes a comma-separated list of expressions and expands it calculate the
arithmetic
average of those expressions.
This will be compiled to:
There are different kinds of expansion blocks, you can find them in the documentation.
Scoped expansion blocks
There are also special kinds of expansion blocks, which have a scope attached to it.
In the normal expansion blocks, you need to manually specify the different expressions, separating them with a
comma.
The Scoped expansion blocks, you specify a template (which is the expression inside of the block), and an
iteration scope (eg. iterating over a list).
Together with compound variables and scoped expansion blocks, you can do things like creating a summation over a
list or range.
As an example, here is creating a summation of
will be compiled to:
there are different scoped expansion blocks that can be used to expand the expressions, you can find them in the documentation.
Domains
After the data you can define in which domain each variable will be part of, those variables are the ones that
will remain after the compilation is finished. The domain knowledge will then be used by solvers.
Every variable that will end up in the compiled model must be defined, you can use the "for" iteration like in
the constraints to define compound variables.
The domains are "Real", "NonNegativeReal", "Boolean" and "IntegerRange".
You can define a minimum and maximum value for each domain except for the "Boolean" domain.
They are required for the "IntegerRange" domain, and optional for Real (which defaults to -inf and inf) and
NonNegativeReal (which defaults to 0 and inf).
Functions and tuples
The ROOC langage has a set of builtin functions that can be used to manipulate data.
Those functions can be run anywhere in the model or data section.
The language also has support for tuples and tuples destructuring, you can destructure a tuple or array by
writing the name of the variables inside a parenthesis "(a,b,c)". Some builtin values are destructurable, like
arrays, tuples and graph edges.
will be compiled to
Other things
You can write comments in the model by using the "//" or "/* */" syntax, a model is structured (in this order) by the objective function, constraints, data and domains.