Error de longitud máxima de ecuación de Python Gekko para programación entera
Utilicé Gekko para resolver un problema MILP, donde se espera que seleccione el precio óptimo para cada producto, con el fin de maximizar mi función objetivo (la ganancia total). Sin embargo, tuve que dividir el marco de datos, ya que si incluyo todo el conjunto de datos, obtengo un error de longitud máxima de ecuación. ¿Hay alguna forma de resolver esto para ingresar un marco de datos más grande en el solucionador? Originalmente estaba usando el paquete PuLP, pero el tiempo de ejecución del código tardaba excesivamente para conjuntos de datos más grandes. Adjunté mi código a continuación.
# Preprocess data
price_matrix = []
profit_matrix = []
revenue_matrix = []
grevenue_matrix = []
num_prices_per_product = []
df = df_org[:200]
# Iterate over unique products
for product in df['Product'].unique():
# Filter data for the current product
product_data = df[df['Product'] == product]
# Extract unique price points and sort in descending order
price_points = np.sort(product_data['Sales Price'].unique())[::-1]
num_prices = len(price_points)
# Preallocate arrays
product_profit_matrix = np.zeros(num_prices)
product_revenue_matrix = np.zeros(num_prices)
product_grevenue_matrix = np.zeros(num_prices)
# Iterate over price points
for i, price in enumerate(price_points):
# Filter data for the current price point
price_data = product_data[product_data['Sales Price'] == price]
if not price_data.empty:
# Assign values to matrices
product_profit_matrix[i] = price_data['Profit Margin'].iloc[0]
product_revenue_matrix[i] = price_data['Revenue'].iloc[0]
product_grevenue_matrix[i] = price_data['Gross revenue'].iloc[0]
# Append matrices and other information
price_matrix.append(price_points)
profit_matrix.append(product_profit_matrix)
revenue_matrix.append(product_revenue_matrix)
grevenue_matrix.append(product_grevenue_matrix)
num_prices_per_product.append(num_prices)
start = time.time()
# Initialize gekko model
m = GEKKO(remote=False)
# Decision variables
x = {}
for i, product_name in enumerate(df['Product'].unique()):
for j in range(max(num_prices_per_product)):
x[(product_name, j)] = m.Var(value=0, lb=0, ub=1, integer=True)
# Objective function (maximize total profit)
total_profit = 0
for i, product_name in enumerate(df['Product'].unique()):
for j in range(num_prices_per_product[i]):
total_profit += profit_matrix[i][j] * x[(product_name, j)]
m.Maximize(total_profit)
# Constraint: Each product must have exactly one selected price
for i, product_name in enumerate(df['Product'].unique()):
m.Equation(sum(x[(product_name, j)] for j in range(num_prices_per_product[i])) == 1)
# Discount Constraint
revenue_difference = 0
for i, product_name in enumerate(df['Product'].unique()):
for j in range(num_prices_per_product[i]):
revenue_difference += (grevenue_matrix[i][j] - revenue_matrix[i][j]) * x[(product_name, j)]
discount_constraint = 0.13 # Set your desired maximum discount
discount_tolerance = 0.01
total_gross_revenue = 0
for i, product_name in enumerate(df['Product'].unique()):
for j in range(num_prices_per_product[i]):
total_gross_revenue += grevenue_matrix[i][j] * x[(product_name, j)]
m.Equation(revenue_difference <= (discount_constraint + discount_tolerance) * total_gross_revenue)
m.Equation(revenue_difference >= (discount_constraint - discount_tolerance) * total_gross_revenue)
# Profit Constraint
profit_constraint = 6000
profit_tolerance = 0.05
m.Equation(total_profit <= profit_constraint + (profit_tolerance * profit_constraint))
m.Equation(total_profit >= profit_constraint - (profit_tolerance * profit_constraint))
# Solve the optimization problem
m.solve(disp=True)
end = time.time()
print("Optimization Time:", end - start)
# Print the results
print("Status:", m.options.SOLVESTATUS)
print("Total Profit ($):", total_profit)
# Print the selected prices
results_df = pd.DataFrame(selected_prices, columns=["Product", "Selected Price Point", "Value (AED)"])
print(tabulate(results_df, headers='keys', tablefmt='fancy_grid', showindex=False))
Intenté usar Gekko y PuLP para resolver un problema MILP, pero tuve problemas al usar mi algoritmo en conjuntos de datos grandes.
Un problema simple demuestra el problema y una posible solución. Hay n
productos generados aleatoriamente, cada uno con un valor aleatorio price
y revenue
. El objetivo es maximizar el beneficio seleccionando los 10 mejores productos. La solución selecciona los 10 productos con mayores márgenes.
from gekko import GEKKO
import numpy as np
# Random data
n=500
price = np.random.randn(n)
revenue = np.random.randn(n)
# Create Gekko model
m = GEKKO(remote=False)
# Decision variables
x = [None]*n
for i in range(n):
x[i] = m.Var(value=0,lb=0,ub=1,integer=True)
# Select 10 products out of n
m.Equation(sum(x)==10)
# Maximize profit
profit = 0
for i in range(n):
profit+=x[i]*(price[i]-revenue[i])
m.Maximize(profit)
m.options.SOLVER=1
m.solve()
Cuando n>=413
, existe el error de que una expresión simbólica tiene más de 15.000 caracteres.
APM model error: string > 15000 characters
Consider breaking up the line into multiple equations
The may also be due to only using newline character CR
instead of CR LF (for Windows) or LF (for MacOS/Linux)
To fix this problem, save APM file with appropriate newline characters
STOPPING...
Hay dos cambios que abordan el problema de las expresiones largas.
- Usar
m.sum()
en lugar desum()
. Esto utiliza lagekko
suma que tarda más en compilarse, pero no genera una sola expresión grande que pueda violar el límite de 15.000 caracteres.
# Select 10 products out of n
m.Equation(m.sum(x)==10)
- Para la función objetivo, intente utilizar múltiples funciones objetivo sin la suma.
# Maximize profit
for i in range(n):
m.Maximize(x[i]*(price[i]-revenue[i]))
Ahora el modelo puede resolver con n=500
o muchos más productos.
from gekko import GEKKO
import numpy as np
# Random data
n=5000
price = np.random.randn(n)
revenue = np.random.randn(n)
# Create Gekko model
m = GEKKO(remote=False)
# Decision variables
x = [None]*n
for i in range(n):
x[i] = m.Var(value=0,lb=0,ub=1,integer=True)
# Select 10 products out of n
m.Equation(m.sum(x)==10)
# Maximize profit
for i in range(n):
m.Maximize(x[i]*(price[i]-revenue[i]))
m.options.SOLVER=1
m.solve()
Aquí está la solución con n=5000
productos.
--------- APM Model Size ------------
Each time step contains
Objects : 1
Constants : 0
Variables : 5001
Intermediates: 0
Connections : 5001
Equations : 5001
Residuals : 5001
Number of state variables: 5001
Number of total equations: - 2
Number of slack variables: - 0
---------------------------------------
Degrees of freedom : 4999
----------------------------------------------
Steady State Optimization with APOPT Solver
----------------------------------------------
Iter: 1 I: 0 Tm: 0.02 NLPi: 3 Dpth: 0 Lvs: 0 Obj: -4.47E+01 Gap: 0.00E+00
Successful solution
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 3.630000000703149E-002 sec
Objective : -44.6851835859332
Successful solution
---------------------------------------------------
Puede inspeccionar las expresiones simbólicas en la carpeta de ejecución temporal m._path
o abrir la carpeta con m.open_folder()
. Abra el archivo de texto gk0_model.apm
para ver las variables, ecuaciones y la función objetivo como un archivo de lo que se compiló en código de bytes.
Los problemas a gran escala con muchas variables de decisión binarias pueden resolverse lentamente. Existen opciones de solucionador APOPT que pueden ayudar a acelerar la solución con el solucionador MINLP. Si el problema es MILP, existen buenos solucionadores como CPLEX
y Gurobi
que probablemente sean más eficientes que usar un MINLP
solucionador.