Você está na página 1de 53

Universidad de Talca

Facultad de Ingeniera
Departamento de Modelacin y Gestin Industrial
Programa de Magster en Gestin de Operaciones

IBM ILOG CPLEX, OPL y Concert


Technology: Manual prctico

Julio Mardones S. Eduardo lvarez M.

Abril, 2010.
2
Tabla de contenidos

Programacin Matemtica: Conceptos Bsicos ............................................................... 5


Programacin Lineal: Funcin Objetivo y Restricciones ........................................................... 5
Programacin Entera: Funcin Objetivo y Restricciones .......................................................... 6
Solvers para Programacin Lineal y Entera ............................................................................... 7
Uso de OPL para resolucin de Problemas de Programacin Matemtica ..................... 9
Descripcin General: Generacin de un proyecto y de los archivos .mod, .dat y .ops ............. 9
Resolucin de un problema bsico en OPL ............................................................................. 10
Enlace de una planilla Excel para la lectura de datos.......................................................... 13
Resolucin de un problema de mediana complejidad en OPL ............................................... 14
Problema de Transporte: Variante I .................................................................................... 14
Problema de Transporte: Variante II ................................................................................... 18
Resolucin del TSP utilizando OPL (Parte I) .................................................................... 23
TSP: Formulacin del problema como un PLEM ..................................................................... 23
TSP: Formulacin del problema en OPL .................................................................................. 24
Discusin de resultados: Tiempos de Resolucin y alcance de la formulacin ...................... 26

Resolucin del Steiner Tree Problem en grafos utilizando OPL ..................................... 27


StT Problem: Formulacin del problema como un PLEM ....................................................... 27
StT Problem: Formulacin del problema en OPL .................................................................... 28
Discusin del modelo presentado........................................................................................... 31
Resolucin del TSP Problem utilizando OPL (Parte II) .................................................... 32
Descripcin del Algoritmo de Planos de Corte (PC) ................................................................ 32
Descripcin de la Implementacin en OPL del Algoritmo PC ................................................. 33
Discusin de resultados: Tiempos de Resolucin y alcance de la formulacin ...................... 36
Resolucin del TSP Problem utilizando OPL (Parte III) ................................................... 38
Descripcin de la Implementacin utilizando Tecnologa Concierto del Algoritmo PC .......... 38
Discusin de resultados: Tiempos de Resolucin, Configuracin de Parmetros .................. 44
Anexos ............................................................................................................................ 46
Anexo 1: Cdigo implementacin de Algoritmo Planos Cortantes para TSP en OPL.............. 46
Anexo 2: Cdigo implementacin Algoritmo Planos Cortantes para TSP usando Tecnologa
Concierto. (esto puede mostrarse de otra forma??) .............................................................. 48
Anexo 2.1: Cdigo clase TSPSolver.cpp ............................................................................... 48
Anexo 2.2: Cdigo clase TSPTour.cpp ................................................................................. 51

3
Anexo 2.3: Cdigo clase TSPUtil.cpp ................................................................................... 52

4
Programacin Matemtica: Conceptos Bsicos

La Programacin Matemtica es un rea de la Matemtica Aplicada que trata de resolver problemas de


decisin en los que se deben determinar acciones que optimicen un determinado objetivo, pero
satisfaciendo ciertas limitaciones en los recursos disponibles.

En el enfoque cientfico de toma de decisiones, es fundamental el concepto de modelo matemtico, que


corresponde a una representacin matemtica de la situacin real que se quiere representar para tomar
las mejores decisiones. Dicho de otra forma, los modelos de optimizacin tratan de expresar

matemticamente el propsito de resolver un problema de la mejor forma posible.


Un problema de programacin matemtica tiene la siguiente forma:

As, el vector corresponden a las variables de decisin del problema, la funcin


es la funcin objetivo, mientras las funciones son las funciones de
restricciones, y las constantes son los lmites o disponibilidad de recursos para las
restricciones. Un vector corresponde a una solucin factible, si satisface las restricciones, y un vector
corresponde a una solucin ptima, si posee el menor valor de la funcin objetivo entre todos los
vectores que satisfacen las restricciones del problema. Mayores detalles se pueden consultar en un gran
nmero de libros del rea de autores como W. Winston [1] y M. Griva2.

Generalmente se consideran familias o clases de problemas de optimizacin, caracterizados por formas


particulares de la funcin objetivo y las funciones asociadas a las restricciones. As, hay problemas de
programacin lineal, no-lineal, programacin entera, etc.

Programacin Lineal: Funcin Objetivo y Restricciones


Un problema de programacin lineal se refiere a la optimizacin (minimizacin maximizacin) de una
funcin lineal, sujeta a restricciones lineales de igualdad y/ desigualdad, as, tanto la funcin objetivo
como las restricciones estn representadas por funciones lineales en las variables de decisin.

Un problema de programacin lineal puede ser enunciado de manera general de la siguiente forma:

O de manera ms compacta a travs de notacin matricial:

Donde los coeficientes estn representados por la matriz , y . Otro


elemento fundamental es la naturaleza de las variables de decisin, en que se asume que son variables
reales continuas no-negativas.

[1]
W.Winston, Investigacin de Operaciones, 4Edicin,Thomson (2004).
[2]
I. Griva, S. Nash, A. Sofer, Linear and Nonlinear Optimization, 2Edicin,SIAM (2009).

5
Problema Ilustrativo: La compaa Wyndor Glass produce productos de vidrio de alta calidad, incluidos
ventanas y puertas de vidrio. La compaa posee tres plantas, la planta 1 fabrica los marcos de aluminio,
la planta 2 los marcos de madera, y la planta 3 fabrica el vidrio y ensambla ventanas y puertas. Debido a
la disminucin en las ventas, la alta direccin ha decidido descontinuar ciertos productos no rentables
por lo que se dispone de capacidad de produccin para lanzar dos nuevos productos: Una puerta de
cristal de 8 pies con marco de aluminio, y una ventana colgante de doble marco de madera de 4x6 pies.
El producto 1 necesita capacidad de produccin en las plantas 1 y 3, mientras el producto 2 requiere
produccin en las plantas 2 y 3. Las instalaciones de produccin en la planta 1 estarn disponibles 4
horas/semana, mientras la planta 2 dispone de 12 horas/semana y la planta 3 de 18 horas semanales.

La capacidad productiva de cada planta usada por cada lote de productos depende de su tasa de
produccin. Se estima que cada lote de puertas requerir una hora de tiempo de produccin en la
planta 1 y de tres horas en la planta 3. Mientras para cada lote de ventanas, se requerirn alrededor de
dos horas en la planta 2 y de dos horas en la planta 3. Analizando los datos de los costos y la decisin de
los precios, el departamento de contabilidad estima las ganancias de los dos productos (por lotes),
siendo de $300 para las puertas y $500 para las ventanas. La administracin quiere conocer Cul debe
ser la cantidad de puertas y de ventanas a producir por semana tal de maximizar las ganancias?

Para establecer el modelo matemtico, definimos las variables de decisin en relacin a la cantidad de
puertas y ventanas a producir, as definimos:

Y llamaremos por al beneficio total por semana. Adems cada planta posee
limitaciones en su capacidad de produccin, as cada lote del producto 1 requiere una hora de
produccin en la planta 1, teniendo la planta disponible slo 4 horas/semana, lo cual se traduce en la
expresin matemtica que . Situacin similar ocurre en la planta 2, por lo que .
Mientras en la planta 3, el tiempo consumido por ambos productos es y se dispone de slo
18 horas, lo cual se traduce en la restriccin que .

Para resumir, el modelo de programacin lineal consiste en encontrar la tasa de produccin de ambos
productos, tal de:

De esta manera, el problema queda completamente formulado, por lo cual en este punto se podra
ocupar algn algoritmo u otro mtodo de resolucin para resolver el problema.

Programacin Entera: Funcin Objetivo y Restricciones


Supongamos que tenemos un problema de programacin lineal como el estudiado anteriormente, y si
adems ponemos la restriccin adicional de que las variables de decisin deben tomar valor enteros, se
traduce en el siguiente problema de Programacin Lineal Entera.

6
Ahora, si algunas variables pueden tomar valores en los nmeros reales y otras estn restringidas a ser
enteras, corresponde a un problema de Programacin Entera Mixta. Otro caso de gran importancia son
los problemas de Programacin Combinatorial, en que las variables estn restringidas a tomar valores
iguales a 0 1.

Solvers para Programacin Lineal y Entera


En el mbito de la programacin matemtico es fundamental el conocimiento y desarrollo de los Solvers
matemticos. Como fruto del trabajo de cientos de investigadores que durante dcadas han
desarrollado vastas teoras, mtodos, algoritmos en el rea, sumado al desarrollo en las tecnologas de
la computacin y a la necesidad de resolver problemas y aplicaciones cada vez de mayor complejidad y
tamao, han hecho propicio el desarrollo de paquetes computacionales orientados a la resolucin de
problemas de programacin matemtica, y fundamentalmente de programacin lineal y entera.

Ms formalmente, un solver corresponde a una serie de rutinas que forman parte de un software
matemtico, que puede funcionar como un programa independiente o como libreras de programacin,
que resuelven un problema matemtico. Un solver toma la descripcin de un problema de una forma
genrica y lo resuelve. Existen solvers para resolver diversos tipos de problemas, sin embargo nos
enfocaremos principalmente a los solvers de optimizacin lineal, lineal entera y no lineal.

Se debe realizar la distincin existente entre los sistemas de modelamiento, y los solvers. Los primeros
corresponden a un ambiente que otorga una interfaz y un lenguaje para confeccionar los modelos
matemticos a resolver, mientras los segundos, como ya se dijo, brindan las rutinas y algoritmos para la
resolucin de los modelos.

Entre los ambientes de programacin matemtica ms famosos tenemos:

LINGO/LINDO: corresponde a un ambiente integrado de optimizacin para la construccin y resolucin


de modelos de programacin lineal, entera, y no-lineal, de una manera fcil y con un lenguaje de
programacin propio, capaz de integrarse con otras aplicaciones.

AMPL: (A Mathematical Programming Language ) es un poderoso e integral lenguaje para problemas de


optimizacin lineal y no lineal, ya sea para variables continuas y/o discretas. Puede ser usado con
diversos solvers de optimizacin obteniendo gran flexibilidad y rendimiento.

GAMS: (General Algebraic Modeling System) es un sistema de modelamiento de alto nivel, y consiste de
un lenguaje propio de programacin con un solver de alto nivel. Especialmente diseado para resolver
aplicaciones complejas con modelos de gran tamao, puede ser utilizado con una gran cantidad de otros
solvers, inclusive los ms importantes.

OPL Development Studio: corresponde a un poderoso sistema de modelamiento para el desarrollo de


aplicaciones de programacin. Proporciona un intrprete para los modelos escritos en OPL
(Optimization Programming Language ), un ambiente de desarrollo integrado (IDE) para ejecutar y
probar los modelos de optimizacin, una herramienta de lnea de comandos (oplrun) para ejecutar
modelos desde la lnea de comandos y una interfaz de programacin y aplicaciones ( API), para incluir los
modelos en aplicaciones independientes, mientras usa CPLEX como solver.

Mientras los solvers ms conocidos son:

EXCEL: el famoso programa de ofimtica de Windows posee dentro de sus herramientas un solver capaz
de resolver problemas de programacin lineal, lineal entera y no lineal, con un nmero de variables y

7
restricciones limitado, cuya importancia principal radica en el hecho de la cotidianidad y gran nmero de
usuarios que posee Excel, an cuando no posee una gran calidad.

XPRESS: es un solver de alto rendimiento de programacin lineal y programacin lineal entera mixta.
Tambin puede ser usado para resolver problemas de programacin cuadrtica, y funciona solamente
en conjunto con el sistema de modelamiento GAMS.

GLPK: es un software libre que intenta resolver problemas de gran tamao de programacin lineal, y
programacin entera mixta, entre otros. Conformado por una serie de rutinas para ser utilizadas como
libreras de programacin.

CPLEX: es una herramienta para resolver problemas de programacin lineal, problemas de redes, de
programacin entera mixta, cuadrtica, cuadrtica restringida, y programacin por restricciones.
Corresponde al solver comercial ms completo y eficiente que ha dominado el mercado en los ltimos
aos.

Puede ser usando en diferentes formas para satisfacer a las diversas necesidades de los usuarios:

Optimizador Interactivo (CPLEX Interactive Optimizer): corresponde a un programa ejecutable, que


puede leer interactivamente un problema o a travs de archivos en formatos especficos y desplegar los
resultados en los formatos que se desee.

Tecnologa Concierto (Concert Technology): es un conjunto de libreras de clases de C++, Java, .NET y
Python, que ofrecen una interfaz de programacin y aplicaciones que otorgan facilidades de
programacin que permiten utilizar el solver de CPLEX en aplicaciones realizadas en los respectivos
lenguajes de programacin mencionados. Las libreras de la tecnologa concierto hacen uso de la librera
llamable (CPLEX Callable Library).

Librara llamable de CPLEX: corresponden a una librera en C que permiten a los programadores insertar
los optimizadores de CPLEX en aplicaciones escritas en C, Visual Basic, FORTRAN, o cualquier otro
programa que puede ocupar funciones de C.

OPL Development Studio: se describi anteriormente, se menciona nuevamente slo para enfatizarla
como otra modalidad de uso.

Es importante mencionar que posee conexin y compatibilidad con otros softwares ampliamente
usados en ingeniera o de uso comn, como lo son Matlab y Excel.

GUROBI: es un solver para resolver problemas de gran tamao de programacin lineal, y programacin
entera mixta. Lanzado recientemente al mercado, y entre sus desarrolladores se encuentran fundadores
y desarrolladores de CPLEX, es totalmente comparable en eficiencia con los mejores solvers act uales
como CPLEX, slo que fue diseado desde cero para aprovechar el procesamiento paralelo y multi-core.

Debido a la gran relevancia y predominio que ha tenido durante los ltimos aos es que este manual
est completamente enfocado al uso en sus diferentes modalidades de OPL/CPLEX.

8
Uso de OPL para resolucin de Problemas de Programacin Matemtica

Descripcin General: Generacin de un proyecto y de los archivos .mod, .dat y .ops


El software IBM ILOG OPL permite desarrollar y desplegar de forma rpida modelos de optimizacin
mediante depuracin, pruebas, ajuste y generacin de aplicaciones. A travs de una interfaz permite, a
travs de un lenguaje adecuado para modelos de programacin matemtica, modelar un determinado
problema que luego se resuelve utilizando el solver ILOG CPLEX.

En trminos generales, para resolver un determinado problema es necesario generar un


proyecto de OPL, el que est compuesto por tres archivos: uno contiene el modelo, es decir, definicin
de variables, funcin objetivo y restricciones (archivo .mod); uno que contiene los datos o parmetros
del modelo, coeficientes de la funcin objetivo, coeficiente de las restricciones y valores del lado
derecho de stas (archivo .mod); y un archivo que contiene la configuracin que el decisor define para el
funcionamiento del solver (archivo .ops). Dados los alcances de este manual, slo nos concentraremos
en los archivos .mod y .dat. La creacin de un proyecto en el ambiente OPL se muestra en la Ilustracin
1; la creacin supone asignar un nombre a ste y crear los archivos .mod, .dat y .ops descritos
anteriormente.

Ilustracin 1: Creacin de un proyecto en el ambiente OPL

Una vez que el proyecto ha sido creado, entramos directamente en el ambiente de OPL, en
donde efectivamente nuestro proyecto, i.e. el problema que queremos resolver, tomar forma para ser
ejecutado; esto se muestra en la Ilustracin 2. A la izquierda se muestra la ventana de administracin de
proyectos, en este caso slo existe el proyecto ejemplo1_OPL, a la derecha se muestra el archivo .mod,
vaco hasta ahora, y las pestaas de los archivos .dat y .mod.

Ilustracin 2: Ambiente OPL para la edicin de los archivos .mod, .dat y .ops

9
Es importante que el uso de OPL y del solver CPLEX, supone que se cuenta con un modelo
vlido, es decir, que representa la situacin que se quiere resolver ya sea para fines prcticos o
acadmicos.

Resolucin de un problema bsico en OPL


Consideremos el siguiente problema de programacin lineal

sujeto a

Este es un problema muy bsico que resolveremos a travs de OPL. Dada la estructura del problema,
podemos representar todos los datos de ste en forma explcita en el archivo .mod como se muestra en
la Ilustracin 3. En la ilustracin se muestra la declaracin (o definicin) de las variables de decisin
y , por eso el nombre dvar, como variables reales positivas (float+ ); posteriormente se
define la funcin objetivo indicando el sentido de sta ( minimize ) y finalmente se declaran las tres
restricciones dentro del bloque subject to .

Ilustracin 3: Generacin del modelo del problema en el archivo . mod (parmetros explcitos)

Es claro que el modelo est completamente definido y no es necesario utilizar el archivo .dat
puesto que todos los datos del problema estn explcitos en el modelo. Para resolver el problema es
necesario ejecutarlo haciendo click en el botn en la barra de herramientas. Al ejecutar el programa,
OPL internamente pasa el modelo al motor CPLEX y ste lo resuelve, desplegando la solucin en la
pestaa soluciones en la parte inferior del ambiente del programa. En este caso particular la solucin se
muestra en la Ilustracin 4;el valor ptimo y la solucin ptima son entonces y
respectivamente.

Ilustracin 4: Solucin entregada por el solver CPLEX al problema.

10
Como se ha sealado, en este caso no fue necesario enlazar el archivo .mod, que contiene el
modelo, con el archivo .dat que contendra los datos o parmetros, puesto que fue posible definir todo
el modelo del problema en el .mod. Consideraremos ahora el caso en que generemos un archivo .mod
en que sea necesario considerar datos contenidos en el archivo .dat. En la Ilustracin 5 se muestra la
definicin del modelo considerando que los datos sern obtenidos desde el archivo .dat. Un rol
importante lo juega la definicin de rangos (ranges ) que representan las dimensiones de arreglos o
matrices de variables o parmetros. En este caso se deben definir dos rangos uno indica la dimensin
del vector de variables de decisin (n) y el otro el nmero de restricciones ( m), es claro adems que la
dimensin de la matriz de restricciones es m n, la dimensin del vector de coeficientes de la funcin
objetivo es y la dimensin del vector de coeficientes del lado derecho de las restricciones es .
n m
Ilustracin 5: Generacin del modelo del problema en el archivo .mod (parmetros implcitos)

Una vez que se ha definido el archivo .mod donde se contiene el modelo, es necesario escribir
el archivo .dat donde se contienen los datos del modelo, en este caso los datos son los coeficientes de
las variables de decisin en la funcin objetivo, los coeficientes de la matriz de restricciones y los rhs de
las restricciones. En la . Al ejecutar el programa se puede comprobar que valor ptimo y la solucin
ptima son entonces y respectivamente, lo que reafirma la validez
del modelo ahora compuesto por el archivo .dat-

Ilustracin 6 se muestra la definicin de los valores de estos parmetros en el archivo .dat. Al


ejecutar el programa se puede comprobar que valor ptimo y la solucin ptima son entonces
y respectivamente, lo que reafirma la validez del modelo ahora compuesto por
el archivo .dat-

Ilustracin 6: Definicin de los parmetros del modelo en el archivo .dat

11
En el modelo recin presentado era necesario indicar explcitamente el tamao del problema
(nmero de variables y nmero de restricciones) en el archivo .mod, y escribir en funcin de estos
tamaos tanto la funcin objetivo como las restricciones. Una forma ms adecuada y general es
representar tanto la funcin objetivo como las restricciones en trminos de sumatorias, es decir a travs
de un modelo del tipo:

Esto es posible, y es la forma en la que se resuelven los problemas de verdadero inters para
nuestros propsitos. Sin embargo aumenta la complejidad de la escritura del modelo, puesto que se
deben incluir ciclos y, por ejemplo, el operador de sumatoria.

Antes de comenzar a escribir el modelo es necesario redefinir la tercera restriccin del


problema en estudio puesto que est como una restriccin del tipo , por lo que se reescribe como
.

La forma de expresar la funcin objetivo en OPL es utilizando el operador sumatoria ( sum)


indicando que esta sumatoria se realiza sobre un cierto rango. En el caso del problema, el rango de la
sumatoria est dado por el nmero de variables, por lo que la funcin objetivo se define como:
minimize sum(i in n) coeficientesFO[i]*x[i]

Ya definida la funcin objetivo en trminos del operador sumatoria, definiremos ahora las

restricciones en forma similar. Si consideramos la segunda formulacin presentada, podemos escribir la


j-sima restriccin como:

sum(i in n)matrizRestricciones[i][j]*x[i] <= rhs[j],

el rango del ndice j est dado por el nmero de restricciones, en este caso hay tres restricciones, por lo
que j = 1,2,3, un ciclo forall se debe utilizar para recorrer el las restricciones haciendo que el ndice
de este ciclo recorra el rango de j. El conjunto de restricciones entonces se modela a travs del cdigo
que se muestra en la

Ilustracin 7.

Ilustracin 7: Definicin de las restricciones en lenguaje algebraico

El cdigo completo del archivo .mod se muestra en la Ilustracin 8, en sta se puede notar que
la nueva implementacin es mucho ms genrica y podra representar a casi cualquier modelo de
programacin lineal, salvo por el hecho que se especifica el tanto el nmero de variables ( int
numVariables=3) como el nmero de restricciones ( int numRestricciones =3), sin embargo
ambos parmetros podra perfectamente ser definidos en el archivo .dat generalizando as el modelo
presentado.

12
Ilustracin 8:Generacin del modelo del problema en el archivo .mod (operadores
sum y forall)

Al ejecutar el programa se puede comprobar que valor ptimo y la solucin ptima son
entonces y respectivamente, lo que reafirma la validez del modelo
ahora compuesto por el archivo .dat.

Enlace de una planilla Excel para la lectura de datos


Una forma mucho ms sofisticada para la lectura de datos es el uso del mtodo de lectura de archivos
de extensin .xls (excel) que cuenta OPL; a travs de un esquema de comandos es posible definir un
archivo .dat en el que los datos se puedan leer desde una planilla de clculo. En primer lugar es
necesario declarar el siguiente comando:

SheetConnection sheet("nombreArchivo.xls"),
de esta forma OPL sabe que se enlazar con el archivo nombreArchivo que debiese estar contenido en
la carpeta del proyecto. En este caso, el archivo que utilizaremos ser datos_ejemplo1_OPL.xls.

Los datos que leeremos desde el archivo ser el nmero de variables, el nmero de
restricciones, los coeficientes de la funcin objetivo, los coeficientes de la matriz de restricciones y el los
valores de los rhss. Para ello, es necesario conocer la ubicacin de stos parmetros en la planilla Excel
en trminos de sus coordenadas (columna y fila). En la Ilustracin 9 se muestran las celdas conteniendo
los datos del problema.

Ilustracin 9: Planilla .xls conteniendo los datos del problema

De la Ilustracin 9 podemos ver que el nmero de variables est ubicado en la celda C2, por lo
que se debe indicar esto en el archivo .dat, la forma de hacerlo se muestra a continuacin:
numVariables from SheetRead(sheet, "Hoja1!C2")

13
El resto de los datos se leen en forma similar como se muestra en la Ilustracin 10, es importante notar
que en el caso de los coeficientes de la matriz de restricciones ( matrizRestricciones ), estos
deben ser ledos desde una matriz de datos por lo que en el comando de lectura debe sealarse la celda
superior izquierda (C7) y la celda inferior derecha (E9) desde donde se extraern los datos.

Ilustracin 10: Archivo .dat con la lectura de parmetros desde excel

Nuevamente, al ejecutar el programa se puede comprobar que valor ptimo y la solucin


ptima son entonces y respectivamente.

Resolucin de un problema de mediana complejidad en OPL


Consideremos una situacin logstica simplificada en la que tenemos un grupo de centros de
distribucin que ofertan y demandan productos, y existe una red de transporte conectando estos
clientes entre s. En particular hay 10 centros de distribucin ubicados en las ciudades A, B, C, D, E, F, G,
H, I y J, cada una con una oferta y una demanda de los productos en estudio; los productos demandados
y ofertados son X, Y y Z, cuantificndose sus volmenes en kilogramos; se sabe a dems que existe un
costo de transporte que depende tanto de la ciudad de srcen y ciudad de destino como del producto a
ser transportado, es decir, es diferente el costo de transportar una cierta carga del producto Z entre las
ciudades B y F que transportar la misma carga (en kilogramos por ejemplo) de producto Y entre las
mismas dos ciudades; adems se sabe que el vehculo que transporta tiene una capacidad de 625
kilogramos.

El objetivo es buscar una planificacin de transporte tal que se minimicen los costos de traslado
satisfacindose la demanda de cada centro de distribucin y respetando la capacidad del vehculo. Para
representar el problema a travs de un modelo de programacin lineal, consideremos la siguiente
definicin de variables y parmetros:

: cantidad a transportar del producto p entre la ciudad i y la ciudad j.


: costo de transportar el producto p entre la ciudad i y la ciudad j.
: demanda de la ciudad j del producto p.
: oferta de la ciudad j del producto p.
: capacidad del vehculo.
Esto se puede expresar a travs del siguiente modelo de programacin lineal:

14
Ecuacin 1: Modelo de programacin lineal del Problema de Transporte

Con el modelo ya presentado, podemos proceder a generar el proyecto OPL para resolver sta
problema. El proyecto (construido en el proyecto ejemplo_transporte_OPL adjunto a este documento),
tendr dos variantes mostrando diferentes posibles definiciones de estructuras de datos con tal de
mostrar cmo se pueden utilizar definiciones de variables que mejoren la eficiencia del cdigo.

Problema de Transporte: Variante I


Tal como se mostr en el ejemplo considerado en la primera seccin, la generacin de un proyecto de
OPL supone la creacin de archivos .mod, .dat y .ops, pero nos concentraremos en la configuracin de
los dos primeros.

Al observar la formulacin del problema en la Ecuacin 1 y la definicin de variables y


parmetros, una forma natural de presentar esta informacin en el archivo .mod se muestra en la
Ilustracin 11, donde se muestra que los valores de los parmetros sern obtenidos desde el archivo
.dat. Algo interesante de notar es que tanto ciudades como productos se definen como arreglos
(vectores) de variables tipo string, puesto que no toman valores numricos sino que valores
alfanumricos, {A,,J} y {X,Y,Z} respectivamente. Esto muestra l a flexibilidad de lenguaje respecto a la
definicin de parmetros.

Ilustracin 11: Definicin de parmetros y variables del problema (1) en el archivo .mod ( variante I)

Una vez estructurados los parmetros sealando su naturaleza, se debe proceder a escribir el
cdigo del archivo .dat donde se contendr los valores para stos. Los parmetros Ciudades ,
Productos , Capacidad, Oferta y Demanda son simples de definir, puesto que son todos
vectores unidimensionales o bidimensionales. En la Ilustracin 12 se muestran los valores de estos
parmetros definidos a partir de vectores y matrices. Es claro que estos puede ser fcilmente enlazada a
una planilla de clculo .xls (ver Ilustracin 10 para ms detalles).

15
Ilustracin 12 Definicin de valores de parmetros
Ciudades, Productos, Capacidad, Oferta y
Demanda en el archivo .dat (variante I)

De acuerdo a la definicin de parmetros que se muestra en la Ilustracin 11, los costos de


transporte estn dado por un arreglo de tres dimensiones (productos ciudades ciudades) por lo que
su definicin no es tan directa como en el caso de la Oferta o la Demanda . En este caso es
necesario definir tambin una matriz, pero cuyos componentes tambin lo sean, ya que es preciso que
los elementos de este conjunto de datos permitan representar parmetros del tipo , es decir, el
costo de transportar el producto p desde la ciudad i a la ciudad j, esquemticamente, lo que
necesitamos definir est dado por:

Conocida ya la estructura de los datos que contendrn los valores de los costos, es necesario encontrar
ahora una representacin que permita ser ingresada al archivo .dat; en primer lugar, podemos
representar la forma general de la matriz de costos como un vector columna de tres filas, como se
muestra en la Ilustracin 13, en donde se muestra que en cada uno de los componentes (hasta ahora
sin definir, lo que simboliza con ) se deben cargar las matrices de costos para los productos X, Y y Z
respectivamente. Por ejemplo, en la primera componente debe definirse una matriz de dimensin
10 10, conteniendo los costos de transporte entre las 10 ciuda des del producto X, es decir, sus
componentes son del tipo donde {A,,J} y {A,,J}.

Ilustracin 13: Esquema de la definicin de valores de parmetros


Costos en el archivo .dat (variante I)

En la Ilustracin 14 se muestra la definicin de los costos de transporte para el producto X, con la ayuda
de texto comentad o (/* */) se hace ms fcil entender cmo debe ser llenada la matriz. Es claro que
un valor cero en una posicin ij indica que no existe un camino entre ambas ciudades.

16
Ilustracin 14 Definicin de valores del parmetro
Costo (para producto X)en el archivo .dat (variante I)

Una vez definidos ya todos los valores de los parmetros a utilizar por el modelo, se puede

definir el modelo, es decir, funcin objetivo y restricciones. De acuerdo al modelo presentado en la


Ecuacin 1, la funcin objetivo es slo una triple sumatoria definida sobre el conjunto de productos y
sobre pares de ciudades que expresa el costo total de transporte dado por las cantidades a transportar y
el producto de estas cantidades por los respectivos costos unitarios de transporte. Una forma de
representar esto es utilizando tres sumatorias en forma consecutiva como se muestra a continuacin:
minimize sum(p in Productos)
sum(o in Ciudades)
sum(d in Ciudades)
Costo[p][o][d]*Trans[p][o][d];

aunque una forma mucho ms simple de hacerlo, es simplemente utilizar un nico operador sumatoria y
en ste considerar los tres conjuntos, es decir:
minimize
sum(p in Productos, o, d in Ciudades)Costo[p][o][d]*Trans[p][o][d];

formulacin mucho ms natural y fcil de entender.

Como se puede ver en la primera restriccin del modelo dado en Ecuacin 1, es necesario
establecer una restriccin que asegure que cada ciudad satisfaga su demanda de los distintos productos.
Se indica en el modelo de programacin lineal, que hay tantas restricciones como productos y ciudades,
puesto que se debe satisfacer la demanda para cada cliente y para cada producto. Esta restriccin puede
escribirse como
forall(p in Productos, d in Ciudades)
sum(o in Ciudades) Trans[p][o][d] == Demanda[p][d];

esto indica que todo la cantidad total transportada del producto p desde las otras ciudades o hacia la
ciudad d debe ser igual a la demanda del producto p que tiene la ciudad d. De la misma forma, es
necesario adems que, por balance, sea utilizada enviada toda la oferta del producto p que tiene la
ciudad o, lo que puede representarse como
forall(p in Productos, o in Ciudades)
sum(d in Ciudades) Trans[p][o][d] == Oferta[p][o];

restriccin que indica que todo lo transportado del producto p desde la ciudad o hacia todas las
ciudades d debe ser igual a la oferta del producto p que tiene la ciudad o.
Finalmente, es necesario agregar una restriccin asociada a la capacidad del vehculo que
realiza el transporte; dicha restriccin debe sealar que el total transportado debe ser menor a la
capacidad del vehculo, es decir
forall(o, d in Ciudades)
sum(p in Productos) Trans[p][o][d] <= Capacidad.

Generado ya el modelo, podemos proceder a resolver el problema haciendo click en el botn


en la barra de herramientas. Al ejecutar el programa, OPL internamente pasa el modelo al motor

17
CPLEX y ste lo resuelve, desplegando la solucin en la pestaa soluciones en la parte inferior del
ambiente del programa. En este caso particular la solucin se muestra a continuacin:
Trans =
[[ [0 0 0 0 0 0 0 400 0 0 ]
[0 0 0 225 0 0 0 250 0 225 ]
[0 0 0 75 300 100 75 0 225 25 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]

[ [0
[0 0
0 0
0 0
0 0
0 0
0 0
0 0
25 0
625 0
150 ]
] ]
[0 0 0 0 525 400 250 300 125 0 ]
[0 0 0 500 225 0 0 625 100 350 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ] ]
[ [0 0 0 0 0 0 0 200 0 0 ]
[0 0 0 50 1000 50 0 100 0 ]
[0 0 0 50 0 0 0 0 0 250 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]
[0 0 0 0 0 0 0 0 0 0 ]

[0 0 0 0 0 0 0 0 0 0 ] ]];

El valor 250, resaltado en rojo, indica que desde la ciudad B hasta la ciudad H se envan 250 unidades del
producto X; el valor 225, resaltado en anaranjado, indica que desde la ciudad C hasta la ciudad E se
envan 225 unidades del producto Y; y, finalmente, el valor 100, resaltado en verde, indica que desde la
B hasta la ciudad E se envan 100 unidades del producto Z. Se puede ver cada ninguno de los envos
sobrepasa las 625 unidades establecidas como valores lmite de capacidad. El costo ptimo corresponde
a 199500 unidades monetarias.

Problema de Transporte: Variante II


En el modelo trabajado en la seccin anterior, era necesario definir matrices de datos (oferta, demanda
y costo) que contenan varias filas o columnas slo con valores cero, lo que se traduce en una notacin
confusa y adems, hace que esa estructura de datos sea poco adecuada al tratar de resolver problemas
de mayor tamao ya que se necesitan grandes espacios de memoria, y muchos de stos estarn
ocupados por un valor cero. En el lenguaje OPL existe un modelo de datos llamado tuple el que permite
almacenar arreglos de arreglos , en una forma ms eficiente que a travs de matrices. Estos datos
permiten adems poder definir arreglos de tuples, es decir, definir arreglos de arreglos de arreglos. Si
bien inicialmente se hace difcil comprender las estructuras de estos datos y, por ende, se hace tambin
difcil su uso, al comprender un primer ejemplo se hace simple su posterior aplicacin.

La definicin de una estructura de datos que contenga las rutas existentes parece ser til para
evitar la definicin de una matriz excesivamente esparza como la matriz costo del modelo anterior, la
que contiene muchos datos iguales a cero. De esta forma, definiremos en el archivo .mod una tupla que

18
contenga tres elementos producto, ciudad de srcen y ciudad de destino, como se muestra a
continuacin
tuple ruta {
string p;
string o;
string d;}

Con esto hemos definido un metadato que contiene informacin respecto a una ruta determinada, el
conjunto de todas las rutas ser entonces un arreglo de estos metadatos. Este arreglo ser ledo desde
el archivo .mod donde cargaremos la informacin de las rutas existentes. La definicin del arreglo de
estas tuplas en el archivo .mod se presenta ms abajo
{ruta} Rutas = ...;

El conjunto de estas rutas se deben explicar en el archivo .dat, y se pueden obtener de un anlisis de la
matriz Costo presentada anteriormente (ver Ilustracin 14), considerando aquellas componentes
diferentes a cero, lo que significa que consideraremos slo una parte de los datos srcinales; de los 300
registros srcinales (3x10x10) ahora slo tenemos 63 registros, que corresponden a las 63 rutas
existentes como se muestra a continuacin.
Rutas = { <X A D>, <X A E>, <X A F>, <X A G>, <X A H>, <X A I>,
<X A J>, <X B D>, <X B E>, <X B F>, <X B G>, <X B H>,
<X B I>, <X B J>, <X C D>, <X C E>, <X C F>, <X C G>,
<X C H>, <X C I>, <X C J>,

<Y A D>, <Y A E>, <Y A F>, <Y A G>, <Y A H>, <Y A I>,
<Y A J>, <Y B D>, <Y B E>, <Y B F>, <Y B G>, <Y B H>,
<Y B I>, <Y B J>, <Y C D>, <Y C E>, <Y C F>, <Y C G>,
<Y C H>, <Y C I>, <Y C J>,

<Z A D>, <Z A E>, <Z A F>, <Z A G>, <Z A H>, <Z A I>,
<Z A J>, <Z B D>, <Z B E>, <Z B F>, <Z B G>, <Z B H>,
<Z B I>, <Z B J>, <Z C D>, <Z C E>, <Z C F>, <Z C G>,
<Z C H>, <Z C I>, <Z C J> };

Los datos de la oferta tambin pueden definirse utilizando tuples, parece natural generar una tupla
inicial que contenga dos elementos, el producto y la ciudad que lo oferta. De los datos sabemos que
existen slo tres ciudades ofertantes, A, B y C, cada una ofertando diferentes cantidades de los
productos X, Y y Z. De esta forma, se define una tupla oferta como se muestra a continuacin
tuple oferta {
string p;
string o;}

En esta tupla, p corresponde al producto y o a la ciudad de srcen de dicho producto; sin embargo no
est explicitada la informacin de cunto oferta cada ciudad de cada producto. Para ingresar esta
informacin es necesario primero determinar qu ciudades son en realidad ofertantes; esto se puede
realizar con un procedimiento similar al realizado para definir las rutas, utilizando un arreglo de tuplas
que llamaremos CiudadesOferta en el archivo .mod de la siguiente forma:
{oferta} CiudadesOferta = ...;

Estos datos deben entregarse en el archivo .dat y deben contener qu ciudades ofrecen qu productos,
informacin que se presenta en la Ilustracin 12, y que en esta nueva definicin de datos debe
entregarse como sigue,
CiudadesOferta={
<X A>, <Y A>, <Z A>, <X B>, <Y B>, <Z B>, <X C>, <Y C>, <Z C>,};

Ya definidas las ciudades de oferta y los productos que stas ofrecen, es necesario indicar ahora cunto
ofrece cada una de esas ciudades de cada uno de los productos, para ellos generamos un arreglo de

19
datos reales llamado Oferta el que debe contener un dato para cada par <p,o>, es decir, debemos
crear un arreglo como se muestra a continuacin,
float Oferta[CiudadesOferta] = ...;

Los datos son ledos desde el archivo .dat en donde se deben presentar de la siguiente forma:
Oferta = #[ <X A>: 400
<Y A>: 800
<Z A>: 200
<X B>: 700
<Y B>: 1600
<Z B>: 300

<X C>:
<Y 800
C>: 1800
<Z C>: 300 ]#;

Esta notacin para entregar los datos permite asociar un nico dato (cantidad ofertada en este caso)
para cada par <p,o>, cada elemento <p,o>:Q de este arreglo indica que la ciudad o ofrece Q unidades
del producto p. Esto permite que los datos puedan ingresarse en forma desordenada y an as se
asignara la cantidad correcta a cada par <p,o>.

Similar a lo presentado para los datos de oferta, para la demanda es necesario definir variables
similares. Definiremos primero una tupla caracterizando la demanda, dada por un par <p,d>, donde p
indica el producto y d indica la ciudad:
tuple demanda {
string p;
string d; }

Una vez definida la tupla demanda podemos definir cules son las ciudades que demandan y cules son
los productos que ests requieren, generando un arreglo de tuplas demanda al que llamaremos
CiudadesDemanda y cuyos datos leeremos desde el archivo .dat, y son lo presentados debajo.
CiudadesDemanda={ <X D>, <Y D>, <Z D>, <X E>, <Y E>, <Z E>, <X F>, <Y F>,
<Z F>, <X G>, <Y G>, <Z G>, <X H>, <Y H>, <Z H>, <X I>,
<Y I>, <Z I>, <X J>, <Y J>, <Z J>, };

Una vez definidas las demandas en trminos de ciudades y productos, podemos ahora asignar a cada
par <p,d> las correspondientes cantidades demandadas D, utilizando la misma notacin que en el caso
de la oferta, es decir, elementos del tipo <p,d>:D en un arreglo compuesto de elementos reales. La
declaracin de este arreglo en el archivo .mod conteniendo esta informacin de demandas as como los
datos a ingresar en el archivo .dat se muestran debajo.
float Demanda[CiudadesDemanda] = ...;

Demanda = #[ <X D>: 300, <Y D>: 500, <Z D>: 100, <X E>: 300, <Y E>: 750,
<Z E>: 100, <X F>: 100, <Y F>: 400, <Z F>: 0, <X G>: 75,
<Y G>: 250, <Z G>: 50, <X H>: 650, <Y H>: 950, <Z H>: 200,
<X I>: 225, <Y I>: 850, <Z I>: 100, <X J>: 250, <Y J>: 500
<Z J>: 250 ]#;

Los datos que se deben definir ahora, corresponden a los costos de transporte. Sabemos que cada ruta
tiene un costo asociado dependiendo del producto transportado y las ciudades entre cada ruta. Dado
que ya hemos definido un arreglo conteniendo cada ruta, podemos ahora definir un arreglo de valores
reales con tantos elementos como rutas; podemos utilizar el mismo formato usado anteriormente
definiendo un elemento del tipo <p o d>:C indicando que la ruta entre la ciudad o y la ciudad p tiene
un costo C si se transporta el producto p. Pero presentaremos otra forma ms sencilla, en la que
directamente sealaremos los costos de transporte, asumiendo que el orden es exactamente el mismo
que el que tienen los datos de las rutas. Los costos estarn contenidos en la siguiente variable
float Costo[Rutas] = ...;

20
y sus datos son los que siguen;
Costo= [ 30,10,8,10,11,71,6,22,7,10,7,21,82,13,19,11,12,10,25,83,15,39,14,
11,14,16,82,8,27,9,12,9,26,95,17,24,14,17,13,28,99,20,41,15,12,
16,17,86,8,29,9,13,9,28,99,18,26,14,17,13,31,104,20];

Esta misma forma podra haberse utilizado en el caso de Oferta y Demanda, simplificando an ms el
ingreso de datos.

En muchos problemas de flujo en redes como ste, es til definir separar los nodos entre
aquellos que slo son de oferta, aquellos que slo son de demanda y aquellos que son de transbordo. En
este caso, tenemos nodos de oferta y de demanda exclusivamente. Definiremos dos conjuntos de

nodos: Orig y Dest, para indicar nodos de oferta y demanda respectivamente. Podemos determinar
estos conjuntos sin la necesidad de ingresarlos como datos, sino que simplemente con la informacin ya
contenida en los parmetros; por ejemplo, si existe una ruta <p o d>, sabemos que o ser una ciudad
de oferta u srcen y d ser una ciudad de demanda o destino, es claro que los conjuntos Orig y Dest
son en realidad vectores o arreglos de datos de naturaleza string puesto que hemos declarado las
ciudades de tal forma. El cdigo en lenguaje OPL para la creacin de ambos conjuntos es el que se
muestra ms abajo.
{string} Orig[p in Productos] = {o | <p,o,d> in Rutas};
{string} Dest[p in Productos] = {d | <p,o,d> in Rutas};

Se puede interpretar este cdigo de la siguiente forma para el caso del vector Orig: escoger para cada
producto p y de entre todas las rutas, aquellas ciudades de srcen de estas rutas. Similar definicin se
puede dar para el caso del vector Dest.

A este punto ya hemos definido todos los parmetros del modelo, por lo que resta todava
definir las variables de decisin, funcin objetivo y restricciones.

Respecto a la variables de decisin, y puesto que el modelo problema a resolver es el mismo


que el tratado en la primera variante, entonces la variable de decisin es la misma, es decir, la cantidad
transportada de cada producto desde una ciudad de oferta a una de destino. En la primera variante
presentada para este problema, se defini una variable de decisin del tipo Trans[p][o][d], donde el
primer subndice, p, indicaba el producto transportado y los subndices o y d indicaban la ruta de
transporte. En esta segunda variante no tiene sentido definir una variable de esa forma puesto que el
parmetro Rutas implcitamente contiene ya los subndices p, o y d, definiendo completamente el
dominio de la variable de decisin Trans. De esta forma, la variable de decisin se debe definir de la
siguiente manera:
dvar float+ Trans[Rutas];

En esta definicin de variable, se especifica que para cada ruta, definida por un producto, una ciudad de
srcen y una ciudad de destino, existe una cantidad real positiva asociada (por eso se declara la variable
como float+) que representa la cantidad transportada de aquel producto entre aquellas ciudades.

La funcin objetivo ser, por cierto, similar a la definida en la variante I mostrada


anteriormente, pero presentar diferencias puesto que tanto los coeficientes como las variables de
decisin son de una naturaleza diferente. En la variante anterior, la funcin objetivo estaba compuesta
por trminos del tipo Costo[p][o][d]*Trans[p][o][d], sin embargo ahora podemos reemplazar
los subndices [p][o][d] por un nico ndice asociado a cada ruta, es decir podemos definir la funcin
objetivo como sigue:
Minimize sum(l in Rutas) Costo[l] * Trans[l];

21
donde el ndice l contiene la informacin tanto de los productos como de las ciudades entre los que
este producto se transporta.

Las restricciones sern tambin esencialmente iguales variando apenas debido a la definicin
de los parmetros, al igual que en el caso de la funcin objetivo. Las restricciones de Oferta, Demanda y
Capacidad se muestran abajo.
forall(p in Productos , o in Orig[p])
{
ctOferta:
sum(d in Dest[p])Trans[< p,o,d >] == Oferta[<p,o>];
}

forall(p in Productos, d in Dest[p])


{
ctDemanda:
sum(o in Orig[p])Trans[< p,o,d >] == Demanda[<p,d>];
}

ctCapacidad:
forall(o , d in Ciudades)
sum(<p,o,d> in Rutas) Trans[<p,o,d>] <= Capacidad;

Es importante notar el rol que juegan los vectores Orig y Dest en estas restricciones, puesto slo se
generan restricciones de oferta ( ctOferta) para las ciudades de srcen y restricciones de demanda
(ctDemanda) para las ciudades de destino.

Una vez definido el modelo completo y resuelto y se puede corroborar que el valor ptimo de
la funcin objetivo es 199500, igual valor obtenido al obtenido en la primera variante desarrollada.
Respecto a la solucin ptima, es decir, los valores de las variables del vector Trans, estos de presentan
a continuacin:
Trans = [0 0 0 0 400 0 0 225 0 0 0
250 0 225 75 300 100 75 0 225 25 0
0 0 0 25 625 150 0 525 400 250 300 125
0 500 225 0 0 625 100 350 0 0 0 0 200
0 0 50 100 0 50 0 100 0 50 0
0 0 0 0 250];

Podemos ver que la 12va ruta, que corresponde a la ruta <X,B,H> tiene un valor 250, que es el mismo
valor de la solucin encontrada en el primero modelo desarrollado, lo que corrobora la validez de esta
nueva formulacin.

Si bien esta segunda variante eleva el nivel de complejidad de las variables definidas, mejora el
manejo interno de los parmetros y variables en el motor de resolucin de OPL, reduciendo tanto las
iteraciones internas en la generacin del modelo como en la resolucin de ste. Se pueden realizar
mejoras a la segunda variante, una de ellas es bastante intuitiva y corresponde a agregar a cada ruta su
respectivo costo definiendo tuplas del tipo <p,o,d,c>, donde c contiene el costo de dicha ruta.

22
Resolucin del TSP utilizando OPL (Parte I)

En esta seccin estudiaremos un problema clsico de optimizacin combinatorial y que tiene amplias
reas de aplicacin en diferentes disciplinas de ingeniera (telecomunicaciones, logstica, manufactura y
otras); el problema del Vendedor Viajero o TSP (de su nombre en ingls Traveling Salesman Problem).
Presentaremos una formulacin para este problema que permite la resolucin de instancias del
problema utilizando los conocimientos hasta ahora presentados del uso del software OPL y el motor de
solucin CPLEX.

TSP: Formulacin del problema como un PLEM


Dada una red compuesta de n ciudades, servidores, puntos, etc., conformando un conjunto N y
conexiones directas entre cada par de stos con un peso asociado correspondiente a distancia, tiempo
de viaje, costo de construccin, etc., el TSP consiste en encontrar un tour que recorre todos estos
puntos con la mnima suma de los pesos de las conexiones que componen dicho tour. Una formulacin
general para el problema se puede definir en las ecuaciones (1-5) presentada abajo.

(1)

Sujeto a:
(2)
(3)
y enteros (4)
+ restricciones de eliminacin de subtour (5)

En esta formulacin, son las variables de decisin y que toman dos valores posibles (restriccin (4)),
1 y 0; 1 indica que la conexin, camino o link es parte del tour ptimo y 0 indica lo contrario. El
coeficiente representa la ponderacin o peso asociado al elemento , por simplicidad y para
asegurar factibilidad hacemos simplemente para todo , salvo que en la implementacin se
aadan restricciones adicionales que impidan la aparicin de variables . Las ecuaciones (2) y (3)
forman las restricciones de asignacin y aseguran que cada nodo es visitado slo una vez. Dantzig,
Fulkerson y Johnson[3]usan un modelo con variables y restricciones de eliminacin de
subtour del tipo:

para todo tal que (6)

Una formulacin basada en este tipo de restricciones es sumamente ineficiente si se pretende resolver
directamente utilizando un solver como CPLEX, sin embargo son sumamente eficientes si se utilizan en
estrategias algortmicas ms sofisticadas como algoritmos de separacin de restricciones como
subrutina de algoritmos de planos de corte o algoritmos branch and cut especialmente diseados para
TSP u otro problema que considere este mismo tipo de restricciones. En esta seccin, propondremos
una estrategia algortmica ms simple, por lo es necesario utilizar una formulacin alternativa a la
presentada en (6). Miller, Tucker y Zemlin [4] presentan otra formulacin basada en el mismo modelo de
asignacin pero utilizando restricciones de eliminacin de subtour usando variables
continuas adicionales. Estas restricciones son

para todo y , (7)

[3]
G.B. Dantzig, D.R. Fulkerson and S.M. Johnson, "Solutions of large scale travelling salesman problem",
Operations Research 2,
393-410 (1954).
[4]
C.E. Miller, A.W. Tucker and R.A. Zemlin, "Integer programming formulation of travelling salesman problems",
ACMJ. 7, 326-329
(1960).

23
,y para todo , (8)
y las denominaremos como restricciones MTZ.

Por simplicidad, en la continuacin de esta seccin los elementos en N sern llamados nodos y
las conexiones entre stos sern llamadas aristas.

TSP: Formulacin del problema en OPL


Con tal de resolver el TSP utilizando ILOG .OPL y el solver CPLEX, consideraremos la formulacin dada
por:

El modelo se presenta en el archivo ejemplo_TSP_OPL en el directorio que contiene de este documento.


Los parmetros del modelo son el nmero de nodos y los costos o distancias de las aristas. Si
consideramos las definiciones entregadas en la primera seccin respecto de conjuntos y rangos, parece
intuitivo definir estos parmetros de la siguiente forma en el archivo .mod:
int n=...;
range N=1..n;
int c[N][N]=...;

En esta definicin, n corresponde al nmero de nodos y N representa el conjunto de estos n nodos dado
por los enteros entre 1 y n. Es claro que puesto que existen conexiones entre cada par de nodos, el
vector de costos c tiene un dominio en el espacio NxN. En el archivo .dat, se presenta la lectura de estos
datos desde una planilla Excel, por lo que de acuerdo a lo presentado la primera seccin, la estructura
est dada por:

SheetConnection sheet("datos.xls");
n from SheetRead( sheet, "Hoja1!D2" );
c from SheetRead( sheet, "Hoja1!C5:R20" );

Utilizaremos a modo de ejemplo una instancia de 16 nodos, cuya matriz de costos se muestra en la
Tabla 1. En la tabla se puede ver que los elementos en la diagonal, es decir los valores de , tienen
valor cero, hecho que debe considerarse en la generacin del modelo.

Tabla 1: Matriz de costos de la instancia considerada para TSP

1 2 3 4 5 6 7 8 9 1 0 11 12 13 14 15 16

10 6 5 3 11 8 7 1 12 8 26 5 5 5 7 1

2 6 0 1 4 17 1 4 1 3 6 17 1 3 3 2 1 1 1 1 1 1 1 3 7

3 5 1 0 5 16 1 3 1 2 6 16 1 2 3 1 1 0 1 0 1 1 1 2 6

4 3 4 5 0 1 3 11 10 3 1 5 11 28 8 8 8 9 5

5 1 1 17 16 13 0 4 6 11 8 8 16 7 7 6 4 11

6 8 1 4 13 11 4 0 1 8 5 4173 3 3 3 8

7 7 1 3 12 10 6 1 0 7 5 3182 2 2 3 7

81 6 6 3 11 8 7 0 12 8 26 5 5 5 6 2
9 1 2 17 16 15 8 5 5 12 0 4 15 7 7 7 8 11

10 8 1 3 12 11 8 4 3 8 4 0 19 3 4 4 6 7
11 26 32 31 2 8 16 17 18 26 15 19 0 20 21 20 20 25

12 5 11 10 8 7 3 2 5 7 3200 0 1 3 5
13 5 11 10 8 7 3 2 5 7 4210 0 1 3 4

14 5 11 11 8 6 3 2 5 7 4201 1 0 2 5

15 7 13 12 9 4 3 3 6 8 6203 3 2 0 7

16 1 7 6 5118 7 21172 55 4 5 7 0

La definicin de las variables de decisin es bastante intuitiva; variables binarias asociadas a


cada arista y variables continuas asociadas a cada nodo. Las variables binarias en OPL se declaran
como variables del tipo boolean, y el dominio de estas variables est dado por el espacio NxN; mientras

24
que las variables continuas son del tipo float+, as entonces definimos las variables de decisin como
sigue:
dvar boolean x[N][N];
dvar float+ u[N];

Definidas ya las variables de decisin, podemos comenzar a estructurar el modelo. En primer


lugar, definiremos la funcin objetivo. Una definicin intuitiva est dada por
minimize sum(i in N)(sum(j in N)(c[i][j]*x[i][j]));

sin embargo, puesto que las componentes tienen valor cero, las variables x[i][i] tenderan a
tomar valor 1, lo que no tiene sentido en trminos del problema. Por ello, es necesario aadir una
restriccin a la segunda sumatoria con tal de considerar slo aquellas aristas que existen en la instancia,
la funcin objetivo correcta se muestra a continuacin:
minimize sum(i in N)(sum(j in N : i!=j)(c[i][j]*x[i][j])).

Las restricciones de asignacin, que se muestran en (2) y (3), son muy simples de definir, y se presentan
abajo.
forall (j in N)
sum(i in N : i!=j)x[i][j] ==1;

forall (i in N)
sum(j in N : i!=j)x[i][j] ==1;

Las restricciones de MTZ, (7) y (8), tampoco suponen alguna complejidad, y se definen como se muestra
en el siguiente cdigo;
forall (i in N)
{
forall(j in N : j!=1 && j!=i)
{
u[i]-u[j]+1 <= n*(1-x[i][j
}
}

u[1]==1;

forall (i in N: i!=1)
{
u[i]>=2;
u[i]<=n;
}

A este punto ya hemos definido completamente tanto el archivo .mod como el .dat para
resolver el la instancia de TSP definida por los datos en Tabla 1. Sin embargo, agregaremos un nuevo
elemento; utilizaremos una funcin de OPL que permite guardar el valor ptimo de algunas o todas las
variables de decisin definidas en el modelo en una planilla Excel, que podra ser una planilla diferente a
a aquella que contiene los datos del problema. En el modelo utilizado, las variables de decisin de real
importancia son las variables , contenidas en el arreglo x[N][N], por lo que estas son las variables
que quisiramos registrar. A continuacin se muestra el cdigo para realizar eso:
SheetConnection sheet2("solucion.xls");
x to SheetWrite(sheet2, "Hoja1!C5:R20");

Como se aprecia en el c digo, es necesario abrir o generar conexin con otra planilla, en este caso la
planilla solucin.xls, la que debe existir antes de resolver el problema. Posteriormente se indica que la
variable que queremos escribir en el archivo s la variable x y luego indicamos en qu posiciones
queremos que se registre la matriz de valores de la solucin ( Hoja1!C5:R20). Una vez que ejecutamos
el programa, la matriz x[N][N] se escribe en la planilla sealada y el resultado se muestra en la

25
Ilustracin 15. En color rojo hemos destacados las variables que toman valor 1, y por ende, las aristas
que pertenecen al tour ptimo; vemos entonces que el tour ptimo est dado por la secuencia

1-3-2-4-10-9-8-5-12-13-11-14-15-6-7-16-1 ,

y el costo de dicho tour es 71.

.
solucin.xlsen el que se muestra los valores ptimos de las variables
Ilustracin 15: Detalle de la planilla

Discusin de resultados: Tiempos de Resolucin y alcance de la formulacin


La instancia considerada en este ejemplo tiene 16 nodos y puede considerarse como una

instancia didctica apenas; instancias reales se componen por cientos, miles o centenas de miles de
nodos y un nmero an superior de aristas. Esto impacta fuertemente respecto de la real utilidad de la
formulacin y la estrategia presentada para resolver el TSP; esta pequea instancia toma, en promedio,
entre 20 y 25 segundos en un notebook regular (procesador 2.0 GHz, RAM 2 GB). Sin embargo, una
instancia de 25 nodos podra tomar varios minutos y una de 100 nodos podra tomar muchas horas o
incluso das utilizando esta metodologa. Por ello, salvo que se requiera resolver instancias muy
pequeas, esta formulacin no pasa de ser meramente didctica; sin embargo puede ser til en
estrategias algortmicas ms sofisticadas en las que se resuelvan subproblemas usando esta formulacin
en algoritmos como Relajacin Lagrangeana, Branch & Cut, Generacin de Columnas, etc.

En secciones posteriores se mostrarn metologas ms sofisticadas, pero ms eficientes, para


resolver el TSP usando herramientas OPL, CPLEX y Tecnologa Concierto (Concert Technology) capaces
de resolver en tiempo razonable instancias de mayor tamao y por ende de mayor importancia real.

26
Resolucin del Steiner Tree Problem en grafos utilizando OPL

StT Problem: Formulacin del problema como un PLEM


El Steiner Tree Problem (StTP) en grafos, o redes, es, al igual que el TSP, un problema clsico de
Optimizacin Combinatorial estudiado intensamente durante los ltimos 50 aos dada la amplia gama
de aplicaciones en diferentes reas de la ingeniera[5].

El StTP se define a continuacin. Dado un grafo no dirigido y un conjunto ,


llamados nodos terminales; el StTP consiste en encontrar un rbol de tal que
y se minimice la siguiente funcin objetivo

donde es en valor real asociado a cada arista .

A diferencia del problema de Mnimo rbol de Cobertura (ACM) en el que se busca conectar
todos los nodos en a mnimo costo, el StTP busca conectar los nodos en llamados terminales
utilizando, si es necesario, algunos nodos adicionales en , estos nodos adicionales son llamados
nodos steiner. En la segunda figura de la Ilustracin 16 se muestra una solucin factible del StTP para la
instancia de la primera figura; en rojo se muestran los nodos terminales y en verde los nodos steiner
necesarios para conectar los nodos terminales. En la tercera figura se muestra una solucin factible al
problema de rbol de Cobertura en el cual es necesario cubrir todos los nodos del grafo.

Ilustracin 16: De izquierda a derecha: Instancia . Solucin factible del StTP. Solucin factible del
ACM.

Al igual que para el TSP existen diversas formulaciones de programacin matemtica,


especficamente programacin entera mixta, para el StTP. Goemmans [6] resume las principales
[7]
formulaciones desarrolladas para el StTP hasta 1991 y en 2003, Maculan, Plateau y Lisser [REF],
muestran una serie de modelos enteros lineales tiles para conjunto importante de problemas en redes
como TSP, ACM, StTP y otros, a partir de formulaciones basadas en desigualdades de flujo multibien no
simultneo. En esta seccin, utilizaremos los modelos presentados en este ltimo artculo adaptndolos
al StTP.

[5]
Cheng, X. and Du, D. E. (eds): 2002, Steiner Trees in Industry Series (Series on Combinatorial Optimization Vol. 11), 1st edn,
Kluwer Academic Publishers, Dordrecht, The Netherlands.
[6]
Michel X. Goemans, The Steiner tree polytope and related polyhedral, Mathematical Programming 63 (1994) ~5 7-182
[7]
Maculan, N., Plateau, G. and Lisser, A.: 2003, Integer linear models with a polynomial number of variables and constraints for
some classical combinatorial optimization problems, Pesquisa Operacional 23(1), 161168.

27
Sea una variable binaria con valor 1 si la arista es parte y valor 0 en caso
contrario, una variable binaria con valor 1 si el nodo es parte y valor 0 en caso contrario y
el flujo desde el nodo al nodo que pasa a travs del nodo . El StTP puede formularse a travs del
siguiente modelo de programacin lineal entero mixto.

Sujeto a

Esta formulacin necesita de un nodo raz, que sin prdida de generalidad ser el nodo 1 el que adems
forma parte de . El conjunto contiene los nodos de destino las aristas del tipo , mientras que
el conjunto contiene los nodos de srcen de las aristas del tipo .

StT Problem: Formulacin del problema en OPL


Al igual que en el caso del TSP, en el StT consideramos la informacin de una red compuesta por nodos y
aristas, estas ltimas con pesos asociados que representan distancias, tiempos o costos de transporte
entre nodos. Adicionalmente, hay informacin respecto a los nodos; es necesario indicar cules son los
nodos terminales.

Los primeros parmetros a definir estn relacionados con el nmero de nodos, n, puesto que
adems se utilizar el rango o conjunto de nodos dado por n en la definicin tanto de parmetros como
variables de decisin. Tanto n como el conjunto N de estos nodos se definen como sigue:

int n =
range N ...;
= 1..n;

A diferencia de la formulacin dada para TSP, en esta caso ocuparemos las estructuras utilizadas en la
segunda variante del problema de transporte tratado en la Seccin 2, es decir, usaremos tuplas para dar
estructura tanto a la informacin de aristas como nodos. Una arista se define por nodo de srcen y nodo
de destino, y esta informacin es clave para identificar una arista de otra; con esto, introduciremos un
concepto til en el uso de tuplas, el concepto de key. Tal como hemos sealado, el nodo de srcen y
nodo de destino son los elementos que definen una arista, por lo que crearemos una estructura edge
conteniendo las claves srcen y destino, como se muestra abajo.

28
tuple edge
{
key int o;
key int d;
}

Una vez generada la estructura edge, generaremos otra tupla conteniendo la informacin de cada
arista. La informacin de cada arista es el nodo de srcen, el nodo destino y el costo o distancia entre
estos nodos. Tanto el nodo de srcen como el nodo de destino se representan por valores enteros
(1,2,,n) mientras que el costo es, en general, un valor real positivo, sin embargo, y sin prdida de
generalidad, asumiremos que ser entero. A continuacin se muestra el cdigo para declarar la tupla
conteniendo los datos de las aristas ( dataEdge)
tuple dataEdge
{
int o;
int d;
int cost;
}

Respecto a los nodos y puesto que slo tienen un valor clave (1,2, n) podemos generar slo
una tupla conteniendo su identificador y la informacin asociada a l, es decir, si es nodo terminal o no,
incluyendo adems un identificador root (o raz) para el nodo i=1. Abajo se muestra el cdigo para
definir la tupla dataNode.
tuple dataNode
{
key int num;
string type; //Optional, Terminal & Root
}

Ya creadas las estructuras que contendrn la informacin de aristas y nodos, podemos generar
los arreglos de tuplas dataEdge y dataNode y leer la informacin desde el archivo .dat. La declaracin
de los arreglos se muestra a continuacin:
{dataEdge} dataEdges = ...;
{dataNode} dataNodes = ...;

Como se puede ver, se est sealando que la informacin est contenida en el archivo .mod; los datos
de la instancia considerada en esta seccin en el formato de lectura son los siguientes:
dataEdges={ <1 2 4>, <1 3 2>, <2 3 3>, <2 4 6>, <2 7 7>,
<3 4 5>, <3 5 7>, <4 5 1>, <4 6 4>, <4 7 6>,
<5 6 5>, <5 8 8>, <5 9 9>, <6 7 5>,
<6 9 2>, <7 9 3>, <8 9 1>,};

dataNodes ={<1 Root>,


<2 Optional>,
<3 Optional>,
<4 Terminal>,
<5 Optional>,
<6 Optional>,
<7 Optional>,
<8 Terminal>,
<9 Optional>,};

De acuerdo a lo que se muestra, existen 3 nodos terminales: 1, 4 y 8. El resto de los nodos son nodos
opcionales y algunos de ellos podran pasar a ser nodos steiner.

En este punto los arreglos dataEdges y dataNodes contienen toda la informacin de la


instancia. Necesitamos ahora extraer desde estos arreglos informacin aislada para utilizar luego en el
modelo de programacin entera, especficamente en la generacin de variables de decisin, vector de
costos. Esta informacin aislada son las aristas, los costos de las aristas, los nodos terminales, los nodos

29
opcionales, el nodo raz, los conjuntos de nodos de srcen de las aristas y los conjuntos de nodos de
destino de las aristas; toda esta informacin ser contenida en estructuras independientes cuya
naturaleza (tupla, entero, arreglo o valor nico) depender de la informacin que contendr. A
continuacin se muestra el cdigo para extraer esta informacin:
// extraccin de la informacin slo de aristas
{edge} edges = { <o,d> | <o,d,c> in dataEdges};

// extraccin de la informacin de los costos de aristas en un vector


int Cost[edges] = [<t.o , t.d>:t.cost | t in dataEdges ];

// extraccin de la informacin de los nodos de srcen de las aristas

{int} getOut[node in N] = {d | <node,d> in edges};


// extraccin de la informacin de los nodos de destino de las aristas
{int} getIn[node in N] = {o | <o,node> in edges};

// declaracin del nodo raz


{int} rootNode = {node | <node, c> in dataNodes : c == "Root"};

// extraccin de los datos opcionales


{int} optionalNodes = {nodes | <nodes,c> in dataNodes : c == "Optional"};

// extraccin de los datos terminales


{int} terminalNodes ={nodes | <nodes,c> in dataNodes : c == "Terminal"};

Como se puede ver en el modelo de programacin entera, existen cuatro variables de decisin:
y , la declaracin de estas se muestra abajo.
dvar boolean y[edges];
dvar float+ z[N][N][terminalNodes];
dvar boolean x[N];
dvar int+ p;

En este punto ya tenemos todos los elementos para comenzar a formular el modelo en el archivo .mod.
En primer lugar definiremos la funcin objetivo, la que est dada por minimizacin de la suma de los
costos de las aristas incluidas en el rbol, es decir
minimize sum(<i,j> in edges)Cost[<i,j>]*y[<i,j>];

la restriccin , se escribe como,


forall(k in terminalNodes, i in rootNode)
{
sum(j in getOut[i])z[i][j][k] - sum(j in getIn[i])z[j][i][k] == 1;
}

la desigualdad , se declara como


forall(i in optionalNodes, k in terminalNodes)
{
sum(j in getOut[i])z[i][j][k] - sum(j in getIn[i])z[j][i][k] == 0;
}

y la restriccin se escribe como


forall(k in terminalNodes)
{
sum(j in getOut[k])z[k][j][k] - sum(j in getIn[k])z[j][k][k] == -1;
}

El resto de las restricciones no necesitan mayor detalle dada su simpleza y se presentan abajo.
forall(<i,j> in edges, k in terminalNodes)
{
z[i][j][k] <= y[<i,j>];
z[j][i][k] <= y[<i,j>];

30
}

sum(i in N)x[i] == p;

sum(<i,j> in edges) y[<i,j>] == p - 1;

p <= n;

forall(<i,j> in edges)
{
y[<i,j>] <= x[i];
y[<i,j>] <= x[j];
}

forall(i in terminalNodes)
x[i] == 1;

Definido ya el modelo completo, podemos resolverlo obteniendo un valor ptimo 16, y valores de
variables de decisin binarias dados por , y el
resto de las variables con valor cero. Es decir, se conectan los nodos 1,3,4,5 y 8, siendo 3 y 5 nodos
steiner, necesarios para conectar los nodos terminales (1, 4 y 8).

Discusin del modelo presentado


Al igual que en el caso del TSP, la formulacin presentada para el StTP es por un lado simple de
implementar pero ineficiente si lo que se busca es resolver instancias de gran tamao; salvo que, al igual
que en el caso del TSP, se utilice en un diseo algortmico ms elaborado en los que sea necesario
resolver sub problemas de menor tamao en forma iterativa y se utilice esta formulacin para ello.

31
Resolucin del TSP Problem utilizando OPL (Parte II)

Descripcin del Algoritmo de Planos de Corte (PC)


El mtodo de planos de corte es un procedimiento para encontrar soluciones a problemas de
programacin lineal entera, til para problemas de mediano tamao pero muy interesante desde un
punto de vista terico.

Se considera el siguiente problema de programacin lineal entera (ILP) en forma estndar:

Entonces resolvemos la relajacin lineal del ILP anterior, en el cual no se consideran las restricciones de
integralidad. Es decir, se resuelve:

En el caso general es de esperar que la solucin factible obtenida no sea entera, sin embargo, si es
entera, la solucin encontrada a travs de la relajacin lineal tambin ser la solucin ptima del
problema de programacin lineal entera.

La idea principal del algoritmo de planos de corte consiste en que si agregamos una restriccin al ILP que
no excluya soluciones factibles enteras, entonces la solucin no cambia. Por lo que si agregamos
restricciones lineales (cutting planes), una por una, al ILP hasta que la solucin de su relajacin lineal sea
entera, obtendremos la solucin ptima del ILP.
Ahora, para resolver el TSP mediante planos de corte, usaremos la formulacin de Dantzig, Fulkerson y
Johnson[3] que se mostr en secciones previas.

As, dado un conjunto de ciudades , y las variables de decisin sern si es que el


arco pertenece al tour, en caso contrario. La distancia o el costo de ir de la ciudad a la
ciudad es definido como . As, el modelo es el siguiente:

El modelo es similar al descrito por las ecuaciones (1)-(4) y (6), salvo que en esta oportunidad se
considera una pequea variacin en que se agrupan los dos conjuntos de restricciones de asignacin, y
se usan el conjunto de restricciones de eliminacin de subtours de Dantzig, Fulkerson y Johnson. La
resolucin de este modelo es imprctica, debido al conjunto de restricciones de eliminacin de
subtours, pues corresponden a un nmero exponencial de restricciones al considerar todos los
subconjuntos de ciudades.

La idea detrs del algoritmo de planos de corte para resolver el TSP consiste en resolver la relajacin del
modelo anterior (RTSP), en que no se considera ninguna restriccin de eliminacin de subtour, e
iterativamente ir agregando restricciones a medida que en la solucin de la relajacin lineal aparezcan

32
subtours. Obviamente el algoritmo termina cuando no existan subtours. Claramente ser necesario un
procedimiento para, dada una solucin, encuentre los subtours existentes.

Un esquema del algoritmo de planos de corte se presenta a continuacin:

Descripcin de la Implementacin en OPL del Algoritmo PC


Hasta el momento se han realizado implementaciones en que se dispone de un modelo matemtico a
resolver, y los datos necesarios se han entregado en el mismo archivo del modelo, o a travs de un
archivo independiente. En todos los casos se ha dispuesto de toda la informacin necesaria para la
resolucin del problema con antelacin, mientras para resolver el TSP mediante el algoritmo de planos
de corte presentado anteriormente, se resuelven iterativamente relajaciones del problema, y durante el
transcurso del algoritmo se van agregando cortes (restricciones al modelo), y realizando procedimientos
adicionales ajenos al modelo mismo, por lo que se requerir utilizar herramientas de programacin que
posee OPL IDE, aumentando un poco la complejidad de la implementacin.

El lenguaje de programacin es llamado IBM ILOG Script for OPL, el cual es una implementacin de
JavaScript que soporta elementos distintos a los de modelacin de OPL. Entre las cosas que es posible
realizar tenemos:

Agregar instrucciones de pre-procesamiento para preparar los datos del modelo.


Control de flujo mientras el modelo es resuelto.
Controlar los parmetros de CPLEX y/u OPL.
Agregar instrucciones de post-procesamiento para agregar, transformar, y dar formato a los
datos, para enviarlos a otra aplicacin.
Resolver repetidamente instancias de un mismo modelo.
Crear soluciones algortmicas donde la salida de un modelo es usada como la entrada de otro
modelo.

Cuando se usa la programacin en OPL, se evita tener que unir y compilar la aplicacin, slo debes
agregar instrucciones de programacin al archivo del modelo.

Existen dos expresiones de programacin de alto nivel:


La expresin main para las instrucciones de la seccin de control de flujo.
La expresin execute para instrucciones de las secciones de pre-procesamiento y post-
procesamiento.

Es importante mencionar que el orden de declaracin y orden de las instrucciones, siguen una secuencia
lgica. Vale decir, en un programa que posee un modelo matemtico, instrucciones de pre-
procesamiento, post-procesamiento y un control de flujo, el orden y secuencia de las instrucciones de
cada seccin es la siguiente:

33
Declaracin de datos y variables de decisin.
Instrucciones de pre-procesamiento.
Definicin del modelo matemtico.
Instrucciones de post-procesamiento.
Instrucciones de control de flujo.

Ahora, en base a las explicaciones anteriores es posible enunciar la implementacin de planos cortantes
para el TSP, para un mejor entendimiento se explicara identificando cada una de las secciones y el
modelo completo se muestra en el anexo 2.

Declaracin de datos y variables de decisin: adems del tamao del problema , como es un TSP
clsico, los nicos datos que hacen falta son las distancias entre ciudades, y como es simtrico, basta
con la matriz diagonal superior sin la diagonal, pues consta de puros ceros. Las instrucciones necesarias
son:
int n = ...;
range Cities = 1..n;
tuple edge {int i; int j;}
setof(edge) Edges = {<i,j> | ordered i,j in Cities};
int dist[Edges] = ...;

As, se define una estructura de datos (tuple) para las aristas, y se crea un conjunto ordenado de aristas
llamado Edges. Al ser ordenado se establece que el elemento es menor al elemento en la arista
, por lo que en la instruccin int dist[Edges] = ... lo que se realiza es tomar de un archivo de
datos la informacin relacionada a la diagonal superior de matriz de distancias.

Las nicas variables de decisin necesarias en el problema corresponden a si una determinada arista
pertenece o no a la solucin (tour).
dvar boolean x[Edges];

Como en el algoritmo se desea ingresar los cortes relacionados a los subtours encontrados, es necesaria
una estructura que, por ejemplo, guarde el tamao del subtour encontrado y en un arreglo el subtour:
tuple Subtour { int size; int subtour[Cities]; }
{Subtour} subtours = ...;

La ltima instruccin permite leer de un archivo de datos los subtours, sin embargo, como se resuelve el
problema relajado, se comienza sin ningn subtour y a medida que se resuelvan relajaciones se irn
agregando subtours a los datos con el formato establecido.

Definicin del modelo matemtico de Dantzig, Fulkerson, y Johnson:


minimize sum (<i,j> in Edges) dist[<i,j>]*x[<i,j>];
subject to
{
forall (j in Cities) // Restricciones de asignacin
sum (<i,j> in Edges) x[<i,j>] + sum (<j,k> in Edges) x[<j,k>] == 2;
forall (s in subtours) // Restricciones de eliminacin de subtours
sum (i in Citiess.subtour[i]),
x[<minl(i, : s.subtour[i]maxl(i,
!= 0) s.subtour[i])>] <= s.size-1;
};

Notar en la restricciones de eliminacin de subtour que las restricciones se aaden para cada subtour
existente en la seccin de datos subtours, por lo que en la primera iteracin no se agregara ninguna
restriccin de dicho tipo. Adems la sumatoria se realiza sobre cada elemento distinto de cero (por lo
que se espera que sea inicializado con valores iguales a cero) sobre un arreglo de dimensin del nmero
de ciudades, en que la posicin indica la ciudad y el valor de dicha posicin, la siguiente ciudad que se

34
visita. Un ejemplo en que el nmero de ciudades sea 10 y exista un subtour de 3 ciudades podra estar
representado por la siguiente figura:

4 0 0 9 0 0 0 0 1 0
1 2 3 4 5 6 7 8 9 10

Notar adems que el uso de las funciones minl y maxl tiene relacin slo a que el problema es
simtrico.

Instrucciones de post-procesamiento: es necesario para que dada una solucin, identificar el subtour
tour asociado a la solucin. El esquema general presenta la siguiente estructura:
execute
{
/*
* Funcin findSubtour
*/
}

Debido a que el procedimiento no tiene gran complejidad, el procedimiento completo se muestra con
comentarios explicativos en el anexo 1, mientras los archivos se encuentran en la carpeta del proyecto
de OPL TSP -DFJ.

Instrucciones de control de flujo: a travs de la expresin main se especifica el comienzo de las


instrucciones de control de flujo, que otorga gran flexibilidad al resolver el problema, permitiendo usar
diferentes modelos con diferentes datos, resolver multiples modelos, modificar los datos del modelo
entre una resolucin y otra. Es particularmente til cuando se quiere resolver un modelo con diferentes
datos, como en nuestro caso. Resaltar que cuando se ejecute las instrucciones se ejecutaran de acuerdo
al main. La sintaxis de las expresiones relacionadas al lenguaje de programacin IBM ILOG Script for
OPL son muy similares a la sintaxis de C y C++, incluidas expresiones de asignaci n, llamadas a
funciones, propiedades de acceso, entre otros.

Las estructuras necesarias para manipular el modelo y los datos se muestran en la siguiente tabla:

IloOplModelDefinition Une al archivo .mod la representacin del modelo

IloCplex Una instancia del algoritmo CPLEX

IloOplDataSource Une al archivo .dat la representacin de los datos, no es modificable.

IloOplDataElements Una fuente de datos, pero puede modificarse.

IloOplModel Estructura que une una definicin de un modelo con una o varias
fuentes de datos.

Cuando se ejecuta el bloque main, se crea por defecto una variable llamada thisOplModel que
contiene la instancia de IloOplModel disponible, adems de una variable cplex que corresponde
a la instancia ya creada del algoritmo CPLEX. Una vez creada la instancia de IloOplModel se puede
resolver a travs del algoritmo cplex.

Las variables en el lenguaje de programacin de OPL se definen a travs de la palabra var, distinta a la
usada para las variables de decisin dvar de los modelos.

La implementacin del bloque main es la siguiente:

35
main
{
var opl = thisOplModel
var mod = opl.modelDefinition;
var dat = opl.dataElements;

while (1)
{
cplex.clearModel();
opl = new IloOplModel(mod,cplex);
opl.addDataSource(dat);
opl.generate();
if (!cplex.solve())
{
writeln("ERROR");
opl.end();
break;
}
opl.postProcess();

if (opl.newSubtourSize == opl.n)
{
writeln("Tour final ",opl.thisSubtour);
opl.end();
break;
}
dat.subtours.add(opl.newSubtourSize, opl.newSubtour);
opl.end();
}
}

Inicialmente se asigna la instancia de la clase IloOplModel a una variable llamada opl. As, esta variable
contendr la definicin del modelo y sus datos actuales. Por lo que usando el mtodo
opl.modelDefinition se extraer el modelo a la variable mod , y con opl.dataElements se extraen
los datos a una variable llamada dat. Mediante un ciclo while que siempre es verdadero se asegura
que siempre se realice el ciclo para iterativamente ir resolviendo el modelo relajado y agregando los
cortes. Al inicio de cada iteracin se limpia el algoritmo cplex , se crea una instancia de
IloOplModel uniendo el modelo modificado ( mod) con el algoritmo cplex, se le agregan los datos
(modificados), y se genera el modelo completo para ser nuevamente resuelto a travs del comando
cplex.solve(). Si es que no se puede resolver, se despliega un mensaje de error, y si es que se
resuelve, se realiza el post-procesamiento para encontrar un subtour. Si es que el tamao del subtour es
igual al nmero de ciudades, significa que se ha encontrado la solucin, en caso contrario, se agrega a la
seccin de los subtour en los datos, el nuevo tamao del subtour, y el nuevo subtour para que sea
agregado al modelo como restriccin (cut). Al finalizar cada iteracin se elimina la instancia de opl para
el eficiente uso de la memoria, y de esta forma termina la implementacin del TSP usando planos
cortantes.

Discusin de resultados: Tiempos de Resolucin y alcance de la formulacin


La prueba del modelo se realizo con los datos del problema gr17.tsp, el cual es una instancia de la
librera TSPLIB[8], librera que posee una gran cantidad de informacin sobre el problema del vendedor
viajero y datos de prueba comnmente utilizados por la comunidad cientfica internacional. La
dimensin del problema es de 17 ciudades. Es necesario mencionar que los datos fueron adaptados de

8
http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/

36
tal forma que sean ledos de acuerdo al formato de lectura de la implementacin realizada, que en el
caso se utiliza la matriz diagonal superior de distancias entre ciudades.

El tiempo aproximado que demor OPL en resolver la instancia corresponde a 0,12 segundos. Vale decir
que si comparamos con la instancia resuelta mediante el modelo de Miller, Tucker y Zemlin que tena 16
ciudades y demor del orden de 20 segundos, se aprecia claramente la mayor rapidez del mtodo de
planos de corte y la solucin se muestra en la Ilustracin 17. Adems que la principal diferencia se
presenta en instancias de mayor tamao, pues si bien se observa un aumento en los tiempos de
resolucin a mayor nmero de ciudades, es posible resolver instancias de mucho mayor tamao con el
algoritmo de planos de corte que con el modelo de Miller, Tucker y Zemlin. Ilustracin 1

: Solucin de problema gr17.tsp


Ilustracin 17

Una de las deficiencias de la implementacin del algoritmo de planos cortantes en OPL es la flexibilidad
intermedia que otorga el lenguaje de programacin de OPL, por lo que si se desea mayor flexibilidad y
eficiencia es recomendable el uso directo de las libreras otorgadas por la tecnologa concierto en
aplicaciones desarrolladas en diversos lenguajes como C, C++, Java, Python, etc.

37
Resolucin del TSP Problem utilizando OPL (Parte III)

Descripcin de la Implementacin utilizando Tecnologa Concierto del Algoritmo PC


En la seccin de Solvers para Programacin Lineal y Entera se realiz una breve descripcin de lo que
es la Tecnologa Concierto. Ofrece una librera de clases y funciones para C++ que permiten disear
modelos de problemas de programacin matemtica y programacin por restricciones. Resaltar que la
Tecnologa Concierto no es un lenguaje nuevo, sino que te deja usar estructuras de datos y de control
provedas por C++. As la parte de una aplicacin asociada a la Tecnologa Concierto puede ser

perfectamente integrada con el resto de la aplicacin (por ejemplo, la interfaz grfica, y la conexin a
bases de datos) ya que puede compartir los mismos objetos.

Mencionar adems que CPLEX se encuentra disponible en Windows, sistemas UNIX, Mac OS. La interfaz
de programacin funciona de la misma manera y se proveen de las mismas funciones en todas las
plataformas.

Existe cierta notacin y convenio con los nombres definidos en la Tecnologa Concierto. As, los nombres
de tipos, clases y funciones definidos en la librera comienzan con Ilo. Los nombres de las clases se
escriben concatenadamente, con palabras que comienzan con maysculas, por ejemplo: IloNumVar .
Se comienza con letras minsculas la primera palabra en nombres de argumentos, instancias y funciones
miembro. Por ejemplo: IloNumVar::setBounds . Funciones de acceso comienza con la palabra
clave get seguido por el nombre del miembro del dato. Funciones de acceso para miembros booleanos
comienzan con is. Modificadores comienzan con la palabra set.

Las partes de optimizacin en las aplicaciones realizadas son capturadas en un conjunto de objetos de
C++ que interactan entre s (ver ilustracin 18). Estos objetos pueden ser divididos en dos categoras:

Objetos de Modelamiento: son usados para definir el problema de optimizacin. En una aplicacin
generalmente se crean muchos objetos de modelamiento para especificar los problemas de
optimizacin. Esos objetos son agrupados en un objeto de tipo IloModel que representa el objeto de
optimizacin completo.

Objetos de resolucin: son una instancia del objeto IloCplex que son usados para resolver los
modelos creados a travs de los objetos de modelamiento. Una instancia de un objeto IloCplex lee
un modelo y extrae sus datos a un optimizador apropiado de CPLEX. Entonces el objeto IloCplex se
encuentra listo para resolver el modelo extrado. Despus de resuelto un modelo, se puede preguntar
por su solucin.

ilustracin 18: Vista de una aplicacin utilizando Tecnologa Concierto

Aplicacin del Usuario

Objetos IloCplex
Objetos de
--------------------------
Modelamiento de la
Caractersticas
Tecnologa Concierto
internas de CPLEX

38
Introducidos conceptos bsicos en relacin a la Tecnologa Concierto y CPLEX, se est en condiciones de
comenzar a describir elementos propios necesarios para realizar una implementacin en C++, y en
particular el algoritmo de planos cortantes para el TSP.

Para compilar las aplicaciones en C++ usando CPLEX con la Tecnologa Concierto, es necesario decirle al
compilador donde encontrar el directorio include de CPLEX y Concierto, que poseen los archivos de
encabezado (headers). Tambin es necesario decirle al linkeador donde encontrar las libreras de CPLEX
y Concierto.

Como se mencion, los proyectos de CPLEX se pueden desarrollar principalmente en computadores con
sistema operativo Windows Linux. Para desarrollarlos en Windows es necesario el uso del software de
Microsoft Visual Studio en sus versiones de 2005 2008. Su configuracin requiere de una serie de
pasos de relativa complejidad, por lo que es de gran utilidad el uso de los ejemplos disponibles y replicar
su configuracin paso a paso. Para ver detalles del proceso de compilacin de los ejemplos proyectos
nuevos es de gran utilidad el uso de archivo readme.html disponible en el directorio de instalacin de
CPLEX se puede encontrar en internet una versin anterior del archivo 9. En un sistema operativo Linux
es necesario el uso de makefiles, por lo que se puede usar cualquier programa de desarrollo siguiendo
las normas de complilacin establecidas por los makefiles. De igual forma al caso de Windows, es de
gran utilidad el seguimiento de los ejemplos otorgados por ILOG CPLEX para la configuracin de los
proyectos propios.

La implementacin se llevo a cabo en un notebook con sistema operativo Ubuntu 9.04, procesador Intel
Core2 Duo P8400 de 2.26Ghz y 3GB de memoria RAM, usando NetBeans IDE 6.7.1.

Ya que se utiliz un lenguaje de programacin orientado a objetos (C++), se crearon las siguientes
clases:

TSPTour: clase para representar la estructura de datos de un tour.


TSPUtil: clase para crear mtodos estticos tiles para todas las clases.
TSPSolver: alberga el algoritmo de planos cortantes, entre otros mtodos necesarios para
resolver el problema.

Mencionar que no se explicarn en detalles los elementos propios de programacin, como la creacin
de clases, declaracin de mtodos, variables, lectura de datos, entre otros. Se concentrar la atencin
en explicar los mtodos que tienen estrecha relacin con la creacin del modelo y el algoritmo para
resolver el problema, los cuales se encuentran exclusivamente en la clase TSPSolver. Para ver el
programa completo se puede ver el anexo 2 o la carpeta del proyecto desarrollado en Netbeans.

La definicin de la clase TSPSolver posee la siguiente estructura:


class TSPSolver
{
public:
IloEnv env
IloInt nCities;

IloInt **d;char *nombre );


TSPSolver(
TSPTour solverTSP();
private:
IloBoolVar **createDVars();
IloModel buildModel( IloBoolVar **x );
IloBool **getTourInfo(IloBoolVar **x, IloCplex cplex);
vector<TSPTour> buildTours( IloBool **tourInfo );
bool addConstraints(IloCplex cplex,IloBoolVar **x,vector<TSPTour> &tours);
};

[9]
http://ie.tamu.edu/INEN689-602/Papers/ILOG%20CPLEX%209_0%20README.htm

39
En la declaracin del constructor, se recibe como parmetro un puntero a un arreglo de caracteres que
contiene el nombre del archivo de datos del problema que se desea resolver.

La clase contiene un nico mtodo pblico llamado solverTSP() , encargado de implementar el


algoritmo de planos cortantes, y retorna un objeto de tipo TSPTour , que corresponde a un arreglo de
enteros.

Otros mtodos privados de la clase, necesarios para la implementacin del algoritmo de planos
cortantes son:

createDVars: se crean e inicializan las variables de decisin del problema.


buildModel: construye el modelo del TSP relajado.


getTourInfo: permite extraer la informacin de la solucin entregada por CPLEX.
buildTours: construye los subtours Tour en base a la solucin encontrada.
addConstraints: agrega las restricciones (cortes) relacionados a los subtours encontrados, si es
que existen.

En una aplicacin desarrollada en C++ usando Concierto, tpicamente el primer objeto creado
corresponde al ambiente, que corresponde a una instancia de la clase IloEnv . As, como miembro
pblico de la clase, se cre un objeto que contiene el ambiente, a travs de la instruccin:
IloEnv env;

El ambiente es de central importancia y necesita estar disponible para los constructores de todas las
otras clases de la Tecnologa Concierto, pues, entre otras cosas, provee del manejo optimizado de la
memoria para los objetos de las clases de la Tecnologa Concierto (mejora el manejo de la memoria
realizado por el sistema operativo). IloEnv , as como la mayora de las clases de Concierto, es una
clase de manejo (Handle Class), es decir, es un puntero a un objeto de implementacin.

Tambin se crearon otros miembros pblicos:


IloInt nCities;
IloInt **d;

La variable nCites es del tipo IloInt , que corresponde a los tipos de variables enteras en la
Tecnologa Concierto. Esta variable almacena el nmero de ciudades del problema a resolver. Mientras
la variable d, corresponde a un doble puntero que apunta a variables de tipo IloInt. El uso de d es
para representar la matriz de distancias entre ciudades.

Mencionamos que la primera instruccin es crear el ambiente, la siguiente es la sentencia try/catch


que permite el manejo de errores de dos tipos, el primero cubre errores simples de programacin, y el
segundo tipo no puede ser evitado slo con una correcta programacin, como por ejemplo el
agotamiento de la memoria. As, tiene la siguiente estructura:
try
{
// ...
}
catch (IloException& e)
{
cerr << "Concert Exception: " << e << endl;
}
catch (...)
{
cerr << "Other Exception" << endl;
}
env.end();

40
Despus de la sentencia try, en la parte denotada por los puntos suspensivos ( ...), se escribe la
totalidad del cdigo. Las sentencia catch , captura los errores capturados por la Tecnologa Concierto
algn otro error desconocido. Al final de toda implementacin, es necesario destruir el ambiente
construido a travs de la instruccin env.end() .

El cdigo completo del algoritmo se muestra a continuacin:


TSPTour TSPSolver::solverTSP()
{
try
{
IloBoolVar **x = this->createDVars();
IloModel model = this->buildModel(x);
IloCplex cplex(this->env);
cplex.extract(model);
while (cplex.solve())
{
IloBool **tourInfo = this->getTourInfo(x, cplex);
vector<TSPTour> tours = this->buildTours(tourInfo);
if( !this->addConstraints(cplex,x,tours) )
{
return tours[0];
}
}
}
catch (IloException & e)
{
cerr << "Concert Exception: " << e << endl;
}
catch (...)
{
cerr << "Other Exception..." << endl;
}

} env.end();

Lo primero que se realiza, es crear las variables de decisin mediante el mtodo createDVars() (ver
anexo 2.1), que corresponden a variables binarias (boleanas) , que toman un valor igual a 1 0, en
caso a si se va de la ciudad a la ciudad , respectivamente.

En el procedimiento, se crea un puntero a puntero x de tipo IloBoolVar y se asigna memoria al


arreglo bidimensional de dimensin . Tambin se le da nombre, y se inicializan todas
las variables mediante un constructor adecuado, en que se especifica el ambiente en que sern
utilizadas y el nombre asignado. El mtodo retorna un puntero a puntero que se asigna a un puntero a
puntero del mismo tipo tambin llamado x.

El siguiente paso consiste en crear un objeto de modelamiento para construir el modelo matemtico del
problema a resolver. Los objetos de modelamiento tambin son conocidos como extrables, porque
cada objeto es extrado uno por uno cuando se extrae un modelo de optimizacin a un objeto
IloCplex . La clase extrable ms fundamental es la IloModel , pues objetos de esta clase son usados

para definir modelos de optimizacin completos que son posteriormente extrados a un objeto
IloCplex .

Una vez que un objeto IloModel se ha construido, con el propsito de definir el modelo de
optimizacin, es poblado con otros objetos extrables, tales como:

IloNumVar : representan las variables de decisin del modelo.


IloRange : definen restricciones de la forma , donde es una
expresin lineal.
IloObjective : representa una funcin objetivo.

41
As, se crea un objeto de tipo IloModel llamado model, al cual se le asigna el objeto de retorno del
mtodo buildModel (ver anexo 2.1), que corresponde a un objeto IloModel que contiene el modelo
del TSP relajado. En las instrucciones del mtodo buidModel, que recibe como parmetro las variables
de decisin, se construye el modelo.

El primer paso para construir un modelo es su construccin:


IloModel model(this->env);

En que se especifica el ambiente en que se construir, el cual es pasado como argumento al constructor.
En este punto se est en condiciones de poblar el modelo mediante otros objetos extrables. Si
queremos agregar al modelo la funcin objetivo es necesario crear una expresin numrica, pasando
como argumento el ambiente al cual pertenecer la expresin, lo siguiente es crear la expresin:
IloNumExpr objective(this->env);
for (i = 0; i < nCities; i++)
{
for (j = 0; j < nCities; j++)
{
if (i != j)
{
objective += x[i][j] * d[i][j];
}
}
}

Podemos ver que la expresin objective, efectivamente corresponde al trmino , ahora es

necesario agregarla al modelo mediante el mtodo add y la funcin IloMinimize para especificar
que se desea minimizar la funcin objetivo:
model.add(IloMinimize(this->env, objective));

Ahora, para crear las restricciones es necesario el uso de objetos del tipo IloRange . Es posible crear
un rango mediante los constructores de la clase IloRange , a travs de operadores aritmticos sobre
las variables (IloNumVar ) mediante expresiones (instancias de IloExpr y sus subclases). En
nuestro caso se construirn los rangos mediante expresiones.

El conjunto de restricciones (de asignacin) que definen que para cada ciudad, solamente se puede salir
hacia una ciudad corresponde a:

El cdigo necesario para construir el conjunto de restricciones anterior es:


for (j = 0; j < nCities; j++)
{
IloNumExpr expr(env);
for (i = 0; i < nCities; i++)
{
if (i != j)
{
expr += x[i][j];
}
}
IloRange constraint = (expr == 1);
model.add(constraint);
}

Notar que efectivamente se crean nCities restricciones al declararse la espresin expr (instancia de
la clase IloNumExpr ) afuera del segundo ciclo for. As, lo primero es declarar la expresin y decirle

42
en qu ambiente hacerlo. Una vez creada la expresin, es necesario el uso de un objeto IloRange
para crear la restriccin, usando el operador aritmtico de igualdad (==) entre el valor numrico y la
expresin. Ahora, es necesario agregar la restriccin ( constraint ) al modelo mediante el mtodo
add, de caso contrario la restriccin no es tomada en cuenta.

La forma para agregar el segundo conjunto de restricciones en que se asegura que exactamente una vez
se entre a cada cuidad.

Una vez que el modelo se ha realizado, es posible crear una instancia de IloCplex pasndole como
argumento el ambiente, mediante la instruccin:

IloCplex cplex(this->env);
De esta manera, el objeto cplex puede ser usado para extraer el modelo a ser resuelto. Una manera
de extraer el modelo es mediante el mtodo:
cplex.extract(model);

Este mtodo requiere que se haya creado el objeto de tipo IloCplex anteriormente. As, existe otro
mtodo que permite realizar los dos pasos anteriores en uno:
IloCplex cplex(model);

Extrado el modelo por un objeto de la clase IloCplex , es posible resolver el problema mediante la
instruccin:
cplex.solve();

El mtodo solve devuelde un valor de tipo IloBool , donde IloTrue indica que cplex encontr
exitosamente una solucin factible (no necesariamente ptima), e IloFalse significa que no fue
encontrada una solucin.

Por tal motivo, se usa un ciclo while(cplex.solve()) cuya condicin permite que se resuelva
iterativamente el problema mientras se van agregando los cortes relacionados a los subtours de las
soluciones.

Una vez resuelto el problema mediante cplex, es necesario extraer la solucin. La anterior es posible
mediante el mtodo getValue , perteneciente a la clase IloCplex . Para extraer el valor de todas las
variables de decisin, se cre el mtodo getTourInfo , descrito a continuacin:
IloBool **TSPSolver::getTourInfo(IloBoolVar **x, IloCplex cplex)
{
IloBool **values = (IloBool **)malloc(nCities*sizeof(IloBool *));
int i, j;
for (i = 0; i < this->nCities; i++)
{
values[i] = (IloBool *) malloc((nCities)*sizeof (IloBool));
for (j = 0; j < this->nCities; j++)
{
if (i != j)
values[i][j] = cplex.getValue(x[i][j
else
values[i][j] = IloFalse;
}
}
return values;
}

Ya que el mtodo retorna un puntero a puntero de datos de tipo IloBool , se crea una variable local
que es donde se guardarn los valores que se desean extraer, para posteriormente retornarla. En el
procedimiento se asigna memoria dinmicamente para almacenar la matriz de valores IloBool , los

43
cuales se extraen a travs de la instruccin cplex.getValue (x[i][j]) . El valor de retorno se
almacena en tourInfo, que no es ms que una matriz de dimensin de 0 y 1 que
contienen los valores de las variables de decisin, en que de acuerdo a las restricciones, deben tener
solamente un nico 1 por fila y por columna, por lo que haciendo un seguimiento de los valores uno
puede construir un tour subtours asociados a la solucin. As, en la ilustracin 19 ilustracin
19tenemos:

ilustracin 19: Representacin matricial de variables de decisin.

12345 12345

100010 100010
200100 200100
300001 300001
410000 401000
501000 510000

La primera matriz define los subtours 1-4-1 y 2-3-5-2, y la segunda matriz define el tour 1-4-2-3-5-1. As,
mediante el procedimiento buildTours , que recibe como parmetro a la matriz tourInfo , se
construyen los tours subtours. Su funcin es bastante clara, sin embargo escapa del uso de la
Tecnologa Concierto y CPLEX, por lo que slo se muestra en el anexo XXX.

Lo siguiente consiste en agregar, en lo posible, una restriccin de eliminacin de subtours mediante el


mtodo addConstraints . Dicho mtodo retorna un valor booleano en caso de agregar o no una
restriccin. El procedimiento consiste en agregar el subtour de dimensin menor, parte del mtodo se
muestra:
if ( minimum.size() >= 2 && minimum.size() <= nCities )
{
IloNumExpr lhs(this->env);
for ( j = 0 ; j < minimum.size() - 1 ; j++ )
{
for ( k = 0 ; k < minimum.size() - 1 ; k++ )
{
if ( minimum.get(j) != minimum.get(k) )
{
lhs += x[minimum.get(j)][minimum.get(k)];
}
}
}
IloRange constraint = ( lhs <= minimum.size() -2 );
cplex.addCut(constraint);
}

Si el tamao del subtour es mayor o igual a 2 menor o igual a nCities , entonces es posible agregar
una restriccin de eliminacin de subtours para las ciudades pertenecientes al subtours actual. La forma
de crear la restriccin es similar a como se explic para las otras restricciones del modelo, creando una
expresin y posteriormente creando la restriccin mediante un objeto IloRange . Para que la
restriccin (corte) sea tomada en cuenta, es necesario agregarla al solver mediante el mtodo addcut
de la clase IloCplex .

Finalmente, cuando no se haya agregado una restriccin de eliminacin de subtour, el algoritmo


termina y se retorna el tour ptimo encontrado.

Discusin de resultados: Tiempos de Resoluci n, Configuracin de Parmetros

44
A travs de las pruebas realizadas anteriormente se vio la superioridad de la implementacin realizada
en OPL para resolver el TSP mediante planos de corte para la instancia "gr17.tsp" perteneciente a la
librera de datos TSPLIB. Por tal motivo con el propsito de comparacin, tambin se resolvi el mismo
problema con la implementacin realizada del mismo algoritmo pero con tecnologa concierto usando el
lenguaje de programacin C++.

ilustracin 20: solucin gr17.tsp mediante Tecnologa Concierto.

En la figura se aprecia un mejor rendimiento de esta ltima implementacin por sobre las anteriores.
Adems cabe destacar la mayor flexibilidad de programacin que se posee al resolver los problemas
mediante tecnologa concierto. En la implementacin realizada, la lectura de datos se realiz de tal
forma de leer directamente sobre los archivos disponibles de la TSPLIB, por lo que el programa se puede
utilizar para resolver cualquier instancia sin siquiera modificar o adaptar los archivos de datos, como en
las otras implementaciones realizadas.

Es cierto que el uso de la Tecnologa Concierto requiere de mayor conocimiento de programacin, pero
sin duda posee innumerables ventajas en la resolucin de problemas, adems que luego de un tiempo
no tan excesivo se logra un avance considerable en la curva de aprendizaje. No obstante siempre est en
el usuario la decisin final de seleccin de acuerdo a las necesidades e intereses personales.

45
Anexos

Anexo 1: Cdigo implementacin de Algoritmo Planos Cortantes para TSP en OPL


/*****************************************************************************
* Seccion de datos
*****************************************************************************/

// Ciudades
int n = ...;
range Cities = 1..n;

// Conjunto de aristas
tuple edge {int i; int j;}
setof(edge) Edges = {<i,j> | ordered i,j in Cities};
int dist[Edges] = ...;

// Variables de decisin
dvar boolean x[Edges];

tuple Subtour { int size; int subtour[Cities]; }


{Subtour} subtours = ...;

/*****************************************************************************
* Construccin de modelo (TSP relajado)
*****************************************************************************/

// Objective
minimize sum (<i,j> in Edges) dist[<i,j>]*x[<i,j>];
subject to {

// Cada ciudad esta unida a otras dos ciudades


forall (j in Cities)
sum (<i,j> in Edges) x[<i,j>] + sum (<j,k> in Edges) x[<j,k>] == 2;

// Restricciones de eliminacin de subtours


forall (s in subtours)
sum (i in Cities : s.subtour[i] != 0)
x[<minl(i, s.subtour[i]), maxl(i, s.subtour[i])>] <= s.size-1;

};

// Post-procesamiento para encontrar los subtours

// Informacion de la solucion
int thisSubtour[Cities];
int newSubtourSize;
int newSubtour[Cities];

// Informacin auxiliar
int visited[i in Cities] = 0;
// Arreglo que contiene la informacion de las ciuades adjuntas a j
setof(int) adj[j in Cities] = {i | <i,j> in Edges : x[<i,j>] == 1} union
{k | <j,k> in Edges : x[<j,k>] == 1};
execute
{
newSubtourSize = n;
// Encontrar una ciudad que no ha sido visitada
for (var i in Cities)
{
//Si la ciudad i ya fue visitada, seguimos a la siguiente ciudad
if (visited[i]==1)
{
continue;
}
//La ciudad actual es el inicio del tour

46
var start = i;
//La variable nodo toma el valor de la ciudad inicial (actual)
var node = i;
//Se inicializa tamao de subtour a cero
var thisSubtourSize = 0;

//Se inicializa el arreglo que contendr el subtour


for (var j in Cities)
{
thisSubtour[j] = 0;
}

/* Mientras no haya completado el subtour (el fin del subtour es


* el nodo inicial) o thisSubtourSize==0 asegura que la condicin
* sea valida al inicio */
while (node!=start || thisSubtourSize==0)
{
//Marcamos a la ciudad actual como visitada
visited[node] = 1;
//La ciudad sucesora la marcamos como la ciudad inicial
var succ = start;

//Recorro las ciudades adjuntas a la ciudad actual


for (i in adj[node])
{
//Si la ciudad i no ha sido visitada, la visitar
if (visited[i] == 0)
{
succ = i;
break;
}
}
//Agrego al subtour la ciudad recien encontrada
thisSubtour[node] = succ;
//La ciudad actual pasa a ser succ
node = succ;
//Aumento en 1 la cantidad de ciudades del tour
++thisSubtourSize;
}
//Acabamos de encontrar un subtour vlido
//Guarda siempre el subtour ms pequeo
if (thisSubtourSize < newSubtourSize)
{
//Copio las ciudades del subtour actual al nuevo subtour
for (i in Cities)
{
newSubtour[i] = thisSubtour[i];
}
newSubtourSize = thisSubtourSize;
}
}
}

/*****************************************************************************
* Programacin del programa principal (control de flujo)
*****************************************************************************/

main
{

var opl = thisOplModel


var mod = opl.modelDefinition;
var dat = opl.dataElements;

while (1)
{
cplex.clearModel();
opl = new IloOplModel(mod,cplex);

47
opl.addDataSource(dat);
opl.generate();
if (!cplex.solve())
{
writeln("ERROR: could not solve");
opl.end();
break;
}
opl.postProcess();

if (opl.newSubtourSize == opl.n) {
writeln("Tour Final ",opl.thisSubtour);
opl.end();
break;
}
dat.subtours.add(opl.newSubtourSize, opl.newSubtour);
opl.end();
}
}

Anexo 2: Cdigo implementacin Algoritmo Planos Cortantes para TSP usando


Tecnologa Concierto. (esto puede mostrarse de otra forma??)

Anexo 2.1: Cdigo clase TSPSolver.cpp

#include "TSPSolver.h"
#include "TSPUtil.h"
#include <stdlib.h>
#include <stdio.h>

using namespace std;

TSPSolver::TSPSolver(char* nombre)
{
FILE *file;
file = fopen(nombre, "r");
int n, i, j, aux2, aux4;

char aux[MAX_LINE], *aux3;


aux3 = fgets(aux, MAX_LINE, file);
aux3 = fgets(aux, MAX_LINE, file);
aux3 = fgets(aux, MAX_LINE, file);
aux4 = fscanf(file, "DIMENSION: %d", &n);
this->nCities = n;
this->d = (IloInt **) malloc(this->nCities * sizeof (IloInt *));

aux3 = fgets(aux, MAX_LINE, file);


aux3 = fgets(aux, MAX_LINE, file);
aux3 = fgets(aux, MAX_LINE, file);
aux3 = fgets(aux, MAX_LINE, file);

for (i = 0; i < nCities; i++)


{
this->d[i] = (IloInt *) malloc(this->nCities * sizeof (IloInt));
for (j = 0; j <= i; j++)
{
aux4 = fscanf(file, " %d", &aux2);
d[i][j] = d[j][i] = aux2;
}
}
fclose(file);
}

TSPTour TSPSolver::solverWithBranchAndCut()

48
{
try
{
char nombre[100];
IloBoolVar **x = this->createDVars();
IloModel model = this->buildModel(x);
IloCplex cplex(this->env);
cplex.extract(model);
cplex.exportModel("modelo.lp");
int iteration = 0;
while (cplex.solve())
{
IloBool **tourInfo = this->getTourInfo(x, cplex);
vector<TSPTour> tours = this->buildTours(tourInfo);
if( !this->addConstraints(cplex,x,tours) )
{
return tours[0];
}
iteration++;
sprintf( nombre, "modelo%d.lp" , iteration );
cplex.exportModel( nombre );
}
} catch (IloException & e)
{
cerr << "### EXCEPTION: " << e << endl);
} catch (...)
{
cout << "### UNEXPECTED ERROR ..." << endl;
}
}

IloBoolVar ** TSPSolver::createDVars()
{
IloBoolVar **x = (IloBoolVar **) malloc(nCities * sizeof (IloBoolVar *));
int i, j;
char *nombre = (char *) malloc(100 * sizeof (char));
for (i = 0; i < this->nCities; i++)
{
x[i] = (IloBoolVar *) malloc((this->nCities) * sizeof (IloBoolVar));
for (j = 0; j < this->nCities; j++)
{
if (i != j)
{
sprintf(nombre, "x(%d,%d)", i, j);
x[i][j] = IloBoolVar(this->env, nombre);
}
}
}
return x;
}

IloModel TSPSolver::buildModel(IloBoolVar** x)
{
IloModel model(this->env);
int i, j;
//creo y agrego la funcion objetivo (1)
IloNumExpr objective(this->env);
for (i = 0; i < nCities; i++)
{
for (j = 0; j < nCities; j++)
{
if (i != j)
{
objective += x[i][j] * d[i][j];
}
}
}
model.add(IloMinimize(this->env, objective));

49
//restricciones
for (j = 0; j < nCities; j++)
{
IloNumExpr expr(env);
for (i = 0; i < nCities; i++)
{
if (i != j)
{
expr += x[i][j];
}
}
IloRange constraint = (expr == 1);
model.add(constraint);
}

//restricciones
for (i = 0; i < nCities; i++)
{
IloNumExpr expr(env);
for (j = 0; j < nCities; j++)
{
if (i != j)
{
expr += x[i][j];
}
}
IloRange constraint = (expr == 1);
model.add(constraint);
//cplex.add(constraint);
}
return model;
}

IloBool **TSPSolver::getTourInfo(IloBoolVar **x, IloCplex cplex)


{
IloBool **values = (IloBool **) malloc(nCities * sizeof (IloBool *));
int i, j;
for (i = 0; i < this->nCities; i++)
{
values[i] = (IloBool *) malloc((this->nCities) * sizeof (IloBool));
for (j = 0; j < this->nCities; j++)
{
if (i != j)
values[i][j] = cplex.getValue(x[i][j
else
values[i][j] = IloFalse;
}
}
return values;
}

vector<TSPTour> TSPSolver::buildTours(IloBool **tourInfo)


{
vector<TSPTour> tours;
int **tourInfoAux = (int **) malloc(this->nCities * sizeof (int *));
int i, j, start;
for (i = 0; i < this->nCities; i++)
{
tourInfoAux[i] = (int *) malloc((this->nCities) * sizeof (int));
for (j = 0; j < this->nCities; j++)
{
if( tourInfo[i][j] == IloTrue )
//tourInfoAux[i][j] = 1;
*(*(tourInfoAux+i)+j) = 1;
else
//tourInfoAux[i][j] = 0;
*(*(tourInfoAux+i)+j) = 0;

50
}
}
for (start = 0; start < this->nCities; start++)
{
while( TSPUtil::fullyUsed(tourInfoAux,this->nCities,start) == false )
{
int current = start;
int subtourSize = 0;
TSPTour tour;
tour.add(start);
while (start != current || subtourSize == 0)
{
subtourSize++;
int next;
for (next = 0; next < this->nCities; next++)
{
if (next != current && tourInfoAux[current][next] == 1 )
{
tourInfoAux[current][next] = 0;
//tourInfoAux[next][current] = 0;
current = next;
tour.add(current);
break;
}
}
}
tours.push_back(tour);
}
}
return tours;
}

bool TSPSolver::addConstraints(IloCplex cplex,IloBoolVar **x,vector<TSPTour>


&tours )
{
int i, j, k;
bool constraintsAdded = false;

TSPTour minimum = TSPUtil::getMinimumTour(tours);

if ( minimum.size() >= 2 && minimum.size() <= nCities )


{
IloNumExpr lhs(this->env);
for ( j = 0 ; j < minimum.size() - 1 ; j++ )
{
for ( k = 0 ; k < minimum.size() - 1 ; k++ )
{
if ( minimum.get(j) != minimum.get(k) )
{
lhs += x[minimum.get(j)][minimum.get(k)];
}
}
}
IloRange constraint = ( lhs <= minimum.size() -2 );
cplex.addCut(constraint);
constraintsAdded = true;
}
return constraintsAdded;
}

Anexo 2.2: Cdigo clase TSPTour.cpp


#include "TSPTour.h"
TSPTour::TSPTour()
{
this->data = new vector<int>();
}

51
void TSPTour::set(int index, int value)
{
(* this->data)[index] = value;
}

int TSPTour::get(int index)


{
return (* this->data)[index];
return 0; //?
}

void TSPTour::add(int value)


{
this->data->push_back(value);
}

int TSPTour::size()
{
return this->data->size();
}

Anexo 2.3: Cdigo clase TSPUtil.cpp


#include "TSPUtil.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

bool TSPUtil::fullyUsed( int **tourInfo, int size, int row )


{
int j;
for ( j = 0; j < size; j++)
{
if (tourInfo[row][j] == 1)
{
return false;
}
}
return true;
}

TSPTour TSPUtil::getMinimumTour(vector<TSPTour> tours)


{
TSPTour minimumTour = tours[0];
int i;
for ( i = 0; i < tours.size() ; i++ )
{
if ( minimumTour.size() > tours[i].size() )
{
minimumTour = tours[i];
}
}
return minimumTour;

52
53

Você também pode gostar