this repo has no description
1.. _sec-flattening:
2
3FlatZinc y Flattening
4=======================
5
6.. \pjs{Maybe show the toolset at this point?}
7
8
9Los solucionadores de restricciones no son compatibles directamente con los modelos MiniZinc. Sino que para ejecutar un modelo MiniZinc se debe de traducir en un subconjunto simple de MiniZinc llamado FlatZinc. FlatZinc refleja el hecho de que la mayoría de los solucionadores de restricciones solo resuelven problemas de satisfacción de la forma :math:`\bar{exists } c_1 \wedge \cdots \wedge c_m` o problemas de optimización de la forma :math:`\text {minimize} z \text{ sujeto a } c_1 \wedge \cdots \wedge c_m`,
10donde :math:`c_i` son restricciones primitivas y :math:`z` es un entero o expresión flotante en una forma restringida.
11
12.. index::
13 single: mzn2fzn
14
15
16La herramienta ``mzn2fzn`` toma un modelo de MiniZinc y los archivos de datos, posteriormente crea un modelo FlatZinc aplanado que es equivalente al modelo MiniZinc con los datos dados, y que aparece en la forma restringida discutida anteriormente.
17Normalmente, la construcción de un modelo de FlatZinc que se envía a un solucionador está oculta para el usuario, pero puede ver el resultado de un modelo ``model.mzn`` con sus datos ``data.dzn`` de la siguiente manera:
18
19.. code-block:: bash
20
21 mzn2fzn model.mzn data.dzn
22
23El cual crea un modelo FlatZinc llamado ``model.fzn``.
24
25En este capítulo, exploramos el proceso de traducción de MiniZinc a FlatZinc.
26
27
28
29Flattening Expressiones
30-----------------------
31
32Las restricciones del solucionador subyacente significan que las expresiones complejas en MiniZinc deben *flattened* (aplanarse o aplanar) para usar solo conjunciones de restricciones primitivas que no contienen por sí mismas términos estructurados.
33
34Considere el siguiente modelo para asegurarse de que dos círculos en un recuadro rectangular no se superpongan:
35
36.. literalinclude:: examples/cnonoverlap_es.mzn
37 :language: minizinc
38 :caption: Modelado de no superposición de dos círculos (:download:`cnonoverlap_es.mzn <examples/cnonoverlap_es.mzn>`).
39 :name: fig-nonoverlap
40
41
42Simplificación y Evaluación
43~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44
45Dado el siguiente archivo de datos:
46
47.. code-block:: minizinc
48
49 width = 10.0;
50 height = 8.0;
51 r1 = 2.0;
52 r2 = 3.0;
53
54La traducción a FlatZinc primero simplifica el modelo reemplazando todos los parámetros por sus valores y evaluando cualquier expresión fija.
55Después de esta simplificación, los valores de los parámetros ya no son necesarios.
56Una excepción a esto, son las grandes matrices de valores paramétricos. Si se usan más de una vez, el parámetro se conserva para evitar la duplicación expresiones grandes.
57
58
59Después de la simplificación, las partes de declaraciones de variables y parámetros del modelo de :numref:`fig-nonoverlap` se transforman en
60
61.. literalinclude:: examples/cnonoverlap_es.fzn
62 :language: minizinc
63 :start-after: % Variables
64 :end-before: %
65
66.. _sec-flat-sub:
67
68
69
70Definiendo Subexpresiones
71~~~~~~~~~~~~~~~~~~~~~~~~~
72
73Ahora, ningún solucionador de restricciones maneja directamente expresiones de restricciones complejas como la de :numref:`fig-nonoverlap`. En cambio, cada subexpresión en la expresión es nombrada, y posteriormente creamos una restricción para construir el valor de cada expresión. Examinemos las subexpresiones de la expresión de restricción.
74:mzn:`(x1 - x2)` es una subexpresión, si nombramos :mzn:`FLOAT01` podemos definirlo como :mzn:`constraint FLOAT01 = x1 - x2;`. Observe que esta expresión aparece dos veces en el modelo. Solo necesitamos calcular el valor una vez, luego podemos reutilizarlo. Esto se llama *eliminación de subexpresiones comunes*. La subexpresión :mzn:`(x1 - x2) * (x1 - x2)` se puede llamar :mzn:`FLOAT02` y podemos definirla como :mzn:`constraint FLOAT02 = FLOAT01 * FLOAT01;`. Podemos nombrar de forma similar :mzn:`restricción FLOAT03 = y1 - y2;` y :mzn:`restricción FLOAT04 = FLOAT03 * FLOAT03;` y finalmente :mzn:`restricción FLOAT05 = FLOAT02 * FLOAT04;`. La restricción de desigualdad se convierte en :mzn:`restricción FLOAT05> = 25.0;` desde :mzn:`(r1 + r2) * (r1 + r2)` se calcula como :mzn:`25.0`.
75
76La restricción aplanada es por lo tanto:
77
78.. code-block:: minizinc
79
80 constraint FLOAT01 = x1 - x2;
81 constraint FLOAT02 = FLOAT01 * FLOAT01;
82 constraint FLOAT03 = y1 - y2;
83 constraint FLOAT04 = FLOAT03 * FLOAT03;
84 constraint FLOAT05 = FLOAT02 * FLOAT04;
85 constraint FLOAT05 >= 25.0
86
87.. _sec-flat-fzn:
88
89
90Forma de restricciones FlatZinc
91~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
92
93El flatening como su paso final, convierte la forma de la restricción en una forma estándar de FlatZinc que siempre es :math:`p(a_1, \ldots, a_n)`, donde
94:mzn:`p` es el nombre de la restricción primitiva y :math:`a_1, \ldots, a_n` son los argumentos.
95FlatZinc intenta usar un mínimo de diferentes formas de restricción, por ejemplo, la restricción :mzn:`FLOAT01 = x1 - x2` se reescribe primero como :mzn:`FLOAT01 + x2 = x1` y luego se genera usando la restricción primitiva:mzn:`float_plus`.
96La forma de restricción resultante es la siguiente:
97
98.. literalinclude:: examples/cnonoverlap_es.fzn
99 :language: minizinc
100 :start-after: % Restricciones
101 :end-before: %
102
103Análisis de límites
104~~~~~~~~~~~~~~~~~~~
105
106Todavía nos falta una cosa, las declaraciones de las variables introducidas :mzn:`FLOAT01`, ..., :mzn:`FLOAT05`. Si bien estos podrían simplemente declararse como :mzn:`var float`, para facilitar la tarea del solucionador MiniZinc intenta determinar los límites superiores e inferiores de las variables recién introducidas, mediante un simple análisis de límites.
107Por ejemplo desde :mzn:`FLOAT01 = x1 - x2` y :math:`2.0 \leq` :mzn:`x1` :math:`\leq 8.0` y :math:`3.0 \leq` :mzn:`x2`
108:math:`\leq 7.0` luego podemos ver que
109:math:`-5.0 \leq` :mzn:`FLOAT0` :math:`\leq 5.0`. Dada esta información, podemos ver que
110:math:`-25.0 \leq` :mzn:`FLOAT02` :math:`\leq 25.0` (aunque tenga en cuenta que si reconociéramos que la multiplicación era en realidad una cuadratura podríamos dar mucho límites más precisos :math:`0.0 \leq` :mzn:`FLOAT02` :math:`\leq 25.0`).
111
112El lector alerta puede haber notado una discrepancia entre la forma aplanada de las restricciones en :ref:`sec-flat-sub` y :ref:`sec-flat-fzn`. En este último, no hay restricción de desigualdad. Como las desigualdades unarias pueden representarse completamente por los límites de una variable, la desigualdad fuerza al límite inferior de :mzn:`FLOAT05` a ser :mzn:`25.0` y luego es redundante. La forma aplanada final del modelo de :numref:`fig-nonoverlap` es:
113
114.. literalinclude:: examples/cnonoverlap_es.fzn
115 :language: minizinc
116
117Objetivos
118~~~~~~~~~~
119
120MiniZinc aplana los objetivos de minimización o maximización al igual que las restricciones. La expresión objetivo se aplana y se crea una variable para ella, al igual que para otras expresiones. En la salida de FlatZinc, el elemento de resolver siempre está en una sola variable. Ver :ref:`sec-let` para un ejemplo.
121
122.. \pjs{Do we need an example here?}
123
124Expresiones lineales
125--------------------
126
127Una de las restricciones más importantes, ampliamente utilizada para el modelado, son las restricciones lineales de la forma
128
129.. math:: a_1 x_1 + \cdots + a_n x_n \begin{array}[c]{c} = \\ \leq \\ < \end{array} a_0
130
131En donde :math:`a_i` son constantes de coma flotante o entero, y :math:`x_i` son variables enteras o de coma flotante. Son altamente expresivos, y son la única clase de restricción soportada por solucionadores de restricciones de programación lineal (entero). El traductor de MiniZinc a FlatZinc intenta crear restricciones lineales, en lugar de dividir las restricciones lineales en muchas subexpresiones.
132
133.. \pjs{Maybe use the equation from SEND-MORE-MONEY instead?}
134
135.. literalinclude:: examples/linear_es.mzn
136 :language: minizinc
137 :caption: Un modelo MiniZinc para ilustrar el aplanamiento de restricciones lineales (:download:`linear_es.mzn <examples/linear_es.mzn>`).
138 :name: fig-lflat
139
140Considere el modelo que se muestra en :numref:`fig-lflat`. En lugar de crear variables para todas las subexpresiones :math:`3 * x`, :math:`3 * x - y`, :math:`x * z`, :math:`3 * x - y + x * z `, :math:`x + y + z`, :math:`d * (x + y + z)`, :math:`19 + d * (x + y + z)`, y :math:`19 + d * (x + y + z) - 4 * d` la traducción intentará crear una restricción lineal grande que capture la mayor cantidad posible de la restricción en una única restricción FlatZinc.
141
142
143El flatening crea expresiones lineales como una sola unidad en lugar de generar variables intermedias para cada subexpresión. También simboliza la expresión lineal creada. La extracción de la expresión lineal de las restricciones conduce a:
144
145.. code-block:: minizinc
146
147 var 0..80: INT01;
148 constraint 4*x + z + INT01 <= 23;
149 constraint INT01 = x * z;
150
151Observe cómo la *expresión no lineal* :math:`x \times z` se extrae como una nueva subexpresión y se le da un nombre. Mientras que los términos restantes se recopilan juntos para que cada variable aparezca exactamente una vez (y de hecho variable :math:`y` cuyos términos cancelar se eliminan).
152
153Finalmente, cada restricción se escribe en la forma FlatZinc obteniendo:
154
155.. code-block:: minizinc
156
157 var 0..80: INT01;
158 constraint int_lin_le([1,4,1],[INT01,x,z],23);
159 constraint int_times(x,z,INT01);
160
161.. _sec-unroll:
162
163
164
165Desenrollar expresiones
166-----------------------
167
168La mayoría de los modelos requieren la creación de una serie de restricciones que dependen de los datos de entrada. MiniZinc admite estos modelos con tipos de matriz, listas y conjuntos de comprensiones y funciones de agregación.
169
170Considere la siguiente expresión desde el ejemplo de programación de planificación :numref:`ex-prod-planning`.
171
172.. code-block:: minizinc
173
174 int: mproducts = max (p in Products)
175 (min (r in Resources where consumption[p,r] > 0)
176 (capacity[r] div consumption[p,r]));
177
178Como esto utiliza la sintaxis de llamada del generador, podemos reescribirla en forma equivalente, que es procesada por ``mzn2fzn``:
179
180.. code-block:: minizinc
181
182 int: mproducts = max([ min [ capacity[r] div consumption[p,r]
183 | r in Resources where consumption[p,r] > 0])
184 | p in Products]);
185
186Teniendo en cuenta los datos
187
188.. code-block:: minizinc
189
190 nproducts = 2;
191 nresources = 5;
192 capacity = [4000, 6, 2000, 500, 500];
193 consumption= [| 250, 2, 75, 100, 0,
194 | 200, 0, 150, 150, 75 |];
195
196Esto primero construye la matriz de :mzn:`p = 1`
197
198.. code-block:: minizinc
199
200 [ capacity[r] div consumption[p,r]
201 | r in 1..5 where consumption[p,r] > 0]
202
203Que es :mzn:`[16, 3, 26, 5]` y luego calcula el mínimo como 3.
204Luego construye la misma matriz para :mzn:`p = 2` que es :mzn:`[20, 13, 3, 6]` y calcula el mínimo como 3. Luego construye la matriz :mzn:`[3, 3]` y calcula el máximo como 3. No hay representación de :mzn:`mproducts` en la salida FlatZinc, esta evaluación se usa simplemente para reemplazar :mzn:`mproducts` por el valor calculado 3.
205
206La forma más común de expresión agregada en un modelo de restricción es :mzn:`forall`. Todas las expresiones se desenrollan en múltiples restricciones.
207
208Considere el siguiente fragmento de MiniZinc
209
210.. code-block:: minizinc
211
212 array[1..8] of var 0..9: v = [S,E,N,D,M,O,R,Y];
213 constraint forall(i,j in 1..8 where i < j)(v[i] != v[j])
214
215Que surge del ejemplo SEND-MORE-MONEY de :numref:`ex-smm` utilizando una descomposición predeterminada para :mzn:`alldifferent`.
216La expresión :mzn:`forall` crea una restricción para cada par :math:`i, j` que cumple con el requisito :math:`i <j`, creando así:
217
218.. code-block:: minizinc
219
220 constraint v[1] != v[2]; % S != E
221 constraint v[1] != v[3]; % S != N
222 ...
223 constraint v[1] != v[8]; % S != Y
224 constraint v[2] != v[3]; % E != N
225 ...
226 constraint v[7] != v[8]; % R != Y
227
228En la forma de FlatZinc esto es:
229
230.. code-block:: minizinc
231
232 constraint int_neq(S,E);
233 constraint int_neq(S,N);
234 ...
235 constraint int_neq(S,Y);
236 constraint int_neq(E,N);
237 ...
238 constraint int_neq(R,Y);
239
240Observe cómo las variables temporales del arreglo :mzn:`v[i]` son reemplazadas por las variables originales en el resultado FlatZinc.
241
242
243Arreglos
244--------
245
246Los arrays unidimensionales en MiniZinc pueden tener índices arbitrarios siempre que sean enteros contiguos. En FlatZinc todas las matrices están indexadas desde :mzn:`1..l` donde :mzn:`l` es la longitud de la matriz. Esto significa que las búsquedas de matriz deben traducirse a la vista de índices de FlatZinc.
247
248Consider the following MiniZinc model for balancing a seesaw of length :mzn:`2 * l2`, with a child of weight :mzn:`cw` kg using exactly :mzn:`m` 1kg weights.
249
250Considere el siguiente modelo MiniZinc para equilibrar un balance de longitud :mzn:`2 * l2`, con un pequeño de peso :mzn:`cw` kg usando exactamente un peso :mzn:`m` de 1kg.
251
252.. code-block:: minizinc
253
254 int: cw; % Pequeño peso
255 int: l2; % La mitad de la longitud del balance
256 int: m; % Cantidad de 1 kg de peso
257 array[-l2..l2] of var 0..max(m,cw): w; % Peso en cada punto
258 var -l2..l2: p; % Posición del peso.
259 constraint sum(i in -l2..l2)(i * w[i]) = 0; % Balance
260 constraint sum(i in -l2..l2)(w[i]) = m + cw;% Todos los pesos usados.
261 constraint w[p] = cw; % El pequeño peso está en la posición p
262 solve satisfy;
263
264Dado :mzn:`cw = 2`, :mzn:`l2 = 2`, y :mzn:`m = 3` el desenrollado produce las restricciones
265
266.. code-block:: minizinc
267
268 array[-2..2] of var 0..3: w;
269 var -2..2: p
270 constraint -2*w[-2] + -1*w[-1] + 0*w[0] + 1*w[1] + 2*w[2] = 0;
271 constraint w[-2] + w[-1] + w[0] + w[1] + w[2] = 5;
272 constraint w[p] = 2;
273
274Pero FlatZinc insiste en que la matriz :mzn:`w` comienza en el índice 1.
275Esto significa que necesitamos reescribir todos los accesos a la matriz para usar el nuevo valor de índice. Para las búsquedas de arreglos fijos esto es fácil, para las búsquedas de arreglos variables, podemos necesitar crear una nueva variable. El resultado para las ecuaciones anteriores es
276
277.. code-block:: minizinc
278
279 array[1..5] of var 0..3: w;
280 var -2..2: p
281 var 1..5: INT01;
282 constraint -2*w[1] + -1*w[2] + 0*w[3] + 1*w[4] + 2*w[5] = 0;
283 constraint w[1] + w[2] + w[3] + w[4] + w[5] = 5;
284 constraint w[INT01] = 2;
285 constraint INT01 = p + 3;
286
287Finalmente reescribimos las restricciones en forma de FlatZinc. Observe cómo la búsqueda del índice de matriz variable está mapeada a :mzn:`array_var_int_element`.
288
289.. code-block:: minizinc
290
291 array[1..5] of var 0..3: w;
292 var -2..2: p
293 var 1..5: INT01;
294 constraint int_lin_eq([2, 1, -1, -2], [w[1], w[2], w[4], w[5]], 0);
295 constraint int_lin_eq([1, 1, 1, 1, 1], [w[1],w[2],w[3],w[4],w[5]], 5);
296 constraint array_var_int_element(INT01, w, 2);
297 constraint int_lin_eq([-1, 1], [INT01, p], -3);
298
299Las matrices multidimensionales son compatibles con MiniZinc, pero FlatZinc solo soporta las matrices de una sola dimensión (en este momento).
300Esto significa que las matrices multidimensionales se deben asignar a matrices de una dimensión, y el acceso de matriz multidimensional se debe asignar al acceso a una matriz de una dimensión.
301
302Considere las restricciones de ecuación de Laplace definidas para un modelo de placa de elementos finitos en :numref:`ex-laplace`:
303
304.. literalinclude:: examples/laplace_es.mzn
305 :language: minizinc
306 :start-after: % Array declaration.
307 :end-before: % Lados.
308
309Asumiendo :mzn:`w = 4` y :mzn:`h = 4` esto crea las restricciones:
310
311.. code-block:: minizinc
312
313 array[0..4,0..4] of var float: t; % temperatura en el punto (i,j)
314 constraint 4.0*t[1,1] = t[0,1] + t[1,0] + t[2,1] + t[1,2];
315 constraint 4.0*t[1,2] = t[0,2] + t[1,1] + t[2,2] + t[1,3];
316 constraint 4.0*t[1,3] = t[0,3] + t[1,2] + t[2,3] + t[1,4];
317 constraint 4.0*t[2,1] = t[1,1] + t[2,0] + t[3,1] + t[2,2];
318 constraint 4.0*t[2,2] = t[1,2] + t[2,1] + t[3,2] + t[2,3];
319 constraint 4.0*t[2,3] = t[1,3] + t[2,2] + t[3,3] + t[2,4];
320 constraint 4.0*t[3,1] = t[2,1] + t[3,0] + t[4,1] + t[3,2];
321 constraint 4.0*t[3,2] = t[2,2] + t[3,1] + t[4,2] + t[3,3];
322 constraint 4.0*t[3,3] = t[2,3] + t[3,2] + t[4,3] + t[3,4];
323
324La matriz bidimensional de 25 elementos se convierte en una matriz unidimensional y los índices :mzn:`[i, j]` se convierte en :mzn:`[i * 5 + j + 1]`.
325
326.. code-block:: minizinc
327
328 array [1..25] of var float: t;
329 constraint 4.0*t[7] = t[2] + t[6] + t[12] + t[8];
330 constraint 4.0*t[8] = t[3] + t[7] + t[13] + t[9];
331 constraint 4.0*t[9] = t[4] + t[8] + t[14] + t[10];
332 constraint 4.0*t[12] = t[7] + t[11] + t[17] + t[13];
333 constraint 4.0*t[13] = t[8] + t[12] + t[18] + t[14];
334 constraint 4.0*t[14] = t[9] + t[13] + t[19] + t[15];
335 constraint 4.0*t[17] = t[12] + t[16] + t[22] + t[18];
336 constraint 4.0*t[18] = t[13] + t[17] + t[23] + t[19];
337 constraint 4.0*t[19] = t[14] + t[18] + t[24] + t[20];
338
339
340Reificación
341-----------
342
343.. index::
344 single: reification
345
346Los modelos FlatZinc implican solo variables y declaraciones de parámetros y una serie de restricciones primitivas. Por lo tanto, cuando modelamos en MiniZinc con conectivas booleanas distintas a la conjunción, algo tiene que hacerse. El enfoque principal para manejar fórmulas complejas que usan conectivos que no sean la conjunción es por *reificación*. Reificar una restricción :math:`c` crea una nueva restricción equivalente a :math:`b \leftrightarrow c` donde la variable booleana :math:`b` es :mzn:`true` si la restricción se cumple y :mzn:`false` si no se sostiene.
347
348Una vez que tenemos la capacidad de *reificar* las restricciones, el tratamiento de fórmulas complejas no es diferente de las expresiones aritméticas. Creamos un nombre para cada subexpresión y una restricción plana para restringir el nombre y tomar el valor de su subexpresión.
349
350Considere la siguiente expresión de restricción que ocurre en el ejemplo de programación de jobshop de :numref:`ex-jobshop`.
351
352
353
354
355.. code-block:: minizinc
356
357 constraint %% Asegurar que no haya superposición de tareas
358 forall(j in 1..tasks) (
359 forall(i,k in 1..jobs where i < k) (
360 s[i,j] + d[i,j] <= s[k,j] \/
361 s[k,j] + d[k,j] <= s[i,j]
362 ) );
363
364Dado el archivo de datos:
365
366.. code-block:: minizinc
367
368 jobs = 2;
369 tasks = 3;
370 d = [| 5, 3, 4 | 2, 6, 3 |]
371
372Entonces el desenrollar crea:
373
374.. code-block:: minizinc
375
376 constraint s[1,1] + 5 <= s[2,1] \/ s[2,1] + 2 <= s[1,1];
377 constraint s[1,2] + 3 <= s[2,2] \/ s[2,2] + 6 <= s[1,2];
378 constraint s[1,3] + 4 <= s[2,3] \/ s[2,3] + 3 <= s[1,3];
379
380La reificación de las restricciones que aparecen en la disyunción crea nuevas variables booleanas para definir los valores de cada expresión.
381
382.. code-block:: minizinc
383
384 array[1..2,1..3] of var 0..23: s;
385 constraint BOOL01 <-> s[1,1] + 5 <= s[2,1];
386 constraint BOOL02 <-> s[2,1] + 2 <= s[1,1];
387 constraint BOOL03 <-> s[1,2] + 3 <= s[2,2];
388 constraint BOOL04 <-> s[2,2] + 6 <= s[1,2];
389 constraint BOOL05 <-> s[1,3] + 4 <= s[2,3];
390 constraint BOOL06 <-> s[2,3] + 3 <= s[1,3];
391 constraint BOOL01 \/ BOOL02;
392 constraint BOOL03 \/ BOOL04;
393 constraint BOOL05 \/ BOOL06;
394
395Cada restricción primitiva ahora se puede asignar a la forma FlatZinc.
396Observe cómo la matriz de dos dimensiones :mzn:`s` está mapeada a una forma unidimensional.
397Cada restricción se puede asignar a la forma FlatZinc.
398Observe cómo la matriz de dos dimensiones :mzn:`s` está mapeada a una forma unidimensional.
399
400.. code-block:: minizinc
401
402 array[1..6] of var 0..23: s;
403 constraint int_lin_le_reif([1, -1], [s[1], s[4]], -5, BOOL01);
404 constraint int_lin_le_reif([-1, 1], [s[1], s[4]], -2, BOOL02);
405 constraint int_lin_le_reif([1, -1], [s[2], s[5]], -3, BOOL03);
406 constraint int_lin_le_reif([-1, 1], [s[2], s[5]], -6, BOOL04);
407 constraint int_lin_le_reif([1, -1], [s[3], s[6]], -4, BOOL05);
408 constraint int_lin_le_reif([-1, 1], [s[3], s[6]], -3, BOOL06);
409 constraint array_bool_or([BOOL01, BOOL02], true);
410 constraint array_bool_or([BOOL03, BOOL04], true);
411 constraint array_bool_or([BOOL05, BOOL06], true);
412
413El :mzn:`int_lin_le_reif` es la forma reificada de la restricción lineal :mzn:` int_lin_le`.
414
415
416La mayoría de las restricciones primitivas de FlatZinc :math:`p(\bar{x})` tiene una forma reificada :math:`\mathit{p\_reif}(\bar{x},b)` que toma un argumento final adicional :math:`b` y define la restricción :math:`b \leftrightarrow p(\bar{x})`. Las restricciones primitivas de FlatZinc que definen relaciones funcionales, como :mzn:`int_plus` y :mzn:`int_plus`, no necesitan admitir la reificación. En cambio, la igualdad con el resultado de la función se reifica.
417
418Otro uso importante de la reificación surge cuando usamos la función de coerción :mzn:`bool2int` (explícita o implícitamente usando una expresión booleana como una expresión entera). El aplanamiento crea una variable booleana para contener el valor del argumento de expresión booleana, así como una variable entera (restringida a :mzn:`0..1`) para mantener este valor.
419
420
421Considere el problema de la serie mágica de :numref:`ex-magic-series`.
422
423.. literalinclude:: examples/magic-series_es.mzn
424 :language: minizinc
425 :end-before: solve satisfy
426
427Dado :mzn:`n = 2` el desenrollado crea
428
429.. code-block:: minizinc
430
431 constraint s[0] = bool2int(s[0] = 0) + bool2int(s[1] = 0);
432 constraint s[1] = bool2int(s[0] = 1) + bool2int(s[1] = 1);
433
434y el aplanamiento crea:
435
436.. code-block:: minizinc
437
438 constraint BOOL01 <-> s[0] = 0;
439 constraint BOOL03 <-> s[1] = 0;
440 constraint BOOL05 <-> s[0] = 1;
441 constraint BOOL07 <-> s[1] = 1;
442 constraint INT02 = bool2int(BOOL01);
443 constraint INT04 = bool2int(BOOL03);
444 constraint INT06 = bool2int(BOOL05);
445 constraint INT08 = bool2int(BOOL07);
446 constraint s[0] = INT02 + INT04;
447 constraint s[1] = INT06 + INT08;
448
449El formulario final de FlatZinc es:
450
451.. code-block:: minizinc
452
453 var bool: BOOL01;
454 var bool: BOOL03;
455 var bool: BOOL05;
456 var bool: BOOL07;
457 var 0..1: INT02;
458 var 0..1: INT04;
459 var 0..1: INT06;
460 var 0..1: INT08;
461 array [1..2] of var 0..2: s;
462 constraint int_eq_reif(s[1], 0, BOOL01);
463 constraint int_eq_reif(s[2], 0, BOOL03);
464 constraint int_eq_reif(s[1], 1, BOOL05);
465 constraint int_eq_reif(s[2], 1, BOOL07);
466 constraint bool2int(BOOL01, INT02);
467 constraint bool2int(BOOL03, INT04);
468 constraint bool2int(BOOL05, INT06);
469 constraint bool2int(BOOL07, INT08);
470 constraint int_lin_eq([-1, -1, 1], [INT02, INT04, s[1]], 0);
471 constraint int_lin_eq([-1, -1, 1], [INT06, INT08, s[2]], 0);
472 solve satisfy;
473
474Predicados
475----------
476
477Un factor importante en el soporte para MiniZinc por muchos solucionadores diferentes es que las restricciones globales (y de hecho las restricciones de FlatZinc) pueden ser especializadas para el solucionador particular.
478
479Cada solucionador especificará un predicado sin una definición, o con una definición. Por ejemplo, un solucionador que tiene un predicado global integrado :mzn:`alldifferent`, incluirá la definición
480
481.. code-block:: minizinc
482
483 predicate alldifferent(array[int] of var int:x);
484
485en su biblioteca global, mientras que un solucionador que usa la descomposición por defecto tendrá la definición
486
487.. code-block:: minizinc
488
489 predicate alldifferent(array[int] of var int:x) =
490 forall(i,j in index_set(x) where i < j)(x[i] != x[j]);
491
492Las llamadas a predicados :math:`p(\bar{t})` se aplanan construyendo primero las variables :math:`v_i` para cada argumento :math:`t_i`. Si el predicado no tiene definición, simplemente usamos una llamada al predicado con los argumentos construidos: :math:`p(\bar{v})`.
493
494Si el predicado tiene una definición :math:`p(\bar{x}) = \phi(\bar{x})` luego reemplazamos la llamada del predicado :math:`p(\bar{t})` con el cuerpo del predicado con los argumentos formales reemplazados por las variables del argumento, es decir :math:`\phi(\bar{v})`.
495
496Tenga en cuenta que si una llamada de predicado :math:`p(\bar{t})` aparece en una posición reificada y no tiene definición, verificamos la existencia de una versión reificada del predicado :math:`\mathit{p\_reif}(\bar{x},b)` en cuyo caso usamos eso.
497
498Consideremos la restricción :mzn:`alldifferent` en el ejemplo de SEND-MORE-MONEY de :numref:`ex-smm`
499
500.. code-block:: minizinc
501
502 constraint alldifferent([S,E,N,D,M,O,R,Y]);
503
504Si el solucionador tiene una función incorporada :mzn:`alldifferent` simplemente construimos una nueva variable para el argumento y la reemplazamos en la llamada.
505
506.. code-block:: minizinc
507
508 array[1..8] of var 0..9: v = [S,E,N,D,M,O,R,Y];
509 constraint alldifferent(v);
510
511Observe que el análisis de límites intenta encontrar límites estrechos en la nueva variable de matriz. La razón para construir el argumento de la matriz es si usamos la misma matriz dos veces, el solucionador FlatZinc no tiene que construirla dos veces.
512En este caso, dado que no se usa dos veces, una etapa posterior de la traducción reemplazará :mzn:`v` por su definición.
513
514¿Qué ocurre si el solucionador usa la definición predeterminada de :mzn:`alldifferent`?.
515
516Entonces la variable :mzn:`v` se define como de costumbre, y la llamada del predicado se reemplaza por una copia renombrada donde :mzn:`v` reemplaza el argumento formal :mzn:`x`.
517
518El código resultante es:
519
520.. code-block:: minizinc
521
522 array[1..8] of var 0..9: v = [S,E,N,D,M,O,R,Y];
523 constraint forall(i,j in 1..8 where i < j)(v[i] != v[j])
524
525Que examinamos en :ref:`sec-unroll`.
526
527Considere la siguiente restricción, donde :mzn:`alldifferent` aparece en una posición reificada.
528
529.. code-block:: minizinc
530
531 constraint alldifferent([A,B,C]) \/ alldifferent([B,C,D]);
532
533Si el solucionador tiene una forma reificada de :mzn:`alldifferent` esto será flattend a:
534
535.. code-block:: minizinc
536
537 constraint alldifferent_reif([A,B,C],BOOL01);
538 constraint alldifferent_reif([B,C,D],BOOL02);
539 constraint array_bool_or([BOOL01,BOOL02],true);
540
541Usando la descomposición por defecto, el reemplazo del predicado creará primero:
542
543.. code-block:: minizinc
544
545 array[1..3] of var int: v1 = [A,B,C];
546 array[1..3] of var int: v2 = [B,C,D];
547 constraint forall(i,j in 1..3 where i<j)(v1[i] != v1[j]) \/
548 forall(i,j in 1..3 where i<j)(v2[i] != v2[j]);
549
550que eventualmente será aplanado a la forma FlatZinc:
551
552.. code-block:: minizinc
553
554 constraint int_neq_reif(A,B,BOOL01);
555 constraint int_neq_reif(A,C,BOOL02);
556 constraint int_neq_reif(B,C,BOOL03);
557 constraint array_bool_and([BOOL01,BOOL02,BOOL03],BOOL04);
558 constraint int_neq_reif(B,D,BOOL05);
559 constraint int_neq_reif(C,D,BOOL06);
560 constraint array_bool_and([BOOL03,BOOL05,BOOL06],BOOL07);
561 constraint array_bool_or([BOOL04,BOOL07],true);
562
563
564Observe cómo la eliminación de subexpresiones común reutiliza la desigualdad reificada :mzn:`B! = C` (aunque hay una traducción mejor que eleva la restricción común a la conjunción de nivel superior).
565
566.. _sec-let:
567
568
569
570Expresiones Let
571---------------
572
573Las expresiones ``let`` son una poderosa herramienta de MiniZinc para introducir nuevas variables. Esto es útil para crear sub expresiones comunes y para definir variables locales para predicados.
574
575Durante el aplanamiento, las expresiones de ``let`` se traducen a declaraciones de variables y restricciones. La semántica relacional de MiniZinc significa que estas restricciones deben aparecer como si estuvieran conjuntas en la primera expresión booleana adjunta.
576
577Una característica clave de las expresiones ``let`` es que cada vez que se utilizan crean nuevas variables.
578
579Considere el aplanamiento del código:
580
581.. code-block:: minizinc
582
583 constraint even(u) \/ even(v);
584 predicate even(var int: x) =
585 let { var int: y } in x = 2 * y;
586
587Primero, las llamadas de predicados se reemplazan por su definición.
588
589.. code-block:: minizinc
590
591 constraint (let { var int: y} in u = 2 * y) \/
592 (let { var int: y} in v = 2 * y);
593
594A continuación, las variables ``let`` se renombran aparte como:
595
596.. code-block:: minizinc
597
598 constraint (let { var int: y1} in u = 2 * y1) \/
599 (let { var int: y2} in v = 2 * y2);
600
601Finalmente, las declaraciones variables se extraen al nivel superior
602
603.. code-block:: minizinc
604
605 var int: y1;
606 var int: y2;
607 constraint u = 2 * y1 \/ v = 2 * y2;
608
609Una vez que se elimina la expresión ``let`` podemos aplanar como de costumbre.
610
611Recuerde que las expresiones ``let`` pueden definir valores para las variables recién introducidas (y de hecho deben hacerlo para los parámetros).
612Estos definen implícitamente restricciones que también deben aplastarse.
613
614Considere la compleja función objetivo para el problema de asignación de asientos en una bodas :numref:`ex-wedding2`.
615
616.. code-block:: minizinc
617
618 solve maximize sum(h in Hatreds)(
619 let { var Seats: p1 = pos[h1[h]],
620 var Seats: p2 = pos[h2[h]],
621 var 0..1: same = bool2int(p1 <= 6 <-> p2 <= 6) } in
622 same * abs(p1 - p2) + (1-same) * (abs(13 - p1 - p2) + 1));
623
624Por concisión asumimos solo los dos primeros Hatreds (odios), entonces:
625
626.. code-block:: minizinc
627
628 set of int: Hatreds = 1..2;
629 array[Hatreds] of Guests: h1 = [groom, carol];
630 array[Hatreds] of Guests: h2 = [clara, bestman];
631
632
633El primer paso de flattening es desenrollar la expresión :mzn:`sum`, mantenemos los nombres y parámetros de los invitados :mzn:`Seats` solo para mayor claridad, en realidad serían reemplazados por su definición:
634
635.. code-block:: minizinc
636
637 solve maximize
638 (let { var Seats: p1 = pos[groom],
639 var Seats: p2 = pos[clara],
640 var 0..1: same = bool2int(p1 <= 6 <-> p2 <= 6) } in
641 same * abs(p1 - p2) + (1-same) * (abs(13 - p1 - p2) + 1))
642 +
643 (let { var Seats: p1 = pos[carol],
644 var Seats: p2 = pos[bestman],
645 var 0..1: same = bool2int(p1 <= 6 <-> p2 <= 6) } in
646 same * abs(p1 - p2) + (1-same) * (abs(13 - p1 - p2) + 1));
647
648A continuación, cada nueva variable en una expresión ``let`` se renombra para ser distinta
649
650.. code-block:: minizinc
651
652 solve maximize
653 (let { var Seats: p11 = pos[groom],
654 var Seats: p21 = pos[clara],
655 var 0..1: same1 = bool2int(p11 <= 6 <-> p21 <= 6) } in
656 same1 * abs(p11 - p21) + (1-same1) * (abs(13 - p11 - p21) + 1))
657 +
658 (let { var Seats: p12 = pos[carol],
659 var Seats: p22 = pos[bestman],
660 var 0..1: same2 = bool2int(p12 <= 6 <-> p22 <= 6) } in
661 same2 * abs(p12 - p22) + (1-same2) * (abs(13 - p12 - p22) + 1));
662
663Las variables en la expresión ``let`` se extraen al nivel superior y las restricciones de definición se extraen al nivel correcto (que en este caso también es el nivel superior).
664
665.. code-block:: minizinc
666
667 var Seats: p11;
668 var Seats: p21;
669 var 0..1: same1;
670 constraint p12 = pos[clara];
671 constraint p11 = pos[groom];
672 constraint same1 = bool2int(p11 <= 6 <-> p21 <= 6);
673 var Seats p12;
674 var Seats p22;
675 var 0..1: same2;
676 constraint p12 = pos[carol];
677 constraint p22 = pos[bestman];
678 constraint same2 = bool2int(p12 <= 6 <-> p22 <= 6) } in
679 solve maximize
680 same1 * abs(p11 - p21) + (1-same1) * (abs(13 - p11 - p21) + 1))
681 +
682 same2 * abs(p12 - p22) + (1-same2) * (abs(13 - p12 - p22) + 1));
683
684Ahora hemos construido un código en MiniZinc equivalente sin el uso de expresiones ``let`` y el aplanamiento puede continuar como de costumbre.
685
686Como una ilustración de las expresiones ``let`` que no aparecen en el nivel superior, considere el siguiente modelo:
687
688.. code-block:: minizinc
689
690 var 0..9: x;
691 constraint x >= 1 -> let { var 2..9: y = x - 1 } in
692 y + (let { var int: z = x * y } in z * z) < 14;
693
694Extraemos las definiciones de las variables al nivel superior y las restricciones al primer contexto booleano que lo incluye, que aquí está en el lado derecho de la implicación.
695
696.. code-block:: minizinc
697
698 var 0..9: x;
699 var 2..9: y;
700 var int: z;
701 constraint x >= 1 -> (y = x - 1 /\ z = x * y /\ y + z * z < 14);
702
703Tenga en cuenta que si sabemos que la ecuación que define una definición de variable no puede fallar, podemos extraerla al nivel superior. Esto generalmente hará que la resolución sea mucho más rápida.
704
705Para el ejemplo anterior, la restricción :mzn:`y = x - 1` puede fallar ya que el dominio de :mzn:`y` no es lo suficientemente grande para todos los valores posibles de :mzn:`x - 1`. Pero la restricción :mzn:`z = x * y` no puede (de hecho el análisis de límites dará :mzn:`z` límites lo suficientemente grandes como para contener todos los valores posibles de :mzn:`x * y`).
706
707Un mejor aplanamiento dará:
708
709.. code-block:: minizinc
710
711 var 0..9: x;
712 var 2..9: y;
713 var int: z;
714 constraint z = x * y;
715 constraint x >= 1 -> (y = x - 1 /\ y + z * z < 14);
716
717Actualmente ``mzn2fzn`` hace esto definiendo siempre los límites declarados de una variable introducida para que sea lo suficientemente grande como para que su ecuación de definición mantenga siempre y luego agregue restricciones de límites en el contexto correcto para la expresión ``let``. En el ejemplo anterior, esto da como resultado
718
719
720.. code-block:: minizinc
721
722 var 0..9: x;
723 var -1..8: y;
724 var -9..72: z;
725 constraint y = x - 1;
726 constraint z = x * y;
727 constraint x >= 1 -> (y >= 2 /\ y + z * z < 14);
728
729Esta traducción conduce a una solución más eficiente ya que el cálculo posiblemente complejo de la variable ``let`` no se reifica.
730
731Otra razón para este enfoque es que también funciona cuando las variables introducidas aparecen en contextos negativos (siempre que tengan una definición).
732
733Considere el siguiente ejemplo similar al anterior:
734
735.. code-block:: minizinc
736
737 var 0..9: x;
738 constraint (let { var 2..9: y = x - 1 } in
739 y + (let { var int: z = x * y } in z * z) > 14) -> x >= 5;
740
741Las expresiones ``let`` aparecen en un contexto negado, pero se define cada variable introducida.
742
743El código aplanado es:
744
745.. code-block:: minizinc
746
747 var 0..9: x;
748 var -1..8: y;
749 var -9..72: z;
750 constraint y = x - 1;
751 constraint z = x * y;
752 constraint (y >= 2 /\ y + z * z > 14) -> x >= 5;
753
754Tenga en cuenta que el método simple para la eliminación de ``let`` no proporciona una traducción correcta:
755
756.. code-block:: minizinc
757
758 var 0..9: x;
759 var 2..9: y;
760 var int: z;
761 constraint (y = x - 1 /\ z = x * y /\ y + z * z > 14) -> x >= 5;
762
763Da respuestas para todos los valores posibles de :mzn:`x`, mientras que la restricción original elimina la posibilidad de que :mzn:`x = 4`.
764
765El tratamiento de *elementos de restricción* en ``let`` expresiones es análogo a variables definidas. Uno puede pensar en una restricción como equivalente a la definición de una nueva variable booleana. Las definiciones de las nuevas variables booleanas se extraen al nivel superior y las Boolean permanece en el contexto correcto.
766
767.. code-block:: minizinc
768
769 constraint z > 1 -> let { var int: y,
770 constraint (x >= 0) -> y = x,
771 constraint (x < 0) -> y = -x
772 } in y * (y - 2) >= z;
773
774Es tratado como:
775
776.. code-block:: minizinc
777
778 constraint z > 1 -> let { var int: y,
779 var bool: b1 = ((x >= 0) -> y = x),
780 var bool: b2 = ((x < 0) -> y = -x),
781 constraint b1 /\ b2
782 } in y * (y - 2) >= z;
783
784Y se aplana a:
785
786.. code-block:: minizinc
787
788 constraint b1 = ((x >= 0) -> y = x);
789 constraint b2 = ((x < 0) -> y = -x);
790 constraint z > 1 -> (b1 /\ b2 /\ y * (y - 2) >= z);