1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.

946 lines
32 KiB
ReStructuredText

.. index::
single: predicate
single: function
.. _sec-predicates:
Predicates and Functions
========================
Predicates in MiniZinc
allow us to capture complex constraints of our model
in a succinct way. Predicates in MiniZinc
are used to model with both predefined global
constraints, and to capture and define new complex constraints by the
modeller.
Functions are used in MiniZinc to capture common structures of models.
Indeed a predicate is just a function with output type :mzn:`var bool`.
.. _sec-globals:
Global Constraints
------------------
.. index::
single: global constraint
There are many global constraints defined in MiniZinc for use in modelling.
The definitive list is to be found in the documentation for the release, as
the list is slowly growing.
Below we discuss some of the most important global constraints.
Alldifferent
~~~~~~~~~~~~
.. index::
single: alldifferent
single: global constraint; alldifferent
The :mzn:`alldifferent` constraint takes an array of variables and constrains them
to take different values.
A use of the :mzn:`alldifferent` has the form
.. code-block:: minizinc
alldifferent(array[int] of var int: x)
The argument is an array of integer variables.
The :mzn:`alldifferent` constraint is one of the most studied and used global constraints in
constraint programming. It is used to define assignment subproblems, and
efficient global propagators for :mzn:`alldifferent` exist.
The models :download:`send-more-money.mzn <examples/send-more-money.mzn>` (:numref:`ex-smm`)
and :download:`sudoku.mzn <examples/sudoku.mzn>` (:numref:`ex-sudoku`)
are examples of models using :mzn:`alldifferent`.
Cumulative
~~~~~~~~~~
.. index::
single: cumulative
single: global constraint; cumulative
The :mzn:`cumulative` constraint is used for describing cumulative resource
usage.
.. code-block:: minizinc
cumulative(array[int] of var int: s, array[int] of var int: d,
array[int] of var int: r, var int: b)
It requires that a set of tasks given by start times :mzn:`s`, durations :mzn:`d`,
and resource requirements :mzn:`r`, never require more
than a global resource bound :mzn:`b` at any one time.
.. literalinclude:: examples/moving.mzn
:language: minizinc
:name: ex-moving
:caption: Model for moving furniture using ``cumulative`` (:download:`moving.mzn <examples/moving.mzn>`).
.. literalinclude:: examples/moving.dzn
:language: minizinc
:name: ex-movingd
:caption: Data for moving furniture using ``cumulative`` (:download:`moving.dzn <examples/moving.dzn>`).
The model in :numref:`ex-moving` finds a schedule for moving furniture
so that each piece of furniture has enough handlers (people) and enough trolleys
available during the move. The available time, handlers
and trolleys are given, and the data gives for each object the move
duration,
the number of handlers and the number of trolleys required.
Using the data shown in :mzn:`ex-movingd`, the command
.. code-block:: bash
$ minizinc moving.mzn moving.dzn
may result in the output
.. code-block:: none
start = [0, 60, 60, 90, 120, 0, 15, 105]
end = 140
----------
==========
:numref:`fig-histogram-a` and :numref:`fig-histogram-b`
show the requirements for handlers and
trolleys at each time in the move for this solution.
.. _fig-histogram-a:
.. figure:: figures/handlers.*
Histogram of usage of handlers in the move.
.. _fig-histogram-b:
.. figure:: figures/trolleys.*
Histogram of usage of trolleys in the move.
Table
~~~~~
.. index::
single: table
single: global constraint; table
The :mzn:`table` constraint enforces that a tuple of variables
takes a value from a set of tuples. Since there are no tuples in MiniZinc
this is encoded using arrays. The usage of :mzn:`table`
has one of the forms
.. code-block:: minizinc
table(array[int] of var bool: x, array[int, int] of bool: t)
table(array[int] of var int: x, array[int, int] of int: t)
depending on whether the tuples are Boolean or integer.
The constraint enforces :math:`x \in t` where we consider :math:`x`
and each row in :math:`t` to be a tuple,
and :math:`t` to be a set of tuples.
.. literalinclude:: examples/meal.mzn
:language: minizinc
:name: ex-meal
:caption: Model for meal planning using ``table`` constraint (:download:`meal.mzn <examples/meal.mzn>`).
.. literalinclude:: examples/meal.dzn
:language: minizinc
:name: ex-meald
:caption: Data for meal planning defining the ``table`` used (:download:`meal.dzn <examples/meal.dzn>`).
The model in :numref:`ex-meal` searches for balanced meals.
Each meal item has a name (encoded as an integer), a kilojoule count,
protein in grams, salt in milligrams, and fat in grams, as well as cost
in cents. The relationship between these items is encoded using
a :mzn:`table` constraint.
The model searches for a minimal cost meal
which has a minimum kilojoule count
:mzn:`min_energy`, a minimum amount of protein :mzn:`min_protein`,
maximum amount of salt :mzn:`max_salt` and fat :mzn:`max_fat`.
Regular
~~~~~~~
.. index::
single: regular
single: global constraint; regular
The :mzn:`regular` constraint is used to enforce that a sequence of
variables takes a value defined by a finite automaton.
The usage of :mzn:`regular` has the form
.. code-block:: minizinc
regular(array[int] of var int: x, int: Q, int: S,
array[int,int] of int: d, int: q0, set of int: F)
It constrains that
the sequence of values in array :mzn:`x` (which must all be in the :index:`range`
:mzn:`1..S`)
is accepted by the :index:`DFA` of :mzn:`Q` states with input :mzn:`1..S`
and transition function :mzn:`d` (which maps :mzn:`<1..Q, 1..S>` to
:mzn:`0..Q`) and initial state
:mzn:`q0` (which must be in :mzn:`1..Q`) and accepting states :mzn:`F`
(which all must be in :mzn:`1..Q`).
State 0 is reserved to be an always failing state.
.. _fig-dfa:
.. figure:: figures/dfa.*
A DFA determining correct rosters.
Consider a nurse rostering problem. Each nurse is scheduled for each day as
either: (d) on day shift, (n) on night shift, or (o) off.
In each four day period a nurse must have at least one day off, and
no nurse can be scheduled for 3 night shifts in a row.
This can be encoded using the incomplete DFA shown in :numref:`fig-dfa`.
We can encode this DFA as having start state :mzn:`1`, final states :mzn:`1..6`,
and transition function
.. cssclass:: table-nonfluid table-bordered
+---+---+---+---+
| | d | n | o |
+===+===+===+===+
| 1 | 2 | 3 | 1 |
+---+---+---+---+
| 2 | 4 | 4 | 1 |
+---+---+---+---+
| 3 | 4 | 5 | 1 |
+---+---+---+---+
| 4 | 6 | 6 | 1 |
+---+---+---+---+
| 5 | 6 | 0 | 1 |
+---+---+---+---+
| 6 | 0 | 0 | 1 |
+---+---+---+---+
Note that state 0 in the table indicates an error state.
The model shown in :numref:`ex-nurse` finds a schedule for
:mzn:`num_nurses` nurses over :mzn:`num_days` days, where we
require :mzn:`req_day` nurses on day shift each day, and
:mzn:`req_night` nurses on night shift, and that each nurse
takes at least :mzn:`min_night` night shifts.
.. literalinclude:: examples/nurse.mzn
:language: minizinc
:name: ex-nurse
:caption: Model for nurse rostering using ``regular`` constraint (:download:`nurse.mzn <examples/nurse.mzn>`)
Running the command
.. code-block:: bash
$ minizinc nurse.mzn nurse.dzn
finds a 10 day schedule for 7 nurses, requiring 3 on each day shift
and 2 on each night shift, with a minimum 2 night shifts per nurse.
A possible output is
.. code-block:: none
d o n o n o d n o o
d o d n n o d d n o
o d d n o n d n o n
o d d d o n n o n n
d d n o d d n o d d
n n o d d d o d d d
n n o d d d o d d d
----------
There is an alternate form of the regular constraint
:mzn:`regular_nfa` which specifies the regular
expression using an NFA (without :math:`\epsilon` arcs).
This constraint has the form
.. code-block:: minizinc
regular_nfa(array[int] of var int: x, int: Q, int: S,
array[int,int] of set of int: d, int: q0, set of int: F)
It constrains that
the sequence of values in array :mzn:`x` (which must all be in the range
:mzn:`1..S`)
is accepted by the :index:`NFA` of :mzn:`Q` states with input :mzn:`1..S`
and transition function :mzn:`d` (which maps :mzn:`<1..Q, 1..S>` to
subsets of :mzn:`1..Q`) and initial state
:mzn:`q0` (which must be in :mzn:`1..Q`) and accepting states :mzn:`F`
(which all must be in :mzn:`1..Q`).
There is no need for a failing state 0, since the transition function can
map to an empty set of states.
Defining Predicates
-------------------
.. index::
single: predicate; definition
One of the most powerful modelling features
of MiniZinc is the ability for
the modeller to define their own high-level constraints. This allows them to
abstract and modularise their model. It also allows re-use of constraints in
different models and allows the development of application specific
libraries defining the standard constraints and types.
.. literalinclude:: examples/jobshop2.mzn
:language: minizinc
:name: ex-jobshop2
:caption: Model for job shop scheduling using predicates (:download:`jobshop2.mzn <examples/jobshop2.mzn>`)
We start with a simple example, revisiting the job shop scheduling problem
from the previous section. The model is shown in
:numref:`ex-jobshop2`. The item of interest is the
:mzn:`predicate`
item:
.. literalinclude:: examples/jobshop2.mzn
:language: minizinc
:lines: 12-13
This defines a new constraint that enforces that a task with start time
:mzn:`s1` and duration :mzn:`d1` does not overlap with a task with start
time :mzn:`s2` and duration :mzn:`d2`. This can now be used inside the
model anywhere any other :index:`Boolean expression <expression; Boolean>`
(involving decision variables)
can be used.
As well as predicates the modeller can define new constraints that only
involve parameters. These are useful to write fixed tests for a
conditional expression. These are defined using the keyword :mzn:`test`.
For example
.. code-block:: minizinc
test even(int:x) = x mod 2 = 0;
.. \ignore{ % for capture for testing!
.. $ mzn-g12fd jobshop2.mzn jobshop.dzn
.. } % $
.. defblock:: Predicate definitions
.. index::
single: predicate; definition
Predicates are defined by a statement of the form
.. code-block:: minizincdef
predicate <pred-name> ( <arg-def>, ..., <arg-def> ) = <bool-exp>
The :mzndef:`<pred-name>` must be a valid MiniZinc identifier, and
each :mzndef:`<arg-def>` is a valid MiniZinc :index:`type` declaration.
.. \ignore{The type-insts\index{type-inst}
.. of arguments may include type-inst variables\index{type-inst!variable}
.. which are of the
.. form \texttt{\$T} or \texttt{any \$T} with \texttt{T} an identifier. A type-inst
.. variable \texttt{\$T}\ttindexdef{\$T}
.. can match any fixed type-inst, whereas a type-inst
.. variable \texttt{any \$T} can
.. also match non-fixed type-insts\index{type-index!non-fixed}
.. (such as \texttt{var int}).
.. Because predicate arguments\index{argument}
.. receive an assignment when calling the predicate, the
.. argument type-insts may include
.. implicitly indexed arrays\index{array!index set!implicit},
.. as well as set variables with a
.. non-finite element type.}
One relaxation of :index:`argument`
definitions is that the index types for arrays
can be :index:`unbounded <array; index set; unbounded>`, written :mzn:`int`.
.. code-block:: minizincdef
test <pred-name> ( <arg-def>, ..., <arg-def> ) = <bool-exp>
The :mzndef:`<bool-exp>` of the body must be fixed.
We also introduce a new form of the :mzn:`assert` command for use in
predicates.
.. code-block:: minizincdef
assert ( <bool-exp>, <string-exp>, <exp> )
The type of the :mzn:`assert`
:index:`expression <expression; assert>`
is the same as the type of the
last argument.
The :mzn:`assert` expression checks whether the first argument is false,
and if so prints the second argument string. If the first argument is true
it returns the third argument.
Note that :index:`assert expressions <expression; assert>`
are lazy in the third argument, that is if the
first argument is false they will not be evaluated.
Hence, they can be used for checking:
.. code-block:: minizinc
predicate lookup(array[int] of var int:x, int: i, var int: y) =
assert(i in index_set(x), "index out of range in lookup",
y = x[i]
);
This code will not evaluate :mzn:`x[i]` if :mzn:`i` is out of the range of the array
:mzn:`x`.
Defining Functions
------------------
.. index::
single: function; definition
Functions are defined in MiniZinc
similarly to predicates, but with a more
general return type.
The function below defines the index in a Sudoku matrix
of the :math:`a1^{th}` row (or column) of the :math:`a^{th}` subsquare.
.. code-block:: minizinc
function int: posn(int: a, int: a1) = (a-1) * S + a1;
With this definition we can replace the last constraint in the
Sudoku problem shown in :numref:`ex-sudoku` by
.. code-block:: minizinc
constraint forall(a, o in SubSquareRange)(
alldifferent([ puzzle [ posn(a,a1), posn(o,o1) ] |
a1, o1 in SubSquareRange ] ) );
Functions are useful for encoding complex expressions that
are used frequently in the model. For example, imagine
placing the numbers 1 to :math:`n` in different positions
in an :math:`n \times n` grid such that
the Manhattan distance between any two numbers :math:`i` and :math:`j`
is greater than the maximum of the two numbers minus 1.
The aim is to minimize the total of the Manhattan distances
between the pairs. The Manhattan distance function
can be expressed as:
.. literalinclude:: examples/manhattan.mzn
:language: minizinc
:lines: 12-14
The complete model is shown in :numref:`ex-manhattan`.
.. literalinclude:: examples/manhattan.mzn
:language: minizinc
:name: ex-manhattan
:caption: Model for a number placement problem illustrating the use of functions (:download:`manhattan.mzn <examples/manhattan.mzn>`).
.. defblock:: Function definitions
.. index::
single: function; definition
Functions are defined by a statement of the form
.. code-block:: minizincdef
function <ret-type> : <func-name> ( <arg-def>, ..., <arg-def> ) = <exp>
The :mzndef:`<func-name>` must be a valid MiniZinc identifier, and
each :mzndef:`<arg-def>` is a valid MiniZinc type declaration.
The :mzndef:`<ret-type>` is the return type of the function which must be
the type of :mzndef:`<exp>`. Arguments have the same restrictions as in
predicate definitions.
Functions in MiniZinc can have any return type, not just fixed
return types.
Functions are useful for defining and documenting complex expressions that
are used multiple times in a model.
Reflection Functions
--------------------
To help write generic tests and predicates, various reflection functions
return information about array index sets, var set domains and decision
variable ranges. Those for index sets are
:mzndef:`index_set(<1-D array>)`,
:mzndef:`index_set_1of2(<2-D array>)`,
:mzndef:`index_set_2of2(<2-D array>)`,
and so on for higher
dimensional arrays.
A better model of the job shop conjoins all the non-overlap constraints for a
single machine into a single disjunctive constraint.
An advantage of this approach is that while we may initially model this
simply as a conjunction of :mzn:`non-overlap` constraints, if the underlying solver has a
better approach to solving disjunctive constraints we can use that instead,
with minimal changes to our model. The model is shown in
:numref:`ex-jobshop3`.
.. literalinclude:: examples/jobshop3.mzn
:language: minizinc
:name: ex-jobshop3
:caption: Model for job shop scheduling using ``disjunctive`` predicate (:download:`jobshop3.mzn <examples/jobshop3.mzn>`).
.. index::
single: global constraint; disjunctive
The :mzn:`disjunctive`
constraint takes an array of start times for each
task and an array of their durations and makes sure that only one task is
active at any one
time. We define the disjunctive constraint as a :index:`predicate <predicate; definition>` with
signature
.. code-block:: minizinc
predicate disjunctive(array[int] of var int:s, array[int] of int:d);
We can use the disjunctive constraint to define the non-overlap of tasks as
shown in :numref:`ex-jobshop3`.
We assume a definition for the :mzn:`disjunctive` predicate is given
by the file :download:`disjunctive.mzn <examples/disjunctive.mzn>` which is included in the model.
If the underlying system
supports :mzn:`disjunctive` directly, it will include a file
:download:`disjunctive.mzn <examples/disjunctive.mzn>` in its globals directory (with contents
just the signature definition above).
If the system we are using does not support disjunctive directly
we can give our own definition by creating the file
:download:`disjunctive.mzn <examples/disjunctive.mzn>`.
The simplest implementation simply makes use of the :mzn:`no_overlap`
predicate defined above.
A better implementation is to make use of a global :mzn:`cumulative`
constraint assuming it is supported by the underlying solver.
:numref:`ex-disj` shows an implementation of :mzn:`disjunctive`.
Note how we use the :mzn:`index_set` reflection function to
(a) check that the arguments to :mzn:`disjunctive` make sense,
and (b) construct the array of resource utilisations of the appropriate
size for :mzn:`cumulative`.
Note also that we use a ternary version of :mzn:`assert` here.
.. literalinclude:: examples/disjunctive.mzn
:language: minizinc
:name: ex-disj
:caption: Defining a ``disjunctive`` predicate using ``cumulative`` (:download:`disjunctive.mzn <examples/disjunctive.mzn>`).
.. \ignore{ % for capture for testing!
.. $ mzn-g12fd jobshop3.mzn jobshop.dzn
.. } % $
Local Variables
---------------
.. index::
single: variable; local
single: let
It is often useful to introduce *local variables* in a predicate,
function
or test.
The :mzn:`let` expression allows you to do so.
It can be used to introduce
both decision :index:`variables <variable>`
and
:index:`parameters <parameter>`,
but parameters must be initialised. For example:
.. code-block:: minizinc
var s..e: x;
let {int: l = s div 2; int: u = e div 2; var l .. u: y;} in x = 2*y
introduces parameters :mzn:`l` and :mzn:`u` and variable :mzn:`y`.
While most useful in :index:`predicate`, :index:`function`
and test definitions,
:mzn:`let` expressions can also be used in other expressions, for example
for eliminating common subexpressions:
.. code-block:: minizinc
constraint let { var int: s = x1 + x2 + x3 + x4 } in
l <= s /\ s <= u;
Local variables can be used anywhere and can be quite useful
for simplifying complex expressions.
:numref:`ex-wedding2`
gives a revised version of the wedding model, using local variables to
define the :index:`objective` function,
rather than adding lots of variables
to the model explicitly.
.. literalinclude:: examples/wedding2.mzn
:language: minizinc
:name: ex-wedding2
:caption: Using local variables to define a complex objective function (:download:`wedding2.mzn <examples/wedding2.mzn>`).
Using :mzn:`let` expressions, it is possible to define a function whose result is not well-defined. For example, we could write the following:
.. code-block:: minizinc
function var int: x_or_x_plus_1(var int: x) = let {
var 0..1: y;
} in x+y; % result is not well-defined!
The result, :mzn:`x+y`, is indeed not functionally defined by the argument of the function, :mzn:`x`. The MiniZinc compiler does not detect this, and **the behaviour of the resulting model is undefined**. In particular, calling this function twice with the same argument may or may not return the same result. It is therefore important to make sure that any function you define is indeed a function! **If you need non-deterministic behaviour, use a predicate:**
.. code-block:: minizinc
predicate x_or_x_plus_1(var int: x, var int: z) = let {
var 0..1: y;
} in z=x+y;
.. _pred-context:
Context
-------
.. index::
single: context
single: context; negative
single: predicate
single: function
One limitation is that predicates and functions
containing decision variables that are not
initialised in the declaration cannot be used inside a negative
context.
The following is illegal:
.. code-block:: minizinc
predicate even(var int:x) =
let { var int: y } in x = 2 * y;
constraint not even(z);
The reason for this is that solvers only solve existentially constrained
problems, and if we introduce a local variable in a negative context, then
the variable is *universally quantified* and hence out of scope of the
underlying solvers. For example the :math:`\neg \mathit{even}(z)` is equivalent to
:math:`\neg \exists y. z = 2y` which is equivalent to
:math:`\forall y. z \neq 2y`.
If local variables are given values, then they can be used in negative
contexts. The following is legal
.. code-block:: minizinc
predicate even(var int:x) =
let { var int: y = x div 2; } in x = 2 * y;
constraint not even(z);
Note that the meaning of :mzn:`even` is correct, since if :mzn:`x` is even
then :math:`x = 2 * (x ~\mbox{div}~ 2)`. Note that for this definition
:math:`\neg \mathit{even}(z)` is equivalent to
:math:`\neg \exists y. y = z ~\mbox{div}~ 2 \wedge z = 2y` which is equivalent to
:math:`\exists y. y = z ~\mbox{div}~ 2 \wedge \neg z \neq 2y`, because :math:`y` is
functionally defined by :math:`z`.
Every expression in MiniZinc appears in one of the four
*contexts*: :index:`root <context; !root>`, :index:`positive <context; !positive>`,
:index:`negative <context; !negative>`,
or :index:`mixed <context; !mixed>`.
The context of a non-Boolean expression is simply the context of its nearest
enclosing Boolean expression. The one exception is that the objective
expression appears in a root context (since it has no enclosing Boolean expression).
For the purposes of defining contexts we assume implication expressions
:mzn:`e1 -> e2` are rewritten equivalently as :mzn:`not e1 \/ e2`,
and similarly :mzn:`e1 <- e2` is rewritten as :mzn:`e1 \/ not e2`.
The context for a Boolean expression is given by:
root
root context is the context for any expression :mzn:`e` appearing as
the argument of :mzn:`constraint` or as an
:index:`assignment` item, or appearing as a sub expression :mzn:`e1`
or :mzn:`e2` in an expression :mzn:`e1 /\ e2` occurring in a root context.
Root context Boolean expressions must hold in any model of the problem.
positive
positive context is the context for any expression
appearing as a sub expression :mzn:`e1`
or :mzn:`e2` in an expression :mzn:`e1 \/ e2` occurring in a root
or positive context,
appearing as a sub expression :mzn:`e1`
or :mzn:`e2` in an expression :mzn:`e1 /\ e2` occurring in a positive context,
or appearing as a sub expression :mzn:`e` in an expression
:mzn:`not e` appearing in a negative context.
Positive context Boolean expressions need not hold in a model, but
making them hold will only increase the possibility that the enclosing
constraint holds. A positive context expression has an even number of
negations in the path from the enclosing root context to the expression.
negative
negative context is the context for any expression appearing as a sub expression :mzn:`e1`
or :mzn:`e2` in an expression :mzn:`e1 \/ e2` or :mzn:`e1 /\ e2` occurring in a negative context,
or appearing as a sub expression :mzn:`e` in an expression
:mzn:`not e` appearing in a positive context.
Negative context Boolean expressions need not hold in a model, but
making them false will increase the possibility that the enclosing
constraint holds. A negative context expression has an odd number of
negations in the path from the enclosing root context to the expression.
mixed
mixed context is the context for any Boolean expression appearing
as a subexpression :mzn:`e1` or :mzn:`e2` in :mzn:`e1 <-> e2`, :mzn:`e1 = e2`, or :mzn:`bool2int(e)`.
Mixed context expression are effectively both positive and
negative. This can be seen from the fact that :mzn:`e1 <-> e2` is equivalent
to :mzn:`(e1 /\ e2) \/ (not e1 /\ not e2)` and
:mzn:`x = bool2int(e)` is equivalent to :mzn:`(e /\ x=1) \/ (not e /\ x=0)`.
Consider the code fragment
.. code-block:: minizinc
constraint x > 0 /\ (i <= 4 -> x + bool2int(x > i) = 5);
then :mzn:`x > 0` is in the root context, :mzn:`i <= 4}` is in a negative
context,
:mzn:`x + bool2int(x > i) = 5`
is in a positive context, and :mzn:`x > i` is in a mixed context.
Local Constraints
-----------------
.. index::
single: constraint; local
Let expressions can also be used to include local constraints,
usually to constrain the behaviour of local variables.
For example, consider defining an integer square root function making use
of only multiplication:
.. code-block:: minizinc
function var int: mysqrt(var int:x) =
let { var 0..infinity: y;
constraint x = y * y; } in y;
The local constraints ensure
:mzn:`y` takes the correct value; which is then returned
by the function.
Local constraints can be used in any let expression,
though the most common
usage is in defining functions.
A function with local constraints is often *partial*, which means that it is not defined for all possible inputs. For example, the :mzn:`mysqrt` function above constrains its argument :mzn:`x` to take a value that is in fact the square of an integer. The MiniZinc compiler handles these cases according to the *relational semantics*, which means that the result of applying a partial function may become false in its enclosing Boolean context. For example, consider the following model:
.. code-block:: minizinc
var 1..9: x;
var 0..9: y;
constraint (x=3 /\ y=0) \/ y = mysqrt(x);
Clearly, the intention of the modeller is that :mzn:`x=3, y=0` should be a solution. This requires the compiler to take care not to "lift" the constraint :mzn:`x=y*y` out of the context of the function, because that would prevent it from finding any solution with :mzn:`x=3`. You can verify that the set of solutions contains :mzn:`x=3, y=0` as well as the expected :mzn:`x=1, y=1`, :mzn:`x=4, y=2` and :mzn:`x=9, y=3`.
If you define a *total* function using local constraints, you can give the compiler a hint that allows it to produce more efficient code. For example, you could write the square function (not square root, note the subtle difference) as follows:
.. code-block:: minizinc
function var int: mysqr(var int:x) ::promise_total =
let { var 0..infinity: y;
constraint y = x * x; } in y;
This function is indeed defined for any input value :mzn:`x`. The :mzn:`::promise_total` annotation tells the compiler that it can safely lift all local constraints out of the context of the function call.
.. defblock:: Let expressions
.. index::
single: expression; let
:index:`Local variables <variable;local>`
can be introduced in any expression with a *let expression*
of the form:
.. code-block:: minizincdef
let { <dec>; ... <dec> ; } in <exp>
The declarations :mzndef:`<dec>`
can be declarations of decision variables and
parameters (which must be initialised) or constraint items.
No declaration can make use of a newly declared variable
before it is introduced.
Note that local variables and constraints
cannot occur in tests.
Local variables cannot occur in predicates
or functions that appear in a
:index:`negative <context; negative>` or :index:`mixed <context; mixed>` context,
unless the variable is defined by an expression.
Domain Reflection Functions
---------------------------
.. index::
single: domain; reflection
Other important reflection functions are those that allow us to access
the domains of variables. The expression :mzn:`lb(x)`
returns
a value that is lower than or equal to any value that :mzn:`x` may take in
a solution of the problem. Usually it will just be the
declared lower :index:`bound <variable; bound>` of :mzn:`x`.
If :mzn:`x` is declared as a non-finite type, e.g.
simply :mzn:`var int`
then it is an error.
Similarly the expression :mzn:`dom(x)`
returns a (non-strict)
superset of the possible values of :mzn:`x` in any solution of the problem.
Again it is usually the declared values, and again if it is not
declared as finite then there is an error.
.. \ignore{ % for capture for testing!
.. $ mzn-g12fd reflection.mzn
.. } % $
.. literalinclude:: examples/reflection.mzn
:language: minizinc
:name: ex-reflect
:caption: Using reflection predicates (:download:`reflection.mzn <examples/reflection.mzn>`).
For example, the model show in :numref:`ex-reflect`
may output
.. code-block:: none
y = -10
D = -10..10
----------
or
.. code-block:: none
y = 0
D = {0, 1, 2, 3, 4}
----------
or any answer with
:math:`-10 \leq y \leq 0` and
:math:`\{0, \ldots, 4\} \subseteq D \subseteq \{-10, \ldots, 10\}`.
Variable domain reflection expressions should be used in a manner where they are
correct for any safe approximations, but note this is not checked!
For example the additional code
.. code-block:: minizinc
var -10..10: z;
constraint z <= y;
is not a safe usage of the domain information.
Since using the tighter (correct) approximation leads to
more solutions than the weaker initial approximation.
.. TODO: this sounds wrong!
.. defblock:: Domain reflection
.. index::
single: domain; reflection
There are reflection functions to interrogate
the possible values of expressions containing variables:
- :mzndef:`dom(<exp>)`
returns a safe approximation to the possible values of the expression.
- :mzndef:`lb(<exp>)`
returns a safe approximation to the lower bound value of the expression.
- :mzndef:`ub(<exp>)`
returns a safe approximation to the upper bound value of the expression.
The expressions for :mzn:`lb` and :mzn:`ub`
can only be of types :mzn:`int`, :mzn:`bool`,
:mzn:`float` or :mzn:`set of int`.
For :mzn:`dom` the type cannot be :mzn:`float`.
If one of the variables appearing in :mzndef:`<exp>` has a
:index:`non-finite declared type <type; non-finite>`
(e.g. :mzn:`var int` or :mzn:`var float`)
then an error can occur.
There are also versions that work directly on arrays of expressions (with
similar restrictions):
- :mzndef:`dom_array(<array-exp>)`:
returns a safe approximation to the union of all
possible values of the expressions appearing in the array.
- :mzndef:`lb_array(<array-exp>)`
returns a safe approximation to the lower bound of all expressions appearing
in the array.
- :mzndef:`ub_array(<array-exp>)`
returns a safe approximation to the upper bound of all expressions appearing
in the array.
The combinations of predicates, local variables and domain reflection
allows the definition of complex global constraints by decomposition.
We can define the time based decomposition
of the :mzn:`cumulative`
constraint using the code shown in :numref:`ex-cumul`.
.. literalinclude:: examples/cumulative.mzn
:language: minizinc
:name: ex-cumul
:caption: Defining a ``cumulative`` predicate by decomposition (:download:`cumulative.mzn <examples/cumulative.mzn>`).
The decomposition uses :mzn:`lb` and :mzn:`ub` to determine
the set of times :mzn:`times` over which tasks could range.
It then asserts for each time :mzn:`t` in :mzn:`times` that the
sum of resources for the active tasks at time :mzn:`t` is less than
the bound :mzn:`b`.
Scope
-----
.. index::
single: scope
It is worth briefly mentioning the scope of declarations in MiniZinc.
MiniZinc has a single namespace, so all variables appearing
in declarations are visible in every expression in the model.
MiniZinc introduces locally scoped
variables in a number of ways:
- as :index:`iterator <variable; iterator>`
variables in :index:`comprehension` expressions
- using :mzn:`let` expressions
- as predicate and function :index:`arguments <argument>`
Any local scoped variable overshadows the outer scoped variables
of the same name.
.. literalinclude:: examples/scope.mzn
:language: minizinc
:name: ex-scope
:caption: A model for illustrating scopes of variables (:download:`scope.mzn <examples/scope.mzn>`).
For example, in the model shown in :numref:`ex-scope`
the :mzn:`x` in :mzn:`-x <= y` is the global :mzn:`x`,
the :mzn:`x` in
:mzn:`smallx(x)` is the iterator :mzn:`x in 1..u`,
while the :mzn:`y` in the disjunction is the second
argument of the predicate.