Producción de jugos - Solución#
Tags: Mezcla, Recurso
Show code cell source
import os
# Por precaución, cambiamos el directorio activo de Python a aquel que contenga este notebook
if "optimizacion" in os.listdir():
os.chdir(r"optimizacion/Formulaciones/3. Produccion de jugos/")
Enunciado#
OptiJuice es una empresa que produce jugos. Ellos han decidido producir un nuevo conjunto de jugos autóctonos (\(K\)). Los jugos son una mezcla de diferentes frutas tropicales (\(R\)) dentro de las que se encuentran la piña, la guayaba, el níspero y el zapote. Cada uno de los tipos de jugo se diferencia de los demás en la cantidad de litros de zumo que tiene de las distintas frutas. Es por esto que, para garantizar la calidad de los jugos es necesario que el jugo del tipo \(k \in K\) contenga entre un \(l_{ki}\)% y un \(u_{ki}\)% de litros de zumo de la fruta \(i \in R\). Para la producción de jugos, OptiJuice tiene disponibles \(b_i\) litros de zumo de la fruta \(i \in R\). La compañía espera una demanda mínima de \(d_k\) litros y desea vender cada litro de jugo del tipo \(k \in K\) a \(p_k\) pesos. La información mencionada se presenta en las Tablas 1 a 4.
Tabla 1. Mínimo porcentaje de las frutas en los jugos
Mínimo porcentaje | Frutas | |||
---|---|---|---|---|
Jugos | Piña (%) | Guayaba (%) | Nispero (%) | Zapote (%) |
Saludable | 32 | 27 | 12 | 13 |
Tropical | 18 | 20 | 21 | 11 |
Mañanero | 7 | 31 | 28 | 22 |
Colombiano | 11 | 5 | 15 | 18 |
Refrescante | 16 | 29 | 2 | 23 |
Light | 26 | 19 | 14 | 30 |
Tabla 2. Máximo porcentaje de las frutas en los jugos
Máximo porcentaje | Frutas | |||
---|---|---|---|---|
Jugos | Piña (%) | Guayaba (%) | Nispero (%) | Zapote (%) |
Saludable | 95 | 83 | 66 | 87 |
Tropical | 92 | 76 | 69 | 56 |
Mañanero | 81 | 61 | 78 | 94 |
Colombiano | 82 | 88 | 63 | 98 |
Refrescante | 60 | 85 | 73 | 78 |
Light | 50 | 55 | 82 | 91 |
Tabla 3. Litros disponibles de cada fruta
Frutas | Litros disponibles |
---|---|
Piña | 6,318 |
Guayaba | 4,500 |
Nispero | 4,683 |
Zapote | 2,121 |
Tabla 4. Demanda mínima y precio de cada jugo
Jugos | Demanda mínima | Precio |
---|---|---|
Saludable | 1,200 | 9,000 |
Tropical | 925 | 5,000 |
Mañanero | 1,865 | 6,000 |
Colombiano | 1,035 | 10,000 |
Refrescante | 2,231 | 7,000 |
Light | 1,353 | 8,000 |
Formula un programa lineal que le permita OptiJuice responder la siguiente pregunta: ¿Cuántos litros de zumo de cada fruta se deben mezclar para producir cada uno de los tipos de jugos, de manera que se cumplan las condiciones previamente expuestas y se maximicen los ingresos totales?
Formulación#
a. Formula matemáticamente un modelo de optimización de forma general que represente la situación anterior. Defina clara y rigurosamente:
Conjuntos
Parámetros
Variables de decisión
Restricciones
Naturaleza de las variables
Función objetivo
Conjuntos#
\(K\): conjunto de jugos autóctonos
\(R\): conjunto de frutas tropicales
Parámetros#
\(l_{ki}\)%: porcentaje mínimo de litros de zumo de la fruta \(i\in R\) que debe tener el jugo del tipo \(k\in K\)
\(u_{ki}\)%: porcentaje máximo de litros de zumo de la fruta \(i\in R\) que debe tener el jugo del tipo \(k\in K\)
\(b_i\): litros de zumo de la fruta \(i\in R\) disponibles
\(d_k\): demanda mínima (en litros) del jugo de tipo \(k\in K\)
\(p_k\): precio de un litro del jugo de tipo \(k\in K\)
Variables de decisión#
\(x_{ki}\): cantidad (en litros) del zumo de la fruta \(i\in R\) destinados a la producción del jugo de tipo \(k\in K\)
Restricciones#
El jugo del tipo \(k\in K\) debe contener entre un \(l_{ki}\)% y un \(u_{ki}\)% de zumo de la fruta \(i\in R\):
OptiJuice puede utilizar máximo \(b_i\) litros de zumo de la fruta \(i\in R\):
OptiJuice desea cumplir con la demanda mínima de \(d_k\) litros jugo del tipo \(k\in K\):
Naturaleza de las Variables#
Solo pueden usarse cantidades positivas de cada fruta \(i\in R\) para el jugo \(k\in K\):
Función objetivo#
Ingresos totales:
Implementación#
b. Resuelve el modelo planteado utilizando la librería de PuLP en Python. ¿Cuál es la solución óptima del problema?
Librerías#
Importa la librería pulp
para crear y resolver el modelo.
import pulp as lp
Conjuntos#
Define los conjuntos K
y R
que representan los jugos y las frutas respectivamente.
Recuerda que por conveniencia de preservar el orden de los elementos de los conjuntos, no siempre deberás definirlos con el tipo set
.
# Jugos
K = ["Saludable", "Tropical", "Mañanero", "Colombiano", "Refrescante", "Light"]
# Frutas
R = ["Piña", "Guayaba", "Níspero", "Zapote"]
Parámetros#
Define o importa los parámetros del modelo.
# Porcentaje mínimo de litros de la fruta i in R en el jugo k in K
l = {
("Saludable", "Piña"): 0.32,
("Saludable", "Guayaba"): 0.27,
("Saludable", "Níspero"): 0.12,
("Saludable", "Zapote"): 0.13,
("Tropical", "Piña"): 0.18,
("Tropical", "Guayaba"): 0.20,
("Tropical", "Níspero"): 0.21,
("Tropical", "Zapote"): 0.11,
("Mañanero", "Piña"): 0.07,
("Mañanero", "Guayaba"): 0.31,
("Mañanero", "Níspero"): 0.28,
("Mañanero", "Zapote"): 0.22,
("Colombiano", "Piña"): 0.11,
("Colombiano", "Guayaba"): 0.05,
("Colombiano", "Níspero"): 0.15,
("Colombiano", "Zapote"): 0.18,
("Refrescante", "Piña"): 0.16,
("Refrescante", "Guayaba"): 0.29,
("Refrescante", "Níspero"): 0.02,
("Refrescante", "Zapote"): 0.23,
("Light", "Piña"): 0.26,
("Light", "Guayaba"): 0.10,
("Light", "Níspero"): 0.14,
("Light", "Zapote"): 0.30,
}
# Porcentaje máximo de litros de la fruta i in R en el jugo k in K
u = {
("Saludable", "Piña"): 0.95,
("Saludable", "Guayaba"): 0.83,
("Saludable", "Níspero"): 0.66,
("Saludable", "Zapote"): 0.87,
("Tropical", "Piña"): 0.92,
("Tropical", "Guayaba"): 0.76,
("Tropical", "Níspero"): 0.69,
("Tropical", "Zapote"): 0.56,
("Mañanero", "Piña"): 0.81,
("Mañanero", "Guayaba"): 0.61,
("Mañanero", "Níspero"): 0.78,
("Mañanero", "Zapote"): 0.94,
("Colombiano", "Piña"): 0.82,
("Colombiano", "Guayaba"): 0.88,
("Colombiano", "Níspero"): 0.63,
("Colombiano", "Zapote"): 0.98,
("Refrescante", "Piña"): 0.60,
("Refrescante", "Guayaba"): 0.85,
("Refrescante", "Níspero"): 0.73,
("Refrescante", "Zapote"): 0.78,
("Light", "Piña"): 0.50,
("Light", "Guayaba"): 0.55,
("Light", "Níspero"): 0.82,
("Light", "Zapote"): 0.91,
}
# Litros disponibles de la fruta i in R
b = {
"Piña": 6318,
"Guayaba": 4500,
"Níspero": 4683,
"Zapote": 2121,
}
# Demanda del jugo k in K
d = {
"Saludable": 1200,
"Tropical": 925,
"Mañanero": 1865,
"Colombiano": 1035,
"Refrescante": 2231,
"Light": 1353,
}
# Precio del jugo k in K
p = {
"Saludable": 9000,
"Tropical": 5000,
"Mañanero": 6000,
"Colombiano": 10000,
"Refrescante": 7000,
"Light": 8000,
}
Objeto del modelo#
Construye un problema al que luego agregarás las restricciones y la función objetivo.
problema = lp.LpProblem(name="optijuice", sense=lp.LpMinimize)
Variables de decisión#
Define las variables del problema de manera que estén contenidas en diccionarios indexados en los conjuntos de sus variables respectivas.
# Litros de la fruta i in R para producir el jugo k in K
x = {
(k, i): lp.LpVariable(
name=f"litros_fruta_{i}_jugo_{k}", lowBound=0, upBound=None, cat=lp.LpContinuous
)
for k in K
for i in R
}
Función objetivo#
Agrega al problema la función objetivo. Recuerda que al definir el problema, ya definiste si este es de maximización o minimización.
# Ingresos totales
problema += sum(p[k] * x[k, i] for k in K for i in R), "ingresos_totales"
Restricciones#
Agrega al problema las restricciones del modelo.
# Se garantiza el mínimo de fruta i in R en el jugo k in K
for k in K:
for i in R:
problema += (
x[k, i] >= l[k, i] * sum(x[k, j] for j in R),
f"minimo_fruta_{i}_jugo_{k}",
)
# Se garantiza el máximo de fruta i in R en el jugo k in K
for k in K:
for i in R:
problema += (
x[k, i] <= u[k, i] * sum(x[k, j] for j in R),
f"maximo_fruta_{i}_jugo_{k}",
)
# Se garantiza que no se utilice más fruta de la que hay disponible
for i in R:
problema += (
sum(x[k, i] for k in K) <= b[i],
f"límite_fruta_{i}",
)
# Se satisface la demanda
for k in K:
problema += (
sum(x[k, i] for i in R) >= d[k],
f"demanda_mínima_jugo_{k}",
)
Resolver el problema#
Invoca el optimizador. Este paso le asigna un valor a las variables incluidas en las restricciones o función objetivo del modelo.
problema.solve()
Welcome to the CBC MILP Solver
Version: 2.10.8
Build Date: May 6 2022
command line - cbc /tmp/09f0e5d329344edc81b92632b2721f8e-pulp.mps timeMode elapsed branch printingOptions all solution /tmp/09f0e5d329344edc81b92632b2721f8e-pulp.sol (default strategy 1)
At line 2 NAME MODEL
At line 3 ROWS
At line 63 COLUMNS
At line 328 RHS
At line 387 BOUNDS
At line 388 ENDATA
Problem MODEL has 58 rows, 24 columns and 240 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Presolve 58 (0) rows, 24 (0) columns and 240 (0) elements
Perturbing problem by 0.001% of 20534.118 - largest nonzero change 0.072968449 ( 0.0011611913%) - largest zero change 0
0 Obj 0 Primal inf 8439.9001 (6)
28 Obj 63406111
Optimal - objective value 63406000
Optimal objective 63406000 - 28 iterations time 0.002
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.00
1
Imprimir resultados#
Antes de estudiar el óptimo del modelo, identifica en el estado del optimizador si pudo resolver el problema.
f"Estado del optimizador: {lp.LpStatus[problema.status]}"
'Estado del optimizador: Optimal'
Identifica también el valor de la función objetivo.
f"Ingresos totales: {lp.value(problema.objective)}"
'Ingresos totales: 63406000.0'
Por último, imprime de manera estructurada el valor de las variables de decisión y otras expresiones de interés.
for i in R:
print(f"\t{i}", end="")
print()
for k in K:
print(k, end="\t")
for i in R:
print(f"{lp.value(x[k, i]): .2f}", end="\t",
)
print()
Piña Guayaba Níspero Zapote
Saludable 576.00 324.00 144.00 156.00
Tropical 444.00 185.00 194.25 101.75
Mañanero 130.55 801.95 522.20 410.30
Colombiano 113.85 231.98 155.25 533.92
Refrescante 356.96 1316.29 44.62 513.13
Light 622.38 135.30 189.42 405.90
Créditos#
Equipo Principios de Optimización
Autores: Juan Felipe Rengifo
Desarrollo: Juan Felipe Rengifo, Alejandro Mantilla
Última fecha de modificación: 07/04/2023