Modelos más complejos ===================== En la última sección presentamos la estructura básica de un modelo MiniZinc. En esta sección presentamos la matriz, las estructuras de datos establecidas, los tipos enumerados y las restricciones más complejas. .. _sec-arrayset: Las matrices y conjuntos ------------------------ Casi siempre estamos interesados en construir modelos donde el número de restricciones y variables depende de los datos de entrada. Para ello, usualmente utilizaremos index: `matrices `. Consideremos un modelo simple de elementos finitos para modelar las temperaturas en una chapa de metal rectangular. Aproximamos las temperaturas a través de la hoja, rompiendo la hoja en un número finito de elementos en una matriz bidimensional. Un modelo es mostrado en :numref:`ex-laplace`. Declara el ancho ``w`` y la altura ``h`` del modelo de elementos finitos. La declaración .. literalinclude:: examples/laplace_es.mzn :language: minizinc :lines: 5-9 declara cuatro conjuntos fijos de enteros que describen las dimensiones del modelo de elementos finitos: ``HEIGHT`` es toda la altura del modelo, mientras que ``CHEIGHT`` es el centro de la altura omitiendo la parte superior e inferior, ``WIDTH`` es el ancho total del modelo, mientras que ``CWIDTH`` es el centro de la anchura omitiendo la izquierda y derecha, Finalmente, una matriz bidimensional de variables flotantes ``t`` con filas numeradas :math:`0` to :math:`h` (``HEIGHT``) y columnas :math:`0` to :math:`w` (``WIDTH``), para representar las temperaturas en cada punto de la placa metálica. Podemos acceder al elemento de la matriz en la fila :math:`i^{th}` y columna :math:`j^{th}` usando una expresión :mzn:`t[i,j]`. La ecuación de Laplace indica que cuando la placa alcanza un estado estacionario la temperatura en cada punto interno es la media de sus vecinos ortogonales. La restricción .. literalinclude:: examples/laplace_es.mzn :language: minizinc :lines: 16-18 Asegura que cada punto interno :math:`(i,j)` es el promedio de sus cuatro vecinos ortogonales. Las restricciones .. literalinclude:: examples/laplace_es.mzn :language: minizinc :lines: 20-24 Restringir las temperaturas en cada borde para ser iguales, y da estos nombres de las temperaturas: ``left``, ``right``, ``top`` and ``bottom``. Si bien las limitaciones .. literalinclude:: examples/laplace_es.mzn :language: minizinc :lines: 26-30 Asegurarse que las esquinas (que son irrelevantes) se ajustan a 0.0. Podemos determinar las temperaturas en una placa dividida en 5 :math:`\times` 5 elementos con temperatura izquierda, derecha e inferior 0 y temperatura superior 100 con el modelo mostrado en:numref:`ex-laplace`. .. literalinclude:: examples/laplace_es.mzn :language: minizinc :name: ex-laplace :caption: Modelo de placa de elementos finitos para determinar las temperaturas en estado estable (:download:`laplace_es.mzn `). Ejecutando el comando .. code-block:: bash $ mzn-cbc laplace_es.mzn Da la salida .. code-block:: none 0.00 100.00 100.00 100.00 0.00 0.00 42.86 52.68 42.86 0.00 0.00 18.75 25.00 18.75 0.00 0.00 7.14 9.82 7.14 0.00 0.00 0.00 0.00 0.00 0.00 ---------- .. defblock:: Conjuntos .. index:: single: set Las variables de conjunto se declaran con una declaración de la forma .. code-block:: minizincdef set of : ; donde se permiten conjuntos de enteros, enumeraciones (ver más adelante), flotantes o booleanos. El único tipo permitido para conjuntos de variables de decisión son conjuntos variables de enteros o enumeraciones. Los literales establecidos son de la forma .. code-block:: minizincdef { , ..., } o son expresiones :index:`range` sobre enteros, enumeraciones o flotantes de la forma .. code-block:: minizincdef .. El estandar :index:`set operations ` están provistos por: pertenencia elemento (:mzn:`in`), (no-estricto) relación de subconjuntos (:mzn:`subset`), (no-estricto) relación superconjunto (:mzn:`superset`), union (:mzn:`union`), intersecciñon (:mzn:`intersect`), diferencia de conjuntos (:mzn:`diff`), diferencia de conjunto simétrico (:mzn:`symdiff`) y la cantidad de elementos en el conjunto (:mzn:`card`). Como hemos visto, las variables establecidas y los literales establecidos (incluidos los rangos), se pueden usar como un tipo implícito en las declaraciones de variables. En cuyo caso la variable tiene el tipo de los elementos en el conjunto y la variable está implícitamente restringida para ser miembro del conjunto. Nuestro problema de horneado de pastele, es un ejemplo de un tipo muy simple de problema de planificación de la producción. En este tipo de problema, deseamos determinar qué cantidad de cada tipo de producto se debe hacer para maximizar los beneficios cuando la fabricación de un producto consume cantidades variables de algunos recursos fijos. Podemos generalizar el modelo MiniZinc en :numref:`ex-cakes2` para manejar este tipo de problema con un modelo que sea genérico en los tipos de recursos y productos. El modelo se muestra en :numref:`ex-prod-planning` y un archivo de datos de muestra (para el ejemplo de bicarbonato) se muestra en :numref:`fig-prod-planning-data`. .. literalinclude:: examples/simple-prod-planning_es.mzn :language: minizinc :name: ex-prod-planning :caption: Modelo para planificación de producción simple (:download:`simple-prod-planning_es.mzn `). .. literalinclude:: examples/simple-prod-planning-data_es.dzn :language: minizinc :name: fig-prod-planning-data :caption: Archivo de datos de ejemplo para el problema simple de planificación de la producción (:download:`simple-prod-planning-data_es.dzn `). La nueva característica de este modelo es el uso de :index:`enumerated types `. Esto nos permite tratar la elección de recursos y productos como parámetros para el modelo. El primer artículo en el modelo .. code-block:: minizinc enum Products; Declara ``Products`` como un conjunto de productos *desconocido*. .. defblock:: Enumerated Types .. index:: single: enumerated type single enum Los tipos enumerados, a los que nos referiremos como ``enumeraciones``, se declaran con una declaración de la forma: .. code-block:: minizincdef enum ; Un tipo enumerado se define mediante una asignación del formulario .. code-block:: minizincdef enum = { , ..., } ; Donde :mzndef:``, ..., :mzndef:`` son los elementos del tipo enumerado, con el nombre :mzndef:``. Cada uno de los elementos del tipo enumerado también es declarado efectivamente por esta definición como una nueva constante de ese tipo. La declaración y la definición se pueden combinar en una línea como de costumbre. El segundo elemento declara una matriz de enteros: .. code-block:: minizinc array[Products] of int: profit; El :index:`index set ` del array ``profit`` es ``Products``. Esto significa que solo los elementos del conjunto ``Products`` se pueden usar para indexar la matriz. Los elementos de un tipo enumerado de :math:`n` elements actúan de forma muy similar a los enteros :math:`1 \dots n`. Se pueden comparar, están ordenados, por el orden en que aparecen en la definición del tipo enumerado, se pueden volver a procesar, pueden aparecer como índices de matrices, de hecho, pueden aparecer en cualquier lugar donde pueda aparecer un número entero. En el archivo de datos de ejemplo, hemos inicializado la matriz usando una lista de enteros .. code-block:: minizinc Products = { BananaCake, ChocolateCake }; profit = [400,450]; Lo que significa que la ganancia de un pastel de plátano es de 400, mientras que para un pastel de chocolate es de 450. Internamente, ``BananaCake`` se tratará como el número entero 1, mientras que ``ChocolateCake`` se tratará como el número entero 2. Aunque MiniZinc no proporciona un tipo de lista explícito, las matrices unidimensionales con un conjunto de índices :mzn:`1..n` se comportan como listas, y a veces nos referiremos a ellas como :index:`lists `. De manera similar, en los siguientes dos elementos declaramos un conjunto de recursos ``Recursos``, y una matriz ``capacidad`` que da la cantidad de cada recurso que está disponible. Más interesante, el artículo .. code-block:: minizinc array[Products, Resources] of int: consumption; Declara una matriz 2-D ``consumption``. El valor de :mzn:`consumption[p, r]` es la cantidad de recurso :mzn:`r` requerido para producir una unidad de producto :mzn:`p`. Tenga en cuenta que el primer índice es la fila y el segundo es la columna. El archivo de datos contiene una inicialización de ejemplo de una matriz 2-D: .. code-block:: minizinc consumption= [| 250, 2, 75, 100, 0, | 200, 0, 150, 150, 75 |]; Observe cómo el delimitador ``|`` se usa para separar filas. .. defblock:: Arrays .. index: single: array Por lo tanto, MiniZinc proporciona matrices de una y varias dimensiones que se declaran utilizando el tipo: .. code-block:: minizincdef array [ , ..., ] of MiniZinc requiere que la declaración de matriz contenga el conjunto de índices de cada dimensión y que el conjunto de índices sea un rango entero, una variable establecida inicializada a un rango entero, o un :index:`enumeration type `. Las matrices pueden contener cualquiera de los tipos base: enteros, enumeraciones, booleanos, flotantes o cadenas. Estos pueden ser fijos o no, a excepción de cadenas que solo pueden ser parámetros. Las matrices también pueden contener conjuntos, pero no pueden contener matrices. :index:`One-dimensional array literals ` son de la forma: .. code-block:: minizincdef [ , ..., ] Mientras que :index:`two-dimensional array literals ` son de la forma: .. code-block:: minizincdef [| , ..., | ... | , ..., |] Donde la matriz tiene columnas ``m`` y ``n``. La familia de funciones incorporadas :mzn:`array1d`, :mzn:`array2d`, etc., se puede utilizar para inicializar una matriz de cualquier dimensión de una lista (o más exactamente una matriz unidimensional). La llamada: .. code-block:: minizincdef arrayd(, ..., , ) returns an ``n`` dimensional array with index sets given by the first ``n`` arguments and the last argument contains the elements of the array. For instance, :mzn:`array2d(1..3, 1..2, [1, 2, 3, 4, 5, 6])` is equivalent to :mzn:`[|1, 2 |3, 4 |5, 6|]`. Devuelve una matriz dimensional ``n`` con conjuntos de índices dados por los primeros argumentos ``n`` y el último argumento contiene los elementos de la matriz. Por ejemplo, :mzn:`array2d (1..3, 1..2, [1, 2, 3, 4, 5, 6])` es equivalente a :mzn:`[| 1, 2 | 3, 4 | 5, 6 |]`. Los elementos de la matriz son :index:`accessed ` de la forma habitual: :mzn:`a[i, j]` da el elemento en la fila :math:`i^{th}` y en la columna :math:`j^{th}`. .. \pjs{New array functions!} El operador de concatenación ``++`` se puede usar para concatenar dos matrices unidimensionales juntas. El resultado es una lista, es decir, una matriz unidimensional cuyos elementos están indexados desde 1. Por ejemplo, :mzn:`[4000, 6] ++ [2000, 500, 500]` evalúa a :mzn:`[4000, 6 , 2000, 500, 500]`. La función incorporada :mzn:`length` devuelve la longitud de una matriz unidimensional. El siguiente elemento en el modelo define el parámetro :mzn:`mproducts`. Esto se establece en un límite superior en la cantidad de productos de cualquier tipo que se pueden producir. Este es un ejemplo bastante complejo de comprensiones de conjuntos anidados y operadores de agregación. Los presentaremos antes de tratar de entender este artículo y el resto del modelo. En primer lugar, MiniZinc proporciona comprensiones de listas similares a las provistas en muchos lenguajes de programación funcionales, o Python. Por ejemplo, la lista de comprensión :mzn:`[i + j | i, j in 1..3 where j < i]` evalúa a :mzn:`[2 + 1, 3 + 1, 3 + 2]` que es :mzn:`[3, 4, 5]`. Por supuesto :mzn:`[3, 4, 5]` es simplemente una matriz con conjunto de índices :mzn:`1..3`. MiniZinc también proporciona conjuntos de comprensiones que tienen una sintaxis similar: por ejemplo, :mzn:`{i + j | i, j in 1..3 where j < i}` evalúa al conjunto :mzn:`{3, 4, 5}`. .. defblock:: Enumeraciones y Establecer comprensiones .. index: single: comprehension single: comprehension; list La forma genérica de una lista de comprensión es .. code-block:: minizincdef [ | ] La expresión :mzndef:`` especifica cómo construir elementos en la lista de salida a partir de los elementos generados por :mzndef:``. El generador :mzndef:`` consiste en una secuencia separada por comas de expresiones generadoras opcionalmente, seguidas de una expresión booleana. Las dos formas son: .. code-block:: minizincdef , ..., , ..., where El opcional :mzndef:`` en la segunda forma actúa como un filtro en la expresión del generador. Solo los elementos que satisfacen la expresión booleana se usan para construir elementos en la lista de salida. Un :index:`generator ` :mzndef:`` tiene la forma: .. code-block:: minizincdef , ..., in Cada identificador es un *iterador* que toma los valores de la expresión de la matriz a su vez, con el último identificador que varía más rápidamente. Los generadores de una lista de comprensión y :mzndef:`` generalmente no involucran variables de decisión. Si implican variables de decisión, la lista producida es una lista de :mzndef:`var opt ` donde :mzndef:`` es el tipo de :mzndef:``. Vea la discusión de :index:`option types