Templating

add_package(package_name[, package_path, …]) Adds the given package to the template search routine
add_path(searchpath[, encoding, followlinks]) Adds the given path to the template search routine

PyMzn supports templating as a form of dynamic modelling. PyMzn allows to embed code from the Jinja2 templating language within a MiniZinc model to make a PyMzn template file (usually distinguished with the .pmzn extension). An example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int: n;                     % number of objects
set of int: OBJ = 1..n;
array[OBJ] of int: profit;  % the profit of each object
array[OBJ] of int: size;    % the size of each object
int: capacity;              % the capacity of the knapsack

var set of OBJ: x;
constraint sum(i in x)(size[i]) <= capacity;

{% if with_compatibility %}
    array[OBJ, OBJ] of bool: compatibility;
    constraint forall(i, j in x where i != j)(
        compatibility[i, j]
    );
{% endif %}

var int: obj = sum(i in x)(profit[i]);
solve maximize obj;

output [
    "knapsack = ", show(x), "\n",
    "objective = ", show(obj)
];
1
2
3
4
n = 5;
profit = [10, 3, 9, 4, 8];
size = [14, 4, 10, 6, 9];
capacity = 24;

The above MiniZinc model encodes a 0-1 knapsack problem with optional compatibility constraint. By default the template engine argument with_compatibility is None, so the constraint is not enabled. In this case, the model can be solved as usual by running:

pymzn.minizinc('knapsack.pmzn', 'knapsack.dzn')

which returns:

[{'x': {2, 3, 5}}]

If we want to use the compatibility constraint, we define a compatibility matrix e.g. in a dzn file:

1
2
3
4
5
6
7
compatibility = [|
    true,  true, false,  true,  true |
    true,  true, false,  true, false |
   false, false,  true,  true,  true |
    true,  true,  true,  true, false |
    true, false,  true, false,  true
|];

Now it is possible to pass with_compatibility argument to pymzn.minizinc function, along with the dzn file with the compatibility matrix:

pymzn.minizinc('knapsack.pmzn', 'knapsack.dzn', 'compatibility.dzn', args={'with_compatibility': True})

which yields:

[{'x': {1, 5}}]

As mentioned, PyMzn employs Jinja2 under the hood, so anything you can do with Jinja2 is also possible in PyMzn, including variables, control structures, template inheritance, and filters. PyMzn implements few custom filters as well:

  • int(value, factor=100) : discretizes the given input or array, pre-multiplying by the given factor. Usage: {{ float_value_or_array | int}} or {{ float_value_or_array | int(factor=1000) }}
  • dzn(value) : transform the input into its equivalent dzn string. Usage: {{ dzn_argument | dzn }}

To provide a custom search path to the template engine you can use the function add_path:

pymzn.templates.add_path('path/to/templates/directory/')

This ensures that the template engine will look for imported tempates into the provided path as well.