1 Sobre esta guía
En este documento se presenta una breve guía de introducción al lenguaje de modelización GAMS. El objetivo es proporcionar la información necesaria para iniciarse en el manejo de este programa.
El documento está estructurado como se indica a continuación. En la sección 2 se presentan las líneas generales del programa GAMS. A continuación, en la sección 3 se presenta una introducción a GAMS a través de la escritura de un ejemplo sencillo de programación lineal. En el apartado 4 se describen las principales reglas de escritura en lenguaje GAMS, detallando la sintaxis GAMS para los principales elementos de los modelos de programación matemática. La sección 5 se dedica a comentar algunos comandos especiales. En el Anexo se dan indicaciones sobre cómo instalar el programa.
Si no tiene GAMS instalado en su computadora, o si su versión es demasiado antigua, se recomienda comenzar esta guía instalando el programa (ver Anexo).
2 Una introducción a GAMS
2.1 Lenguaje GAMS: Principios generales
Gams (General Algebraic Modeling System) es un lenguaje de modelización desarrollado en los años 1980 en el seno del Banco Mundial con el objetivo de dotar a los economistas de un instrumento muy versátil para resolver problemas de optimización (tanto modelos de programación lineal como no lineal y entera). El programa GAMS ha experimentado un continuo desarrollo y resulta especialmente útil para plantear y resolver modelos complejos de programación matemática.
Una de las ventajas de GAMS es que permite que el modelizador se concentre en la formulación del modelo. Al no tener que preocuparnos del método de resolución, gams aumenta el tiempo disponible para formular el modelo, así como para analizar los resultados. Además, utilizando GAMS la escritura del modelo resulta fácil de comprender por otros modelizadores o potenciales usuarios:
La escritura del modelo es concisa, análogamente a la notación matemática. Pueden introducirse varias restricciones en la misma ecuación, de la misma forma que hacemos al formular algebraicamente el problema.
Los datos pueden introducirse en forma de listas o de tablas, de forma que no resulta difícil revisarlos o modificarlos. GAMS permite importar datos de otros programas (hojas de cálculo) y exportar los resultados del modelo. Los datos pueden estar separados de la formulación matemática del modelo.
Podemos introducir texto explicativo (por ejemplo, de los símbolos utilizados).
Toda la información necesaria para comprender el modelo se encuentra en el mismo documento.
El programa GAMS consiste en un lenguaje de modelización (módulo base) y un conjunto de solvers (algoritmos de resolución de problemas) a elección del usuario (dependiendo del tipo de problema que se pretenda resolver). En la Tabla 1 se muestran algunos de los solvers más utilizados. Además, puede usar GAMS en diferentes plataformas y utilizar muchas herramientas de conectividad y productividad.
Tabla 1. Algunos solvers utilizados para cada tipo de problema
Tipo de problema | Solvers |
LP (programación lineal) | OSL, CPLEX, MINOS, BDMLP, etc. |
NLP (programación no lineal) | CONOPT, MINOS, etc. |
MIP (programación mixta) | OSL, CPLEX, ZOOM, etc. |
MINLP (programación no lineal mixta) | SBB, DICOPT |
MCP (mixed complementarity problems) | PATH, MILES |
CNS (sistema de ecuaciones no lineales) | CONOPT, PATH |
También puede elegirse entre distintas versiones de GAMS (estudiante, profesional, etc.), que se diferencian básicamente en su capacidad para resolver problemas de diferente dimensión. Así, aunque la versión estudiante dispone de todos los solvers, el tamaño de los modelos que permite resolver está limitado.
La utilización de GAMS para resolver problemas de optimización comporta dos etapas:
En primer lugar, escribimos el modelo en lenguaje GAMS utilizando un editor de texto (creamos un archivo de entrada nombre_archivo.gms).
A continuación, presentamos nuestro archivo a gams, que resolverá el problema y generará automáticamente un archivo de resultado o archivo de salida (archivo nombre_archivo.lst).
Es posible ejecutar GAMS en dos (o unix) utilizando un editor de texto para crear los archivos de entrada nombre_archivo.gms o bien podemos utilizar una interfaz:
GAMS IDE (Integrated Development Environment) es un entorno gráfico para para editar y ejecutar archivos GAMS. Este entorno permite resaltar elementos del lenguaje con colores e incorpora, en la sección help, varios documentos de ayuda (en inglés), entre ellos la guía de utilización de GAMS (GAMS Documentation).
GAMS Studio es un entorno gráfico desarrollado más recientemente que tiene la ventaja de poder ejecutarse en distintos sistemas operativos. En esta guía se explica el uso de GAMS a través de GAMS Studio.
¿Cómo utilizar GAMS Studio?
Una vez que GAMS está instalado en el ordenador (ver guía de instalación en Anexos), vamos a abrirlo haciendo clic en el icono
En primer lugar, podemos elegir los ajustes (settings) indicando la carpeta donde se van a colocar los modelos, etc.
A continuación, podemos crear un archivo de entrada nombre_archivo.gms con los datos e instrucciones del modelo a resolver.
Ahora podemos ejecutar el modelo haciendo clic en run gams.
Una vez completada la ejecución, GAMS genera automáticamente un archivo de salida. El archivo archivo de salida o de solución llevará el mismo nombre que el archivo de entrada pero con la extensión lst (nombre_archivo.lst).
Si hay errores en el modelo que hacen imposible su resolución, GAMS interrumpe la ejecución, y genera un archivo nombre_archivo.lst en el cual señala los errores encontrados, dando asimismo valiosas indicaciones sobre la localización y la naturaleza de los errores. Una vez corregidos los errores se retoma el proceso en el punto 4.
2.2 La estructura base de un modelo en lenguaje GAMS
GAMS utiliza la notación algebraica:
La representación del problema en GAMS es similar a la formulación algebraica del problema (facilita la interpretación del modelo por otros usuarios).
La formulación del modelo está separada de los datos. El modelo puede aumentar de tamaño sin que ello implique aumentar la complejidad del mismo.
La Tabla 2 muestra la estructura de los archivos de entrada y de salida de GAMS. Como acabamos de ver, la primera etapa consiste en escribir el modelo en un archivo de entrada (.gms). La columna izquierda del cuadro muestra la estructura base del archivo de entrada. Los términos en mayúsculas son palabras clave, que permiten al programa reconocer los diferentes elementos del problema (por esta razón mantenemos la terminología en inglés).
Los principales elementos del lenguaje GAMS se detallan en la sección 4. Pero antes vamos a escribir y resolver un primer ejemplo en lenguaje GAMS. Ello nos permitirá comprender mejor los principios de utilización de GAMS y la lógica del lenguaje de programación utilizado por GAMS.
Tabla 2. Estructura base de un modelo en lenguaje GAMS
Input (archivo de entrada) | Output (archivo de salida) |
SET Conjuntos. Corresponden a los índices de la escritura algebraica |
ECHO PRINT Reescritura del modelo con numeración de líneas |
PARAMETER, SCALAR, TABLE Datos, o parámetros exógenos del modelo |
EQUATION LISTING Lista de ecuaciones del modelo (permite verificar que no hay errores) |
VARIABLES Variables, o parámetros endógenos del modelo |
COLUMN LISTING Lista de variables y coeficientes asociados a cada variable |
EQUATIONS Ecuaciones del modelo, es decir, las restricciones y la función objetivo |
MODEL STATISTICS Algunas estadísticas del modelo (número de ecuaciones, número de variables, etc.) |
MODEL Asignación de un nombre al modelo |
SOLVE SUMMARY Tipo de solución (óptimo, etc.) |
SOLVE Instrucciones de ejecución |
SOLUTION Resultados y valores duales |
3 Un primer ejemplo en lenguaje GAMS
3.1 Ejemplo de modelo de programación lineal
Para ilustrar el funcionamiento de GAMS, en esta guía nos centraremos en los modelos de optimización con restricciones. En concreto, utilizaremos un ejemplo simple de programación lineal. Una aplicación clásica de la programación lineal es la planificación agraria, donde el objetivo es determinar el plan de producción que maximiza el beneficio de la explotación respetando las restricciones de recursos y las condiciones externas.
Recordemos que un modelo de programación lineal consta de tres ingredientes esenciales:
Una función objetivo
Un conjunto de restricciones (de desigualdad)
Un conjunto de condiciones de no-negatividad de las variables
En general, la formulación algebraica del modelo de optimización de la producción agraria es:
siendo c actividad de producción (cultivo)
r recurso fijo de la explotación (tierra, trabajo,…)
Z margen global de la explotación (variable objetivo)
mc margen del cultivo (coeficiente de la función objetivo)
xc nivel de actividad (variable de decisión)
nrc,r necesidades de recursos por cultivo y recurso
drr disponibilidad de recursos
En términos matemáticos, el problema consiste en encontrar el vector xc que maximice la función objetivo respetando las condiciones impuestas al sistema.
Todo vector xc que satisface las restricciones del modelo se denomina solución factible del programa lineal (o programa posible). El dominio D de soluciones factibles es la región factible o región admisible.
La solución factible xc que hace máxima la función objetivo es la solución óptima, que denotaremos xc*. El valor que toma la función objetivo para la solución óptima se denomina valor óptimo.
Para resolver el problema de forma analítica, utilizamos el método de los multiplicadores de Lagrange. Partimos de la función de Lagrange, que incorpora un multiplicador de Lagrange λr asociado a cada restricción de recursos:
Las condiciones de optimalidad de primer orden (condiciones de Kuhn-Tucker) son:
Los multiplicadores de Lagrange tienen una interpretación económica, representando el valor marginal de los recursos. El grupo de ecuaciones [4] indica que si la superficie asignada al cultivo c es positiva (xc > 0) entonces el beneficio marginal por hectárea de este cultivo (mc) es igual al coste de utilización de los recursos medido a sus valores marginales. En cambio, la superficie asignada a un cultivo será nula (xc = 0) cuando el beneficio marginal sea estrictamente inferior al coste de oportunidad.
El grupo de ecuaciones [5] indica que el valor marginal del recurso r será nulo (λr = 0) cuando el recurso no sea utilizado en su totalidad. Cuando el recurso sea utilizado en su totalidad, λr será positivo. Para cada recurso r, el multiplicador λr mide el impacto sobre la función objetivo de una variación marginal en la disponibilidad del recurso. Este multiplicador corresponde al valor dual o valor marginal del recurso (precio sombra) y puede interpretarse como el precio máximo que estaríamos dispuestos a pagar, en el óptimo, por disponer de una unidad adicional del recurso.
Para resolver el problema de forma numérica, es preciso utilizar algún algoritmo de resolución. En nuestro caso, utilizaremos el programa GAMS. Veámoslo con un ejemplo simple, que denominaremos ejemplo1:
Un agricultor tiene 50 hectáreas de tierra y dispone de 1800 horas de trabajo al año. Puede elegir entre dos cultivos: trigo y maíz. El trigo tiene un margen bruto1 de 450 euros/ha y el maíz de 1000 euros/ha. El trigo necesita 20 horas de trabajo por hectárea y por año y el maíz 60 horas. El agricultor desea determinar el plan de producción que le permite obtener el margen global de la explotación más elevado posible.
Denominando c al subíndice correspondiente a los cultivos, dti a la superficie de la explotación, dtr a la disponibilidad de trabajo, ntr a las necesidades de trabajo por cultivo, mb al margen bruto, X a la superficie dedicada a cada cultivo (variables de decisión que tratamos de determinar), y Z al margen global de la explotación (variable objetivo), el problema en notación algebraica puede escribirse:
Nótese que también podríamos haber formulado el problema utilizando una notación algebraica más concisa, a través de las ecuaciones [1] a [3]. Sin embargo, se ha preferido utilizar esta notación para facilitar el aprendizaje de GAMS.
En forma de matriz de programación lineal, este programa lineal se escribe como se muestra en la Tabla 3.
Tabla 3. Ejemplo de matriz de programación lineal
Columnas | ||||||||
Filas | xTRIGO | xMAIZ | RHS | |||||
Función objetivo | 450 | 1000 | Maximizar | |||||
Restricción de tierra | 1 | 1 | ≤ | 50 | ||||
Restricción de trabajo | 20 | 60 | ≤ | 1800 |
Al tratarse de un problema muy simple, con solo dos actividades de producción, el problema puede resolverse de forma gráfica. En primer lugar, representamos la región admisible, es decir, el conjunto de combinaciones de cultivos de satisfacen todas las restricciones (Figura 1). Para ello, representamos los niveles de actividad en un plano cartesiano. Debido a las restricciones de no negatividad, solo necesitamos considerar el cuadrante no negativo.
Cada restricción de recursos puede representarse como un área delimitada por los ejes cartesianos y una línea frontera (puntos en los cuales el recurso se utiliza en su totalidad). Solo los puntos ubicados en el área sombreada de la Figura satisfacen ambas restricciones simultáneamente. La región sombreada se denomina región factible o región admisible, y cada punto de dicha región se conoce como solución o programa factible. Los puntos de la región admisible representan el conjunto de todas las combinaciones de cultivos que satisfacen todas las restricciones, así como las restricciones de no negatividad. Pero algunas de estas combinaciones implican un mayor margen global que otras.
Figura 1. Primer ejemplo de programación lineal: región admisible
A continuación, representamos las rectas de isobeneficio para determinar la solución óptima. Una recta de isobeneficio representa el conjunto de puntos que permiten obtener el mismo nivel de margen global. Parametrizando la variable Z, podemos trazar la función objetivo como una familia de rectas paralelas (Figura 2).
Figura 2. Primer ejemplo de programación lineal: solución óptima
La solución óptima se encontrará en el punto de tangencia de una recta de isobeneficio con la región admisible (Figura 2). En el ejemplo, la combinación óptima de cultivos es 30 hectáreas de trigo y 20 hectáreas de maíz, resultando un margen global máximo de 33500 euros.
¿Cómo varía la solución óptima si cambia el margen bruto de los cultivos? ¿Cómo varía la solución óptima si aumenta la disponibilidad de trabajo?
Hemos visto que la resolución gráfica nos ayuda a comprender los modelos de optimización con restricciones de desigualdad. Sin embargo, solamente es aplicable en modelos con un número reducido de variables de decisión. En general, debemos recurrir a algún algoritmo de programación matemática para resolver el problema.
En el apartado siguiente, formularemos y resolveremos este ejemplo utilizando el lenguaje de modelización GAMS.
3.2 Escritura del primer modelo en lenguaje GAMS
Suponiendo que sea la primera vez que utilizamos GAMS Studio, para escribir un primer modelo seguiremos las siguientes etapas:
Creamos una carpeta de trabajo (utilizando windows explorer, por ejemplo) donde vamos a guardar nuestro trabajo con GAMS. Se recomienda crear una carpeta de nombre corto y sin espacios. Por ejemplo, podemos crear una carpeta agrimod directamente en la unidad C: (C:\agrimod).
Abrimos GAMS Studio haciendo clic en el icono
. Se abrirá una ventana (Figura 3).
Figura 3. Pantalla de inicio de GAMS STUDIO
- A continuación, vamos a ajustes seleccionando el icono “settings” o a través del menú File>>Settings (Figura 4). Elegimos C:/agrimod como carpeta de trabajo.
Figura 4. Seleccionar los ajustes
- Haciendo clic en File>>New en el menú, abrimos un nuevo archivo (Figura 5).
Figura 5. Abrir un archivo GAMS
Se abrirá un archivo nuevo donde podremos escribir nuestro primer modelo (Figura 6).
Figura 6. Crear un nuevo archivo GAMS
- Para poner nombre al archivo, seleccionamos File>>Save as y guardamos el archivo en la carpeta C:\agrimod con el nombre ejemplo1. GAMS añade automáticamente la extensión .gms (Figura 7).
Figura 7. Poner nombre a un archivo GAMS
- Ahora podemos escribir nuestro primer modelo en lenguaje GAMS. Recordemos el enunciado del ejemplo 1:
Un agricultor tiene 50 hectáreas de tierra y dispone de 1800 horas de trabajo al año. Puede elegir entre dos cultivos: trigo y maíz. El trigo tiene un margen bruto de 450 €/ha y el maíz de 1000 €/ha. El trigo necesita 20 horas de trabajo por hectárea y por año y el maíz 60 horas. El agricultor desea determinar el plan de producción que le permite obtener el margen global de la explotación más elevado posible.
Y la escritura algebraica del mismo:
Para escribir este problema en lenguaje GAMS, debemos tener en cuenta la correspondencia entre la notación algebraica y el lenguaje GAMS (Tabla 4).
Tabla 4. Correspondencia entre notación algebraica y lenguaje GAMS
Símbolo | Entidad | Correspondencia GAMS |
---|---|---|
c | índices | sets |
mb, dti, dtr, ntr | datos | scalar, parameter, table |
Z, x | variables | variables |
[6], [7], [8], [9] | f.o. y restricciones | equations |
Debemos comenzar por definir los conjuntos o listas de elementos (sets), que corresponden a los subíndices de la escritura algebraica. En nuestro ejemplo, el único subíndice que encontramos es el correspondiente a los cultivos. Por tanto, vamos a crear un set para los cultivos, de la manera siguiente: en primer lugar, es necesario anunciar a gams que vamos a introducir un conjunto, utilizando para ello la palabra clave set; a continuación, damos un nombre al conjunto (por ejemplo, C), dejamos un espacio e introducimos los elementos del conjunto entre signos slash (/); por último, vamos a anunciar que hemos terminado de introducir conjuntos utilizando un punto y coma (;). Resulta muy útil, aunque no es indispensable, escribir un texto explicativo al lado del nombre del set para hacer comprensible el modelo para otros modelizadores:
set C cultivos / trigo , maiz /
;
A continuación, vamos a introducir los datos del problema, es decir, los parámetros exógenos. Para ello, vamos a utilizar las palabras clave parameter, scalar o table.
La palabra clave scalar se utiliza para introducir datos simples. En nuestro ejemplo, el tamaño de la explotación y la disponibilidad de trabajo son datos simples. Vamos a utilizar, por tanto, la palabra clave scalar para introducir estos datos, siguiendo la misma lógica que para los conjuntos, es decir, primero vamos a dar un nombre al escalar y, después de un espacio, vamos a introducir los valores entre signos slash (/), terminando siempre con un punto y coma (;):
scalar
DTI disponibilidad de superficie (ha) /50/
DTR disponibilidad de trabajo familiar (horas) /1800/
;
La palabra clave parameter se utiliza para introducir datos con una o más dimensiones (es decir, parámetros con uno o varios subíndices). En el ejemplo, algunos datos (las necesidades de trabajo o el margen bruto) están definidos en función del cultivo. Para introducir cada uno de estos parámetros, en primer lugar le daremos un nombre e indicaremos, entre paréntesis, su dominio de definición; a continuación, y siempre dejando un espacio, introduciremos los valores entre signos slash (/). Utilizaremos un espacio para separar cada elemento del dato asociado y una coma (o un salto de línea) para separar los elementos entre sí:
parameter
NTR(C) ‘necesidades de trabajo (horas/ha)’ /trigo 20, maiz 60/
MB(C) ‘margen bruto (euros/ha)’ /trigo 450, maiz 1000/
;
De esta forma, hemos terminado de introducir los datos del problema. El archivo ejemplo1.gms tendrá el aspecto mostrado en la Figura 8.
Figura 8. Escritura de los datos del primer ejemplo en GAMS
Ahora, podemos definir las variables y escribir las ecuaciones del modelo. Las variables son las hectáreas de cada cultivo (Xc) y la función objetivo (Z).
La función objetivo debe ser siempre una variable libre mientras que la variable Xc no puede tomar valores negativos. Para especificar esto, introducimos las variables por separado (para la variable libre utilizamos la palabra clave variable y para las variables no-negativas positive variable).
variable
Z ‘margen total de la explotacion (euros)’
;
positive variable
X(C) ‘nivel de actividad (ha)’
;
La escritura de las ecuaciones requiere algunos comentarios. En primer lugar, utilizaremos la palabra clave equation para anunciar que vamos a introducir las ecuaciones. A continuación, vamos a declarar las ecuaciones dándoles un nombre (y escribiendo un comentario si es necesario) y vamos a terminar la declaración con un punto y coma (;). Posteriormente vamos a definir cada ecuación; para ello comenzamos por escribir el nombre de la ecuación seguido de dos puntos (..) y formulamos la ecuación (Figura 9).
Una vez que las ecuaciones han sido formuladas, nos queda dar un nombre a nuestro modelo (utilizando la palabra clave model) y dar a gams las instrucciones para resolverlo (utilizando el comando solve).
Figura 9. Escritura de las variables y ecuaciones del primer ejemplo en GAMS
3.3 Resolución del modelo y archivo de resultados
Una vez que hemos escrito nuestro modelo en lenguaje GAMS, podemos resolverlo haciendo clic en Run GAMS. Si no hay errores de escritura, GAMS resolverá el modelo y generará automáticamente un archivo ejemplo1.lst en el que presentará la solución (Figura 10).
Figura 10. Resolución del primer ejemplo en GAMS
El archivo ejemplo1.lst generado por gams es un archivo bastante extenso que contiene mucha información, entre la que podemos destacar la lista de ecuaciones detalladas y la solución del modelo. Las ecuaciones detalladas permiten verificar que no nos hemos equivocado al formular el problema (Figura 11).
Equation Listing SOLVE ejemplo1 Using LP From line 29 —- MARGEN =E= definicion del margen total MARGEN.. Z - 450*X(trigo) - 1000*X(maiz) =E= 0 ; (LHS = 0) —- TIERRA =L= restriccion de tierra TIERRA.. X(trigo) + X(maiz) =L= 50 ; (LHS = 0) —- TRABAJO =L= restriccion de trabajo TRABAJO.. 20*X(trigo) + 60*X(maiz) =L= 1800 ; (LHS = 0) |
Figura 11. Archivo de resultados: lista de ecuaciones
Otra forma de especificar las ecuaciones consiste en listar las variables con sus coeficientes asociados en cada ecuación del modelo (Figura 12). Esta lista de variables permite asimismo verificar que los coeficientes son los correctos.
Column Listing SOLVE ejemplo1 Using LP From line 29 —- Z margen total de la explotacion (euros) Z (.LO, .L, .UP, .M = -INF, 0, +INF, 0) 1 MARGEN —- X nivel de actividad (ha) X(trigo) (.LO, .L, .UP, .M = 0, 0, +INF, 0) -450 MARGEN 1 TIERRA 20 TRABAJO X(maiz) (.LO, .L, .UP, .M = 0, 0, +INF, 0) -1000 MARGEN 1 TIERRA 60 TRABAJO |
Figura 12. Archivo de resultados: lista de variables
En el archivo ejemplo1.lst encontramos también algunas estadísticas, principalmente el número de líneas y de columnas del modelo (Figura 13).
MODEL STATISTICS BLOCKS OF EQUATIONS 3 SINGLE EQUATIONS 3 BLOCKS OF VARIABLES 2 SINGLE VARIABLES 3 NON ZERO ELEMENTS 7 |
Figura 13. Archivo de resultados: estadísticas
Después de las estadísticas, se presenta un resumen del problema ejecutado (Figura 14). GAMS indica el nombre del modelo a resolver, la función objetivo, el sentido de la optimización, el solver utilizado (conopt) y el tipo de solución obtenida (óptimo, optimo local, etc.). Esta última información es muy importante: debemos siempre verificar que GAMS ha encontrado una solución óptima, es decir, que model status es 1 (Optimal).
S O L V E S U M M A R Y MODEL ejemplo1 OBJECTIVE Z TYPE LP DIRECTION MAXIMIZE SOLVER CONOPT FROM LINE 29 **** SOLVER STATUS 1 Normal Completion **** MODEL STATUS 1 Optimal **** OBJECTIVE VALUE 33500.0000 |
Figura 14. Archivo de resultados: solve summary
Los resultados aparecen al final del archivo ejemplo1.lst (Figura 15).
LOWER LEVEL UPPER MARGINAL —- EQU MARGEN . . . 1.0000 —- EQU TIERRA -INF 50.0000 50.0000 175.0000 —- EQU TRABAJO -INF 1800.0000 1800.0000 13.7500 LOWER LEVEL UPPER MARGINAL —- VAR Z -INF 33500.0000 +INF . —- VAR X nivel de actividad (ha) LOWER LEVEL UPPER MARGINAL trigo . 30.0000 +INF . maiz . 20.0000 +INF . |
Figura 15. Archivo de resultados: solución
Observamos que para cada ecuación y cada variable del modelo, gams muestra el nivel elegido (level), así como el valor marginal (marginal) y los límites inferior (lower) y superior (upper). La solución óptima es X(trigo)=30 ha y X(maiz)=20 ha. El valor máximo de la variable objetivo es Z=33500 euros.
Una información muy relevante es el valor marginal asociado a cada restricción de recursos. Además de la solución óptima, GAMS proporciona información sobre los valores marginales de los recursos (multiplicadores de Lagrange) en el óptimo. En el ejemplo, el valor marginal o precio sombra de la tierra es de 175 euros/ha.
4 Principales elementos del lenguaje GAMS
4.1 Introducción al lenguaje GAMS
Vamos a presentar a continuación una breve guía de utilización de gams (para un manual más completo, ver la guía oficial2, en inglés). Comenzaremos por algunas cuestiones generales, para pasar a introducir los principales elementos del lenguaje GAMS y, por último, trataremos algunas cuestiones más avanzadas.
Los términos del lenguaje gams se pueden clasificar en dos grupos (Tabla 5):
términos de declaración y definición de los diferentes elementos del modelo, y
términos de ejecución o instrucciones a dar al programa.
Tabla 5. Principales palabras clave del lenguaje GAMS
Términos de declaración | Términos de ejecución | ||||
SET | SCALAR | VARIABLE | OPTION | DISPLAY | SOLVE |
ALIAS | PARAMETER | MODEL | LOOP | ABORT | WHILE |
ACRONYM | TABLE | FOR | EXECUTE | REPEAT |
El lenguaje gams es un lenguaje de programación y, como tal, tiene reglas muy estrictas de escritura. El modelo puede escribirse con cualquier editor de texto pero sin utilizar formatos particulares ni tabulaciones (el archivo que contiene el modelo debe estar escrito en texto plano).
Antes de describir los principales elementos, daremos algunas indicaciones generales relativas al lenguaje GAMS:
Un modelo en lenguaje GAMS es un conjunto de expresiones separadas por “punto y coma”. Siempre debemos terminar cada enunciado utilizando punto y coma (;).
Los elementos del código GAMS se identifican por tipo. La creación de cada elemento comporta dos pasos: declaración y atribución. La declaración consiste en indicar el tipo de elemento y darle un nombre. La atribución o definición consiste en asignar valores o forma al elemento.
GAMS lee el modelo de principio a fin, de modo que no podemos utilizar un elemento antes de que haya sido declarado (el orden de los elementos puede variar siempre que ningún símbolo se utilice antes de ser declarado).
El modelo debe escribirse en formato ASCII. No se permite la utilización de acentos en el código.
GAMS no diferencia entre mayúsculas y minúsculas. Tampoco tiene en cuenta el número de espacios o de líneas en blanco.
Algunas palabras y símbolos tienen un significado predeterminado en GAMS y, por tanto, no pueden utilizarse para dar nombre a los elementos del código (Tabla 6).
Tabla 6. Términos con un significado predeterminado en GAMS
Términos reservados | Símbolos reservados | |||
|
|
|
|
=l= |
|
|
|
|
=g= |
|
|
|
|
=e= |
|
|
|
|
=n= |
|
|
|
|
– |
|
|
|
|
++ |
|
|
|
|
** |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Existen diversas formas de introducir comentarios: a) comenzando una línea por un asterisco (*) en la primera posición; b) en el interior de los enunciados de declaración de entidades como se verá más adelante; c) introduciendo antes del comentario la palabra clave $ontext y después del comentario la clave $offtext3.
Los identificadores (los nombres asignados a los elementos) deben obedecer las siguientes reglas: a) comenzar por una letra; b) tener un máximo de 63 caracteres, c) contener únicamente caracteres alfanuméricos, es decir, no incluir signos como @ ? ” ( ) % $ (Tabla 7).
Tabla 7. Algunos ejemplos de nombres de identificadores
Ejemplos de identificadores correctos | Ejemplos de identificadores incorrectos |
A | %A |
tecnica | técnica |
A1234 | 1234A |
producto | prod |
Es posible introducir texto asociado a identificadores, etiquetas, etc. para facilitar la interpretación del modelo.
Además del punto y coma (finaliza una expresión), otros delimitadores importantes son la coma (separa elementos) y la barra inclinada (da comienzo y fin a listas de datos).
La estructura de un modelo en lenguaje GAMS varía en función del modelizador. Uno de los estilos más comunes de organizar el modelo, recomendable para principiantes, se presenta en la Figura 16.
Figura 16. Estructura base de un modelo GAMS
4.2 Los conjuntos o listas de elementos
Los conjuntos (sets) corresponden a los índices de la escritura algebraica. Las reglas principales para definir los conjuntos se presentan a continuación. Algunas cuestiones más avanzadas, como la atribución de elementos a los conjuntos o la representación del paso del tiempo serán tratadas posteriormente.
4.2.1 Los conjuntos simples
Un conjunto de elementos S que contiene los elementos a, b y c, se escribe en notación matemática: S = {a,b,c}. En lenguaje gams, el mismo conjunto se escribe:
set S /a,b,c/;
De manera general, la definición de un conjunto consiste en:
comenzar con la palabra clave set (sets) para indicar que vamos a introducir uno o varios conjuntos,
dar un nombre diferente (identificador) a cada set, dejar un espacio, y añadir un texto (comentario) si se desea,
asignar los elementos (etiquetas) de cada conjunto entre signos slash (/),
terminar el enunciado con un «punto y coma» (;).
Los comentarios no forman parte del código GAMS y, por tanto, no son necesarios. Pero resultan muy útiles para hacer fácilmente comprensible el modelo, tanto para el modelizador como para otros potenciales usuarios. Conviene recordar que no está permitido utilizar símbolos especiales tampoco en los comentarios.
La sintaxis de GAMS es, por tanto:
set
nombre_set1 “comentario” /etiquetal_1, etiquetal_2,…, etiquetal_N/
nombre_set2 “comentario”
/etiqueta2_1 “comentario”
etiqueta2_2 “comentario”
etiqueta2_3 “comentario”
…
etiqueta2_N/
nombre_set3 “comentario”
;
Algunas reglas de escritura a tener en cuenta son:
La palabra clave (set o sets) debe ir seguida por un espacio.
El nombre de un conjunto es un identificador y debe seguir las reglas mencionadas para los identificadores (comenzar por una letra, no contener símbolos, no exceder de 63 caracteres).
Los elementos de un conjunto (etiquetas) pueden escribirse en la misma línea si van separados por comas (como en el conjunto nombre_set1 del ejemplo genérico). También es posible escribir cada elemento en una línea (como el conjunto nombre_set2 del ejemplo genérico).
Es posible crear un conjunto sin especificar los elementos que lo componen, como en el conjunto nombre_set3. Es decir, es posible, asignar el nombre y el tipo (declaración) y dejar la asignación de elementos (atribución o definición) para un enunciado posterior.
También es posible introducir comentarios para cada elemento de un conjunto (como en el conjunto nombre_set2). Recordemos que se utiliza un espacio para separar cada elemento del comentario asociado mientras que se utiliza una coma para separar los elementos entre sí.
Si los elementos de un conjunto están ordenados (siguen una secuencia), es posible utilizar el asterisco para simplificar la escritura. Por ejemplo, los conjuntos:
set
M meses /M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12/
T tiempo /T1,T2, T3, T4, T5/
;
pueden escribirse también:
set
M meses /M1*M12/
T tiempo /T1*T5/
;
4.2.2 Alias
A veces resulta útil dar dos nombres diferentes al mismo conjunto de elementos. La palabra clave que permite hacer esto es alias.
Para ello, una vez que hayamos declarado un conjunto, por ejemplo set_a, y le hayamos asignado los elementos correspondientes, la instrucción a escribir es:
alias(set_a, set_aa);
Esta instrucción equivale a crear un conjunto set_aa que contiene los mismos elementos que el conjunto set_a.
Esto resulta particularmente útil en los modelos input-output donde cada producto puede ser utilizado en la producción de otros productos. En economía de la explotación agraria, resulta útil para introducir rotaciones de cultivo o cuando deseamos introducir datos de rendimiento para cada cultivo en función del precedente cultural:
set
C cultivos /trigo, maiz, girasol, remolacha, patata / ;
alias (C, CP);
El conjunto CP de los precedentes culturales contiene los mismos elementos que el conjunto C de los cultivos. En notación matemática: CP= C = {trigo, maíz, girasol, remolacha, patata }.
4.2.3 Subconjuntos
A menudo es necesario definir conjuntos cuyos elementos pertenecen a un conjunto más amplio. Por ejemplo, cuando algunas de las restricciones o actividades de un modelo se definen para todo el conjunto mientras que otras se definen sólo para un subconjunto.
La sintaxis GAMS para crear subconjuntos es:
set
set1 “comentario” /etiquetal_1, etiquetal_2,…, etiquetal_N/
subset11(set1) “comentario” /etiquetal_1, etiquetal_2/
;
subset11 es un subconjunto del conjunto set1 que contiene únicamente los dos primeros elementos del conjunto set1.
Así, podemos escribir, por ejemplo:
set
C cultivos /trigo, maiz, girasol, remolacha, patata /
CC(C) cereales / trigo, maiz /
;
En notación matemática: CR ⊂ C; CC= { trigo, maiz }
4.2.4 Conjuntos multidimensionales
Llamaremos conjunto multidimensional a un subconjunto definido por la combinación de dos o más conjuntos.
Por ejemplo, podemos definir CI(C,I) como conjunto multidimensional que corresponde al conjunto de combinaciones posibles de cultivos (C) y técnicas de cultivo (I):
set
C cultivo /trigo, maiz, tomate/
I itinerario tecnico /extensivo, intensivo/
CI(C,I) actividades productivas /trigo.extensivo
trigo.intensivo
maiz.extensivo
maiz.intensivo
tomate.intensivo/
;
También podemos escribir el subconjunto CI de la siguiente forma:
CI(C,I) /trigo.(extensivo,intensivo), maiz.(extensivo,intensivo)
tomate.intensivo/
4.3 La introducción de datos
Las palabras clave utilizadas para introducir datos son scalar, parameter y table:
scalar permite introducir escalares, es decir datos que no tienen un dominio de referencia o parámetros sin dimensiones.
parameter permite introducir, en forma de lista, datos de una o más dimensiones.
table permite introducir, en forma de tabla, datos de dos o más dimensiones.
Conviene señalar que por dimensión entendemos el número de dominios de definición, es decir, el número de subíndices en notación algebraica. Por otro lado, es importante mencionar que los valores o datos utilizan como separador decimal el punto (.).
En algunos casos (scalar, parameter), existen dos formas de introducir datos:
Introducción directa (declaración y asignación de valores simultáneamente).
Introducción mediante fórmulas de cálculo (declaración y asignación de valores en enunciados separados).
Veremos en primer la introducción directa y, a continuación, la introducción mediante fórmulas de cálculo.
4.3.1 Escalares
La palabra clave scalar se utiliza para definir un parámetro de dimensión cero, es decir, un parámetro que no está definido sobre un índice.
Para definir un scalar seguimos las siguientes reglas:
1. comenzar con la palabra clave scalar (scalars) para indicar que vamos a introducir datos simples,
2. dar un nombre específico (identificador) a cada escalar y añadir un comentario si se desea,
4. asignar el valor entre signos slash (/),
5. terminar el enunciado con un punto y coma (;).
La sintaxis de gams es la siguiente:
scalar
nombre_sca1 “comentario” /valor1/
nombre_sca2 “comentario”
nombre_sca3 “comentario” /valor3/
;
Nombre_sca1 es el nombre interno (o identificador) del primer dato en gams. También es posible declarar un scalar sin atribuirle un valor. En nuestro ejemplo, nombre_sca1 y nombre_sca3 tienen asociado un valor, mientras que nombre_sca2 no tiene un valor asociado. Posteriormente podremos asignar un valor a nombre_sca2 utilizando instrucciones de atribución, como veremos en la sección siguiente.
En el modelo ejemplo1.gms (primer ejemplo), la superficie de la explotación y la disponibilidad de trabajo son escalares :
scalar
DTI disponibilidad de superficie (ha) /50/
DTR disponibilidad de trabajo familiar (horas) /1800/
;
4.3.2 Parámetros
La palabra clave parameter permite introducir datos con una o más dimensiones, es decir, parámetros con uno o varios índices. Esta clave permite igualmente realizar cálculos, como veremos en la sección siguiente.
De manera general, las reglas son similares a las de los escalares:
1. comenzar con la clave parameter para indicar que vamos a introducir datos,
2. dar un nombre específico a cada parámetro señalando entre paréntesis sus dominios de definición (separados por comas si hay varios) y añadir eventualmente un comentario,
4. asignar los valores entre signos slash (/),
5. terminar el enunciado con un punto y coma (;).
La sintaxis para un parámetro de una dimensión será:
parameter nombre_par(dominio) “comentario”
/etiqueta1 valor1
etiqueta2 valor2
etiqueta3 valor3
…
etiquetaN valorN/
;
Los parámetros pueden tener hasta diez dimensiones diferentes. Si existe más de una dimensión, en la asignación de valores los elementos se separan con puntos.
En el modelo ejemplo1.gms (primer ejemplo), las necesidades de mano de obra por cultivo y el margen bruto del cultivo son parámetros:
parameter
NTR(C) ‘necesidades de trabajo (horas/ha)’ /trigo 20, maiz 60/
MB(C) ‘margen bruto (euros/ha)’ /trigo 450, maiz 1000/
;
4.3.3 Tablas
Los parámetros con dos o más dimensiones pueden escribirse en forma de tablas. En general, se repiten las reglas presentadas para los parámetros:
1. anunciar con la clave table que vamos a introducir una tabla,
2. dar un nombre a la tabla señalando entre paréntesis sus dominios de definición (separados por comas) y añadir eventualmente un comentario,
3. colocar los elementos en filas y en columnas y asignar los valores,
5. terminar el enunciado con un punto y coma (;).
La sintaxis para una tabla de dos dimensiones es:
table nombre_tab(dominio1, dominio2) “comentario”
elemento2-1 elemento2-2 … elemento2-N
elemento1-1 valor valor … valor
elemento1-2 valor valor … valor
… … … … …
elemento1-N valor valor … valor
;
Destaquemos que, a diferencia de los datos introducidos con la palabra clave parameter, los elementos introducidos con la clave table no se introducen entre signos slash (/). Siempre la primera dimensión de la tabla se introduce en filas y la última en columnas.
Mientras que con la clave parameter podemos introducir varios parámetros al mismo tiempo, para las tablas es preciso escribir cada vez la clave table y terminar la definición con punto y coma (;).
Si una tabla tiene muchas columnas de forma que no es posible introducir todos los datos en una misma línea, podemos fragmentar la tabla utilizando el signo “+” para indicar que la tabla continúa debajo.
Una tabla puede tener hasta 10 dimensiones diferentes. Es posible escribir varios índices en la misma fila o en la misma columna, separándolos por puntos. Recordemos que el primer índice de cada fila debe corresponder al primer dominio de definición y el último índice de cada columna debe corresponder al último dominio de definición, es decir, es preciso respetar el orden.
Todas las formas que hemos comentado para condensar la escritura de los sets pueden utilizarse también aquí.
4.3.4 Introducción de datos mediante fórmulas de cálculo
Las instrucciones de atribución de valores nos permiten definir o modificar los valores asociados a los escalares y a los parámetros.
Podemos utilizar estas instrucciones para asignar valores a los escalares y a los parámetros declarados con anterioridad. Por ejemplo, el escalar nombre_sca2 fue declarado en la sección 4.3.1 pero sin atribuirle un valor. Podemos atribuirle un valor ahora escribiendo:
nombre_sca2= valor2;
Recordad que la atribución de valores no comienza por una palabra clave (puesto que la entidad nombre_sca2 ya se ha declarado previamente como un scalar) y que cada expresión debe terminar con un punto y coma (;).
De la misma forma, podemos cambiar el valor de un escalar o de un parámetro definido anteriormente. En este caso, gams tendrá en cuenta únicamente la última atribución de valor. Si queremos cambiar solamente el valor correspondiente a uno de los elementos de un conjunto, debemos utilizar apóstrofes. Podemos, por ejemplo, cambiar el valor del margen bruto del trigo en el modelo ejemplo1.gms. Para ello, escribimos:
MB(‘trigo’) = 480;
Por otro lado, podemos obtener nuevos datos realizando cálculos con datos introducidos previamente. En este caso, conviene utilizar la clave parameter independientemente de cuál sea la dimensión, así como separar la declaración del parámetro y la atribución de valor (que se realizará en este caso utilizando una fórmula de cálculo) por un signo punto y coma (;):
parameter nombre_par(dominio);
nombre_par(dominio) = formula de calculo;
Gams utiliza los símbolos habituales para las operaciones aritméticas corrientes (Tabla 8).
Tabla 8. Operadores aritméticos
|
|
|
|
|
|
|
|
|
|
|
|
En el modelo ejemplo1.gms encontramos ejemplos simples de introducción de datos:
parameter
NTR(C) ‘necesidades de trabajo (horas/ha)’ /trigo 20, maiz 60/
MB(C) ‘margen bruto (euros/ha)’ /trigo 450, maiz 1000/
;
Si disponemos de datos desagregados, podemos también introducir el margen bruto como un parámetro de cálculo, a partir de los datos de rendimiento de cultivo, de precio de los productos y de costes variables.
parameter
RDT(C) rendimiento de cultivo /TRIGO 5, MAIZ 10/
PC(C) precio del producto /TRIGO 150, MAIZ 150/
CV(C) costes variables /TRIGO 300, MAIZ 500/
;
parameter
MB(C) margen bruto del cultivo (euros por hectarea)
MB(C)= RDT(C)*PC(C) - CV(C);
Para realizar cálculos sobre índices, podemos utilizar los siguientes operadores (Tabla 9).
Tabla 9. Operadores indexados
|
|
|
|
|
|
|
|
|
|
La sintaxis de gams es:
operador((dominio1,dominio2), expresion);
Además, el lenguaje gams dispone de un cierto número de funciones matemáticas, de las cuales algunas de las más utilizadas se muestran en la Tabla 10.
Tabla 10. Algunas funciones en GAMS
|
|
|
|
|
|
|
|
|
|
|
|
4.4 Las variables
4.4.1 Definición de las variables
Las variables en lenguaje gams corresponden a las variables endógenas en economía, a las columnas o actividades en programación lineal, o a las variables de decisión en investigación de operaciones.
La palabra clave variable anuncia que vamos a introducir variables endógenas, cuyos valores serán determinados en el proceso de optimización. En general, la declaración de las variables es similar a la declaración de los parámetros. De este modo, para cada variable se establece un nombre, se especifica el dominio de definición y eventualmente se introduce un comentario:
[tipo_variable] variable nombre_var1(dominio) “comentario”
nombre_var2(dominio) “comentario”
…
nombre_var(dominio) “comentario”
;
Aunque obviamente no vamos a atribuir valores a las variables, sí debemos especificar el tipo de variable, utilizando para ello la palabra clave correspondiente. Existen varios tipos posibles (Tabla 11).
Tabla 11. Tipos de variables
|
|
|
|
|
-inf | +inf |
|
|
0 | +inf |
|
|
-inf | 0 |
|
|
0 | 1 |
|
|
0 | 100 |
|
Existen dos métodos alternativos para declarar las variables y especificar la clase: a) declarar primero todas las variables y especificar a continuación la clase para las variables no libres; b) efectuar la declaración de las variables por clase. El cuadro que se presenta a continuación ilustra ambos métodos para el modelo ejemplo1.gms (Tabla 12).
Tabla 12. Dos maneras de definir el tipo de variable
|
|
VARIABLE Z margen total X(c) superficie de cultivo ; POSITIVE VARIABLE X ; |
VARIABLE Z margen total ; POSITIVE VARIABLE X(c) superficie de cultivo ; |
La variable que representa la cantidad a optimizar (Z en el ejemplo) debe ser siempre una variable libre.
4.4.2 Atributos de las variables
A cada variable le corresponden 4 valores asociados, que se diferencian utilizando sufijos (Tabla 13).
Tabla 13. Valores asociados a cada variable
|
Sufijo |
|
|
.lo |
|
|
.up |
|
|
.l |
|
|
.m |
|
Para fijar el límite superior o inferior de una variable, utilizamos los sufijos que acabamos de presentar. Así, por ejemplo, para indicar que el valor superior que puede tomar la variable nombre_var es valor, escribiremos:
nombre_var.UP(dominio) = valor;
En los modelos lineales, los límites por defecto (ver Tabla 11) son suficientes en la mayor parte de los casos. Por el contrario, en los modelos no lineales es recomendable definir límites para evitar operaciones indefinidas (división por cero, por ejemplo) o para definir un espacio de soluciones “razonable” (lo que ayudará al programa a resolver el problema).
También es posible fijar el valor que puede tomar la variable. Para ello utilizamos el sufijo .fx.
4.5 Las ecuaciones
Las ecuaciones permiten escribir las relaciones algebraicas que definen las ecuaciones y la función objetivo del modelo.
La introducción de las ecuaciones en gams se realiza en dos etapas. En primer lugar, todas las ecuaciones deben ser declaradas y, a continuación, cada ecuación debe ser formulada. La forma de introducir las ecuaciones es la siguiente:
equation
nom_eq2 “comentario”
nom_eq1 “comentario”
…
nom_eqN “comentario”
;
nom_eq1(dominio).. expresion_lhs1 tipo_eq1 expresion_rhs1;
nom_eq2(dominio).. expresion_lhs2 tipo_eq2 expresion_rhs2;
…
nom_eqN(dominio).. expresion_lhsN tipo_eqN expresion_rhsN;
Los nombres de las ecuaciones deben respetar las reglas enunciadas para el resto de identificadores. Es preciso introducir un punto y coma (;) al final de la declaración de todas las ecuaciones, y también al final de la formulación de cada ecuación.
Para formular una ecuación, comenzamos escribiendo su nombre, a continuación introducimos el signo « .. » y después escribimos la expresión correspondiente. Las ecuaciones pueden ser igualdades o desigualdades. El símbolo entre el término de la izquierda y el término de la derecha indica el tipo de ecuación (Tabla 14).
Tabla 14. Tipos de ecuaciones
|
|
=E= |
|
=L= |
|
=G= |
|
Observación: estos símbolos no se utilizan para hacer cálculos intermedios sino únicamente para definir las ecuaciones. Recordemos que en los parámetros de cálculo es símbolo igual es simplemente =.
En la definición de los términos de la izquierda y de la derecha podemos utilizar operadores de cálculo matemático y funciones matemáticas, de la misma forma que hemos visto para los parámetros de cálculo.
La mención del dominio de definición de una ecuación permite, como para las variables o los parámetros de cálculo, beneficiarse de comodidad de la escritura con índices. Así, por ejemplo, para expresar una restricción mensual no es preciso escribir la ecuación doce veces sino una sola.
En el caso en el que una ecuación no se defina para todos los elementos de un conjunto, sino sólo para algunos de ellos, podemos utilizar etiquetas de forma explícita en las ecuaciones. Por ejemplo, si en el modelo ejemplo1.gms queremos introducir una restricción para indicar que la superficie de maíz no puede exceder de 20 hectáreas, escribiremos:
equation SUP_ MAIZ restriccion de superficie de maiz
;
SUP_MAIZ.. X(‘maiz’) =L= 20;
4.6 Los comandos MODEL y SOLVE
4.6.1 El comando MODEL
El comando model permite dar un nombre al modelo así como identificar las ecuaciones que forman parte de ese modelo. Si todas las ecuaciones definidas con anterioridad forman parte del modelo que queremos resolver, utilizamos la palabra clave all; en caso contrario es preciso especificar el nombre de las ecuaciones implicadas.
La forma general de identificar un modelo es, por tanto:
model Nombre_mod “comentario” /ALL/;
o bien
model Nombre_mod “comentario” /Nombre_equ1,…,Nombre_equN/;
Es posible definir varios modelos en el mismo archivo de entrada dando un nombre diferente a cada uno. Este procedimiento resulta muy útil cuando queremos resolver distintas versiones de un problema, cuando algunas ecuaciones difieren de una versión a otra.
4.6.2 El comando SOLVE
El comando solve se utiliza para dar a gams las instrucciones de resolución del problema. Indica:
1. El modelo a resolver,
2. El tipo de cálculos a realizar:
- Programación lineal LP (linear programming)
- Programación no lineal NLP (non linear programming)
- Programación mixta MIP (mixed integer programming)
- Otros.
3. El sentido de la optimización (maximizing o minimizing)
4. La variable a optimizar.
La sintaxis gams es:
solve nombre_mod USING tipo_mod MAXIMIZING|MINIMIZING nombre_varobj;
La variable objetivo (nombre_varobj) debe ser una variable libre y debe aparecer al menos una vez en las ecuaciones.
Escribiremos, por ejemplo:
solve ejemplo1 USING LP MAXIMIZING Z;
Con esta instrucción estamos diciendo a gams que tiene que resolver (solve) un modelo que se llama ejemplo1 utilizando (using) un algoritmo de cálculo de programación lineal (lp) y maximizando (maximizing) la variable Z. gams puede resolver differentes tipos de modelos (Tabla 15).
Tabla 15. Tipos de modelos
Tipo de modelo | Descripción |
|
Linear programming (programación lineal) |
|
Nonlinear programming (programación no lineal) |
|
Nonlinear programming with discontinous derivatives (programación no lineal con derivadas no continuas) |
|
Relaxed mixed integer programming |
|
Mixed integer programming (programación mixta) |
|
Relaxed mixed integer nonlinear programming |
|
Mixed integer nonlinear programming (programación no lineal mixta) |
|
Mathematical programs with equilibrium constraints (programas matemáticos con restricciones de equilibrio) |
|
Mixed complementarity problem |
|
Constrained nonlinear system (sistema no lineal con restricciones) |
4.6.3 Atributos de los modelos
El modelizador puede cambiar algunos atributos de los modelos utilizando para ello opciones. Algunas de las más utilizadas se presentan en la Tabla 16.
Tabla 16. Atributos de los modelos
Sufijo | Descripción | Por defecto | Opción global |
|
número de iteraciones | 1000 |
|
|
número de columnas en el archivo de salida para cada bloque de variables | 3 |
|
|
número de líneas en el archivo de salida para cada bloque de ecuaciones | 3 |
|
|
límite de tiempo para el solver (en segundos CPU) | 1000 |
|
|
opción para cambiar la escala de variables o ecuaciones | 0 | |
|
opción para presentar o no la solución | 1 |
|
Veremos algunos ejemplos en el apartado siguiente.
4.7 Los resultados del modelo
4.7.1 El archivo de salida
El archivo de salida (archivo .lst) se genera automáticamente al ejecutar el modelo y contiene mucha información útil para comprender y contrastar el modelo. Parte de la información se genera durante la compilación del programa, fase en la que gams comprueba que no hay errores y que el modelo es coherente, mientras que otra parte de la información se genera durante la resolución.
La primera parte del archivo de resultados es siempre el echo print, donde gams presenta el modelo (elementos del archivo .gms) añadiendo números de línea.
Si el programa detecta errores de escritura, los presentará en esta sección.
Si queremos que el archivo de salida no muestre el modelo, podemos utilizar la opción $offlisting.
4.7.2 Mensajes de error
En las primeras etapas de desarrollo de un modelo, es frecuente cometer errores de escritura. Al ejecutar un modelo que contiene errores, gams interrumpe el proceso de ejecución y señala los errores en los archivos .log y .lst.
Además de señalar los errores, Gams da información sobre la naturaleza de los mismos y sugerencias para corregirlos.
4.7.2.1 Errores de compilación
Si gams encuentra errores durante la fase de compilación del modelo, interrumpe la ejecución y señala cada error en los archivos .log y .lst.
En el archivo .log, gams señala el error en una línea en rojo que comienza con tres asteriscos (***) y la palabra error seguida de un número que indica la naturaleza del error. A continuación, sugiere la forma de corregirlo. Haciendo doble clic en la línea roja, gams se posiciona en el archivo .gms al lado del error.
En el archivo .lst, gams señala el error con cuatro asteriscos (****) y el símbolo $ seguido de un número que indica la naturaleza del error. Las sugerencias para corregir el error se presentan justo después del echo print del modelo.
La mayor parte de los errores tienen lugar porque olvidamos declarar un identificador, olvidamos un punto y coma (;), declaramos una ecuación sin definirla o a la inversa, etc.
También puede ocurrir que gams no muestre mensajes de error pero que el modelo no proporcione los resultados esperados. Esto puede deberse a que existen errores de escritura que gams no puede detectar porque no se trata de errores de lenguaje. Para evitarlos, debemos verificar que la escritura de las ecuaciones es correcta. Para ello, debemos observar la escritura detallada de las ecuaciones (que se encuentra en el archivo .lst) y verificar que corresponde a aquello que queríamos expresar.
4.7.2.2 Errores de ejecución
Estos errores aparecen cuando el solver es llevado a hacer operaciones ilegales como la división por cero. El modelo crazy de la biblioteca de modelos de gams ilustra todas las operaciones no estándar.
4.7.2.3 Errores de resolución
Otros errores aparecen durante el proceso de resolución. Los problemas más frecuentes se originan porque se han definido límites no coherentes, dando lugar a un problema donde la región admisible es ilimitada o bien no existe.
4.7.3 Elementos producidos por el comando SOLVE
4.7.3.1 Listado de ecuaciones
Justo después de la escritura del modelo, se presenta un listado de ecuaciones (equation listing). La lista de ecuaciones del modelo ejemplo1.gms se muestra en la Figura 17.
Equation Listing SOLVE EJEMPLO Using LP From line 42 —- MARGEN =E= definicion del margen global MARGEN.. Z - 450*X(TRIGO) - 1000*X(MAIZ) =E= 0 ; (LHS = 0) —- TIERRA =L= restriccion de tierra TIERRA.. X(TRIGO) + X(MAIZ) =L= 50 ; (LHS = 0) —- TRABAJO =L= restriccion de trabajo TRABAJO.. 20*X(TRIGO) + 60*X(MAIZ) =L= 1800 ; (LHS = 0) |
Figura 17. Resultados: lista de ecuaciones
En la lista de ecuaciones, todos los términos que contienen variables se colocan a la izquierda, mientras que los términos constantes se colocan a la derecha. El término entre paréntesis (lhs = 0) significa que el término de la izquierda toma el valor inicial cero. Esto es así porque cuando no se introducen valores iniciales para las variables, gams toma por defecto el valor cero.
Las ecuaciones se presentan de forma detallada, permitiendo así verificar que la escritura es correcta. Recordemos que gams detecta los errores de lenguaje pero no otros errores, por lo que siempre es necesario comprobar que las ecuaciones son correctas.
Las ecuaciones del modelo pueden agruparse en bloques (como en la escritura algebraica). En este caso, en el archivo de salida, gams presenta las tres primeras ecuaciones de cada bloque. El modelizador puede cambiar el número de ecuaciones presentadas utilizando la opción limrow. Por ejemplo, si escribimos:
OPTION LIMROW = 1;
gams presenta solamente la primera ecuación de cada bloque en el archivo de salida.
4.7.3.2 Listado de variables
La siguiente sección del archivo de salida (column listing) corresponde a un listado de las variables del modelo. Cada variable aparece con los coeficientes asociados en cada una de las ecuaciones en que interviene. La lista de variables del modelo ejemplo1.gms se presenta en la Figura 18.
Column Listing SOLVE EJEMPLO Using LP From line 42 —- Z margen global de la explotacion (euros) Z (.LO, .L, .UP, .M = -INF, 0, +INF, 0) 1 MARGEN —- X superficie de cultivo (ha) X(TRIGO) (.LO, .L, .UP, .M = 0, 0, +INF, 0) -450 MARGEN 1 TIERRA 20 TRABAJO X(MAIZ) (.LO, .L, .UP, .M = 0, 0, +INF, 0) -1000 MARGEN 1 TIERRA 60 TRABAJO |
Figura 18. Resultados: lista de variables
Como acabamos de ver para las ecuaciones, cuando se trata de variables con varias dimensiones (como la variable X del ejemplo precedente), gams presenta las tres primeras variables del bloque. Siempre es posible cambiar esta opción por defecto utilizando la palabra clave limcol. Por ejemplo, si escribimos en el ejemplo1.gms :
OPTION LIMCOL = 1;
gams presenta solamente la variable X(trigo).
4.7.3.3 Estadísticas
Después de la presentación de las ecuaciones y las variables, gams da algunas estadísticas, principalmente el número de ecuaciones (bloques de ecuaciones y ecuaciones simples), el número de variables (bloques de variables y variables simples) y el número de elementos no nulos de la matriz (Figura 19).
MODEL STATISTICS BLOCKS OF EQUATIONS 3 SINGLE EQUATIONS 3 BLOCKS OF VARIABLES 2 SINGLE VARIABLES 3 NON ZERO ELEMENTS 7 |
Figura 19. Resultados: estadísticas
4.7.3.4 Resumen de la resolución
El solve summary contiene algunas informaciones sobre el proceso de resolución del modelo (Figura 20).
S O L V E S U M M A R Y MODEL EJEMPLO OBJECTIVE Z TYPE LP DIRECTION MAXIMIZE SOLVER CONOPT FROM LINE 84 **** SOLVER STATUS 1 Normal Completion **** MODEL STATUS 1 Optimal **** OBJECTIVE VALUE 35500.0000 RESOURCE USAGE, LIMIT 0.000 1000.000 ITERATION COUNT, LIMIT 5 2000000000 |
Figura 20. Resultados: resumen de la tarea de resolución
En esta sección se indica el nombre del modelo (ejemplo), la función objetivo (z), el tipo de problema (lp), el sentido de la optimización (maximize), el solver utilizado (conopt) y la línea donde se encuentra el comando solve (84).
A continuación, se especifica el solver status y el model status y se presenta el valor que toma la función objetivo en la solución.
El solver status indica el estado del programa (Tabla 17):
Tabla 17. Atributos de los modelos: solver status
Solver status | Descripción |
1 normal completion | El solver ha resuelto el problema sin interrupción. |
2 iteration interrupt | El solver ha interrumpido la resolución porque el número de iteraciones necesarias para encontrar la solución es demasiado elevado (podemos utilizar la opción iterlim para incrementar el número de iteraciones). |
3 resource interrupt | El solver ha interrumpido la resolución porque se ha excedido el límite de tiempo (podemos utilizar la opción reslim para aumentar este límite). |
4 terminated by solver | El solver ha encontrado dificultades para resolver el problema. |
5 evaluation error limit | Hay muchos términos no lineales con valores no definidos (debemos evitar operaciones no válidas, como la división por cero). |
El model status indica el tipo de solución encontrada por el solver (Tabla 18):
Tabla 18. Atributos de los modelos: model status
Model status | Descripción |
1 optimal | Solución óptima (este tipo solo aparece en los modelos lineales) |
2 locally optimal | Óptimo local (solución óptima, en el caso de un modelo no lineal) |
3 unbounded | Solución indefinida. |
4 infeasible | Solución no admisible. |
5 locally infeasible | Solución no admisible de un programa no lineal dado el punto de partida considerado. |
6 intermediate infeasible | Solución incompleta y no admisible (el solver ha interrumpido la ejecución por alguna razón). |
7 intermediate non optimal | Solución incompleta y admisible (el solver ha interrumpido la ejecución por alguna razón). |
8 integer solution | Solución entera (programas MIP). |
9 intermediate non integer | Solución incompleta de un programa con variables enteras. |
10 integer infeasible | No existe solución entera. |
error no solution | No existe solución (es preciso verificar la escritura del problema). |
Siempre es necesario verificar que el model status es optimal (para los modelos lineales), locally optimal (modelos no lineales) o integer solution (modeles con variables enteras).
4.7.3.5 La solución del modelo
La siguiente sección muestra los resultados del programa, ecuación por ecuación y variable por variable (Figura 21). Para cada ecuación y cada variable se presentan cuatro valores: lower (límite inferior), level (nivel en la solución), upper (límite superior) y marginal (valor marginal).
LOWER LEVEL UPPER MARGINAL —- EQU MARGEN . . . 1.000 —- EQU TIERRA -INF 50.000 50.000 175.000 —- EQU TRABAJO -INF 1800.000 2000.000 13.750 MARGEN definicion del margen global TIERRA restriccion de tierra TRABAJO restriccion de trabajo LOWER LEVEL UPPER MARGINAL —- VAR Z -INF 35500.000 +INF . Z margen global de la explotacion (euros) —- VAR X superficie de cultivo (ha) LOWER LEVEL UPPER MARGINAL TRIGO . 30.000 +INF . MAIZ . 20.000 +INF . |
Figura 21. Resultados: solución del modelo
Los valores level y marginal son determinados por el solver. Gams utiliza la notación siguiente (Tabla 19):
Tabla 19. Algunos valores especiales
Símbolo |
|
- i nf |
|
+ i nf |
|
. |
|
e ps |
|
4.7.3.6 Informe final
La última sección del archivo de salida es el report summary. En él se muestra el número de líneas o de columnas marcadas como no óptimas, (nonopt), no admisibles (infeasible) o no limitadas (unbounded).
Una forma de verificar que el solver ha encontrado una solución óptima consiste en verificar que el report summary es como sigue:
**** REPORT SUMMARY : 0 NONOPT 0 INFEASIBLE 0 UNBOUNDED |
5 Algunos comandos especiales
5.1 Utilización del condicional
5.1.1 Las condiciones lógicas
Una condición lógica es una expresión que puede ser verdadera o falsa. Gams utiliza números como condiciones lógicas : así, un resultado verdadero se representa por 1 y un resultado falso por 0.
Los operadores relacionales se utilizan para establecer comparaciones entre expresiones de un modelo (Tabla 20):
Tabla 20. Operadores relacionales
Operador |
|
|
|
|
|
|
|
|
|
|
|
|
|
Los operadores lógicos se utilizan para escribir condiciones (Tabla 21):
Tabla 21. Operadores lógicos
Operador |
|
n ot |
|
a nd |
|
or |
|
x or |
|
Así, por ejemplo, dadas dos expresiones a y b, podemos expresar las condiciones lógicas siguientes (Tabla 22):
Tabla 22. Ejemplos de condiciones lógicas
Expresiones | Condiciones lógicas | |||||
a | b | a and b | a or b | a xor b | not a | not b |
0 | 0 | 0 | 0 | 0 | 1 | 1 |
0 | ≠ 0 | 0 | 1 | 1 | 1 | 0 |
≠ 0 | 0 | 0 | 1 | 1 | 0 | 1 |
≠ 0 | ≠ 0 | 1 | 1 | 0 | 0 | 0 |
5.1.2 El operador $
El operador $ permite introducir condiciones en los cálculos o en las ecuaciones del modelo. Por ejemplo, si queremos escribir:
si (b > 15), entonces a = 2
en lenguaje gams escribiremos :
a$(b > 15) = 2 ;
Es decir, « a, si b>15, es igual a 2 ».
5.1.2.1 El operador $ en los parámetros de cálculo
Cuando introducimos condiciones en los parámetros de cálculo, el significado del operador $ es diferente si se utiliza en el término de la izquierda o en el término de la derecha de la expresión.
Si se utiliza el $ a la izquierda, gams sólo atribuye un valor cuando la condición se satisface. Es decir, que si el parámetro había sido definido con antelación, gams sólo cambia los valores cuando la condición es respetada.
Si se utiliza el $ a la derecha, gams atribuye valores en todos los casos, es decir, si la condición no se satisface, gams atribuye el valor cero.
Si escribimos, por ejemplo :
parameter a /10/
b /10/
;
a$(b > 15) = 2 ;
El valor del parámetro a no ha cambiado (a=10, b=10). En cambio, si escribimos:
parameter a /10/
b /10/
;
a = 2$(b > 15);
Ahora, el valor del parámetro a ha cambiado (a=0, b=10).
5.1.2.2 El operador $ en las ecuaciones del modelo
Podemos hacer la analogía « $ a la izquierda » y « $ a la derecha » que acabamos de ver pero considerando ahora « a la izquierda del símbolo ‘..’ » y « a la derecha del símbolo ‘..’ ».
5.2 Conjuntos ordenados
En los modelos dinámicos, a menudo es necesario hacer referencia al periodo de tiempo precedente o siguiente, puesto que existen actividades que ligan los periodos entre sí. El programa gams permite tener en cuenta estas interrelaciones por medio de los conjuntos ordenados.
Como ya hemos comentado, un conjunto puede ser declarado como una secuencia. Por ejemplo:
set T periodos de tiempo /2001, 2002, 2003, 2004, 2005/;
5.2.1 Los operadores ord y card
Para indicar la posición relativa de un elemento dentro de un conjunto, se utiliza el operador ord. Es decir, el operador ord asocia a cada elemento de un conjunto un número real que indica su posición relativa dentro del conjunto.
Para comprender esto, podemos crear un parámetro orden:
set T periodos de tiempo /2001, 2002, 2003, 2004, 2005/
parameter p_orden(t);
p_orden(t)= ord(t);
display p_orden(t);
En el archivo de salida, encontraremos:
—- PARAMETER p_orden
2001 1, 2002 2, 2003 3, 2004 4, 2005 5
El operador ord resulta muy útil para expresar cantidades que varían según una regla aritmética. Por ejemplo, si un país tiene 56 millones de habitantes en el periodo base y la tasa de crecimiento de la población es de 1,5 % anual, entonces el número de habitantes en un periodo genérico posterior puede expresarse:
poblacion(t) = 56 * (1,015**(ord(t)-1));
El operador card indica el número de elementos de un conjunto. En nuestro ejemplo:
set T periodos de tiempo /2001, 2002, 2003, 2004, 2005/
parameter p_card;
p_card = card(T);
DISPLAY p_card;
En el archivo de salida, encontraremos: p_card = 5.
5.2.2 Los operadores de paso del tiempo
Para indicar el paso del tiempo en los modelos dinámicos, podemos utilizar operadores lineales (+, -) y circulares (++, –).
Los operadores lineales se utilizan para modelizar periodos de tiempo que no se repiten, por ejemplo, los años. Los operadores circulares, en cambio, se utilizan para representar periodos de tiempo que pueden repetirse, como los meses. En este caso, se considera que el primer y el último periodo son adyacentes, de forma que «último ++ 1» es igual a «primero» y «primero – 1» es igual a «último».
5.2.2.1 Utilización en los cálculos
Cuando se utilizan operadores lineales en los parámetros de cálculo, hay que tener en cuenta que algunos términos no estarán definidos (el elemento «último +1» o el «primero -1»). El sentido será diferente dependiendo de que utilicemos el operador en el término a la izquierda o en el término a la derecha de la expresión. Veamos esto con un ejemplo :
set t /2001,2002,2003,2004,2005/
parameter a(t), b(t),c(t);
a(t) = 10 + ord(t);
b(t) = 10; b(t) = a(t-1);
c(t) = 10; c(t+1) = a(t);
option decimals=0;
display a,b,c;
Los parámetros b y c tienen el mismo valor inicial y expresiones homólogas, pero el operador de paso del tiempo está a la derecha en el parámetro b (esto quiere decir que gams va a cambiar siempre los valores) y a la izquierda en el parámetro c (esto quiere decir que gams sólo va a cambiar los valores cuando la expresión existe). Por tanto, en el archivo de resultados, podremos leer:
—- 12 PARAMETER a
2001 11, 2002 12, 2003 13, 2004 14, 2005 15
—- 12 PARAMETER b
2002 11, 2003 12, 2004 13, 2005 14
—- 12 PARAMETER c
2001 10, 2002 11, 2003 12, 2004 13, 2005 14
En el caso de los operadores circulares, todos los términos están definidos. El siguiente ejemplo ilustra el uso de operadores circulares:
set S estacion /primavera, verano, otogno, invierno/
parameter valor(s) /primavera 10, verano 15, otogno 20, invierno 25/
mas(s)
menos(s);
mas(s) = 1; mas(s) = valor(s++1);
menos(s) = 1; menos(s++1) = valor(s);
option decimals=0; display valor, mas, menos;
En el archivo de salida, leemos:
—- 18 PARAMETER valor
primavera 10, verano 15, otogno 20, invierno 25
—- 18 PARAMETER mas
primavera 15, verano 20, otogno 25, invierno 10
—- 18 PARAMETER menos
primavera 25, verano 10, otogno 15, invierno 20
5.2.2.2 Utilización en las ecuaciones
En el caso de operadores lineales, la utilización a la izquierda del símbolo «..» modifica el dominio de definición de la ecuación; la utilización a la derecha del símbolo «..» atribuye un valor nulo cuando el elemento no está definido.
El siguiente ejemplo de gestión de un embalse permite ilustrar el uso de los operadores de paso del tiempo en las ecuaciones: el estado del embalse en cada periodo depende del estado en el periodo anterior, del desembalse de agua en el periodo considerado y de la recarga anual. Definimos:
set t periodos de tiempo /T1*T5/
parameter X0(t) nivel inicial de agua /T1 5/
Q(t) recarga anual /T1*T5 3/
;
positive variable X(t) nivel de agua en el embalse
U(t) desembalse de agua
;
equation TRANSITION(T) estado del embalse
;
Si formulamos la ecuación de transición de la manera siguiente:
TRANSITION(t+1).. X(t+1) =e= x0(t) + X(t) - U(t) + q(t);
En el archivo .lst, encontraremos 4 ecuaciones detalladas (la ecuación no está definida para «t1») :
—- TRANSITION =E= estado del embalse
TRANSITION(T2).. - X(T1) + X(T2) + U(T1) =E= 8 ;
TRANSITION(T3).. - X(T2) + X(T3) + U(T2) =E= 3 ;
TRANSITION(T4).. - X(T3) + X(T4) + U(T3) =E= 3 ;
TRANSITION(T5).. - X(T4) + X(T5) + U(T4) =E= 3 ;
En cambio, la formulación:
TRANSITION(t).. X(t+1) =e= x0(t) + X(t) - U(t) + q(t);
producirá 5 ecuaciones de transición (la ecuación está definida para «t1»):
—- TRANSITION =E= estado del embalse
TRANSITION(T1).. - X(T1) + X(T2) + U(T1) =E= 8 ;
TRANSITION(T2).. - X(T2) + X(T3) + U(T2) =E= 3 ;
TRANSITION(T3).. - X(T3) + X(T4) + U(T3) =E= 3 ;
TRANSITION(T4).. - X(T4) + X(T5) + U(T4) =E= 3 ;
TRANSITION(T5).. - X(T5) + U(T5) =E= 3 ;
5.3 El comando DISPLAY
El comando solve genera una gran cantidad de información en el archivo de salida. Al final del archivo de salida se presentan los resultados, incluyendo para cada variable tanto los valores primal y dual como los límites superior e inferior.
Para obtener una presentación más concisa de los resultados, podemos utilizar el comando display.
Si utilizamos el comando display para seleccionar los resultados a presentar, debemos utilizarlo después del comando solve. Así, si escribimos en el modelo ejemplo1.gms:
display X.L, X.M; .L = nivel de la solucion o valor primal
.M = valor dual o marginal
obtendremos la solución del modelo.
El comando display también resulta útil para mostrar cálculos intermediarios que no provienen de la optimización. Es, por ejemplo, el caso de los parámetros de cálculo. Si queremos que gams presente en el archivo de salida los valores de los parámetros de cálculo, utilizamos el comando display:
display nombre_par1, nombre_par2 ;
También podemos elegir el formato de salida con el comando option. Por ejemplo, si queremos que gams muestre los parámetros con dos cifras decimales, escribiremos:
option nombre_par1:2;
Si los parámetros están definidos sobre varios índices, es posible especificar el formato de presentación de los resultados. Por ejemplo:
option nombre_par2:2:1:1;
Especifica que deseamos dos cifras decimales, un índice por fila y un índice por columna.
El comando display permite seleccionar y reagrupar los resultados del problema. También permite realizar cálculos utilizando los resultados. Para ellos, podemos crear parámetros de resultados después del comando solve, incluyendo las informaciones que nos interesen.
5.4 El comando LOOP
El comando loop permite repetir una operación varias veces, es decir, gams ejecuta las instrucciones que se encuentran en el interior del loop de forma iterativa, una vez por cada elemento del set sobre el cual hemos definido el loop.
La sintaxis de este comando es:
loop(set_control,expresion);
Si hay más de un índice de control, se introducen entre paréntesis y separados por comas.
El comando loop puede utilizarse para:
Asignar valores a un parámetro cuando existe una relación entre ellos.
Resolver el modelo de forma iterativa.
Crear tablas de resultados utilizando el comando put.
Veamos un ejemplo del primer tipo de utilización. Queremos expresar el precio del agua en distintos escenarios. Para ello, creamos un conjunto de escenarios:
set L simulaciones /L1*L5/;
y definimos un parámetro indicando el aumento del precio del agua a partir de un nivel base:
parameter TM3L(L) precio del agua;
TM3L(‘L1’) = 32;
loop(l, TM3L(L+1) = TM3L(L) + 2);
Retomando el ejemplo anterior podemos ilustrar el segundo tipo de utilización. Si deseamos ejecutar un modelo de optimización varias veces, de forma que en cada ejecución un parámetro (creamos un parámetro tm3, por ejemplo) tome un valor diferente, escribiremos:
loop(L,
TM3 = 30 + 2*ord(l);
solve MOD_LOOP using LP maximizing Z;
);
De esta forma, gams resolverá el modelo cinco veces, tomando cada vez un valor diferente para el parámetro tm3.
Está prohibido declarar parámetros dentro de un loop (si podemos, sin embargo, cambiar la definición de un parámetro) y también está prohibido definir ecuaciones dentro de un loop).