1.- PROGRAMACIÓN DE LOS DIFERENTES MODELOS
En este primer punto se muestra la programación implementada para la ejecución de los diferentes modelos planteados.
Así, en primer lugar cabe destacar que el programa ILOG OPL trabaja de la siguiente forma. El programa ejecuta un proyecto (project) que está integrado por dos archivos: “nombre.mod” y “nombre.dat”. El primero de ellos contiene la programación misma, es decir, en él se declaran las diferentes variables, restricciones, función objetivo,… mientras que en el archivo “nombre.dat” se encuentran los datos a utilizar por el archivo “nombre.mod” para su correspondiente resolución.
En este proyecto, para simplificar la ejecución de los diferentes problemas, se ha decidido que sólo halla un archivo “nombre.mod”, y lo que se ha hecho es crear un archivo de EXCEL que contiene todos los datos necesarios para la resolución de cada problema; así, lo que se ha hecho es que el archivo “nombre.mod” lea los datos iniciales del archivo de EXCEL “caso.xls”, pero antes se ha preparado la página de EXCEL definiendo ciertos campos, que son los que luego lee el archivo software.
A continuación, se muestra los diferentes pasos seguidos para desarrollar la implementación computacional de los modelos planteados.
1.1 Procedimiento pre-proceso de reducción de variables
En este punto se presenta cómo se puede reducir el número de variables de un problema sin afectar la calidad de la solución, pues como se verá a continuación, no son necesarias todas las variables para solucionar el problema (Valero 1991).
Considérese el siguiente ejemplo de un producto cuyo montaje se ha dividido en 10 tareas cuya duración es la siguiente:
Tarea (i) Duración(ti)
1 5 2 10 3 5 4 2 5 7 6 5 7 10 8 2 9 5 10 7 Duración total 58
, y las relaciones de precedencia son las siguientes:
2 3 4 5 8 10 9 7 6 1
Pero a lo mejor, la suma de tiempos no se puedan agrupar formando grupos de 15, de manera que se necesitarán más estaciones de trabajo que mmin, se considerará que se trabajará como máximo con mmax=5 estaciones de trabajo.
Así, el número de variables binarias tipo x[i,j] serán:
Número de variables=n*mmax=10*5=50
Donde mmax es el número máximo de estaciones de trabajo con el cual se trabaja y n el número de tareas.
Se puede ver fácilmente que si la tarea 1 se realiza en la estación de trabajo 5, no existe espacio posible para que se realicen las otras tareas descendentes, y por lo tanto no existe solución factible en la que la tarea 1 se realice en la estación número 5. De forma que en el modelo, a priori ya se conoce que la variable x[1,5] toma por valor 0.
De la misma forma, se puede ver que si se coloca la tarea 10 en la estación 1, no existe espacio suficiente para que se realicen las tareas que la preceden, con esto se sabe que no existe solución factible en la cual la tarea 10 se realice en la estación de trabajo 1. Esto se traduce de forma que a priori se sabe que la variable x[10,1] vale 0.
Esto que se ha visto hasta ahora, no es sólo válido para las tareas inicial y final, sino que se puede generalizar para otras tareas en función de cuál sea la suma de tiempos de las tareas que se deben realizar después de ella (incluyendo la propia tarea) y cuál sea la suma de las tareas que se hayan de realizar con anterioridad (incluyendo la propia tarea), teniendo en cuenta además el tiempo de ciclo y el número de estaciones de trabajo mínimo (mmin) y máximo (mmax).
1 2 3 4 5 6 7 8 9 10 1 0 1 1 0 0 0 0 0 0 0 2 0 0 0 1 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 4 0 0 0 0 1 1 0 0 0 0 5 0 0 0 0 0 0 0 1 0 0 6 0 0 0 0 0 0 1 0 0 0 7 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 1 0 9 0 0 0 0 0 0 0 0 0 1 10 0 0 0 0 0 0 0 0 0 0
Donde un 1 en la posición (i,j) de la matriz indica que la tarea i precede a la tarea j, o lo que es lo mismo, la tarea j es descendente de la tarea i.
Esta matriz de precedencias se ve modificada al añadir 1’s en la diagonal principal.
Si esta matriz, la cual se denomina A, se multiplica sucesivamente por sí misma, se obtienen 1’s en aquellas posiciones que indican descendientes no inmediatos. De la misma manera y por columnas, se obtienen 1’s en aquellas posiciones que indican predecesores. En este razonamiento se excluyen los elementos de la diagonal principal.
Por lo tanto, en A2 se obtiene:
1 2 3 4 5 6 7 8 9 10 1 1 1 1 1 0 0 0 0 0 0 2 0 1 0 1 1 1 0 0 0 0 3 0 0 1 1 1 1 0 0 0 0 4 0 0 0 1 1 1 1 1 0 0 5 0 0 0 0 1 0 0 1 1 0 6 0 0 0 0 0 1 1 0 1 0 7 0 0 0 0 0 0 1 0 1 1 8 0 0 0 0 0 0 0 1 1 1 9 0 0 0 0 0 0 0 0 1 1 10 0 0 0 0 0 0 0 0 0 1
Fijarse, por ejemplo, en la tarea 5, o lo que es lo mismo, en la fila y columna 5. El cambio que se ha producido en la fila 5 ha sido el siguiente:
En la matriz A, la fila 5 es:
1 2 3 4 5 6 7 8 9 10
5 0 0 0 0 1 0 0 1 0 0
En esta fila se puede ver que la tarea 8 es descendiente de la tarea 5.
1 2 3 4 5 6 7 8 9 10
5 0 0 0 0 1 0 0 1 1 0
En esta fila se puede ver que la tarea 9 es también descendiente, se puede decir de segundo grado, de la tarea 5.
Un razonamiento similar puede realizarse con el resto de las filas, y para las columnas.
En la matriz A, la columna 5 es:
5 1 0 2 0 3 0 4 1 5 1 6 0 7 0 8 0 9 0 10 0
En esta columna se puede ver que la tarea 4 precede la tarea 5.
5 1 0 2 1 3 1 4 1 5 1 6 0 7 0 8 0 9 0 10 0
En esta columna se puede ver que las tareas 2 y 3 son descendientes, en un segundo grado, de la tarea 5.
Este mismo razonamiento es extensible al resto de tareas.
Las potencias sucesivas que se obtienen de la matriz A sonA A A2, 4, 8,...
Cuando se calcula la matriz se observa que ya no varía más, por lo tanto, ya no se multiplica la matriz por sí misma más veces.
8
A
1 2 3 4 5 6 7 8 9 10 1 1 1 1 1 1 1 1 1 1 1 2 0 1 0 1 1 1 1 1 1 1 3 0 0 1 1 1 1 1 1 1 1 4 0 0 0 1 1 1 1 1 1 1 5 0 0 0 0 1 0 0 1 1 1 6 0 0 0 0 0 1 1 0 1 1 7 0 0 0 0 0 0 1 0 1 1 8 0 0 0 0 0 0 0 1 1 1 9 0 0 0 0 0 0 0 0 1 1 10 0 0 0 0 0 0 0 0 0 1
Con esta última matriz obtenida, se calcula por filas la suma de los tiempos de los descendientes de cada tarea, y por columnas la suma de tiempos de los antecesores de cada tarea; incluyendo en ambos casos la propia tarea.
En el caso planteado se obtiene:
Tarea (i) Duración (ti)
∑
tdesc∑
tprec (4) (5)donde:
(4)=
∑
tdesc / Tmax-ε (5)=∑
tprec / Tmax-ε, ε es un número pequeño en comparación con Tmax (tiempo de ciclo), y sirve para que (4) y (5) tomen como valor el entero superior en el caso que el cociente no dé un número entero exacto, y
x es el menor entero mayor que x.
Observar por ejemplo la tarea 4. En la columna (4) el 2 indica que la tarea no puede realizarse en las dos últimas estaciones de trabajo, es decir, x[4,4]=x[4,5]=0. De la misma manera, en la columna (5), el 1 indica que la tarea 4 no puede realizarse en la primera estación, de manera que
x[4,1]=0.
Generalizando con todas las tareas, resulta fácil ver que las siguientes variables son nulas:
x[1,3/4/5], x[2,3/4/5], x[3,4/5], x[4,1/4/5], x[5,1/5], x[6,1/5], x[7,1/2/5], x[8,1/2], x[9,1/2/3], x[10,1/2/3].
Con lo cual, el número de variables se ve reducido a:
Número de variables=50-26=24
Esta reducción del número de variables dependerá de cuál sea la forma del grafo de precedencias, siendo mayor la reducción de variables cuanto más “alargado” sea el grafo, mientras que cuánto más “ancho” sea el grafo, menor será la reducción de variables.
Así, para la ejecución de este procedimiento de pre-proceso, sólo se requiere introducir como dato inicial un archivo de texto (“nombre.txt”), con los datos presentados de la siguiente forma:
número de tareas, número de relaciones de precedencia, tiempo de ciclo, número máximo estaciones, tarea, duración, tarea precedente 1, tarea precedente 2,
Y como resultado devuelve un archivo de texto (“nombre-resultado.txt”) con el mismo formato de presentación de datos que el archivo de entrada, pero añadiendo al final, para cada tarea, entre qué estaciones de trabajo puede ser asignada.
Lo que se realiza a continuación es copiar la estación mínima y máxima entre las que puede ser asignada una actividad, y engancharlo en la página de EXCEL que contiene el resto de datos necesarios para la resolución del problema.
1.2 Preparación del archivo Excel de entrada de datos
Como se ha comentado, el archivo “nombre.dat” que contiene la programación de cada caso a resolver (sólo precedencias, sólo incompatibilidades o precedencias e incompatibilidades a la vez; y cada uno según las diferentes variantes planteadas), lee todos los datos iniciales de un archivo de EXCEL (“datos.xls”).
A continuación se muestra cómo se presenta la página de EXCEL que contiene los datos para resolver los diferentes casos.
Los datos se colocan de una forma determinada; un ejemplo sencillo:
Tarea Duracion Precedencias Incompatibilidades Primera Ultima
1 11 1 2 2 4 1 5 nbincompatibilidades 4 2 17 2 3 5 3 2 5 TC 20 3 9 2 4 7 8 2 6 nbprecedencias 8 4 5 3 5 8 2 2 8 nbestacio 8 5 8 3 6 3 8 6 12 4 6 3 8 7 10 5 7 3 8 8 3 6 8 3 8
En la tercera y cuarta columna se presentan las relaciones de precedencia e incompatibilidad, respectivamente, existentes entre pares de tareas; así, en este caso, existen relaciones de precedencia entre las tareas: 1-2, 2-3, 2-4,… y relaciones de incompatibilidad entre las tareas: 2-4,
5-3,…
En las siguientes columnas, denominadas “Primera” y “Ultima”, se refleja el intervalo de estaciones en el cual puede asignarse cada tarea, o sea, es el resultado de aplicar el procedimiento de pre-proceso que permite una reducción considerable de variables. Así, una tarea i puede ser asignada, según las relaciones de precedencia existentes, en el intervalo [primera[i], ultima[i]].
También es necesario definir cuatro constantes, que son:
- nbincompatibilidades: número de relaciones de incompatibilidad existentes. - TC: tiempo de ciclo, que es igual en todas las estaciones de trabajo.
- nbprecedencias: número de relaciones de precedencia existentes.
- nbestacio: número máximo de estaciones de trabajo que puede haber en la línea (recordar
que su valor era: . 1 2 num tareas i i t TC = ∗
∑
)Por último, destacar que resulta necesario que para que el archivo “nombre.mod” pueda identificar cada campo (tarea, duración, precedencias,…) se defina cada uno de ellos en EXCEL. Para hacerlo se debe diferenciar entre definir un conjunto (tareas, precedencias, incompatibilidades, primera, ultima) o una constante (nbprecedencias, nbestacio, nbincompatibilidades, TC).
En el segundo caso, el de las cuatro constantes, se debe seleccionar primero la casilla donde se ha escrito el nombre de la constante (nbestacio, nbprecedencias, nbincompatibilidades, TC) y la casilla de al lado que contiene el valor de ésta, y después seleccionar: “Insertar – Nombre –
Crear – Columna Izquierda”.
De esta forma se definen todos los campos necesarios y que lee el archivo “nombre.mod” del programa ILOG OPL.
1.3 Programación para leer la entrada de datos
En este punto se muestra aquella parte de los modelos planteados que corresponde a las órdenes de lectura de toda la información que contiene el archivo de EXCEL “datos.xls”.
En primer lugar, recordar que se han planteado 3 formas diferentes de cálculo del valor de la variable “objetivo”, que es el número mínimo de estaciones de trabajo que debe haber en la línea de montaje:
- O1: Objetivo≥ j x i j* [ , ] ∀,i j - O2: Objetivo=max(j*x[i,j]) ∀,i j - O3: Objetivo=max(R[i]) ∀i
Recordar también que se han probado las siguientes cuatro variantes de modelización y técnica de resolución:
- R : modelizar en programación matemática con la función objetivo O y resolver con CPLEX 1 1
- R : modelizar en programación matemática con la función objetivo O y resolver con SOLVER 2 1
- R : modelizar en programación matemática con la función objetivo O y resolver con SOLVER 3 2
- R : modelizar en programación lógica de restricciones con la función objetivo O y resolver 4
con SOLVER.
Seguidamente se mostrará el caso de los modelos realizados para la resolución de casos en los que sólo existen relaciones de precedencia entre las tareas, en segundo lugar los modelos para la resolución de casos en los que sólo existen relaciones de incompatibilidad entre las tareas, y por último, los modelos planteados para la resolución de casos en los que se dan a la vez relaciones de precedencia e incompatibilidad entre las tareas que conforman la línea de montaje.
A continuación se van a mostrar los diferentes modelos tratados, así como el nombre de cada uno de ellos:
1.- Sólo relaciones de precedencias entre tareas: se han planteado tres formas de representar la resolución de la relación de precedencia entre parejas de tareas, como se ha visto en la memoria, y se ha planteado diferentes formas de resolución, de manera que se han obtenido los siguientes programas:
- Tipo 1: esta primera manera de plantear las relaciones de precedencia se ha podido resolver de cuatro formas diferentes, dando lugar a cuatro programas: PR1 1/ 2 / 3/ 4
- Tipo 2: esta segunda forma de plantear las relaciones de precedencia sólo ha sido posible plantear su resolución modelizando pensando en programación matemática, pues la modelización en programación lógica de restricciones no es posible. Los tres programas que se han obtenido han sido: P R2 1/ 2 / 3
- Tipo 3: a esta tercera forma de plantear las relaciones de precedencia entre tareas, le ocurre lo mismo que al tipo 2, es decir, su modelización pensando en programación lógica de restricciones resulta imposible; de manera que los tres programas que se han obtenido son: P R3 1/ 2/ 3
- Tipo 1: como se ha comentado, ha sido posible resolver la relación de incompatibilidad tipo 1 de cuatro formas diferentes, obteniendo los siguientes programas: I R1 1/ 2 / 3/ 4
- Tipo 2: igual que en el caso anterior, ha sido posible su resolución de cuatro formas diferentes, que han sido las siguientes:I R2 1/ 2 / 3/ 4
3.- Relaciones de precedencia e incompatibilidad entre tareas: para finalizar, tal como se ha comentado, se ha procedido a la resolución de problemas en los que existen relaciones de precedencia e incompatibilidad entre las tareas que conforman la línea de montaje. Para ello, se han combinado entre sí los mejores modelos para la resolución de “sólo precedencias” con los mejores modelos para la resolución de “sólo incompatibilidades”. A continuación se muestra una tabla en la que se expone el nombre del modelos que resulta de la combinación de los diferentes “pequeños modelos”:
3 P I 2 R 2 P3 I2 R 2 1 P I 1 R 4 P1 I1 R 4 1 P I 2 R 4 P1 I2 R 4
Tabla 1.3.1 Modelos para la resolución de problemas con precedencia e incompatibilidades a la vez
Destacar que no se han expuesto modelos para la resolución de problemas de equilibrado de líneas (con relaciones de precedencia e incompatibilidad a la vez) con la variante R debido al 3
hecho que los resultados parciales obtenidos en la resolución de datos test, con sólo relaciones de precedencia o sólo de incompatibilidad, han resultado ser de una calidad inferior a los que se consiguen con las otras tres variantes.
Se han mostrado el nombre de los diferentes modelos debido a que a continuación se muestra la programación que tienen en común cada uno de ellos, para ver finalmente, la programación de la relación de precedencia e incompatibilidad que presentan cada uno de los modelos.
1.3.1 Programación común a modelos con sólo relaciones de precedencia
En primer lugar se muestra aquella parte de la programación del archivo “nombre.mod” común a todos los programas planteados en los que sólo existen relaciones de precedencia entre las tareas en las cuales se divide la línea de montaje.
Destacar que esta parte de la programación es común a las tres formas de plantear las relaciones de precedencia, y a las diferentes variantes de modelizar y resolver cada una de ellas; entre claudátors se incluye una breve explicación de la orden.
SheetConnection sheetdat("z:\Documents/interval/datos.xls",0); [Lectura del archive de EXCEL] {int} tasca from SheetRead (sheetdat,"Tarea"); [Lectura de la columna de “Tarea”]
int nbprecedencias=first(nbprec); [Definición de la constante número de precedencias] {int} tcc from SheetRead(sheetdat, "TC"); [Lectura de la constante tiempo de ciclo “TC”] int TC=first(tcc); [Definición de la constante tiempo de ciclo]
{int} nbest from SheetRead(sheetdat, "nbestacio"); [Lectura del número máximo de estaciones “nbestacio”] int nbestacio=first(nbest); [Definición de la constante número máximo de estaciones de trabajo]
Esta programación la incluyen todos los modelos de nombre tipo: PRi j, con i∈[1,3] y j [1,4]∈
1.3.2 Programación común a modelos con sólo relaciones de incompatibilidad
En segundo lugar se recoge la programación realizada para la lectura de los datos que contienen los archivos “datos.xls”, y que el archivo “nombre.mod” del programa ILOG OPL necesita para la resolución de los casos.
Igual que ocurría anteriormente, esta parte de la programación es común a los dos tipos de maneras planteadas para resolver el problema, pero no sólo eso, sino que también es común a las cuatro formas de modelizar y resolver planteadas para cada uno de los tipos mencionados.
SheetConnection sheetdat("z:\Documents/interval/datos..xls",0); [Lectura del archive de EXCEL] {int} tasca from SheetRead (sheetdat,"Tarea"); [Lectura de la columna de “Tarea”]
int duracion[tasca] from SheetRead(sheetdat,"Duracion");[Lectura de la columna “Duracion”]
{int} nbinco from SheetRead(sheetdat, "nbincompatibilidades");[Lectura de la constante “nbincompatibilidades”] int nbincompatibilidades=first(nbinco); [Definición de la constante número de incompatibilidades]
{int} tcc from SheetRead(sheetdat, "TC"); [Lectura de la constante tiempo de ciclo “TC”] int TC=first(tcc); [Definición de la constante tiempo de ciclo]
{int} nbest from SheetRead(sheetdat, "nbestacio"); [Lectura del número máximo de estaciones “nbestacio”] int nbestacio=first(nbest); [Definición de la constante número máximo de estaciones de trabajo]
Esta programación la incluyen todos los modelos de nombre: I R , i [1, 2] y j [1,4]i j ∈ ∈
1.3.3 Programación común a los modelos con relaciones de precedencia e incompatibilidad
Destacar que esta parte de la programación que se expone a continuación es común a todos los modelos tipo PI (precedencia e incompatibilidad) planteados, pues lo único que hacen estas órdenes es leer los datos del archivo “datos.xls”, para poder resolver el problema de equilibrado
planteado.
i jRk
SheetConnection sheetdat("z:\Documents/interval/datos.xls",0); [Lectura del archive de EXCEL] {int} tasca from SheetRead (sheetdat,"Tarea"); [Lectura de la columna de “Tarea”]
int primera[tasca] from SheetRead(sheetdat,"Primera"); [Lectura de la columna “Primera”] int ultima[tasca] from SheetRead(sheetdat,"Ultima"); [Lectura de la columna “Ultima”] int duracion[tasca] from SheetRead(sheetdat,"Duracion"); [Lectura de la columna “Duracion”] {int} nbprec from SheetRead(sheetdat, "nbprecedencias"); [Lectura de la constante “nbprecedencias”] int nbprecedencias=first(nbprec); [Definición de la constante número de precedencias]
{int} nbinco from SheetRead(sheetdat, "nbincompatibilidades");[Lectura de la constante “nbincompatibilidades”] int nbincompatibilidades=first(nbinco); [Definición de la constante número de incompatibilidades]
{int} tcc from SheetRead(sheetdat, "TC"); [Lectura de la constante tiempo de ciclo “TC”] int TC=first(tcc); [Definición de la constante tiempo de ciclo]
{int} nbest from SheetRead(sheetdat, "nbestacio"); [Lectura del número máximo de estaciones “nbestacio”] int nbestacio=first(nbest); [Definición de la constante número máximo de estaciones de trabajo]
1.4 Declaración de variables
En este punto se va a proceder a la definición de las variables que intervienen en los diferentes modelos planteados. Destacar que se van a mostrar diferentes formas de modelización, y se determinará en cada caso cuando deben ser y han sido aplicadas cada una de ellas.
1.- Modelización pensando en programación matemática: Como se ha comentado a lo largo del
proyecto, el tipo de variables a usar son variables binarias del tipo x[i,j], que toma por valor 1 si
la tarea i es asignada a la estación de trabajo j, y 0 en caso contrario.
Lo que se hará es diferenciar los tres casos existentes, es decir, primero el caso de existir sólo relaciones de precedencia entre las tareas, en segundo lugar el caso de existir sólo relaciones de incompatibilidad entre las tareas, y en último lugar, los casos en los que existen relaciones de precedencia e incompatibilidad entre las tareas que forman la línea de montaje.
a) Sólo relaciones de precedencia: en este caso, la forma programar es:
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; [Define para cada tarea el intervalo de estaciones de trabajo al que puede ser asignado]
struct parella{ int tasc; int est;
}; [Definición del tipo parella que incluye a tarea y estación] {parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1; [Definición de las variables binarias]
var int Objetivo in 1..nbestacio; [Definición de la variable correspondiente al número de estaciones de trabajo necesarias]
El caso de los modelos P R merecen un punto y aparte, pues son muy parecidos al anterior,
pero sólo varía en que se incluye también la definición de variables enteras tipo R[i]:
3 1/ 2/ 3
var int R[tasca] in 1..nbestacio; [Definición de las variables enteras]
b) Sólo relaciones de incompatibilidad: en este caso, la forma de programar resulta la siguiente.
{int} epos[i in tasca]={es|es in estacio: es>=1 & es<=nbestacio}; [Define para cada tarea el intervalo de estaciones de trabajo al que puede ser asignado, que en este caso es todo, pues no existe grafo de precedencias]
struct parella{ int tasc; int est;
}; [Definición del tipo parella que incluye a tarea y estación]
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; [Definición del tipo parella que incluye a tarea y estación]
var int x[tepos] in 0..1; [Definición de las variables binarias]
var int Objetivo in 1..nbestacio; [Definición de la variable correspondiente al número de estaciones de trabajo necesarias]
Destacar que en este caso, el intervalo de estaciones al que puede ser asignado cada tarea va de 1
hasta el número máximo de estaciones de trabajo que puede haber en la línea, pues como se ha comentado, no existen relaciones de precedencia entre las tareas.
Esta programación es la que aparece en los modelos siguientes: I R1/ 2 1/ 2 / 3
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; [Define para cada tarea el intervalo de estaciones de trabajo al que puede ser asignado]
struct parella{ int tasc; int est;
}; [Definición del tipo parella que incluye a tarea y estación] {parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1; [Definición de las variables binarias]
var int Objetivo in 1..nbestacio; [Definición de la variable correspondiente al número de estaciones de trabajo necesarias]
Esta programación es la que incluyen los modelos P I R1/ 2 1/ 2 1/ 2
Los modelos P I contienen lo mismo, pero falta añadir la declaración de las variables
enteras R[i], de manera que sólo faltaría añadir la siguiente línea:
3 1/ 2 1/ 2R
var int R[tasca] in 1..nbestacio; [Definición de las variables enteras]
2.- Modelización pensando en programación lógica de restricciones: en este punto se muestra la
declaración de variables en el caso que se plantee la modelización del problema pensando en la programación lógica de restricciones. Se seguirá el mismo orden que antes, es decir, primero el caso de sólo haber relaciones de precedencia entre tareas, seguidamente el caso de existir sólo relaciones de incompatibilidad entre las tareas, y en tercer lugar el caso de existir relaciones de precedencia e incompatibilidad entre las tareas que conforman la línea de montaje.
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; [Define para cada tarea el intervalo de estaciones de trabajo al que puede ser asignado]
struct parella{ int tasc; int est;
}; [Definición del tipo parella que incluye a tarea y estación] {parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]};
var int Objetivo in 1..nbestacio; [Definición de la variable correspondiente al número de estaciones de trabajo necesarias]
var int R[tasca] in 1..nbestacio; [Definición de las variables enteras]
Cabe destacar que en este caso es posible llevar a cabo la reducción de variables mediante el programa mostrado anteriormente, pues existe un grafo de precedencias entre las tareas. Pero el problema resido en que se debe definir las variables enteras R[i] en todo el intervalo posible, pero
la información de en qué intervalo puede ser asignada cada tarea se añade mediante una pareja de restricciones, que son:
forall(i in tasca)
R[i]<=ultima[i]; R[i]>=primera[i];
b) Sólo relaciones de incompatibilidad: en el caso de existir sólo relaciones de incompatibilidad entre las tareas, resulta posible plantear la modelización para los dos tipos de formas planteados; por lo tanto, la parte de programación que se muestra a continuación es común a los programasI R . 1/ 2 4
{int} epos[i in tasca]={es|es in estacio: es>=1 & es<=nbestacio}; [Define para cada tarea el intervalo de estaciones de trabajo al que puede ser asignado, que en este caso es todo, pues no existe grafo de precedencias]
}; [Definición del tipo parella que incluye a tarea y estación]
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; [Definición del tipo parella que incluye a tarea y estación]
var int R[tasca] in 1..nbestacio; [Definición de las variables enteras]
var int Objetivo in 1..nbestacio; [Definición de la variable correspondiente al número de estaciones de trabajo necesarias]
Como ocurría en el caso de existir sólo relaciones de incompatibilidad modelizando pensando en programación matemática, en estos casos no es posible delimitar para cada tarea el intervalo de estaciones al que puede ser asignado, pues no existen relaciones de precedencia entre tareas.
c) Relaciones de precedencia e incompatibilidad: los únicos modelos planteados para la resolución de casos en los que existen relaciones de precedencia e incompatibilidad entre tareas, modelizando pensando en programación lógica de restricciones son los modelos denominadosPI R1 1/ 2 4. La programación que tienen en común es la siguiente:
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; [Define para cada tarea el intervalo de estaciones de trabajo al que puede ser asignado]
struct parella{ int tasc; int est;
}; [Definición del tipo parella que incluye a tarea y estación] {parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int R[tasca] in 1..nbestacio; [Definición de las variables enteras]
var int Objetivo in 1..nbestacio; [Definición de la variable correspondiente al número de estaciones de trabajo necesarias]
forall(i in tasca)
R[i]<=ultima[i]; R[i]>=primera[i];
1.5 Restricciones comunes a todos los modelos
En este punto se muestran aquellas restricciones que aparecen en todos lo modelos planteados; así, estas restricciones pueden variar un poco de un programa a otro, pero no mucho.
Las restricciones que se muestran a continuación hacen referencia a diferentes aspectos, así, estos son: delimitar el tiempo de ciclo en cada estación de trabajo, una tarea sólo puede ser asignada a una estación, que la variable “Objetivo” tiene una cota inferior que es el número mínimo de
estaciones de trabajo, que se calcula en todos los programas y calcular el valor de la variable “Objetivo”.
1.- Acotación de la variable “Objetivo”: la variable “Objetivo” presenta una cota inferior que es el
número mínimo de estaciones de trabajo, dicha cota se calcula como el cociente entre la suma total de las duraciones de todas las tareas y el tiempo de ciclo, destacar que si el número que se obtiene no es entero, se toma como valor de la cota el entero inmediatamente superior.
Esta restricción o acotación del valor de la variable se escribe de igual forma en todos los modelos planteados:
Objetivo>=min_objetivo;
2.- Cálculo del valor de la variable “Objetivo”: otra restricción que presentan todos los programas
es la que realiza el cálculo del valor de la variable “Objetivo”, que es el número mínimo de
El cálculo del valor de la variable “Objetivo” se ha planteado de tres posibles formas, según cuál era el modelo, así, a continuación se exponen estas tres diferentes expresiones, y en qué modelos aparecen cada una:
- O1: P1/ 2 / 3 1/ 2R −I R1/ 2 1/ 2−P1/ 2 / 3 1/ 2 1/ 2I R
forall(i in tasca) forall(j in epos[i])
Objetivo>=j*x[<i,j>];
- O2: P1/ 2 / 3 3R −I R1/ 2 3
Objetivo=max(i in tasca, j in epos[i])j*x[<i,j>];
- O3:PR1 4 −I R1/ 2 4−PI R1 1/ 2 4
Objetivo=max(i in tasca)R[i];
Recordar que “epos[i]” es el intervalo de estaciones de trabajo al que puede ser asignada la tarea i,
y viene definido por el procedimiento de reducción de variables aplicado (apartado 1.1 del Anexo).
En todos los casos se calcula la estación mayor a la que se ha asignado una tarea, que es el valor que se desea minimizar.
3.- Limitación del tiempo de ciclo en cada estación de trabajo: en toda estación de trabajo, la suma de las duraciones de las tareas que se le asignan no puede ser mayor que el tiempo de ciclo; así, para conseguir que esto se cumpla se introduce una restricción en el programa.
- En los modelos P R esta restricción se escribe de la siguiente forma:
forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC;
En este punto cabe destacar que se planteó la posibilidad de escribir la restricción de la siguiente forma:
forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>]=1)*duracion[i]<=TC;
, pero se comprobó a simple vista y con ejemplos sencillos que se tardaba mucho en alcanzar la solución óptima; así, se escribió un “e-mail” de consulta a la empresa ILOG, y la respuesta que se
dio fue que no era recomendable escribir la igualdad dentro de la restricción pues ésta empeoraba el comportamiento del programa.
- En los modelos , es decir, todos aquellos en que la modelización se realiza pensando en programación lógica de restricciones, y se resuelven los casos mediante SOLVER, la forma de escribir esta restricción es:
1 4 1/ 2 4 1 1/ 2 4
PR −I R −PI R
forall(j in estacio)
sum(i in tasca)(R[i]=j)*duracion[i]<=TC;
Destacar que resulta ahora necesario escribir “R[i]=j” pues sólo se debe sumar para cada estación
de trabajo las duraciones de las tareas que han sido asignadas a ella, y la única forma de saber a qué estación se ha asignado una tarea es consultando el valor de la variable R[i].
4.- Cada tarea sólo puede asignarse a una estación: como se ha comentado cuando se planteaba el tipo de problema que se resolvería (memoria, punto 4), una tarea no puede realizarse entre dos o más estaciones, sino que deben ser realizadas en una sola estación. Por ello, resulta necesario introducir esta información en el programa cuando se han declarado variables binarias x[i,j]; la
forma de escribir esta restricción es:
forall(i in tasca)
sum(j in epos[i])x[<i,j>]=1;
Destacar que esta restricción aparece en todos los modelos excepto en los siguientes:
; que son los únicos en los que no se declaran variables binarias x[i,j].
1 4 1/ 2 4 1 1/ 2 4
PR −I R −PI R
1.6 Modelización de las relaciones de precedencia e incompatibilidad
En este punto se exponen las formas de modelización de las diferentes formas de resolución de las relaciones de precedencia e incompatibilidad entre tareas.
Para ello, se expondrá primero los modelos en los que sólo existen relaciones de precedencia entre las tareas, seguidamente los modelos en los que sólo existen relaciones de incompatibilidad entre tarea, y finalmente los modelos que resuelven casos en los que existen relaciones de precedencia e incompatibilidad entre las tareas que conforman la línea, y por lo tanto, resultan de combinar los dos anteriores.
1.6.1 Sólo relaciones de precedencia
Como se ha comentado, se han planteado tres maneras diferentes de modelizar las relaciones de precedencia entre tareas, y a la vez, cada una de estas formas se ha intentado modelizar y resolver de diferentes maneras.
A continuación se expone cada una de las formas planteadas:
1.- Tipo 1: de este tipo han surgido cuatro modelos , y la forma de escribir la restricción que permite cumplir las relaciones de precedencia son las siguientes:
3 1/ 2/ 3
P R
forall(i in precedencias)
sum(j in epos[parejas2[i,2]])j*x[<parejas2[i,2],j>]>=sum(j in epos[parejas2[i,1]])j*x[<parejas2[i,1],j>];
Para toda pareja de tareas entre las que existe una relación de precedencia (“i-k”), el número de
estación a la que se asigne la tarea k debe ser igual o mayor que a la que se asigne la tarea i.
- : en este caso se modeliza pensando en programación lógica de restricciones, y se resuelve con SOLVER. La forma de escribir la restricción es:
1 4
PR
forall(i in precedencias)
R[parejas2[i,1]]<=R[parejas2[i,2]];
2.- Tipo 2: de esta segunda forma de modelizar la restricción que permite el cumplimiento de las
relaciones de precedencia sólo se ha podido modelizar pensando en programación matemática.
Así, para los tres modelos,P R2 1/ 2 / 3, la restricción se escribe:
forall(i in precedencias)
forall(j in epos[parejas2[i,2]])
x[<parejas2[i,2],j>]<=sum(l in 1..j:l in epos[parejas2[i,1]])x[<parejas2[i,1],l>];
, en este caso, lo que se hace es que para toda pareja de tareas entre las que existe una relación de precedencia (“i-k”), para todo valor de j que esté en el intervalo de estaciones al que se puede
asignar la tarea k, se comprueba la expresión escrita.
3.- Tipo 3: igual que ocurría en el caso anterior, esta tercera forma no es posible de modelizar
Esta forma de representar la relación de precedencia en la forma tipo 3 es la siguiente:
forall(i in precedencias)
R[parejas2[i,1]]<=R[parejas2[i,2]];
Recordar que era en este tercer tipo en el cual se declaraban variables binarias x[i,j] y variables
enteras R[i] a la vez; así, lo que dice la restricción expuesta es que para toda pareja de tareas
entre las que exista una relación de precedencia (“i-k”), la estación a la que se asigne la tarea i
debe ser, en número, igual o menor que el número de la estación de trabajo a la que se asigne la tarea k.
1.6.2 Sólo relaciones de incompatibilidad
Como se ha visto, se han planteado dos formas diferentes de representar la restricción que permite la resolución de casos en los que existen relaciones de incompatibilidad entre las tareas; además, resulta interesante destacar que en ambos casos se han podido plantear cuatro casos, de manera que se han obtenido ocho programas.
A continuación vamos a ver cada una de las formas planteadas:
1.- Tipo 1: de esta primera forma de plantear la restricción de incompatibilidad entre tareas se han
extraído cuatro programas.
- : la forma de representar la restricción que permite la resolución de las relaciones de incompatibilidad es la siguiente:
1 1/ 2 / 3
I R
forall(i in incompatibilidades)
sum(j in epos[parejas[i,2]])j*x[<parejas[i,2],j>]>=sum(t in epos[parejas[i,1]])t*x[<parejas[i,1],t>]+1-(nbestacio+1)*S[i];
forall(i in incompatibilidades)
sum(j in epos[parejas[i,2]])j*x[<parejas[i,2],j>]<=sum(t in epos[parejas[i,1]])t*x[<parejas[i,1],t>]-1+nbestacio*(1-S[i]);
Destacar que para modelizar pensando en programación matemática se ha tenido que sustituir el signo ≠; así, se ha sustituido este signo por dos restricciones, pero sólo se cumple una de las dos,
no ambas a la vez, y esto se consigue mediante declarando una variable binaria S[i] para cada
pareja de tareas entre las que existe una relación de incompatibilidad.
En la memoria ya se ha expuesto el comportamiento de esta restricción, según el valor que tomara la variable binaria S[i] (apartado 5.2 de la Memoria).
- I R : en este caso, la escritura de la restricción resulta más sencilla al permitir usar el signo ≠, 1 4
así, la expresión utilizada es la siguiente:
forall(i in incompatibilidades)
R[parejas[i,2]]<>R[parejas[i,1]];
Lo que dice esta expresión es que si entre dos tareas existe una relación de incompatibilidad, la estación a la que se asigna cada una debe ser diferente.
2.- Tipo 2: de esta segunda manera de plantear la restricción de incompatibilidad entre tareas se
han extraído cuatro modelos.
- : la forma de representar la restricción que permite la resolución de las relaciones de incompatibilidad es la siguiente:
2 1/ 2 / 3
I R
forall(i in incompatibilidades)
; que permite asegurar que si entre dos tareas existe una relación de incompatibilidad, no serán asignadas a la misma estación de trabajo.
- I R : este es el modelo en el cual se modeliza en programación lógica de restricciones y se 2 4
resuelve mediante SOLVER; la expresión es la siguiente:
forall(i in incompatibilidades)
abs(R[parejas[i,2]]-R[parejas[i,1]])>=1;
; que permite asignar tareas entre las cuales existe una relación de incompatibilidad a estaciones de trabajo distintas.
1.6.3 Relaciones de precedencia e incompatibilidad
Por último, se ha procedido a la resolución de casos de equilibrado de líneas de montaje en los que existen relaciones de precedencia e incompatibilidad entre las tareas que conforman la línea.
Por lo tanto, en este punto se repetirán las expresiones anteriormente expuestas. Lo único que es necesario saber es qué modelizaciones incluye cada archivo del tipo , que son los modelos desarrollados para la resolución de los problemas en los que existen relaciones de precedencia e incompatibilidad entre las tareas que conforman la línea de montaje.
i j k PI R
Así, para averiguarlo se muestra aquí una tabla qué muestra a partir de qué archivos se obtienen cada uno de los modelos:
2 P I2 R1 P2 I2 R1 3 P I1 R1 P3 I1R1 3 P I2 R1 P3 I2 R1 1 P I1 R2 P1 I1R2 1 P I2 R2 P1 I2 R2 2 P I1 R2 P2 I1 R2 2 P I2 R2 P2 I2 R2 3 P I1 R2 P3 I1 R2 3 P I2 R2 P3 I2 R2 1 P I1 R4 P1 I1R4 1 P I2 R4 P1 I2 R4
2.- MODELOS PARA LA RESOLUCIÓN DE PROBLEMAS CON RELACIONES DE PRECEDENCIA
En este segundo punto se exponen los modelos planteados para la resolución de casos de equilibrado de líneas de montaje en las cuales sólo existen relaciones de precedencia entre las tareas que la conforman.
Recordar, que se han presentado tres formas diferentes de representar las relaciones de precedencia entre tareas, y sólo la primera de ellas ha sido planteada de cuatro variantes diferentes, las otras dos sólo se han podido modelizar pensando en programación matemática.
2.1 Precedencias tipo 1
A continuación se muestran los diferentes modelos planteados empleando este primer tipo de precedencia:
- : estos dos modelos se han modelizado en programación matemática, pero sólo el primero se ha resuelto con CPLEX; en el caso de , se ha impuesto que el método usado en la
resolución fuera SOLVER. 1 1/ 2
PR
1 2
PR
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int primera[tasca] from SheetRead(sheetdat,"Primera"); int ultima[tasca] from SheetRead(sheetdat,"Ultima"); int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbprec from SheetRead(sheetdat, "nbprecedencias"); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
int nbestacio=first(nbest); {int} estacio=1..nbestacio;
range precedencias 1..nbprecedencias; range lasparejas 1..2;
int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,"Precedencias"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1;
forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in precedencias) sum(j in epos[parejas2[i,2]])j*x[<parejas2[i,2],j>]>=sum(j in epos[parejas2[i,1]])j*x[<parejas2[i,1],j>]; forall(i in tasca) forall(j in epos[i]) Objetivo>=j*x[<i,j>]; Objetivo>=min_objetivo; };
- : el modelo es muy parecido al anterior, sólo varía el cálculo del valor de la variable “Objetivo”.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int primera[tasca] from SheetRead(sheetdat,"Primera"); int ultima[tasca] from SheetRead(sheetdat,"Ultima"); int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbprec from SheetRead(sheetdat, "nbprecedencias"); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
1 3
range precedencias 1..nbprecedencias; range lasparejas 1..2;
int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,"Precedencias"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]};
var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; minimize Objetivo subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in precedencias)
sum(j in epos[parejas2[i,2]])j*x[<parejas2[i,2],j>]>=sum(j in epos[parejas2[i,1]])j*x[<parejas2[i,1],j>];
Objetivo=max(i in tasca, j in epos[i])j*x[<i,j>];
Objetivo>=min_objetivo; };
- : en este último caso se modeliza en programación lógica de restricciones, y se resuelve con SOLVER.
SheetConnection sheetdat("z:\datos.xls”,0); {int} tasca from SheetRead (sheetdat,”Tarea”);
int primera[tasca] from SheetRead(sheetdat,”Primera”); int ultima[tasca] from SheetRead(sheetdat,”Ultima”); int duracion[tasca] from SheetRead(sheetdat,”Duracion”); {int} nbprec from SheetRead(sheetdat, “nbprecedencias”); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, “TC”); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, “nbestacio”); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range precedencias 1..nbprecedencias; range lasparejas 1..2;
int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,”Precedencias”); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
1 4
var int Objetivo in 1..nbestacio; var int R[tasca] in 1..nbestacio; minimize Objetivo subject to{ forall(i in tasca) R[i]<=ultima[i]; forall(i in tasca) R[i]>=primera[i]; forall(j in estacio) sum(i in tasca)(R[i]=j)*duracion[i]<=TC; forall(i in precedencias) R[parejas2[i,1]]<=R[parejas2[i,2]]; Objetivo=max(i in tasca)R[i]; Objetivo>=min_objetivo; }; 2.2 Precedencias tipo 2
- : al igual que antes, la escritura del modelo es igual en ambos casos, la única variación es que en el modelo se escoge el método de resolución SOLVER.
2 1/ 2
P R
2 2
P R
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int primera[tasca] from SheetRead(sheetdat,"Primera"); int ultima[tasca] from SheetRead(sheetdat,"Ultima"); int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbprec from SheetRead(sheetdat, "nbprecedencias"); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range precedencias 1..nbprecedencias; range lasparejas 1..2;
int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,"Precedencias"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; struct parella{
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; minimize Objetivo subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in precedencias) forall(j in epos[parejas2[i,2]]) x[<parejas2[i,2],j>]<=sum(l in 1..j:l in epos[parejas2[i,1]])x[<parejas2[i,1],l>]; forall(i in tasca) forall(j in epos[i]) Objetivo>=j*x[<i,j>]; Objetivo>=min_objetivo; };
- : la única diferencia con el caso anterior, reside en la variación de la fórmula de cálculo de la variable “Objetivo”, que provoca que el programa utilice SOLVER en el proceso de resolución;
pero igualmente se modeliza en programación matemática. 2 3
P R
int primera[tasca] from SheetRead(sheetdat,"Primera"); int ultima[tasca] from SheetRead(sheetdat,"Ultima"); int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbprec from SheetRead(sheetdat, "nbprecedencias"); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range precedencias 1..nbprecedencias; range lasparejas 1..2;
int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,"Precedencias"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1;
Objetivo subject to{ forall(i in tasca)
sum(j in epos[i])x[<i,j>]=1;
forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in precedencias)
forall(j in epos[parejas2[i,2]])
x[<parejas2[i,2],j>]<=sum(l in 1..j:l in epos[parejas2[i,1]])x[<parejas2[i,1],l>];
Objetivo=max(i in tasca, j in epos[i])j*x[<i,j>]; Objetivo>=min_objetivo;
};
2.3 Precedencias tipo 3
En tercer lugar se exponen los modelos planteados para la resolución de casos en los que existen sólo relaciones de precedencias entre las tareas, aplicando la segunda forma de representación de precedencias.
- : al igual que antes, la escritura del modelo es igual en ambos casos, la única variación es que en el modelo se escoge el método de resolución SOLVER.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int primera[tasca] from SheetRead(sheetdat,"Primera"); int ultima[tasca] from SheetRead(sheetdat,"Ultima");
3 1/ 2
P R
3 2
int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbprec from SheetRead(sheetdat, "nbprecedencias"); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range precedencias 1..nbprecedencias; range lasparejas 1..2;
int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,"Precedencias"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]};
var int x[tepos] in 0..1;
minimize Objetivo subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(i in tasca) R[i]=sum(j in epos[i])j*x[<i,j>]; forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in precedencias) R[parejas2[i,1]]<=R[parejas2[i,2]]; forall(i in tasca) forall(j in epos[i]) Objetivo>=j*x[<i,j>]; Objetivo>=min_objetivo; };
- : en este caso, el modelo es muy parecido al anterior, pues en ambos se modeliza en programación matemática, sólo varía el cálculo de la variable “Objetivo”; de manera que el se
utiliza, en este caso, el SOLVER en la resolución.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int primera[tasca] from SheetRead(sheetdat,"Primera"); int ultima[tasca] from SheetRead(sheetdat,"Ultima"); int duracion[tasca] from SheetRead(sheetdat,"Duracion");
3 3
{int} nbprec from SheetRead(sheetdat, "nbprecedencias"); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range precedencias 1..nbprecedencias; range lasparejas 1..2;
int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,"Precedencias"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; var int R[tasca] in 1..nbestacio; minimize
subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(i in tasca) R[i]=sum(j in epos[i])j*x[<i,j>]; forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in precedencias)
R[parejas2[i,1]]<=R[parejas2[i,2]];
Objetivo=max(i in tasca, j in epos[i])j*x[<i,j>]; Objetivo>=min_objetivo;
3.- MODELOS PARA LA RESOLUCIÓN DE CASOS CON RELACIONES DE INCOMPATIBILIDAD
A continuación se exponen los modelos planteados para la resolución de casos de equilibrado de líneas de montaje en las cuales sólo existen relaciones de incompatibilidad entre las tareas que la conforman.
Recordar, que se han presentado dos formas diferentes de representar las relaciones de incompatibilidad entre tareas, y cada una de ellas ha sido planteada según las cuatro variantes de modelización y resolución expuestas..
3.1 Incompatibilidades tipo 1
A continuación se muestran los diferentes modelos utilizados basándose en este primer tipo de incompatibilidad.
-I R : el modelo es el mismo en ambos, sólo varía el hecho que en 1 1/ 2 I R se resuelve usando 1 2
SOLVER, debido a que es la opción que se selecciona.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbinco from SheetRead(sheetdat, "nbincompatibilidades"); int nbincompatibilidades=first(nbinco);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
range incompatibilidades 1..nbincompatibilidades; range lasparejas 1..2;
int parejas[incompatibilidades, lasparejas] fromSheetRead(sheetdat,"Incompatibilidades"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=1 & es<=nbestacio}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int S[incompatibilidades] in 0..1;
var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; minimize Objetivo subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(j in estacio)
forall(i in incompatibilidades)
sum(j in epos[parejas[i,2]])j*x[<parejas[i,2],j>]>=sum(t in epos[parejas[i,1]])t*x[<parejas[i,1],t>]+1-(nbestacio+1)*S[i];
forall(i in incompatibilidades)
sum(j in epos[parejas[i,2]])j*x[<parejas[i,2],j>]<=sum(t in epos[parejas[i,1]])t*x[<parejas[i,1],t>]-1+nbestacio*(1-S[i]); forall(i in tasca) forall(j in epos[i]) Objetivo>=j*x[<i,j>]; Objetivo>=min_objetivo; };
- I R : es igual que el caso anterior, sólo varía la expresión de cálculo del valor de la variable 1 3
“Objetivo”, que deja de ser una expresión lineal; de manera que el programa debe usar SOLVER
en la resolución.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbinco from SheetRead(sheetdat, "nbincompatibilidades"); int nbincompatibilidades=first(nbinco);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range lasparejas 1..2;
int parejas[incompatibilidades, lasparejas] from SheetRead(sheetdat,"Incompatibilidades"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=1 & es<=nbestacio}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int S[incompatibilidades] in 0..1;
var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; minimize Objetivo subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in incompatibilidades)
forall(i in incompatibilidades)
sum(j in epos[parejas[i,2]])j*x[<parejas[i,2],j>]<=sum(t in epos[parejas[i,1]])t*x[<parejas[i,1],t>]-1+nbestacio*(1-S[i]);
Objetivo=max(i in tasca, j in epos[i])j*x[<i,j>];
Objetivo>=min_objetivo; };
-I R : en este caso, la modelización se realiza en programación lógica de restricciones, y se 1 4
resuelve usando SOLVER.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbinco from SheetRead(sheetdat, "nbincompatibilidades"); int nbincompatibilidades=first(nbinco);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range incompatibilidades 1..nbincompatibilidades; range lasparejas 1..2;
int min_objetivo=abs(duracion_total/TC); var int Objetivo in 1..nbestacio;
var int R[tasca] in 1..nbestacio;
minimize Objetivo subject to{ forall(j in estacio) sum(i in tasca)(R[i]=j)*duracion[i]<=TC; forall(i in incompatibilidades) R[parejas[i,2]]<>R[parejas[i,1]]; Objetivo=max(i in tasca)R[i]; Objetivo>=min_objetivo; }; 3.2 Incompatibilidades tipo 2
A continuación se muestran los diferentes modelos que se han planteado basados en el segundo tipo de incompatibilidad.
-I R : la escritura del modelo es igual, sólo varía el hecho que utilizar 2 1/ 2 I R , se selecciona 2 2
SOLVER para su resolución.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int nbincompatibilidades=first(nbinco); {int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range incompatibilidades 1..nbincompatibilidades; range lasparejas 1..2;
int parejas[incompatibilidades, lasparejas] from SheetRead(sheetdat,"Incompatibilidades"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=1 & es<=nbestacio}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; minimize
Objetivo
subject to{
sum(j in epos[i])x[<i,j>]=1;
forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC; forall(i in incompatibilidades)
forall(j in estacio:j in epos[parejas[i,1]] & j in epos[parejas[i,2]])
x[<parejas[i,1],j>]+x[<parejas[i,2],j>]<=1; forall(i in tasca) forall(j in epos[i]) Objetivo>=j*x[<i,j>]; Objetivo>=min_objetivo; };
-I R : es igual que el caso anterior, sólo varía una expresión del final, en la que se calcula el 2 3
valor de la variable “Objetivo”.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbinco from SheetRead(sheetdat, "nbincompatibilidades"); int nbincompatibilidades=first(nbinco);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
range incompatibilidades 1..nbincompatibilidades; range lasparejas 1..2;
int parejas[incompatibilidades, lasparejas] from SheetRead(sheetdat,"Incompatibilidades"); int duracion_total=sum(i in tasca)duracion[i];
int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=1 & es<=nbestacio}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; minimize Objetivo subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(j in estacio)
sum(i in tasca: j in epos[i])(x[<i,j>])*duracion[i]<=TC;
forall(i in incompatibilidades)
x[<parejas[i,1],j>]+x[<parejas[i,2],j>]<=1; Objetivo=max(i in tasca, j in epos[i])j*x[<i,j>];
Objetivo>=min_objetivo; };
- I R : en este caso, al contrario que los anteriores, se modeliza en programación lógica de 2 4
restricciones y se resuelve con SOLVER.
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbinco from SheetRead(sheetdat, "nbincompatibilidades"); int nbincompatibilidades=first(nbinco);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
{int} estacio=1..nbestacio;
range incompatibilidades 1..nbincompatibilidades; range lasparejas 1..2;
int parejas[incompatibilidades, lasparejas] from SheetRead(sheetdat,"Incompatibilidades"); int duracion_total=sum(i in tasca)duracion[i];
4.- MODELOS PARA LA RESOLUCIÓN DE PROBLEMAS CON RELACIONES DE PRECEDENCIA E INCOMPATIBILIDAD
Seguidamente se exponen los modelos planteados para la resolución de problemas de equilibrado de líneas de montaje en las que existen relaciones de precedencia e incompatibilidad entre las tareas que la conforman.
Como ya se ha comentado, estos modelos son resultado de la combinación de los anteriormente expuestos.
a) : estos dos modelos son iguales, la única variación es que PI usa CPLEX
como método de resolución mientras que usa SOLVER.
1 1 1/ 2
PI R 1 1 1R
1 1 2
PI R
SheetConnection sheetdat("z:\datos.xls",0); {int} tasca from SheetRead (sheetdat,"Tarea");
int primera[tasca] from SheetRead(sheetdat,"Primera"); int ultima[tasca] from SheetRead(sheetdat,"Ultima"); int duracion[tasca] from SheetRead(sheetdat,"Duracion"); {int} nbinco from SheetRead(sheetdat, "nbincompatibilidades"); int nbincompatibilidades=first(nbinco);
{int} nbprec from SheetRead(sheetdat, "nbprecedencias"); int nbprecedencias=first(nbprec);
{int} tcc from SheetRead(sheetdat, "TC"); int TC=first(tcc);
{int} nbest from SheetRead(sheetdat, "nbestacio"); int nbestacio=first(nbest);
range incompatibilidades 1..nbincompatibilidades; range precedencias 1..nbprecedencias;
range lasparejas 1..2;
int parejas[incompatibilidades, lasparejas] from SheetRead(sheetdat, "Incompatibilidades"); int parejas2[precedencias, lasparejas] from SheetRead(sheetdat,"Precedencias");
int duracion_total=sum(i in tasca)duracion[i]; int min_objetivo=abs(duracion_total/TC);
{int} epos[i in tasca]={es|es in estacio: es>=primera[i] & es<=ultima[i]}; struct parella{
int tasc; int est; };
{parella} tepos={<tasc,est>|tasc in tasca & est in epos[tasc]}; var int S[incompatibilidades] in 0..1;
var int x[tepos] in 0..1;
var int Objetivo in 1..nbestacio; minimize Objetivo subject to{ forall(i in tasca) sum(j in epos[i])x[<i,j>]=1; forall(j in estacio)