this repo has no description
at develop 33 kB view raw
1.. _sec-flattening: 2 3FlatZinc and Flattening 4======================= 5 6.. \pjs{Maybe show the toolset at this point?} 7 8Constraint solvers do not directly support MiniZinc models, rather in order 9to run a MiniZinc model, it is translated into a simple subset of MiniZinc 10called FlatZinc. FlatZinc reflects the fact that most constraint solvers 11only solve satisfaction 12problems of the form :math:`\bar{exists} c_1 \wedge \cdots \wedge c_m` 13or optimization problems of the form 14:math:`\text{minimize } z \text{ subject to } c_1 \wedge \cdots \wedge c_m`, 15where :math:`c_i` are primitive constraints and :math:`z` is an integer or float 16expression in a restricted form. 17 18.. index:: 19 single: minizinc -c 20 21The ``minizinc`` tool includes the MiniZinc *compiler*, which 22takes a MiniZinc model and data files and creates 23a flattened FlatZinc model which is equivalent to the MiniZinc model with 24the given data, and that appears in the restricted form discussed above. 25Normally the construction of a FlatZinc model which is sent to a solver is 26hidden from the user but you can view the result of flattening a model 27``model.mzn`` with 28its data ``data.dzn`` as follows: 29 30.. code-block:: bash 31 32 minizinc -c model.mzn data.dzn 33 34which creates a FlatZinc model called ``model.fzn``. 35 36In this chapter we explore the process of translation from MiniZinc to FlatZinc. 37 38Flattening Expressions 39---------------------- 40 41The restrictions of the underlying solver mean that complex expressions in 42MiniZinc need to be *flattened* to only use conjunctions of primitive 43constraints which do not themselves contain structured terms. 44 45Consider the following model for ensuring that two circles in a rectangular 46box do not overlap: 47 48.. literalinclude:: examples/cnonoverlap.mzn 49 :language: minizinc 50 :caption: Modelling non overlap of two circles (:download:`cnonoverlap.mzn <examples/cnonoverlap.mzn>`). 51 :name: fig-nonoverlap 52 53 54Simplification and Evaluation 55~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 56 57Given the data file 58 59.. code-block:: minizinc 60 61 width = 10.0; 62 height = 8.0; 63 r1 = 2.0; 64 r2 = 3.0; 65 66the translation to FlatZinc first simplifies the model by replacing all the 67parameters by their values, and evaluating any fixed expression. 68After this simplification the values of parameters are not longer needed. 69An exception to this is large arrays of parametric values. If they are 70used more than once, then the parameter is retained to avoid duplicating the 71large expression. 72 73After simplification the variable and parameter declarations parts of 74the model of :numref:`fig-nonoverlap` become 75 76.. literalinclude:: examples/cnonoverlap.fzn 77 :language: minizinc 78 :start-after: % Variables 79 :end-before: % 80 81.. _sec-flat-sub: 82 83Defining Subexpressions 84~~~~~~~~~~~~~~~~~~~~~~~ 85 86Now no constraint solver directly handles complex constraint expressions 87like the one in :numref:`fig-nonoverlap`. 88Instead, each subexpression in the expression is named, and we create a 89constraint to construct the value of each expression. Let's examine the 90subexpressions of the constraint expression. :mzn:`(x1 - x2)` is a 91subexpression, if we name if :mzn:`FLOAT01` we can define it as 92:mzn:`constraint FLOAT01 = x1 - x2;` Notice that this expression occurs 93twice in the model. We only need to construct the value once, we can then 94reuse it. This is called *common subexpression elimination*. 95The subexpression :mzn:`(x1 - x2)*(x1 - x2)` can be named 96:mzn:`FLOAT02` 97and we can define it as 98:mzn:`constraint FLOAT02 = FLOAT01 * FLOAT01;` 99We can similarly name :mzn:`constraint FLOAT03 = y1 - y2;` 100and 101:mzn:`constraint FLOAT04 = FLOAT03 * FLOAT03;` 102and finally :mzn:`constraint FLOAT05 = FLOAT02 * FLOAT04;`. 103The inequality constraint itself becomes 104:mzn:`constraint FLOAT05 >= 25.0;` since :mzn:`(r1+r2)*(r1 + r2)` 105is calculated as :mzn:`25.0`. 106The flattened constraint is hence 107 108.. code-block:: minizinc 109 110 constraint FLOAT01 = x1 - x2; 111 constraint FLOAT02 = FLOAT01 * FLOAT01; 112 constraint FLOAT03 = y1 - y2; 113 constraint FLOAT04 = FLOAT03 * FLOAT03; 114 constraint FLOAT05 = FLOAT02 * FLOAT04; 115 constraint FLOAT05 >= 25.0 116 117.. _sec-flat-fzn: 118 119FlatZinc constraint form 120~~~~~~~~~~~~~~~~~~~~~~~~ 121 122Flattening as its final step converts the form of the constraint to a 123standard FlatZinc form which is always :math:`p(a_1, \ldots, a_n)` where 124:mzn:`p` is the name of the primitive constraint and :math:`a_1, \ldots, a_n` are the 125arguments. FlatZinc tries to use a minimum of different constraint forms so 126for example the constraint :mzn:`FLOAT01 = x1 - x2` is first rewritten to 127:mzn:`FLOAT01 + x2 = x1` and then output using the :mzn:`float_plus` primitive 128constraint. The resulting constraint form is as follows: 129 130.. literalinclude:: examples/cnonoverlap.fzn 131 :language: minizinc 132 :start-after: % Constraints 133 :end-before: % 134 135Bounds analysis 136~~~~~~~~~~~~~~~ 137 138We are still missing one thing, the 139declarations for the introduced variables :mzn:`FLOAT01`, ..., 140:mzn:`FLOAT05`. While these could just be declared as 141:mzn:`var float`, in order to make the solver's task easier MiniZinc tries to 142determine upper and lower bounds on newly introduced variables, by a simple 143bounds analysis. For example since :mzn:`FLOAT01 = x1 - x2` 144and :math:`2.0 \leq` :mzn:`x1` :math:`\leq 8.0` and :math:`3.0 \leq` :mzn:`x2` :math:`\leq 7.0` then we can see that 145:math:`-5.0 \leq` :mzn:`FLOAT0` :math:`\leq 5.0`. Given this information we can see that 146:math:`-25.0 \leq` :mzn:`FLOAT02` :math:`\leq 25.0` (although note that if we recognized that the 147multiplication was in fact a squaring we could give the much more accurate 148bounds :math:`0.0 \leq` :mzn:`FLOAT02` :math:`\leq 25.0`). 149 150The alert reader may have noticed a discrepancy between the flattened form 151of the constraints in :ref:`sec-flat-sub` and :ref:`sec-flat-fzn`. In 152the latter there is no inequality constraint. Since unary inequalities can 153be fully represented by the bounds of a variable, the inequality forces the 154lower bound of :mzn:`FLOAT05` to be :mzn:`25.0` and is then redundant. The final 155flattened form of the model of :numref:`fig-nonoverlap` is: 156 157.. literalinclude:: examples/cnonoverlap.fzn 158 :language: minizinc 159 160Objectives 161~~~~~~~~~~ 162 163MiniZinc flattens minimization or maximization 164objectives just like constraints. The objective 165expression is flattened and a variable is created for it, just as for other 166expressions. In the FlatZinc output the solve item is always on a single 167variable. See :ref:`sec-let` for an example. 168 169.. \pjs{Do we need an example here?} 170 171Linear Expressions 172------------------ 173 174One of the most important form of constraints, widely used for modelling, 175are linear constraints of the form 176 177 178.. math:: a_1 x_1 + \cdots + a_n x_n \begin{array}[c]{c} = \\ \leq \\ < \end{array} a_0 179 180where :math:`a_i` are integer or floating point constants, and 181:math:`x_i` are integer or floating point variables. 182They are highly expressive, and are the only class of constraint supported 183by (integer) linear programming constraint solvers. 184The translator from MiniZinc to FlatZinc tries to create linear 185constraints, rather than break up linear constraints into many 186subexpressions. 187 188.. \pjs{Maybe use the equation from SEND-MORE-MONEY instead?} 189 190.. literalinclude:: examples/linear.mzn 191 :language: minizinc 192 :caption: A MiniZinc model to illustrate linear constraint flattening (:download:`linear.mzn <examples/linear.mzn>`). 193 :name: fig-lflat 194 195Consider the model shown in :numref:`fig-lflat`. Rather than create 196variables for all the subexpressions :math:`3*x`, :math:`3*x - y`, :math:`x * z`, :math:`3*x - y + x*z`, 197:math:`x + y + z`, :math:`d * (x + y + z)`, :math:`19 + d * (x + y + z)`, 198and :math:`19 + d * (x + y + z) - 4*d` 199translation will attempt to create a large linear constraint which captures 200as much as possible of the constraint in a single FlatZinc 201constraint. 202 203Flattening creates linear expressions as a single unit rather than building 204intermediate variables for each subexpression. It also simplifies the linear 205expression created. Extracting the linear expression from the constraints 206leads to 207 208.. code-block:: minizinc 209 210 var 0..80: INT01; 211 constraint 4*x + z + INT01 <= 23; 212 constraint INT01 = x * z; 213 214Notice how the *nonlinear expression* :math:`x \times z` is extracted as a 215new subexpression and given a name, while the remaining terms are collected 216together so that each variable appears exactly once (and indeed variable :math:`y` 217whose terms cancel is eliminated). 218 219Finally each constraint is written to FlatZinc form obtaining: 220 221.. code-block:: minizinc 222 223 var 0..80: INT01; 224 constraint int_lin_le([1,4,1],[INT01,x,z],23); 225 constraint int_times(x,z,INT01); 226 227.. _sec-unroll: 228 229Unrolling Expressions 230--------------------- 231 232Most models require creating a number of constraints which is 233dependent on the input data. MiniZinc supports these models with array 234types, list and set comprehensions, and aggregation functions. 235 236Consider the following aggregate expression from the production scheduling 237example of :numref:`ex-prod-planning`. 238 239.. code-block:: minizinc 240 241 int: mproducts = max (p in Products) 242 (min (r in Resources where consumption[p,r] > 0) 243 (capacity[r] div consumption[p,r])); 244 245Since this uses generator call syntax we can rewrite it to equivalent 246form which is processed by the compiler: 247 248.. code-block:: minizinc 249 250 int: mproducts = max([ min [ capacity[r] div consumption[p,r] 251 | r in Resources where consumption[p,r] > 0]) 252 | p in Products]); 253 254Given the data 255 256.. code-block:: minizinc 257 258 nproducts = 2; 259 nresources = 5; 260 capacity = [4000, 6, 2000, 500, 500]; 261 consumption= [| 250, 2, 75, 100, 0, 262 | 200, 0, 150, 150, 75 |]; 263 264this first builds the array for :mzn:`p = 1` 265 266.. code-block:: minizinc 267 268 [ capacity[r] div consumption[p,r] 269 | r in 1..5 where consumption[p,r] > 0] 270 271which is :mzn:`[16, 3, 26, 5]` and then calculates the minimum as 3. 272It then builds the same array for :mzn:`p = 2` which is 273:mzn:`[20, 13, 3, 6]` and calculates the minimum as 3. It then constructs the 274array :mzn:`[3, 3]` and calculates the maximum as 3. 275There is no representation of :mzn:`mproducts` in the output FlatZinc, 276this evaluation is simply used to replace :mzn:`mproducts` by the 277calculated value 3. 278 279The most common form of aggregate expression in a constraint model is 280:mzn:`forall`. Forall expressions are unrolled into multiple constraints. 281 282Consider the MiniZinc fragment 283 284.. code-block:: minizinc 285 286 array[1..8] of var 0..9: v = [S,E,N,D,M,O,R,Y]; 287 constraint forall(i,j in 1..8 where i < j)(v[i] != v[j]) 288 289which arises from the SEND-MORE-MONEY example of :numref:`ex-smm` 290using a default decomposition for :mzn:`alldifferent`. 291The :mzn:`forall` expression creates a constraint for each :math:`i, j` pair which meet 292the requirement :math:`i < j`, thus creating 293 294.. code-block:: minizinc 295 296 constraint v[1] != v[2]; % S != E 297 constraint v[1] != v[3]; % S != N 298 ... 299 constraint v[1] != v[8]; % S != Y 300 constraint v[2] != v[3]; % E != N 301 ... 302 constraint v[7] != v[8]; % R != Y 303 304In FlatZinc form this is 305 306.. code-block:: minizinc 307 308 constraint int_neq(S,E); 309 constraint int_neq(S,N); 310 ... 311 constraint int_neq(S,Y); 312 constraint int_neq(E,N); 313 ... 314 constraint int_neq(R,Y); 315 316Notice how the temporary array variables :mzn:`v[i]` are replaced by the 317original variables in the output FlatZinc. 318 319Arrays 320------ 321 322One dimensional arrays 323in MiniZinc can have arbitrary indices as long as they are 324contiguous integers. In FlatZinc all arrays are indexed from :mzn:`1..l` 325where :mzn:`l` is the length of the array. This means that array lookups need to 326be translated to the FlatZinc view of indices. 327 328Consider the following MiniZinc model for balancing a seesaw 329of length :mzn:`2 * l2`, 330with a child of weight :mzn:`cw` kg using exactly :mzn:`m` 1kg weights. 331 332.. code-block:: minizinc 333 334 int: cw; % child weight 335 int: l2; % half seesaw length 336 int: m; % number of 1kg weight 337 array[-l2..l2] of var 0..max(m,cw): w; % weight at each point 338 var -l2..l2: p; % position of child 339 constraint sum(i in -l2..l2)(i * w[i]) = 0; % balance 340 constraint sum(i in -l2..l2)(w[i]) = m + cw; % all weights used 341 constraint w[p] = cw; % child is at position p 342 solve satisfy; 343 344Given :mzn:`cw = 2`, :mzn:`l2 = 2`, and :mzn:`m = 3` the unrolling produces the constraints 345 346.. code-block:: minizinc 347 348 array[-2..2] of var 0..3: w; 349 var -2..2: p 350 constraint -2*w[-2] + -1*w[-1] + 0*w[0] + 1*w[1] + 2*w[2] = 0; 351 constraint w[-2] + w[-1] + w[0] + w[1] + w[2] = 5; 352 constraint w[p] = 2; 353 354But FlatZinc insists that the :mzn:`w` array starts at index 1. 355This means we need to rewrite all the array accesses to use the new index 356value. For fixed array lookups this is easy, for variable array lookups we 357may need to create a new variable. The result for the equations above is 358 359.. code-block:: minizinc 360 361 array[1..5] of var 0..3: w; 362 var -2..2: p 363 var 1..5: INT01; 364 constraint -2*w[1] + -1*w[2] + 0*w[3] + 1*w[4] + 2*w[5] = 0; 365 constraint w[1] + w[2] + w[3] + w[4] + w[5] = 5; 366 constraint w[INT01] = 2; 367 constraint INT01 = p + 3; 368 369Finally we rewrite the constraints into FlatZinc form. Note how the variable 370array index lookup is mapped to :mzn:`array_var_int_element`. 371 372.. code-block:: minizinc 373 374 array[1..5] of var 0..3: w; 375 var -2..2: p 376 var 1..5: INT01; 377 constraint int_lin_eq([2, 1, -1, -2], [w[1], w[2], w[4], w[5]], 0); 378 constraint int_lin_eq([1, 1, 1, 1, 1], [w[1],w[2],w[3],w[4],w[5]], 5); 379 constraint array_var_int_element(INT01, w, 2); 380 constraint int_lin_eq([-1, 1], [INT01, p], -3); 381 382Multidimensional arrays are supported by MiniZinc, but only single 383dimension arrays are supported by FlatZinc (at present). 384This means that multidimensional arrays must be mapped to single dimension 385arrays, and multidimensional array access must be mapped to single dimension 386array access. 387 388Consider the Laplace equation constraints defined for a finite element 389plate model in :numref:`ex-laplace`: 390 391.. literalinclude:: examples/laplace.mzn 392 :language: minizinc 393 :start-after: % arraydec 394 :end-before: % sides 395 396 397Assuming :mzn:`w = 4` and :mzn:`h = 4` this creates the constraints 398 399.. code-block:: minizinc 400 401 array[0..4,0..4] of var float: t; % temperature at point (i,j) 402 constraint 4.0*t[1,1] = t[0,1] + t[1,0] + t[2,1] + t[1,2]; 403 constraint 4.0*t[1,2] = t[0,2] + t[1,1] + t[2,2] + t[1,3]; 404 constraint 4.0*t[1,3] = t[0,3] + t[1,2] + t[2,3] + t[1,4]; 405 constraint 4.0*t[2,1] = t[1,1] + t[2,0] + t[3,1] + t[2,2]; 406 constraint 4.0*t[2,2] = t[1,2] + t[2,1] + t[3,2] + t[2,3]; 407 constraint 4.0*t[2,3] = t[1,3] + t[2,2] + t[3,3] + t[2,4]; 408 constraint 4.0*t[3,1] = t[2,1] + t[3,0] + t[4,1] + t[3,2]; 409 constraint 4.0*t[3,2] = t[2,2] + t[3,1] + t[4,2] + t[3,3]; 410 constraint 4.0*t[3,3] = t[2,3] + t[3,2] + t[4,3] + t[3,4]; 411 412The 2 dimensional array of 25 elements is converted to a one dimensional 413array and the indices are changed accordingly: so index :mzn:`[i,j]` becomes 414:mzn:`[i * 5 + j + 1]`. 415 416.. code-block:: minizinc 417 418 array [1..25] of var float: t; 419 constraint 4.0*t[7] = t[2] + t[6] + t[12] + t[8]; 420 constraint 4.0*t[8] = t[3] + t[7] + t[13] + t[9]; 421 constraint 4.0*t[9] = t[4] + t[8] + t[14] + t[10]; 422 constraint 4.0*t[12] = t[7] + t[11] + t[17] + t[13]; 423 constraint 4.0*t[13] = t[8] + t[12] + t[18] + t[14]; 424 constraint 4.0*t[14] = t[9] + t[13] + t[19] + t[15]; 425 constraint 4.0*t[17] = t[12] + t[16] + t[22] + t[18]; 426 constraint 4.0*t[18] = t[13] + t[17] + t[23] + t[19]; 427 constraint 4.0*t[19] = t[14] + t[18] + t[24] + t[20]; 428 429 430Reification 431----------- 432 433.. index:: 434 single: reification 435 436FlatZinc models involve only variables and parameter declarations 437and a series of primitive constraints. Hence when we model in MiniZinc 438with Boolean connectives other than conjunction, something has to be done. 439The core approach to handling complex formulae that use 440connectives other than conjunction is by 441*reification*. 442Reifying a constraint :math:`c` creates a new constraint equivalent to :math:`b \leftrightarrow c` 443where the Boolean variable :math:`b` is :mzn:`true` 444if the constraint holds and :mzn:`false` if it doesn't hold. 445 446Once we have the capability to *reify* constraints the treatment of 447complex formulae is not different from arithmetic expressions. We create a 448name for each subexpression and a flat constraint to constrain the name to 449take the value of its subexpression. 450 451 452Consider the following constraint expression that occurs in the jobshop 453scheduling example of :numref:`ex-jobshop`. 454 455.. code-block:: minizinc 456 457 constraint %% ensure no overlap of tasks 458 forall(j in 1..tasks) ( 459 forall(i,k in 1..jobs where i < k) ( 460 s[i,j] + d[i,j] <= s[k,j] \/ 461 s[k,j] + d[k,j] <= s[i,j] 462 ) ); 463 464Given the data file 465 466.. code-block:: minizinc 467 468 jobs = 2; 469 tasks = 3; 470 d = [| 5, 3, 4 | 2, 6, 3 |] 471 472then the unrolling creates 473 474.. code-block:: minizinc 475 476 constraint s[1,1] + 5 <= s[2,1] \/ s[2,1] + 2 <= s[1,1]; 477 constraint s[1,2] + 3 <= s[2,2] \/ s[2,2] + 6 <= s[1,2]; 478 constraint s[1,3] + 4 <= s[2,3] \/ s[2,3] + 3 <= s[1,3]; 479 480Reification of the constraints that appear in the disjunction 481creates new Boolean variables to define the values of each expression. 482 483.. code-block:: minizinc 484 485 array[1..2,1..3] of var 0..23: s; 486 constraint BOOL01 <-> s[1,1] + 5 <= s[2,1]; 487 constraint BOOL02 <-> s[2,1] + 2 <= s[1,1]; 488 constraint BOOL03 <-> s[1,2] + 3 <= s[2,2]; 489 constraint BOOL04 <-> s[2,2] + 6 <= s[1,2]; 490 constraint BOOL05 <-> s[1,3] + 4 <= s[2,3]; 491 constraint BOOL06 <-> s[2,3] + 3 <= s[1,3]; 492 constraint BOOL01 \/ BOOL02; 493 constraint BOOL03 \/ BOOL04; 494 constraint BOOL05 \/ BOOL06; 495 496Each primitive constraint can now be mapped to the FlatZinc form. 497Note how the two dimensional array :mzn:`s` is mapped to a one dimensional form. 498 499.. code-block:: minizinc 500 501 array[1..6] of var 0..23: s; 502 constraint int_lin_le_reif([1, -1], [s[1], s[4]], -5, BOOL01); 503 constraint int_lin_le_reif([-1, 1], [s[1], s[4]], -2, BOOL02); 504 constraint int_lin_le_reif([1, -1], [s[2], s[5]], -3, BOOL03); 505 constraint int_lin_le_reif([-1, 1], [s[2], s[5]], -6, BOOL04); 506 constraint int_lin_le_reif([1, -1], [s[3], s[6]], -4, BOOL05); 507 constraint int_lin_le_reif([-1, 1], [s[3], s[6]], -3, BOOL06); 508 constraint array_bool_or([BOOL01, BOOL02], true); 509 constraint array_bool_or([BOOL03, BOOL04], true); 510 constraint array_bool_or([BOOL05, BOOL06], true); 511 512The :mzn:`int_lin_le_reif` is the reified form of the linear constraint 513:mzn:`int_lin_le`. 514 515Most FlatZinc primitive constraints :math:`p(\bar{x})` have a reified form 516:math:`\mathit{p\_reif}(\bar{x},b)` which takes an additional final argument :math:`b` 517and defines the constraint :math:`b \leftrightarrow p(\bar{x})`. 518FlatZinc primitive constraints which define functional relationships, 519like :mzn:`int_plus` and :mzn:`int_plus`, do 520not need to support reification. Instead, the equality with the result of the function is reified. 521 522Another important use of reification arises when we use the coercion 523function :mzn:`bool2int` (either explicitly, or implicitly by using a Boolean 524expression as an integer expression). Flattening creates a Boolean 525variable to hold the value of the Boolean expression argument, as well as an 526integer variable (restricted to :mzn:`0..1`) to hold this value. 527 528Consider the magic series problem of :numref:`ex-magic-series`. 529 530.. literalinclude:: examples/magic-series.mzn 531 :language: minizinc 532 :end-before: solve satisfy 533 534Given :mzn:`n = 2` the unrolling creates 535 536.. code-block:: minizinc 537 538 constraint s[0] = bool2int(s[0] = 0) + bool2int(s[1] = 0); 539 constraint s[1] = bool2int(s[0] = 1) + bool2int(s[1] = 1); 540 541and flattening creates 542 543.. code-block:: minizinc 544 545 constraint BOOL01 <-> s[0] = 0; 546 constraint BOOL03 <-> s[1] = 0; 547 constraint BOOL05 <-> s[0] = 1; 548 constraint BOOL07 <-> s[1] = 1; 549 constraint INT02 = bool2int(BOOL01); 550 constraint INT04 = bool2int(BOOL03); 551 constraint INT06 = bool2int(BOOL05); 552 constraint INT08 = bool2int(BOOL07); 553 constraint s[0] = INT02 + INT04; 554 constraint s[1] = INT06 + INT08; 555 556The final FlatZinc form is 557 558.. code-block:: minizinc 559 560 var bool: BOOL01; 561 var bool: BOOL03; 562 var bool: BOOL05; 563 var bool: BOOL07; 564 var 0..1: INT02; 565 var 0..1: INT04; 566 var 0..1: INT06; 567 var 0..1: INT08; 568 array [1..2] of var 0..2: s; 569 constraint int_eq_reif(s[1], 0, BOOL01); 570 constraint int_eq_reif(s[2], 0, BOOL03); 571 constraint int_eq_reif(s[1], 1, BOOL05); 572 constraint int_eq_reif(s[2], 1, BOOL07); 573 constraint bool2int(BOOL01, INT02); 574 constraint bool2int(BOOL03, INT04); 575 constraint bool2int(BOOL05, INT06); 576 constraint bool2int(BOOL07, INT08); 577 constraint int_lin_eq([-1, -1, 1], [INT02, INT04, s[1]], 0); 578 constraint int_lin_eq([-1, -1, 1], [INT06, INT08, s[2]], 0); 579 solve satisfy; 580 581Predicates 582---------- 583 584An important factor in the support for MiniZinc by many different solvers 585is that global constraints (and indeed FlatZinc constraints) can be 586specialized for the particular solver. 587 588Each solver will either specify a predicate without a definition, or with a 589definition. For example a solver that has a builtin global :mzn:`alldifferent` 590predicate, will include the definition 591 592.. code-block:: minizinc 593 594 predicate alldifferent(array[int] of var int:x); 595 596in its globals library, while a solver using the default decomposition will 597have the definition 598 599.. code-block:: minizinc 600 601 predicate alldifferent(array[int] of var int:x) = 602 forall(i,j in index_set(x) where i < j)(x[i] != x[j]); 603 604Predicate calls :math:`p(\bar{t})` 605are flattened by first constructing variables :math:`v_i` for 606each argument terms :math:`t_i`. 607If the predicate has no definition we simply use a call to the predicate 608with the constructed arguments: :math:`p(\bar{v})`. 609If the predicate has a definition :math:`p(\bar{x}) = \phi(\bar{x})` 610then we replace the predicate call :math:`p(\bar{t})` 611with the body of the predicate with the formal arguments replaced by the 612argument variables, that is :math:`\phi(\bar{v})`. 613Note that if a predicate call :math:`p(\bar{t})` 614appears in a reified position and it has no definition, we check for the 615existence of a reified version of the predicate :math:`\mathit{p\_reif}(\bar{x},b)` in which 616case we use that. 617 618Consider the :mzn:`alldifferent` constraint in the 619SEND-MORE-MONEY example of :numref:`ex-smm` 620 621.. code-block:: minizinc 622 623 constraint alldifferent([S,E,N,D,M,O,R,Y]); 624 625If the solver has a builtin :mzn:`alldifferent` we simply construct a new variable 626for the argument, and replace it in the call. 627 628.. code-block:: minizinc 629 630 array[1..8] of var 0..9: v = [S,E,N,D,M,O,R,Y]; 631 constraint alldifferent(v); 632 633Notice that bounds analysis attempts to find tight bounds on the new array 634variable. The reason for constructing the array argument is if we use the 635same array twice the FlatZinc solver does not have to construct it twice. 636In this case since it is not used twice a later stage of the translation 637will replace :mzn:`v` by its definition. 638 639What if the solver uses the default definition of :mzn:`alldifferent`? 640Then the variable :mzn:`v` is defined as usual, and the predicate call is 641replaced by a renamed copy where :mzn:`v` replaces the formal argument :mzn:`x`. 642The resulting code is 643 644.. code-block:: minizinc 645 646 array[1..8] of var 0..9: v = [S,E,N,D,M,O,R,Y]; 647 constraint forall(i,j in 1..8 where i < j)(v[i] != v[j]) 648 649which we examined in :ref:`sec-unroll`. 650 651Consider the following constraint, where :mzn:`alldifferent` appears in a reified 652position. 653 654.. code-block:: minizinc 655 656 constraint alldifferent([A,B,C]) \/ alldifferent([B,C,D]); 657 658If the solver has a reified form of :mzn:`alldifferent` this will be flattened to 659 660.. code-block:: minizinc 661 662 constraint alldifferent_reif([A,B,C],BOOL01); 663 constraint alldifferent_reif([B,C,D],BOOL02); 664 constraint array_bool_or([BOOL01,BOOL02],true); 665 666Using the default decomposition, the predicate replacement will first create 667 668.. code-block:: minizinc 669 670 array[1..3] of var int: v1 = [A,B,C]; 671 array[1..3] of var int: v2 = [B,C,D]; 672 constraint forall(i,j in 1..3 where i<j)(v1[i] != v1[j]) \/ 673 forall(i,j in 1..3 where i<j)(v2[i] != v2[j]); 674 675which will eventually be flattened to the FlatZinc form 676 677.. code-block:: minizinc 678 679 constraint int_neq_reif(A,B,BOOL01); 680 constraint int_neq_reif(A,C,BOOL02); 681 constraint int_neq_reif(B,C,BOOL03); 682 constraint array_bool_and([BOOL01,BOOL02,BOOL03],BOOL04); 683 constraint int_neq_reif(B,D,BOOL05); 684 constraint int_neq_reif(C,D,BOOL06); 685 constraint array_bool_and([BOOL03,BOOL05,BOOL06],BOOL07); 686 constraint array_bool_or([BOOL04,BOOL07],true); 687 688Note how common subexpression elimination reuses the 689reified inequality :mzn:`B != C` (although there is a better translation which 690lifts the common constraint to the top level conjunction). 691 692.. _sec-let: 693 694Let Expressions 695--------------- 696 697Let expressions are a powerful facility of MiniZinc to introduce new 698variables. This is useful for creating common sub expressions, and for 699defining local variables for predicates. 700During flattening let expressions are translated to variable and constraint 701declarations. The relational semantics of MiniZinc means that these 702constraints must appear as if conjoined 703in the first enclosing Boolean expression. 704 705A key feature of let expressions is that each time they are used they 706create new variables. 707 708Consider the flattening of the code 709 710.. code-block:: minizinc 711 712 constraint even(u) \/ even(v); 713 predicate even(var int: x) = 714 let { var int: y } in x = 2 * y; 715 716First the predicate calls are replaced by their definition. 717 718.. code-block:: minizinc 719 720 constraint (let { var int: y} in u = 2 * y) \/ 721 (let { var int: y} in v = 2 * y); 722 723Next let variables are renamed apart 724 725.. code-block:: minizinc 726 727 constraint (let { var int: y1} in u = 2 * y1) \/ 728 (let { var int: y2} in v = 2 * y2); 729 730Finally variable declarations are extracted to the top level 731 732.. code-block:: minizinc 733 734 var int: y1; 735 var int: y2; 736 constraint u = 2 * y1 \/ v = 2 * y2; 737 738Once the let expression is removed we can flatten as usual. 739 740Remember that let expressions can define values for newly introduced 741variables (and indeed must do so for parameters). 742These implicitly define constraints that must also be flattened. 743 744Consider the complex objective function for wedding seating problem of 745:numref:`ex-wedding2`. 746 747.. code-block:: minizinc 748 749 solve maximize sum(h in Hatreds)( 750 let { var Seats: p1 = pos[h1[h]], 751 var Seats: p2 = pos[h2[h]], 752 var 0..1: same = bool2int(p1 <= 6 <-> p2 <= 6) } in 753 same * abs(p1 - p2) + (1-same) * (abs(13 - p1 - p2) + 1)); 754 755For conciseness we assume only the first two Hatreds, so 756 757.. code-block:: minizinc 758 759 set of int: Hatreds = 1..2; 760 array[Hatreds] of Guests: h1 = [groom, carol]; 761 array[Hatreds] of Guests: h2 = [clara, bestman]; 762 763The first step of flattening is to unroll the :mzn:`sum` expression, giving 764(we keep the guest names and parameter :mzn:`Seats` for clarity only, in 765reality they would be replaced by their definition): 766 767.. code-block:: minizinc 768 769 solve maximize 770 (let { var Seats: p1 = pos[groom], 771 var Seats: p2 = pos[clara], 772 var 0..1: same = bool2int(p1 <= 6 <-> p2 <= 6) } in 773 same * abs(p1 - p2) + (1-same) * (abs(13 - p1 - p2) + 1)) 774 + 775 (let { var Seats: p1 = pos[carol], 776 var Seats: p2 = pos[bestman], 777 var 0..1: same = bool2int(p1 <= 6 <-> p2 <= 6) } in 778 same * abs(p1 - p2) + (1-same) * (abs(13 - p1 - p2) + 1)); 779 780Next each new variable in a let expression is renamed to be distinct 781 782.. code-block:: minizinc 783 784 solve maximize 785 (let { var Seats: p11 = pos[groom], 786 var Seats: p21 = pos[clara], 787 var 0..1: same1 = bool2int(p11 <= 6 <-> p21 <= 6) } in 788 same1 * abs(p11 - p21) + (1-same1) * (abs(13 - p11 - p21) + 1)) 789 + 790 (let { var Seats: p12 = pos[carol], 791 var Seats: p22 = pos[bestman], 792 var 0..1: same2 = bool2int(p12 <= 6 <-> p22 <= 6) } in 793 same2 * abs(p12 - p22) + (1-same2) * (abs(13 - p12 - p22) + 1)); 794 795Variables in the let expression 796are extracted to the top level and 797defining constraints are extracted to the correct 798level (which in this case is also the top level). 799 800.. code-block:: minizinc 801 802 var Seats: p11; 803 var Seats: p21; 804 var 0..1: same1; 805 constraint p12 = pos[clara]; 806 constraint p11 = pos[groom]; 807 constraint same1 = bool2int(p11 <= 6 <-> p21 <= 6); 808 var Seats p12; 809 var Seats p22; 810 var 0..1: same2; 811 constraint p12 = pos[carol]; 812 constraint p22 = pos[bestman]; 813 constraint same2 = bool2int(p12 <= 6 <-> p22 <= 6) } in 814 solve maximize 815 same1 * abs(p11 - p21) + (1-same1) * (abs(13 - p11 - p21) + 1)) 816 + 817 same2 * abs(p12 - p22) + (1-same2) * (abs(13 - p12 - p22) + 1)); 818 819Now we have constructed equivalent MiniZinc code without the use of let 820expressions and the flattening can proceed as usual. 821 822As an illustration of let expressions that do not appear at the top level 823consider the following model 824 825.. code-block:: minizinc 826 827 var 0..9: x; 828 constraint x >= 1 -> let { var 2..9: y = x - 1 } in 829 y + (let { var int: z = x * y } in z * z) < 14; 830 831We extract the variable definitions to the top level and the constraints to 832the first enclosing Boolean context, which here is the right hand side of 833the implication. 834 835.. code-block:: minizinc 836 837 var 0..9: x; 838 var 2..9: y; 839 var int: z; 840 constraint x >= 1 -> (y = x - 1 /\ z = x * y /\ y + z * z < 14); 841 842Note that if we know that the equation defining a variable definition 843cannot fail we can extract it to the top level. This will usually make 844solving substantially faster. 845 846For the example above the constraint :mzn:`y = x - 1` can fail since the domain 847of :mzn:`y` is not big enough for all possible values of :mzn:`x - 1`. But the 848constraint :mzn:`z = x * y` cannot (indeed bounds analysis will give :mzn:`z` 849bounds big enough to hold all possible values of :mzn:`x * y`). 850A better flattening will give 851 852.. code-block:: minizinc 853 854 var 0..9: x; 855 var 2..9: y; 856 var int: z; 857 constraint z = x * y; 858 constraint x >= 1 -> (y = x - 1 /\ y + z * z < 14); 859 860Currently the MiniZinc compiler does this by always defining the declared 861bounds of an 862introduced variable to be big enough for its defining equation to always 863hold and then adding bounds constraints in the correct context for the let 864expression. On the example above this results in 865 866.. code-block:: minizinc 867 868 var 0..9: x; 869 var -1..8: y; 870 var -9..72: z; 871 constraint y = x - 1; 872 constraint z = x * y; 873 constraint x >= 1 -> (y >= 2 /\ y + z * z < 14); 874 875This translation leads to more efficient solving since the possibly 876complex calculation of the let variable is not reified. 877 878Another reason for this approach is that it also works when introduced variables 879appear in negative contexts (as long as they have a definition). 880Consider the following example similar to the previous one 881 882.. code-block:: minizinc 883 884 var 0..9: x; 885 constraint (let { var 2..9: y = x - 1 } in 886 y + (let { var int: z = x * y } in z * z) > 14) -> x >= 5; 887 888The let expressions appear in a negated context, but each introduced 889variable is defined. The flattened code is 890 891.. code-block:: minizinc 892 893 var 0..9: x; 894 var -1..8: y; 895 var -9..72: z; 896 constraint y = x - 1; 897 constraint z = x * y; 898 constraint (y >= 2 /\ y + z * z > 14) -> x >= 5; 899 900Note the analog to the simple approach to let elimination 901does not give a correct translation: 902 903.. code-block:: minizinc 904 905 var 0..9: x; 906 var 2..9: y; 907 var int: z; 908 constraint (y = x - 1 /\ z = x * y /\ y + z * z > 14) -> x >= 5; 909 910gives answers for all possible values of :mzn:`x`, whereas the original 911constraint removes the possibility that :mzn:`x = 4`. 912 913The treatment of *constraint items* in let expressions is analogous 914to defined variables. One can think of a constraint as equivalent to 915defining a new Boolean variable. The definitions of the new Boolean variables 916are extracted to the top level, and the Boolean remains in the correct 917context. 918 919.. code-block:: minizinc 920 921 constraint z > 1 -> let { var int: y, 922 constraint (x >= 0) -> y = x, 923 constraint (x < 0) -> y = -x 924 } in y * (y - 2) >= z; 925 926is treated like 927 928.. code-block:: minizinc 929 930 constraint z > 1 -> let { var int: y, 931 var bool: b1 = ((x >= 0) -> y = x), 932 var bool: b2 = ((x < 0) -> y = -x), 933 constraint b1 /\ b2 934 } in y * (y - 2) >= z; 935 936and flattens to 937 938.. code-block:: minizinc 939 940 constraint b1 = ((x >= 0) -> y = x); 941 constraint b2 = ((x < 0) -> y = -x); 942 constraint z > 1 -> (b1 /\ b2 /\ y * (y - 2) >= z);