En la sesión 5, usted encontró un modelo para una compañía de producción que usa multiples plantas para producir sus productos. Lo que usted debe notar acerca del modelo es que mientras que cada una de las diferentes plantas puede ser usada para producir individualmente, todas las ventas e inventarios fueron manejadas colectivamente, desde un origen común para toda la compañía. Ahorá ampliará ese modelo para permitir que cada planta venda los productos y mantenga inventario individualmente. Además, para satisfacer la demanda en la manera más eficiente, los productos pueden ser embarcados entre las plantas, según se necesite.
Para ampliar el modelo usted necesitará incluir dos nuevos índices ficticios, fromplant, y toplant, los cuales representan las plantas origen y las plantas destino. También actualizará las variables Inventory y Sales para incluirlas en el índice plant, así como cada planta puede ahora vender los productos y mantener inventario cada uno independientemente del otro.
Los Modelos que permiten embarques entre localizaciones son algunas veces llamados Modelos de Distribución o de Transporte. Normalmente, en modelos de transporte, usted tiene orígenes con cierta disponibilidad, destinos con ciertos requerimientos, y usted necesita embarcar los productos desde esos orígenes a esos destinos. En algunos casos, usted tiene modelos de transporte con multiples niveles. Por ejemplo, pueden haber embarques desde las plantas hacia los depósitos, y desde esos depósitos a las tiendas de ventas al por menor.
Otro grupo de modelos de distribución son los modelos de transbordo. Esos modelos típicamente surgen cuando usted tiene múltiples localizaciones, donde se producen bienes y también son centros de demanda. Puesto que no hay orígenes ni destinos específicos, usted puede embarcar desde una localización a cualquier otra localización.
Los índices Ficticios son muy usados cuando usted necesita definir un vector, el cual usa el mismo índice más de una vez, como un subíndice. Cuando se embarcan los productos entre plantas usted necesita crear una variable vector, que representa cuánto embarcar entre las plantas. Puesto que las plantas orígen y plantas destino vienen del mismo conjunto de plantas, usted necesita dos índices ficticios para las plantas. El primer índice ficticio es requerido para representar las plantas orígen, y el segundo índice ficticio es requerido para representar las plantas destino.
Algunas veces, cuando se trabaja con variables vector multidimensionales encontrará casos donde no todos los elementos del vector son válidos, o tienen algún significado. Por ejemplo, en los modelos de transbordo tendría poco sentido embarcar un producto de cierta parte de la planta a esa misma planta. En esos casos, se usa la condición WHERE(Dónde) sobre la variable para eliminar elementos innecesarios.
Por ejemplo, en un modelo de transbordo puede eliminar la posibilidad de embarcar a la misma localización, definiendo la variable comos sigue:
VARIABLES Ship[fromplant,toplant] WHERE (fromplant <> toplant);
En este caso, la condición (fromplant <> toplant) elimina todos los elementos del vector, donde la planta orígen es la misma que la planta destino.
En algunos casos, hay elementos que necesitan ser excluídos cuando no están basados sobre los valores de los índices. Ellos deben estar basados en algún vector de datos en el modelo. Normalmente, se tiene un vector costo conteniendo cuánto es el costo de embarcar entre las plantas. Para esas plantas, sí el embarque entre ellas no es factible, puede ingresar un valor especial para el costo, tal como cero, para usarlo como un identificador. Luego, en la definición de la variable, puede usar este vector de datos para excluír las rutas de embarque que no son factibles, como sigue:
VARIABLES Ship[fromplant,toplant] WHERE (ShipCost[fromplant,toplant] > 0);
Cuando se trabaja con modelo de transbordo, necesita asegurarse que la cantidad de productos embarcados a una planta, más cuánto es producido y cuánto es retirado del inventario, debe ser igual a cuánto es embarcado desde la planta, más cuánto es vendido y cuánto ha quedado en inventario. En pocas palabras, cada cosa que llega a la planta debe ser igual a lo que sale de la planta. Esta clase de restricciones es normalmente llamada Restricciones de Equilibrio de plantas . A continuación vemos un ejemplo simple de una restricción de equilibrio de planta:
PlantBal[plant, product, month]: Produce + Inventory[month-1] + SUM(fromplant: Ship[fromplant, toplant:=plant]) = Sales + Inventory + SUM(toplant: Ship[fromplant:=plant, toplant]);
Notará que esta restricción es similar a la restricción de equilibrio de inventario que usted encontró en sesiones anteriores. La única diferencia es que ahora tiene que tomar en cuenta que estamos embarcando a y desde cada una de las plantas ingresando una suma sobre cada una de las plantas para la variable Ship(Embarque).
La asignación del índice 'toplant:=plant', en la primera suma, nos permite especificar que el subíndice toplant debe tomar el valor del subíndice plant para la restricción PlantBal. Esta suma adiciona todos los embarques de cada una de las plantas a una planta en particular en esa restricción. De manera similar, la asignación del índice 'fromplant:=plant', en la segunda suma, especifica que el subíndice fromplant debe tomar el valor del subíndice plant.
En esta sesión, un nuevo modelo será creado donde cada planta actúa ahora separadamente como un centro de demanda para esos productos, y puede también mantener inventario. Usará el modelo creado en la Sesión 5, y haga las adiciones necesarias y luego actualicelo.
Puesto que cada planta puede vender los productos, ahora tenemos una demanda diferente para cada planta, así como también para cada producto y cada mes. La demanda está dada en la siguiente tabla:
Plant | Product | Jan | Feb | Mar | Apr |
---|---|---|---|---|---|
p1 | A1 | 4300 | 4200 | 6400 | 5300 |
A2 | 4500 | 5400 | 6500 | 7200 | |
A3 | 5400 | 6700 | 7800 | 8200 | |
p2 | A1 | 5100 | 6200 | 5400 | 7600 |
A2 | 6300 | 7100 | 5200 | 6300 | |
A3 | 4800 | 6500 | 5000 | 7200 | |
p3 | A1 | 4100 | 6100 | 4700 | 5800 |
A2 | 5300 | 5200 | 5700 | 4100 | |
A3 | 4200 | 4100 | 5200 | 6300 | |
p4 | A1 | 4300 | 4100 | 5300 | 4500 |
A2 | 5300 | 6400 | 4200 | 6200 | |
A3 | 5600 | 5200 | 3800 | 4100 |
Estos datos tienen tres dimensiones, plantas, productos y meses. En modelos de programación Lineal es muy típico tener datos con dimensiones múltiples, posiblemente hasta ocho o más. En la próxima sesión, actualizaremos los datos de la demanda para incluir una dimensión más; máquinas, creando así un vector de cuatro dimensiones.
La capacidad del inventario es ahora diferente para cada planta. Tenemos cuatro valores para la capacidad del inventario, uno para cada planta, 800, 400, 500, y 400 respectivamente.
Puesto que ahora tenemos múltiples plantas, cada una de las cuales puede mantener su inventario, ahora tenemos diferentes costos de inventario para cada planta y cada producto. Los nuevos valores de costos para el inventario se muestran a continuación:
Inventory Cost | A1 | A2 | A3 |
---|---|---|---|
p1 | $8.50 | $7.00 | $6.50 |
p2 | $9.80 | $9.80 | $9.80 |
p3 | $7.50 | $7.50 | $7.50 |
p4 | $9.30 | $8.00 | $6.50 |
Finalmente, puesto que estamos permitiendo embarques entre las plantas, hay ciertos costos ivolucrados para embarcar un producto, como se muestra en la tabla que sigue:
Shipping Cost | p1 | p2 | p3 | p4 |
---|---|---|---|---|
p1 | - | $15.00 | $21.00 | $13.00 |
p2 | $16.00 | - | $12.00 | $12.00 |
p3 | $14.00 | $17.00 | - | $15.00 |
p4 | $21.00 | $13.00 | $10.00 | - |
Como usted puede ver, no hay valores en la tabla donde la planta orígen es la misma que la planta destino. Esto es porque no hay ningún beneficio embarcar a la misma planta.
El listado de abajo es la formulación del modelo completo para Planning6. Las adiciones al modelo están resaltadas en negrita para hacer más fácil que se vean los cambios respecto al modelo de la Sesión 5.
TITLE Production_Planning6; INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); plant := (p1, p2, p3, p4); fromplant := plant; toplant := plant; DATA Price[product] := (120.00, 100.00, 115.00); Demand[plant, product, month] := DATAFILE("Demand6.dat"); ProdCost[plant, product] := DATAFILE("ProdCost.dat"); ProdRate[plant, product] := DATAFILE("ProdRate.dat"); ProdDaysAvail[month] := (23, 20, 23, 22); InvtCost[plant, product] := DATAFILE("InvtCost.dat"); InvtCapacity[plant] := (800, 400, 500, 400); ShipCost[fromplant, toplant] := DATAFILE ("ShipCost.dat"); VARIABLES Produce[plant, product,month] -> Prod; Inventory[plant, product, month] -> Invt; Sales[plant,product, month] -> Sale; Ship[product, month, fromplant, toplant] WHERE (fromplant <> toplant); MACROS TotalRevenue := SUM(plant, product,month: Price * Sales); TotalProdCost := SUM(plant, product, month: ProdCost* Produce); TotalInvtCost := SUM(plant, product, month: InvtCost * Inventory); TotalShipCost := SUM(product, month, fromplant,toplant: ShipCost * Ship); TotalCost := TotalProdCost + TotalInvtCost + TotalShipCost; MODEL MAX Profit= TotalRevenue - TotalCost; SUBJECT TO ProdCapacity[plant, month] -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail; PlantBal[plant, product, month] -> PBal: Produce + Inventory[month-1] + SUM(fromplant:Ship[fromplant, toplant:=plant]) = Sales + Inventory + SUM(toplant: Ship[fromplant:=plant,toplant]); MaxInventory[plant, month] -> MaxI: SUM(product: Inventory) <= InvtCapacity; BOUNDS Sales < Demand ; END
Inicie la aplicación MPL.
Elija File(Archivo) | Open (Abrir) y abra el modelo de la sesión anterior Planning5.mpl.
Elija File(Archivo) | Save As(Guardar Como) para guardarlo como un nuevo archivo del modelo Planning6.mpl.
Cambie el Título al modelo para que refleje que se está trabajando con el modelo Planning6:
TITLE Production_Planning6;
En esta sesión, expandiremos el modelo para permitir embarques entre las plantas. Los Alias indexes (índices ficticios) son usados cuando se necesita definir un vector, el cual se refiere al mismo índice más de una vez, como un subíndice. Los índices ficticios son una copia exacta del índice definido previamente.
En este caso, está creando una variable vector representando cuánto embarcar entre las plantas. Lo que significa que necesita dos índices ficticios para representar las plantas orígen y las plantas destino. Agregue las siguientes definiciones para los nuevos índices ficticios que se llamarán fromplanty toplant al final de la sección INDEX:
INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); plant := (p1, p2,p3, p4); fromplant := plant; toplant := plant;
Ahora tenemos una demanda diferente para cada planta. La definición para el vector de datos Demand necesita ser aumentado para incluir el índice plant. En el editor del modelo, agregue el índice plant al vector de datos Demand y cambie el nombre del archivo a Demand6.dat.
DATA Price[product] := (120.00, 100.00, 115.00); Demand[plant, product, month] := DATAFILE("Demand6.dat");
Puesto que el vector Demand tiene tres índices, el archivo de datos necesita ser actualizado. Además, usted está creando un nuevo archivo de datos llamado 'Demand6.dat' usando los valores de datos de la tabla en la descripción del problema dado anteriormente en esta sesión. Ingrese los valores de los datos al archivo de datos, como sigue:
! ! Demand6.dat - Demand for each product and each plant ! ! Demand[plant,product,month]: ! ! Jan Feb Mar Apr ! --------------------------- !plant 1: 4300, 4200, 6400, 5300, 4500, 5400, 6500, 7200, 5400, 6700, 7800, 8200, !plant 2: 5100, 6200, 5400, 7600, 6300, 7100, 5200, 6300, 4800, 6500, 5000, 7200, !plant 3: 4100, 6100, 4700, 5800, 5300, 5200, 5700, 4100, 4200, 4100, 5200, 6300, !plant 4: 4300, 4100, 5300, 4500, 5300, 6400, 4200, 6200, 5600, 5200, 3800, 4100
Cuando se tiene tres dimensiones de datos en los archivos de datos, liste los valores de los datos en el mismo orden que los índices fueron definidos en el vector de datos del modelo. Por ejemplo, en el archivo de datos de arriba Demand6, el índice que está más a la izquierda es el índice plant, seguido por el índice product y el índice month.
Puesto que ahora se puede almacenar inventarios en cada planta, se necesita actualizar el costo del inventario y los datos de la capacidad del inventario para incluir el índice plant. En el editor del modelo, agregue el índice plant en la declaración del vector de datos InvtCost y el dato constante InvtCapacity. Luego, para el vector de datos InvtCost elimine la lista de números y reemplacelo con la palabra clave DATAFILE y el nombre de archivo 'InvtCost.dat.' Para el InvtCapacity elimine el valor 800 y reemplacelo con la lista de cuatro valores, uno por cada planta, tomándolo de la descripción del problema anterior de esta sesión.
InvtCost[plant, product] := DATAFILE("InvtCost.dat"); InvtCapacity[plant] := (800, 400, 500, 400);
Después, necesita crear un nuevo archivo de datos llamado 'InvtCost.dat' usando los valores de costos de la tabla de costos del inventario de la descripción del problema. Ingrese los valores de los datos al archivo de datos, como sigue:
! ! InvtCost.dat - Inventory cost per item a month ! ! InvtCost[plant,product]: ! ! A1 A2 A3 ! ----------------------- 8.50, 7.00, 6.50 9.80, 9.80, 9.80 7.50, 7.50, 7.50 9.30, 8.00, 6.50
Hay ciertos costos involucrados en embarcar productos entre plantas. En el editor del modelo, agregue un nuevo vector de datos llamado ShipCost definido sobre los índices ficticios fromplant y toplant seguido por la palabra clave DATAFILE y el nombre de archivo ShipCost.dat.
ShipCost[fromplant, toplant] := DATAFILE("ShipCost.dat");
Después, necesita crear un nuevo archivo de datos llamado ShipCost.dat conteniendo los costos figurados de embarque entre las plantas proporcionados en la descripción del problema. Ingrese los valores de los datos para el archivo de datos, como sigue:
! ! ShipCost.dat - Shipping costs from plant to plant ! ! ShipCost[fromplant, toplant] ! 0, 15.00, 21.00, 13.00, 16.00, 0, 12.00, 12.00, 14.00, 17.00, 0, 15.00, 21.00, 13.00, 10.00, 0,
Estamos permitiendo embarques entre plantas, además, se necesita crear una nueva variable que decida cuánto debe ser embarcado de cada producto por cada mes. Esta variable vector será definida sobre los índices ficticios fromplant y toplant como también los índices product y month. Agregue la siguiente definición a la variable vector Ship de la sección VARIABLES:
Ship[product, month, fromplant, toplant] WHERE (fromplant <> toplant);
Puesto que no queremos embarcar un producto de una planta a la misma planta, estamos usando la condición WHERE para eliminar todos los elementos del vector donde la planta orígen es la misma que la planta destino.
En la sección MACROS agregue una nueva definición para el costo total de embarque llamada TotalShipCost y actualice la macro TotalCost para incluir la nueva macro como sigue:
TotalShipCost := SUM(product, month, fromplant, toplant: ShipCost * Ship); TotalCost := TotalProdCost + TotalInvtCost + TotalShipCost;
Note que la actual función objetivo no necesita ser cambiada a la macro TotalCost que contiene todos los cambios.
Puesto que ahora se permiten embarques entre las plantas, se necesita aumentar la restricción de equilibrio de inventario del modelo anterior a la restricción de equilibrio de planta. Primero, cambie el nombre de la restricción de InvtBal a PlantBal y agregue el índice Plant a la declaración. Luego, puesto que la restricción está ahora declarada sobre el índice Plant no tenemos que sumar sobre el índice Plant para la variable Produce una vez más.
PlantBal[plant, product, month] -> PBal: Produce + Inventory[month-1] + SUM (fromplant:Ship[fromplant, toplant:=plant]) = Sales + Inventory + SUM(toplant: Ship[fromplant:=plant, toplant]);
Sobre el lado izquierdo, donde estamos poniendo junto todo lo que está llegando a la planta, agregue una suma para agregar todos los embarques desde cada una de las plantas a la planta actual de la restricción. Dentro de la suma, ingrese la variable Ship con la planta destino tomando el valor del actual índice plant.
Por el lado derecho, donde estamos poniendo junto todo lo que está saliendo de la planta, agregue otra suma para agregar todos los embarques de la planta actual a cada una de las otras plantas. Dentro de la suma ingrese la variable Ship esta vez con la planta orígen tomando el valor del índice actual plant de la restricción.
El próximo paso es resolver el modelo Planning6 eligiendo Solve CPLEX del menú Run. Sí todo está bien MPL visualizará el mensaje "Optimal Solution Found"(Solución Optima Encontrada). Si hubiera algún error de sintáxis, por favor verifique y compare la formulación que ingresó con el modelo detallado anteriormente en esta sesión.
Usará la ventana de definiciones otra vez como en la Sesión 5 para mirar las partes de la solución en que estamos interesados. Para abrir la ventana de definición del modelo para el modelo Planning6 elija Model Definitions(Definiciones del Modelo) del menú View(Ver).
Ventana Arbol de Definición del Modelo Planning6 Model
Para mirar los valores de la variable Produce, haga doble click sobre el item Produce en el árbol, o seleccione el item y presione el botón View. Esto visualizará una ventana conteniendo solo los valores de la variable Produce como se muestra a continuación.
VARIABLE Produce[plant,product,month] : plant product month Activity Reduced Cost ----------------------------------------------------------- p1 A1 Jan 4300.0 0.0 p1 A1 Feb 4200.0 0.0 p1 A1 Mar 6400.0 0.0 p1 A1 Apr 5300.0 0.0 p1 A2 Jan 1080.0 0.0 p1 A3 Jan 5400.0 0.0 p1 A3 Feb 5220.0 0.0 p1 A3 Mar 4590.0 0.0 p1 A3 Apr 5130.0 0.0 p2 A1 Jan 5100.0 0.0 p2 A1 Feb 6200.0 0.0 p2 A1 Mar 5400.0 0.0 p2 A1 Apr 7600.0 0.0 p2 A2 Jan 6177.3 0.0 p2 A2 Feb 3927.3 0.0 p2 A2 Mar 5931.8 0.0 p2 A2 Apr 3681.8 0.0 p3 A1 Jan 4100.0 0.0 p3 A1 Feb 6100.0 0.0 p3 A1 Mar 4700.0 0.0 p3 A1 Apr 5800.0 0.0 p3 A3 Jan 4166.7 0.0 p3 A3 Feb 1933.3 0.0 p3 A3 Mar 3766.7 0.0 p3 A3 Apr 2733.3 0.0 p4 A1 Jan 3850.0 0.0 p4 A1 Feb 2828.6 0.0 p4 A1 Mar 5300.0 0.0 p4 A1 Apr 4500.0 0.0 p4 A3 Jan 5600.0 0.0 p4 A3 Feb 5200.0 0.0 p4 A3 Mar 4677.3 0.0 p4 A3 Apr 4836.4 0.0 -----------------------------------------------------------
Como puede ver, la producción está ahora distribuída entre las plantas de una manera más efectiva. Ciertos productos son claramente mejores de producir en una planta en particular debido a que se toma en cuenta el costo de producción y el costo de embarque. Por ejemplo, el producto A2 es producido en las plantas p1 y p2, pero no en p3 y p4, mientras que el producto A3 es producido en las plantas p1, p3 y p4. El producto A1 es más económicamente producido en todas plantas.
Si va a la ventana del árbol otra vez y abre una ventana para la variable Ship obtendrá los siguientes valores en la solución:
VARIABLE Ship[product,month,fromplant,toplant] : product month fromplant toplant Activity Reduced Cost ------------------------------------------------------------------------ A2 Mar p2 p4 331.8 0.0 A3 Mar p4 p3 877.3 0.0 A3 Apr p4 p3 736.4 0.0 ------------------------------------------------------------------------
Como se puede ver, el modelo propone que debemos embarcar el producto A2 desde la planta p2 a la planta p4. De manera similar, A3 es embarcado desde la planta p4 a la planta p3. Claramente, las plantas p2 y p4 tienen capacidad extra a un costo bajo que puede ser usado para producir bienes que las plantas p4 y p3 necesitan.