Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
2027 views
ubuntu2004
Kernel: SageMath 9.4

Modelo de programación lineal del problema de transporte

En esta entrada, te explicaré como resolver un problema de transporte usando Sagemath, un sistema algebraico de cómputo derivado de Python, por lo que si sabes programar en este lenguaje, te será muy sencillo seguir la solución. En otro caso, te invito a ver este curso.

De acuerdo a la Wikipedia*

Un problema de transporte1​ es, en matemáticas y economía, un caso particular de problema de programación lineal en el cual se debe minimizar el coste del abastecimiento a una serie de puntos de demanda a partir de un grupo de puntos de oferta —posiblemente de distinto número—, teniendo en cuenta los distintos precios de envío de cada punto de oferta a cada punto de demanda.

El siguiente diagrama nos proporciona un modelo.

Modelado del problema de transporte

*Problema de transporte. (2021, 16 de junio). Wikipedia, La enciclopedia libre. Fecha de consulta: 11:33, octubre 24, 2021 desde https://es.wikipedia.org/w/index.php?title=Problema_de_transporte&oldid=136371622.

Los problemas de transporte pueden involucrar muchas etapas en las entregas. Por lo que el modelo se puede reexpresar de la siguiente forma: Modelación del problema de transbordo

Si quieres conocer más sobre estos modelos, consulta

Martínez, I., López, F., & Vertiz, G. (2014). Investigación de Operaciones: Serie Universitaria Patria. Grupo Editorial Patria.

Ahora, vamos a resolver el problema 1 de la unidad 5 de este libro.

Problema resuelto

Una empresa ensambladora de automóviles sedán posee tres plantas, ubicadas en Saltillo, Silao y Hermosillo, respectivamente, con una capacidad de producción de un determinado tipo de sedán de 150, 250 y 100 autos por mes, respectivamente. Sus concesionarias ubicadas en Monterrey, Guadalajara, Guanajuato y Puebla requieren 50, 200, 100 y 150 autos sedán, respectivamente, para este mes. Los costos de envío de un auto de la planta a la concesionaria se describen en la tabla siguiente.

Tabla 501

Formular el MPL que minimiza los costos de transporte para cubrir la demanda de las concesionarias.

Solución

Las fuentes son las tres plantas y los orígenes las cuatro concesionarias, por lo que la variable de decisión xikx_{ik} representa el número de autos sedán que se mandan de la planta i a la concesionaria k.

""" 1. Creamos un objeto de la clase MILP. 2. El parámetro maximization se iguala con False porque queremos minimizar. 3. La variable envio guardará la información de todas las unidades enviadas. """ programa = MixedIntegerLinearProgram(maximization=False) envio = programa.new_variable(nonnegative=True, integer=True, name="Unidades Enviadas")
""" 1. Creamos un marco de datos con la información de las concesionarias. 2. Para cada clave de una concesionaria, asignamos el costo del envío desde la planta correspondiente. """ import pandas as pd costos = pd.DataFrame({ 'plantas' : ['sal', 'sil', 'her'], 'mty': [100, 160, 200], 'gdl': [213, 140, 230], 'gto': [180, 50, 310], 'pue': [250, 200, 350] }) costos = costos.set_index(['plantas']) print(costos)
mty gdl gto pue plantas sal 100 213 180 250 sil 160 140 50 200 her 200 230 310 350
""" 1. Guardamos las claves de las plantas en una lista del mismo nombre. """ plantas = costos.index print(plantas)
Index(['sal', 'sil', 'her'], dtype='object', name='plantas')
""" 1. Hacemos lo mismo con las concesionarias. """ concesionarias = costos.columns print(concesionarias)
Index(['mty', 'gdl', 'gto', 'pue'], dtype='object')
""" 1. Ahora definiramos la función objetivo. 2. Sumaremos los costos de envío de planta a concesionaria. """ objetivo = 0 for p in plantas: for c in concesionarias: costo = costos[c].loc[p] print(p, c, costo) objetivo = objetivo+costo*envio[p,c] programa.set_objective(objetivo)
sal mty 100 sal gdl 213 sal gto 180 sal pue 250 sil mty 160 sil gdl 140 sil gto 50 sil pue 200 her mty 200 her gdl 230 her gto 310 her pue 350
""" 1. Antes de continuar, capturamos la capacidad de producción de cada planta. """ capacidad = { 'sal': 150, 'sil':250, 'her':100 } capacidad = pd.Series(capacidad, dtype='float') print(capacidad)
sal 150.0 sil 250.0 her 100.0 dtype: float64
""" 1. Capturamos las resticciones de la producción de cada planta. 2. En este caso supondremos que la producción total se envía a las diferentes concecionarias. 3. La producción no puede sobrepasar a la capacidad. """ for p in plantas: produccion = 0 for c in concesionarias: produccion = produccion + envio[p, c] programa.add_constraint(produccion<=capacidad[p])
""" 1. Capturamos la demanda por concesionaria. """ demanda = { 'mty': 50, 'gdl':200, 'gto':100, 'pue':150 } demanda = pd.Series(demanda, dtype='float') print(demanda)
mty 50.0 gdl 200.0 gto 100.0 pue 150.0 dtype: float64
""" 1. Ahora capturamos la cantidad de autos enviados desde cada planta a una concesionaria dada. 2. La cantidad de autos enviados, en principio, debería ser mayor o igual que la demanda. """ for c in concesionarias: autos_enviados = 0 for p in plantas: autos_enviados = autos_enviados+envio[p,c] programa.add_constraint(autos_enviados>=demanda[c])
""" 1. Imprimimos en pantalla la información del sistema para cotejar que es correcta. """ programa.show()
Minimization: 100.0 Unidades Enviadas[('sal', 'mty')] + 213.0 Unidades Enviadas[('sal', 'gdl')] + 180.0 Unidades Enviadas[('sal', 'gto')] + 250.0 Unidades Enviadas[('sal', 'pue')] + 160.0 Unidades Enviadas[('sil', 'mty')] + 140.0 Unidades Enviadas[('sil', 'gdl')] + 50.0 Unidades Enviadas[('sil', 'gto')] + 200.0 Unidades Enviadas[('sil', 'pue')] + 200.0 Unidades Enviadas[('her', 'mty')] + 230.0 Unidades Enviadas[('her', 'gdl')] + 310.0 Unidades Enviadas[('her', 'gto')] + 350.0 Unidades Enviadas[('her', 'pue')] Constraints: Unidades Enviadas[('sal', 'mty')] + Unidades Enviadas[('sal', 'gdl')] + Unidades Enviadas[('sal', 'gto')] + Unidades Enviadas[('sal', 'pue')] <= 150.0 Unidades Enviadas[('sil', 'mty')] + Unidades Enviadas[('sil', 'gdl')] + Unidades Enviadas[('sil', 'gto')] + Unidades Enviadas[('sil', 'pue')] <= 250.0 Unidades Enviadas[('her', 'mty')] + Unidades Enviadas[('her', 'gdl')] + Unidades Enviadas[('her', 'gto')] + Unidades Enviadas[('her', 'pue')] <= 100.0 - Unidades Enviadas[('sal', 'mty')] - Unidades Enviadas[('sil', 'mty')] - Unidades Enviadas[('her', 'mty')] <= -50.0 - Unidades Enviadas[('sal', 'gdl')] - Unidades Enviadas[('sil', 'gdl')] - Unidades Enviadas[('her', 'gdl')] <= -200.0 - Unidades Enviadas[('sal', 'gto')] - Unidades Enviadas[('sil', 'gto')] - Unidades Enviadas[('her', 'gto')] <= -100.0 - Unidades Enviadas[('sal', 'pue')] - Unidades Enviadas[('sil', 'pue')] - Unidades Enviadas[('her', 'pue')] <= -150.0 Variables: Unidades Enviadas[('sal', 'mty')] = x_0 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sal', 'gdl')] = x_1 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sal', 'gto')] = x_2 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sal', 'pue')] = x_3 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'mty')] = x_4 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'gdl')] = x_5 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'gto')] = x_6 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'pue')] = x_7 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'mty')] = x_8 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'gdl')] = x_9 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'gto')] = x_10 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'pue')] = x_11 is an integer variable (min=0.0, max=+oo)
""" 1. Resolvemos el problema. 2. El valor resultante será el costo mínimo. """ programa.solve()
82000.0
""" 1. También es de nuestro interés saber como asignar los autos de una planta a una consecionaria. 2. El siguiente método nos indica como debemos de realizar los envío. """ programa.get_values(envio)
{('sal', 'mty'): 50.0, ('sal', 'gdl'): 0.0, ('sal', 'gto'): 0.0, ('sal', 'pue'): 100.0, ('sil', 'mty'): 0.0, ('sil', 'gdl'): 100.0, ('sil', 'gto'): 100.0, ('sil', 'pue'): 50.0, ('her', 'mty'): 0.0, ('her', 'gdl'): 100.0, ('her', 'gto'): 0.0, ('her', 'pue'): 0.0}

¡Listo! Hemos terminado. Ahora añadiremos un poco de dificultad al problema, suponiendo que los envíos deben pasar primero por almacenes.

MPL para el modelo de transbordo

Si la empresa referida en el problema anterior tiene dos centros de distribución ubicados en San Luis Potosí y el Estado de México, respectivamente, con costos de distribución como se muestran en la tabla siguiente. ¿Cuál es el MPL que minimiza los costos totales de distribución?

Tabla 5.2

""" 1. Repetiremos algunos pasos adaptadolos al problema actual. 2. Primero capturamos los costos de las plantas a los almacenes. """ import pandas as pd costos_1 = { 'plantas': ['sal', 'sil', 'her'], 'slp': [40,30,200], 'edo': [200,80,250] } costos_1 = pd.DataFrame(costos_1) costos_1 = costos_1.set_index('plantas') costos_1
""" 1. Posteriormente, capturamos los costos de los almacenes a las concesionarias. """ costos_2 = { 'almacenes': ['slp', 'edo'], 'mty': [50, 80], 'gdl': [30, 80], 'gto': [60, 60], 'pue': [80, 40] } costos_2 = pd.DataFrame(costos_2) costos_2 = costos_2.set_index("almacenes") costos_2
""" 1. Creamos un programa de clase MILP 2. Declaramos la variable unidades, que representará cualquier tipo de unidad enviada entre nodos. 3. Además declaramos la variable inventario, que representará el número de unidades que deben pasar por cada almacén. """ programa = MixedIntegerLinearProgram(maximization=False) unidades = programa.new_variable(nonnegative=True, integer=True, name="Unidades Enviadas") inventario = programa.new_variable(nonnegative=True, integer=True, name="Unidades Almacenadas")
""" 1. Capturamos la lista de plantas. """ plantas = costos_1.index print(plantas)
Index(['sal', 'sil', 'her'], dtype='object', name='plantas')
""" 1. Ahora la de contenedores. """ almacenes = costos_2.index almacenes
Index(['slp', 'edo'], dtype='object', name='almacenes')
""" 1. Finalmente la de concesionaria. """ concesionarias = costos_2.columns concesionarias
Index(['mty', 'gdl', 'gto', 'pue'], dtype='object')
""" 1. En este modelos, capturaremos la función objetivo en dos etapas. 2. En la primera, agregaremos los costos planta -> almacén. """ objetivo = 0 for origen in plantas: for destino in almacenes: costo = costos_1[destino].loc[origen] print(origen, destino, costo) objetivo = objetivo+costo*unidades[origen,destino]
sal slp 40 sal edo 200 sil slp 30 sil edo 80 her slp 200 her edo 250
""" 1. En la segunda etapa, agregaremos los costos almacén -> concesionaria """ for origen in almacenes: for destino in concesionarias: costo = costos_2[destino].loc[origen] print(origen, destino, costo) objetivo = objetivo+costo*unidades[origen, destino]
slp mty 50 slp gdl 30 slp gto 60 slp pue 80 edo mty 80 edo gdl 80 edo gto 60 edo pue 40
""" 1. Ahora fijamos la función objetivo. """ programa.set_objective(objetivo) programa.show()
Minimization: 40.0 Unidades Enviadas[('sal', 'slp')] + 200.0 Unidades Enviadas[('sal', 'edo')] + 30.0 Unidades Enviadas[('sil', 'slp')] + 80.0 Unidades Enviadas[('sil', 'edo')] + 200.0 Unidades Enviadas[('her', 'slp')] + 250.0 Unidades Enviadas[('her', 'edo')] + 50.0 Unidades Enviadas[('slp', 'mty')] + 30.0 Unidades Enviadas[('slp', 'gdl')] + 60.0 Unidades Enviadas[('slp', 'gto')] + 80.0 Unidades Enviadas[('slp', 'pue')] + 80.0 Unidades Enviadas[('edo', 'mty')] + 80.0 Unidades Enviadas[('edo', 'gdl')] + 60.0 Unidades Enviadas[('edo', 'gto')] + 40.0 Unidades Enviadas[('edo', 'pue')] Constraints: Variables: Unidades Enviadas[('sal', 'slp')] = x_0 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sal', 'edo')] = x_1 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'slp')] = x_2 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'edo')] = x_3 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'slp')] = x_4 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'edo')] = x_5 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'mty')] = x_6 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'gdl')] = x_7 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'gto')] = x_8 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'pue')] = x_9 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'mty')] = x_10 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'gdl')] = x_11 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'gto')] = x_12 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'pue')] = x_13 is an integer variable (min=0.0, max=+oo)
""" 1. Capturaremos las resticciones de capacidad de las plantas de manera similar al programa anterior. """ for planta in plantas: produccion = 0 for almacen in almacenes: produccion = produccion + unidades[planta, almacen] programa.add_constraint(produccion<=capacidad[planta])
""" 1. Sin embargo, ahora los autos enviados desde las plantas deberán igualar el inventario de los almacenes. 2. Recuerda que esta es una cantidad desconocida y por tanto usaremos una variable. """ for almacen in almacenes: autos_enviados = 0 for planta in plantas: autos_enviados = autos_enviados+unidades[planta,almacen] programa.add_constraint(autos_enviados==inventario[almacen])
""" 1. Ahora capturamos las restricciones de envío desde los almacenes. 2. Puedes pensar que los almacenes "producen" unidades, tal como lo hacen las plantas en la etapa anterior. 3. Solo recuerda que la capacidad total de envío de los almacenes no debe superar el inventario. """ for almacen in almacenes: produccion = 0 for concesionaria in concesionarias: produccion = produccion + unidades[almacen, concesionaria] programa.add_constraint(produccion<=inventario[almacen])
""" 1. Capturamos el envío de autos desde los almacenes a las concesionarias. 2. Esto es similar al envío que se realizaba desde las plantas en el problema anterior. """ for concesionaria in concesionarias: autos_enviados = 0 for almacen in almacenes: autos_enviados = autos_enviados+unidades[almacen, concesionaria] programa.add_constraint(autos_enviados>=demanda[concesionaria])
""" 1. Imprimimos el programa en pantalla para verificar que todo sea correcto. """ programa.show()
Minimization: 40.0 Unidades Enviadas[('sal', 'slp')] + 200.0 Unidades Enviadas[('sal', 'edo')] + 30.0 Unidades Enviadas[('sil', 'slp')] + 80.0 Unidades Enviadas[('sil', 'edo')] + 200.0 Unidades Enviadas[('her', 'slp')] + 250.0 Unidades Enviadas[('her', 'edo')] + 50.0 Unidades Enviadas[('slp', 'mty')] + 30.0 Unidades Enviadas[('slp', 'gdl')] + 60.0 Unidades Enviadas[('slp', 'gto')] + 80.0 Unidades Enviadas[('slp', 'pue')] + 80.0 Unidades Enviadas[('edo', 'mty')] + 80.0 Unidades Enviadas[('edo', 'gdl')] + 60.0 Unidades Enviadas[('edo', 'gto')] + 40.0 Unidades Enviadas[('edo', 'pue')] Constraints: Unidades Enviadas[('sal', 'slp')] + Unidades Enviadas[('sal', 'edo')] <= 150.0 Unidades Enviadas[('sil', 'slp')] + Unidades Enviadas[('sil', 'edo')] <= 250.0 Unidades Enviadas[('her', 'slp')] + Unidades Enviadas[('her', 'edo')] <= 100.0 0.0 <= Unidades Enviadas[('sal', 'slp')] + Unidades Enviadas[('sil', 'slp')] + Unidades Enviadas[('her', 'slp')] - Unidades Almacenadas[slp] <= 0.0 0.0 <= Unidades Enviadas[('sal', 'edo')] + Unidades Enviadas[('sil', 'edo')] + Unidades Enviadas[('her', 'edo')] - Unidades Almacenadas[edo] <= 0.0 Unidades Enviadas[('slp', 'mty')] + Unidades Enviadas[('slp', 'gdl')] + Unidades Enviadas[('slp', 'gto')] + Unidades Enviadas[('slp', 'pue')] - Unidades Almacenadas[slp] <= 0.0 Unidades Enviadas[('edo', 'mty')] + Unidades Enviadas[('edo', 'gdl')] + Unidades Enviadas[('edo', 'gto')] + Unidades Enviadas[('edo', 'pue')] - Unidades Almacenadas[edo] <= 0.0 - Unidades Enviadas[('slp', 'mty')] - Unidades Enviadas[('edo', 'mty')] <= -50.0 - Unidades Enviadas[('slp', 'gdl')] - Unidades Enviadas[('edo', 'gdl')] <= -200.0 - Unidades Enviadas[('slp', 'gto')] - Unidades Enviadas[('edo', 'gto')] <= -100.0 - Unidades Enviadas[('slp', 'pue')] - Unidades Enviadas[('edo', 'pue')] <= -150.0 Variables: Unidades Enviadas[('sal', 'slp')] = x_0 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sal', 'edo')] = x_1 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'slp')] = x_2 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('sil', 'edo')] = x_3 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'slp')] = x_4 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('her', 'edo')] = x_5 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'mty')] = x_6 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'gdl')] = x_7 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'gto')] = x_8 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('slp', 'pue')] = x_9 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'mty')] = x_10 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'gdl')] = x_11 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'gto')] = x_12 is an integer variable (min=0.0, max=+oo) Unidades Enviadas[('edo', 'pue')] = x_13 is an integer variable (min=0.0, max=+oo) Unidades Almacenadas[slp] = x_14 is an integer variable (min=0.0, max=+oo) Unidades Almacenadas[edo] = x_15 is an integer variable (min=0.0, max=+oo)
""" 1. Finalmente resolvemos el problema. 2. El valor impreso en pantalla corresponderá con el costo mínimo. """ programa.solve()
60000.0
""" 1. En esta versión del problema de transporte, tennemos dos variables. 2. Primero extraemos la información sobre los inventarios que se deben tener en cada almacén. """ programa.get_values(inventario)
{'slp': 500.0, 'edo': 0.0}
""" 1. Finalmente, extraemos la información de las unidades que se deben enviar entre cada nodo. 2. Los envíos son tanto para planta->almacén como almacén->concesionaria. """ programa.get_values(unidades)
{('sal', 'slp'): 150.0, ('sal', 'edo'): 0.0, ('sil', 'slp'): 250.0, ('sil', 'edo'): 0.0, ('her', 'slp'): 100.0, ('her', 'edo'): 0.0, ('slp', 'mty'): 50.0, ('slp', 'gdl'): 200.0, ('slp', 'gto'): 100.0, ('slp', 'pue'): 150.0, ('edo', 'mty'): 0.0, ('edo', 'gdl'): 0.0, ('edo', 'gto'): 0.0, ('edo', 'pue'): 0.0}

¡Listo! Hemos concluído. Si deseas saber más sobre como resolver problemas de problamación lineal con SageMath, puedes consultar la documentación, y suscribirte para recibir las notificaciones sobre más técnicas interesantes para resolver problemas de matemáticas aplicadas.