Logarithmic Transformation
AnsweredHi, I am currently modifying an existing notebook titled "Part Two: Avocado Pricing and Supply Using Mathematical Optimization" https://github.com/Gurobi/modeling-examples/blob/master/price_optimization/price_optimization_gurobiML_gcl.ipynb. My objective is to apply logarithmic transformations to the price and units_sold variables in the "predict sales" section. After making the necessary changes, the updated code looks like this. However, I am facing an issue in the last part where I try to "fire up the solver", and it indicates that the problem is infeasible or unbounded. I am unsure if I made any errors or if I missed changing some lines of code in the previous section. If the problem had a solution before, it should still have one even after applying the logarithmic transformations, right?
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.compose import make_column_transformer
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
from sklearn.metrics import r2_score
import gurobipy_pandas as gppd
from gurobi_ml import add_predictor_constr
X = df[["region", "price", "year", "peak"]]
y = df["units_sold"]
X_copy = X.copy()
X_copy["price"] = np.log1p(X_copy["price"])
y = np.log1p(y)
df.loc[:, "price"] = X_copy["price"].values
df.loc[:, "units_sold"] = y.values
X_train, X_test, y_train, y_test = train_test_split(
X_copy, y, train_size=0.8, random_state=1
)
feat_transform = make_column_transformer(
(OneHotEncoder(drop="first"), ["region"]),
(StandardScaler(), ["price", "year"]),
("passthrough", ["peak"]),
verbose_feature_names_out=False,
remainder='drop'
)
reg = make_pipeline(feat_transform, LinearRegression())
y_pred = reg.predict(X_test)
print(f"The R^2 value in the test set is {np.round(r2_score(y_test, y_pred),5)}")
reg.fit(X, y)
y_pred_full = reg.predict(X)
r2_full = r2_score(y, y_pred_full)
print(f"El valor de R^2 en todo el conjunto de datos es {np.round(r2_full, 5)}")
# Sets and parameters
#Definir la cantidad total de suministro de palta (B).
B = 35 # total amount of avocado supply
#Definir si es temporada alta o no (peak_or_not) y el año actual (year).
peak_or_not = 1 # 1 if it is the peak season; 0 if isn't
year = 2022
#Definir el costo de desperdiciar una palta (c_waste).
c_waste = 0.1 # the cost ($) of wasting an avocado
#Definir el costo de transporte de una palta por región (c_transport) en un DataFrame de pandas.
# the cost of transporting an avocado
c_transport = pd.Series(
{
"Great_Lakes": 0.3,
"Midsouth": 0.1,
"Northeast": 0.4,
"Northern_New_England": 0.5,
"SouthCentral": 0.3,
"Southeast": 0.2,
"West": 0.2,
"Plains": 0.2,
}, name='transport_cost'
)
c_transport = c_transport.loc[regions]
a_min = 0 # minimum avocado price
a_max = 3 # maximum avocado price
data = pd.concat([c_transport,
df.groupby("region")["units_sold"].min().rename('min_delivery'),
df.groupby("region")["units_sold"].max().rename('max_delivery')], axis=1)
feats = pd.DataFrame(
data={
"year": year,
"peak": peak_or_not,
"region": regions,
},
index=regions
)
m = gp.Model("Avocado_Price_Allocation")
p = gppd.add_vars(m, data, name="price", lb=a_min, ub=a_max) # price of an avocado for each region
x = gppd.add_vars(m, data, name="x", lb='min_delivery', ub='max_delivery') # number of avocados supplied to each reagion
s = gppd.add_vars(m, data, name="s") # predicted amount of sales in each region for the given price
w = gppd.add_vars(m, data, name="w") # excess wasteage in each region
d = gppd.add_vars(m, data, lb=-gp.GRB.INFINITY, name="demand") # Add variables for the regression
m.addConstr(x.sum() == B)
gppd.add_constrs(m, s, gp.GRB.LESS_EQUAL, x)
gppd.add_constrs(m, s, gp.GRB.LESS_EQUAL, d)
gppd.add_constrs(m, w, gp.GRB.EQUAL, x - s)
m_feats = pd.concat([feats, p], axis=1)[["region", "price", "year", "peak"]]
pred_constr = add_predictor_constr(m, reg, m_feats, d)
pred_constr.print_stats()
m.setObjective((p * s).sum() - c_waste * w.sum() - (c_transport * x).sum(), gp.GRB.MAXIMIZE)
m.Params.NonConvex = 2
m.optimize()
Set parameter NonConvex to value 2
Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)
CPU model: Intel(R) Xeon(R) CPU @ 2.20GHz, instruction set [SSE2|AVX|AVX2]
Thread count: 1 physical cores, 2 logical processors, using up to 2 threads
Optimize a model with 49 rows, 128 columns and 184 nonzeros
Model fingerprint: 0xcb340a3b
Model has 8 quadratic objective terms
Coefficient statistics:
Matrix range [6e-02, 2e+00]
Objective range [1e-01, 5e-01]
QObjective range [2e+00, 2e+00]
Bounds range [2e-01, 2e+03]
RHS range [1e+00, 2e+03]
Presolve removed 8 rows and 80 columns
Presolve time: 0.01s
Barrier solved model in 0 iterations and 0.01 seconds (0.00 work units)
Model is infeasible or unbounded
-
Hi Florencia,
I just ran your code, if I compute the IIS and write this to a file:
m.computeIIS()
m.write("avocado.ilp")I get the following:
Maximize
Subject To
R0: x[Great_Lakes] + x[Midsouth] + x[Northeast] + x[Northern_New_England]
+ x[SouthCentral] + x[Southeast] + x[West] + x[Plains] = 35
Bounds
-infinity <= x[Great_Lakes] <= 2.091217523087725
-infinity <= x[Midsouth] <= 1.969706417191851
-infinity <= x[Northeast] <= 2.286090422809815
-infinity <= x[Northern_New_England] <= 0.6512746083344716
-infinity <= x[SouthCentral] <= 2.426851474271583
-infinity <= x[Southeast] <= 2.175941311368734
-infinity <= x[West] <= 2.507544234367833
-infinity <= x[Plains] <= 1.520715809714555
EndSo your supply constraint can no longer be met \(\texttt{B}=35\).
You can change this constraint to \(\leq\) or recompute a valid supply value (of course, this must be lower than the sum of the upper bounds of the \(\texttt{x}\) variables).
Cheers,
David0
Please sign in to leave a comment.
Comments
1 comment