Error de longitud máxima de ecuación de Python Gekko para programación entera

Resuelto mhk897 asked hace 8 meses • 0 respuestas

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.

mhk897 avatar Feb 15 '24 22:02 mhk897
Aceptado

Un problema simple demuestra el problema y una posible solución. Hay nproductos generados aleatoriamente, cada uno con un valor aleatorio pricey 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.

  1. Usar m.sum()en lugar de sum(). Esto utiliza la gekkosuma 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)
  1. 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=500o 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=5000productos.

 --------- 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._patho abrir la carpeta con m.open_folder(). Abra el archivo de texto gk0_model.apmpara 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 CPLEXy Gurobique probablemente sean más eficientes que usar un MINLPsolucionador.

John Hedengren avatar Feb 16 '2024 03:02 John Hedengren