Writing in Minimum Order Quantity and Order Discounts into Jupyter Notebook
回答済みHello,
I am having some trouble figuring out how to add a minimum order quantity and an order discount into my code. The minimum order quantity is 5 and the discount is if you order 5 pieces, your price is $2, if you order 10 pieces, your price is $1 and if you order 15 pieces, your price is $.50. Here is the code I have so far in Jupyter Notebook. Any help is appreciated!
import gurobipy as gp
from gurobipy import GRB
import sys
# Number of workers required for each shift
fixedChargeForOrders = 100
initialInv = 20
minOrderQty = 0
weeks, demand, holdingCost = gp.multidict({
1 : [10,2],
2 : [10,2],
3 : [10,2],
4 : [0,2],
5 : [0,2],
6 : [15,2],
7 : [20,2],
8 : [20,2],
9 : [0,2],
10 : [10,2],
})
minInventory = {t : 0.1*demand[t] for t in weeks}
# Model
m = gp.Model("lotsize")
x = m.addVars(weeks, name="x")
y = m.addVars(weeks, name="y")
z = m.addVars(weeks, vtype=GRB.BINARY, name="z")
M = sum(demand)
m.setObjective(y.prod(holdingCost) + sum(z[t]*fixedChargeForOrders for t in␣
,→weeks), GRB.MINIMIZE)
m.addConstrs(y[t] >= minInventory[t] for t in weeks)
m.addConstr(initialInv + x[1] - demand[1] == y[1])
m.addConstrs(y[t] + x[t+1] - demand[t+1] == y[t+1] for t in range(1,len(weeks)))
m.addConstrs(x[t] <= M*z[t] for t in weeks)
# Optimize
m.optimize()
2
print("cost = ", round(m.ObjVal))
print("total holding cost = ", round(sum(y[t].X*holdingCost[t] for t in weeks)))
print("total fixed charges = ", sum(z[t].X*fixedChargeForOrders for t in weeks))
print("demand", "orderAmt\t", "inv\t", "minInv\t", "placed", "holdingCost")
for t in weeks:
print(demand[t], "\t", round(x[t].X), "\t", round(y[t].X),␣
,→"\t",minInventory[t], "\t", round(z[t].X), "\t", round(y[t].X*holdingCost[t]))
-
正式なコメント
This post is more than three years old. Some information may not be up to date. For current information, please check the Gurobi Documentation or Knowledge Base. If you need more help, please create a new post in the community forum, or try Gurobot, our chatbot interface offering instant, expert-level support. -
One way to model your discount is to use a piecewise-linear function (cf. piecewise-linear section in the documentation for more details)
Let's assume that the maximum order quantity is \(100\). You can define the cost depending on the quantity as
import gurobipy as gp
from gurobipy import GRB
m = gp.Model("test")
quantity = m.addVar(lb=5, ub=100, vtype=GRB.INTEGER, name="quantity")
cost = m.addVar(lb=0.5, ub=2, vtype=GRB.CONTINUOUS, name="cost")
m.addGenConstrPWL(quantity, cost, [5, 9, 10, 14, 15, 100], [2, 2, 1, 1, 0.5, 0.5])The piecewise-linear function states
\[\text{cost}=\begin{cases} 2, &\text{if quantity} \in [5,9] \\ 1, &\text{if quantity} \in [10,14] \\ 0.5, &\text{if quantity} \in [15,100] \end{cases}.\]
The same function also works if the maximum order quantity is \(>100\) because Gurobi interpolate the last two points of the piecewise-linear function to compute the function value for larger quantities.
Best regards,
Jaromił0 -
Hello
I'm not a native English speaker, please forgive me
Can you please tell me how to write the objective function of piecewise-linear function in gurobi for this questionBest regards
0 -
Hi Chou,
It seems like you did not successfully post your question regarding the piecewise-linear formulation. Could you please retry?
Best regards,
Jaromił0 -
0
-
Could you show the last ~20 lines of the log file?
You could try fixing the variable flow_add_Artificial_i3 to value 8 via an equality constraint. If the model solves with a worse objective value, then value 9 is indeed better. If the objective value is better, then you should try to lower the MIPGap value. If the model is infeasible, then please refer to How do I determine why my model is infeasible?
0 -
Thank you for your reply Jaromił Najman
I'm not sure what the log file is, so I searched online for the log file of gurobi, and my log file should be as followsRestricted license - for non-production use only - expires 2023-10-25
Set parameter NonConvex to value 2
Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
Thread count: 6 physical cores, 12 logical processors, using up to 12 threads
Optimize a model with 96 rows, 138 columns and 276 nonzeros
Model fingerprint: 0x6713140c
Model has 46 quadratic objective terms
Model has 46 general constraints
Variable types: 92 continuous, 46 integer (0 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [9e+00, 9e+00]
QObjective range [2e+00, 2e+00]
Bounds range [8e+00, 1e+06]
RHS range [3e+00, 1e+06]
PWLCon x range [3e+02, 2e+03]
PWLCon y range [8e+00, 1e+01]
Presolve added 0 rows and 68 columns
Presolve removed 6 rows and 0 columns
Presolve time: 0.00s
Presolved: 145 rows, 234 columns, 629 nonzeros
Presolved model has 27 SOS constraint(s)
Presolved model has 27 bilinear constraint(s)
Variable types: 207 continuous, 27 integer (0 binary)
Root relaxation: objective 5.659300e+05, 129 iterations, 0.00 seconds (0.00 work units)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 565930.000 0 3 - 565930.000 - - 0s
H 0 0 567889.00000 565930.000 0.34% - 0s
* 0 0 0 565930.00000 565930.000 0.00% - 0s
Cutting planes:
Gomory: 1
RLT: 1
Explored 1 nodes (130 simplex iterations) in 0.01 seconds (0.00 work units)
Thread count was 12 (of 12 available processors)
Solution count 2: 565930 567889
Optimal solution found (tolerance 1.00e-04)
Best objective 5.659300000000e+05, best bound 5.659300000000e+05, gap 0.0000%
obj:565930
flow_add_Incentive_i1-> 134
flow_add_Incentive_i2-> 3853
flow_add_Incentive_i3-> 1738
flow_add_Incentive_i4-> 0
flow_add_Incentive_i5-> 1850
flow_add_Incentive_i6-> 603
flow_add_Incentive_i7-> 987
flow_add_Incentive_i8-> 0
flow_add_Incentive_i9-> 358
flow_add_Incentive_i10-> 0
flow_add_Incentive_i11-> 530
flow_add_Incentive_i12-> 3
flow_add_Incentive_i13-> 9548
flow_add_Incentive_i14-> 522
flow_add_Incentive_i15-> 141
flow_add_Incentive_i16-> 168
flow_add_Incentive_i17-> 0
flow_add_Incentive_i18-> 300
flow_add_Incentive_i19-> 161
flow_add_Incentive_i20-> 436
flow_add_Incentive_i21-> 49
flow_add_Incentive_i22-> 0
flow_add_Incentive_i23-> 1170
flow_add_Artificial_i1-> 3780
flow_add_Artificial_i2-> 2219
flow_add_Artificial_i3-> 1959
flow_add_Artificial_i4-> 2790
flow_add_Artificial_i5-> 0
flow_add_Artificial_i6-> 3780
flow_add_Artificial_i7-> 0
flow_add_Artificial_i8-> 0
flow_add_Artificial_i9-> 0
flow_add_Artificial_i10-> 3073
flow_add_Artificial_i11-> 0
flow_add_Artificial_i12-> 0
flow_add_Artificial_i13-> 0
flow_add_Artificial_i14-> 0
flow_add_Artificial_i15-> 0
flow_add_Artificial_i16-> 0
flow_add_Artificial_i17-> 2276
flow_add_Artificial_i18-> 0
flow_add_Artificial_i19-> 0
flow_add_Artificial_i20-> 0
flow_add_Artificial_i21-> 0
flow_add_Artificial_i22-> 2673
flow_add_Artificial_i23-> 0
flow_cut_Incentive_i1-> 1188
flow_cut_Incentive_i2-> 0
flow_cut_Incentive_i3-> 2330
flow_cut_Incentive_i4-> 1856
flow_cut_Incentive_i5-> 0
flow_cut_Incentive_i6-> 1071
flow_cut_Incentive_i7-> 0
flow_cut_Incentive_i8-> 0
flow_cut_Incentive_i9-> 332
flow_cut_Incentive_i10-> 301
flow_cut_Incentive_i11-> 430
flow_cut_Incentive_i12-> 0
flow_cut_Incentive_i13-> 2433
flow_cut_Incentive_i14-> 0
flow_cut_Incentive_i15-> 23
flow_cut_Incentive_i16-> 208
flow_cut_Incentive_i17-> 0
flow_cut_Incentive_i18-> 37
flow_cut_Incentive_i19-> 41
flow_cut_Incentive_i20-> 35
flow_cut_Incentive_i21-> 0
flow_cut_Incentive_i22-> 0
flow_cut_Incentive_i23-> 453
flow_cut_Artificial_i1-> 0
flow_cut_Artificial_i2-> 874
flow_cut_Artificial_i3-> 0
flow_cut_Artificial_i4-> 990
flow_cut_Artificial_i5-> 1805
flow_cut_Artificial_i6-> 0
flow_cut_Artificial_i7-> 694
flow_cut_Artificial_i8-> 0
flow_cut_Artificial_i9-> 0
flow_cut_Artificial_i10-> 707
flow_cut_Artificial_i11-> 0
flow_cut_Artificial_i12-> 0
flow_cut_Artificial_i13-> 3125
flow_cut_Artificial_i14-> 923
flow_cut_Artificial_i15-> 0
flow_cut_Artificial_i16-> 0
flow_cut_Artificial_i17-> 922
flow_cut_Artificial_i18-> 0
flow_cut_Artificial_i19-> 0
flow_cut_Artificial_i20-> 0
flow_cut_Artificial_i21-> 0
flow_cut_Artificial_i22-> 699
flow_cut_Artificial_i23-> 0
art_cost_add_Artificial_i1-> 8
art_cost_add_Artificial_i2-> 8
art_cost_add_Artificial_i3-> 8
art_cost_add_Artificial_i4-> 8
art_cost_add_Artificial_i5-> 10
art_cost_add_Artificial_i6-> 8
art_cost_add_Artificial_i7-> 10
art_cost_add_Artificial_i8-> 10
art_cost_add_Artificial_i9-> 10
art_cost_add_Artificial_i10-> 8
art_cost_add_Artificial_i11-> 10
art_cost_add_Artificial_i12-> 10
art_cost_add_Artificial_i13-> 10
art_cost_add_Artificial_i14-> 10
art_cost_add_Artificial_i15-> 10
art_cost_add_Artificial_i16-> 10
art_cost_add_Artificial_i17-> 8
art_cost_add_Artificial_i18-> 10
art_cost_add_Artificial_i19-> 10
art_cost_add_Artificial_i20-> 10
art_cost_add_Artificial_i21-> 10
art_cost_add_Artificial_i22-> 8
art_cost_add_Artificial_i23-> 10
art_cost_cut_Artificial_i1-> 10
art_cost_cut_Artificial_i2-> 8
art_cost_cut_Artificial_i3-> 10
art_cost_cut_Artificial_i4-> 8
art_cost_cut_Artificial_i5-> 8
art_cost_cut_Artificial_i6-> 10
art_cost_cut_Artificial_i7-> 8
art_cost_cut_Artificial_i8-> 10
art_cost_cut_Artificial_i9-> 10
art_cost_cut_Artificial_i10-> 8
art_cost_cut_Artificial_i11-> 10
art_cost_cut_Artificial_i12-> 10
art_cost_cut_Artificial_i13-> 8
art_cost_cut_Artificial_i14-> 8
art_cost_cut_Artificial_i15-> 10
art_cost_cut_Artificial_i16-> 10
art_cost_cut_Artificial_i17-> 8
art_cost_cut_Artificial_i18-> 10
art_cost_cut_Artificial_i19-> 10
art_cost_cut_Artificial_i20-> 10
art_cost_cut_Artificial_i21-> 10
art_cost_cut_Artificial_i22-> 8
art_cost_cut_Artificial_i23-> 100 -
Thanks for the output. Now you can try fixing the particular variable to the value you think is correct.
variablename = "<some_name>"
var = model.getVarByName(variablename)
model.addConstr(var == <the_value_it_should_have>, name="fix_constraint")0 -
Thank you for your reply Jaromił Najman
But my shipping cost variable art _cost variable depends on the number of shipments, so I should not be able to fix my cost variable, I am referring to the quantity discount( piecewise-linear function )you mentioned in this discussion board
for h in lacks:
for i,j in art_arcs:
#add
if h == "add":
m.addGenConstrPWL(flow[h,i,j], art_cost[h,i,j], [0,979,980,1959,1960,2940], [10,10,9,9,8,8])
#cut
else:
m.addGenConstrPWL(flow[h,i,j], art_cost[h,i,j], [0, 279,280,559,560,840], [10,10,9,9,8,8])The model runs out that the flow_add_Artificial_i3-> 1959 shipping volume is 1959, but its cost is 8. According to the above code constraint the shipping cost should be 9 because it doesn't touch 1960. I checked the link of gurobi piecewise-linear function you posted and I don't know where the error is.
0 -
Your flow variables are continuous. However, your print statement prints them as integers
print('%s-> %d'%(v.varName,v.x))The value of the flow_add_Artificial_i3 is not 1959 but 1959.9999999999 which is the float representation of 1960. It is getting truncated because you are telling Python to convert it to an integer by using \(\texttt{%d}\).
The correct output should be
print('%s-> %f'%(v.varName,v.x))Note that even integer variables can attain continuous values, e.g., 0.9999999 instead of 1.
0 -
Omg thank you very much, I did not notice this detail, I have another question, I revise quantity discount cost to [20,20,10,10,5,5], as the code below, but the model becomes infeasible, this should be free to adjust, right. Sorry for taking your time.
#add
if h == "add":
m.addGenConstrPWL(flow[h,i,j], art_cost[h,i,j], [0,980,981,1960,1961,2940], [20,20,10,10,5,5])
#cut
else:
m.addGenConstrPWL(flow[h,i,j], art_cost[h,i,j], [0,280,281,560,561,840], [20,20,10,10,5,5])0 -
The Knowledge Base article How do I determine why my model is infeasible? should be helpful.
0 -
Thanks you so much Jaromił Najman
0
投稿コメントは受け付けていません。
コメント
13件のコメント