MiniZinc 2.6.0
This release adds several new features and fixes a number of bugs, see https://www.minizinc.org/changes.html#v2.6.0 for a full change log. Binary releases for different platforms are available in the bundled packages of the MiniZinc IDE at https://github.com/minizinc/minizincide/releases.
MiniZinc 2.6 - What's new?
This is a big release, with many new language features, more flexible ways to structure output, a visualisation API in the MiniZinc IDE, and lots of small improvements and bug fixes.
1. Language
1.1. Indexed array literals and comprehensions
MiniZinc relies heavily on comprehensions to define data and constraints.
With the introduction of enumerated types, these comprehensions often had to
be combined with calls to functions such as array2d
in order to coerce the result
into the correct shape and index set.
With MiniZinc 2.6, you can now use indexed array comprehensions and indexed array literals:
enum X = {A, B, C, D};
array[_] of int: s = [ A: 300, B: 200, C: 0, D: 100 ];
array[_,_] of int: p = [ (i,j): s[i]*s[j] | i,j in X];
array[int] of int: q = [ 0: 1, 3, 6, 9 ];
The definition of s
uses an implicit index set _
, and it defines an array with explicit indexes (e.g. enum value A
is mapped to integer 300
). The resulting array s
has index set X
. The comprehension that defines p
also uses indexed notation, resulting in a 2d array p
with index sets X,X
. The array q
specifies the index of the first element to be 0
, with the rest of the array being indexed consecutively ([0: 1, 1: 3, 2: 6, 3: 9]
).
1.2. The `any` type declaration
Variables that have a definition can now be declared with the keyword any
instead of a concrete type:
any: x = 3; % x will be of type int
any: y = join(",", [ show(i) | i in 1..10]); % y will be of type array[int] of string
This works both for toplevel variables and those defined in let
expressions.
1.3. Interval notation
We often need to iterate over all except the smallest or largest element in a set. This is now easier using the new interval notation:
a..<b % set including a but excluding b
a<..b % set excluding a but including b
a<..<b % set of elements between a and b (excluding both)
a.. % set of elements starting with a and ending with largest element of type of a
a<.. % set of elements starting with successor of a and ending with largest element of type of a
a..< % set of elements starting with a and ending with second largest element of type of a
a<..< % set of elements starting with successor of a and ending with second largest element of type of a
..b % analogous
..<b
<..b
<..<b
This notation is particularly useful for enumerated types.
1.4. Enumerated types
We believe that, ideally, integers should only be used in cases where you really
need to represent numbers, such as quantities, costs etc. But whenever you
use integers as identifiers for objects, you should use an enum
instead!
That's why we've added more support to make enum
s easier to use and more versatile.
The language now has support for constructing extended enumerated types from
set arguments (rather than just other enumerated types):
enum X = Person(1..10) ++ { Nobody };
This will produce more readable output than the anon_enum
syntax (which is still supported
for backwards compatibility).
The sets can be arbitrary set expressions, but have to result in a contiguous
set value (otherwise MiniZinc aborts with an error).
This syntax is also extended to anonymous enumerated type constructors:
enum X = _(1..10); % anonymous enum type with 10 elements
Construction from subsets of an enum is now also fully supported,
as long as those sets are contiguous.
Another really nice change for enums
is that the enum_next
and enum_prev
functions
now don't require the enumerated type as an argument any more: You can simply write
enum_next(x)
instead of enum_next(X,x)
. The new function enum_of(x)
will return the
base set of the enumerated type of argument x
.
1.5. The `default` operator
The default
operator can be used to guard against undefinedness or optional values being absent:
var 1..10: x;
var 0..10: y;
var int: z = (x div y) default 1;
% z=1 if y=0
var opt 1..10: x;
var int: y = (10 div x) default 1;
% y=1 if x=<>
Note that the default
operator binds quite tightly, so you need to use parentheses in the examples above.
1.6. Better support for polymorphic functions
Many library functions in MiniZinc are polymorphic, i.e., they match multiple different types (this is
similar to generics or templates in other languages). The previous implementation was done before MiniZinc
had enumerated types, which meant that polymorphic functions didn't work well with enum
arguments.
Now, polymorphic functions are compiled by monomorphisation. That means that MiniZinc creates a copy
of the function for each combination of types it is called with. The syntax for polymorphic functions was extended
to make use of the any
type-inst, which can stand for an arbitrary instantiation (par
/var
as well as opt
/non optional).
As a result, we were able to implement the enum_of
function as well as simplify enum_next
and enum_prev
(as mentioned earlier). And functions that use show
, show2d
etc. now correctly print enumerated types.
1.7. Annotations
The annotation language has been extended. It now supports capturing annotations in
predicate and function declarations, and passing the annotated variable as an argument
into an annotation function.
A new empty_annotation
literal has been added, which is simply removed by the compiler.
That means you can now define your model like this:
ann: my_solve_ann;
solve ::my_solve_ann satisfy;
And then you could define my_solve_ann
as empty_annotation
in a data file, or
as an actual search annotation.
Function parameters and output items can now be annotated as well.
The standard library now provides an annotation for a simple Large Neighbourhood Search
(called relax_and_reconstruct
). This section of the library will be extended in the future.
The propagation strength annotations have been renamed to domain_propagation
and bounds_propagation
,
and we added value_propagation
as well.
1.8. Small tweaks to the language
-
Anonymous generators
Write comprehensions like
[0 | _ in 1..10]
instead of[0 | i in 1..10]
-
Generators can iterate over multi-dimensional arrays:
array[_,_] of int: a = [ (i,j): i*j | i,j in 1..3 ]; array[_] of int: b = [ 2*i | i in a ]; % = [2, 4, 6, 4, 8, 12, 6, 12, 18]
-
Add if-then without else for strings, annotations and arrays:
if b then "test" endif % "" if b=false if b then domain_propagation endif % empty annotation if b=false if b then [1,2,3] endif % [] if b=false
-
Add support for hex and octal characters in string literals.
You can use this e.g. to embed terminal control characters and produce colourful output. -
Identifiers can now start with underscores (this was previously reserved for FlatZinc models,
but that distinction was not used by the compiler). -
More option type support (weak versions of
!=
,/
anddiv
, weakmin
/max
, added missing
operators foropt bool
, optionalcircuit
constraint).
2. Output and visualisations
With the improved support for polymorphic functions, we've been able to rewrite
some of the output functionality of MiniZinc. The MiniZinc IDE now includes a
server component that makes it easy to connect visualisations to a model.
2.1. Default output
One- and two-dimensional arrays are now output using the indexed literal syntax
when possible. That means that the default output (without defining any output
items)
is now often much more readable, and doesn't contain any array1d
or array2d
coercions any more.
To give you better control over what ends up in the default output, we have
introduced the ::output
and ::no_output
annotations. Simply add ::output
to
any variable declaration to ensure it is part of the output. Or add ::no_output
to
a top-level variable that would otherwise be printed, but which you're not interested in.
Compared to the previous ::add_to_output
annotation, the new ::output
annotation doesn't cause
the default output of other variables to be switched off. So
var 1..3: x;
array[_] of var bool: y ::output = [ x = i | i in 1..3];
will print both x
and y
(whereas y ::add_to_output
would have suppressed the output of x
).
You can also use ::output
with variables declared locally in a let
expression, which
is really useful when debugging custom functions or predicates!
2.2. Output sections
Output can now be redirected into different sections. For example, we use this to produce JSON output
for visualising solutions, which is kept separate from the textual solution output.
The sections can also indicate that the output is in HTML format, so that tools like the MiniZinc IDE
can display a nicely formatted result.
An output item can be annotated with a string indicating the section to output it to.
All output to a particular section is concatenated together.
The default
section is used if there is no output section annotation.
Sections can be switched on or off on the command line with the options --only-sections
and --not-sections
,
or toggled using the toolbar in the output window in the IDE.
output ["this goes to the default section\n"];
output :: "foo" ["this goes to the foo section\n"];
output ["this is also in the default section\n"];
output :: "my_html" ["<strong>This is some bold output</strong>"];
will result in the following in the MiniZinc IDE:
this goes to the default section
this is also in the default section
this goes to the foo section
This is some bold output
----------
2.3 Visualisations in the MiniZinc IDE
The MiniZinc IDE now features a web-browser based visualisation system that allows MiniZinc models to pass data to HTML/JavaScript applications in real time. There are a number of predefined visualisations which can be used by including the file ide/vis.mzn
. These include line graphs, scatter plots, bar graphs, network visualisations, and 2D packing visualisations. Multiple predefined visualisations can be used in a single MiniZinc model, and will appear side-by-side on the resulting web page. Note that this feature is currently experimental.
For example, to visualise the value of the objective value, the following can be added to a model:
include "ide/vis.mzn";
constraint vis_line(_objective, "Objective value over time");
This is also a JavaScript API for writing custom visualisations for specific models. This is described in detail in the documentation.
3. Debugging
Debugging constraint models can be hard. MiniZinc 2.6 tries to make your life a little bit easier
by adding a few new features.
3.1. Tracing
Printing intermediate values during the compilation from MiniZinc to FlatZinc can often help
identify problems in your model. We noticed that we often ended up writing code like this:
constraint forall (i,j in X) ( trace("x[\(i)] < x[\(j)]\n", x[i] <= x[j]) );
to create output like this:
x[1] < x[1]
x[1] < x[2]
x[1] < x[3]
x[2] < x[1]
x[2] < x[2]
x[2] < x[3]
x[3] < x[1]
x[3] < x[2]
x[3] < x[3]
This is now made much easier with the new trace_exp
function. You can simply wrap it around
any expression, and MiniZinc will try to print a useful, readable output of the expression
you're tracing:
constraint forall (i,j in X) ( trace_exp(x[i] <= x[j]) );
This will automatically produce output like this:
test_trace_exp.mzn:4.32-54:
x[i(≡1)]<=x[j(≡1)]
x[i(≡1)]<=x[j(≡2)]
x[i(≡1)]<=x[j(≡3)]
x[i(≡2)]<=x[j(≡1)]
x[i(≡2)]<=x[j(≡2)]
x[i(≡2)]<=x[j(≡3)]
x[i(≡3)]<=x[j(≡1)]
x[i(≡3)]<=x[j(≡2)]
x[i(≡3)]<=x[j(≡3)]
In addition, the trace_to_section
function can be used to produce tracing output into a different output
section, to keep it separate from e.g. error messages in the output.
3.2. Error messages for internal errors
MiniZinc will now output human-readable error messages in the case of an unrecoverable crash.
In particular, if the compiler runs into a stack overflow (e.g. in an unbounded recursion), it will
print an error message that identifies the recursion and attempts to print part of the
MiniZinc call stack. That should help you identify the issues.
If MiniZinc crashes due to an internal error, you should now also see an error message that
clearly identifies this as a bug.
3.3. Error messages for undefined results
Error messages and warnings arising from undefinedness (such as array access out of bounds) now contain
more information about the error, such as the expected range of values vs. the actual value.
3.4. Debug mode
Finally, we have added debugging builtins (tracing functions and assertions) that are only evaluated if MiniZinc is run with the
--debug
command line option. These are useful in case the debugging information is costly to
compute and not required when the model is used in production.
4. Documentation
The tool that generates the reference documentation has been updated. All overloaded versions
of a function or predicate are now grouped together, which reduces clutter and should make it
easier to find and compare e.g. different versions of the same constraint.
The output of mathematical expressions in the documentation should also look much cleaner now.
Both the tutorial and the reference documentation have been updated with the new features.