Table of contents

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.

will be compiled to

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 x_u, where u is a number from 0 to 3 (3 excluded)

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.