loop for conditioanl constraint
AnsweredDear Community,
I have been trapped in the loop for conditioanl constraints. As shown in below, y, b and count are variables. Train_Sample are knwon. Here I just plan to compare y and Train_Sample. But I can't get corrct y and count. I'm confused. Thanks for your help in advance.
-
For example, $TrainSample=[1,2,3,4,5]$, optimal $y=3$, the count should be $[1,1,1,0,0]$, that's what I expect
0 -
Are you trying to model \( \texttt{Train_Sample[k]} \leq y \iff \texttt{count[k]} = 1 \) for each \(k\)? If so, you can add two indicator constraints for each \( k \):
for k in range(N):
m.addConstr((count[k] == 0) >> (Train_Sample[k] >= y + 1))
m.addConstr((count[k] == 1) >> (Train_Sample[k] <= y))I'm assuming the \(\texttt{count}\) variables are binary and \(\texttt{Train_Sample}\) contains only integers.
0 -
Thanks for your reply. Yes, I'm trying to model $Train_Sample [k] <=y$ for each $k$. The count variables are binary and Train_Sample are contain continuous values. I tried your code, but it says "int object is not callable". I'm confused.
0 -
Let me be clear. count varaiables vary with y. I need the values of count for each y, not just the final optimal y.
0 -
It's hard to say where that error comes from without seeing exactly how you define \(\texttt{count}\), \(\texttt{Train_Sample}\), and \(\texttt{y}\). Can you please post a minimal, self-contained code snippet that reproduces the error? It's also not clear to me what you mean by "each \( y \)". Your code snippet only includes a single \( y \) variable that will assume a single value at any given solution.
The following code executes without error:
import gurobipy as gp
from gurobipy import GRB
N = 10
# Generate random integers for Train_Sample
Train_Sample = [k + 1 for k in range(N)]
m = gp.Model()
count = m.addVars(N, vtype=GRB.BINARY, name="count")
y = m.addVar(name="y")
for k in range(N):
m.addConstr((count[k] == 0) >> (Train_Sample[k] >= y + 1))
m.addConstr((count[k] == 1) >> (Train_Sample[k] <= y))
# Fix y to 5 to illustrate how the count variables react
y.LB = 5
y.UB = 5
m.optimize()
# Check output
print(f"y = {y.X}")
for i in range(N):
print(f"Train_Sample[{i}] = {Train_Sample[i]}, count[{i}] = {count[i].X}")The output:
y = 5.0
Train_Sample[0] = 1, count[0] = 1.0
Train_Sample[1] = 2, count[1] = 1.0
Train_Sample[2] = 3, count[2] = 1.0
Train_Sample[3] = 4, count[3] = 1.0
Train_Sample[4] = 5, count[4] = 1.0
Train_Sample[5] = 6, count[5] = 0.0
Train_Sample[6] = 7, count[6] = 0.0
Train_Sample[7] = 8, count[7] = 0.0
Train_Sample[8] = 9, count[8] = 0.0
Train_Sample[9] = 10, count[9] = 0.00 -
Thanks for your reply. Suppose I'm considering a news vendor problem and define a function to compute the optimal order quantity.
During the process, I'm curious how will the count variables vary.
def SP_opt_P(c,r,Train_Sample):
N=len(Train_Sample)
prob=np.array([1/N]*N)
#create model
m=gp.Model('News Vendor')
M=max(Train_Sample)
#create variables
s=m.addMVar(N,name='Sells')
y=m.addVar(name='Optimal Order')
b=m.addVar(vtype=GRB.BINARY,name='b')
count=m.addMVar(N,name='count')
m.setObjective(gp.quicksum((r*s[k]-c*y)*prob[k] for k in range(N)),GRB.MAXIMIZE) #set objective
#for k in range(N): #model if Train_Sample[k]>y, then b=1, otherwise b=0
#add constraints
m.addConstrs(s[k]<=y for k in range(N))
m.addConstrs(s[k]<=Train_Sample[k] for k in range(N))
for k in range(N): #model if Train_Sample[k]>y, then b=1, otherwise b=0
m.addConstr(Train_Sample[k]>=y-M*(1-b))
m.addConstr(Train_Sample[k]<=y+M*b)
#add indicator constraints
m.addConstr((b==0)>>(count[k]==1))
m.addConstr((b==1)>>(count[k]==0))
m.optimize()
profit=round(m.Objval,3)
Order_amount=round(y.X,3)
sp=[Order_amount,profit]
SP=pd.DataFrame(sp).T
SP.columns=['Order_amount','SP_Tr_P']
print(s.X)
print(count.X)0 -
Train_Sample=[1,2,3,4,5,6,7,8,9,10]
c=5
p=20
SP_opt_P(c,p,Train_Sample)The optimal order should be $y=8$, and the profit should be $64$. But when I add the count variables, the optimal order amount becomes $10$, it's not correct.
0 -
When I replace the following section of your code:
for k in range(N): #model if Train_Sample[k]>y, then b=1, otherwise b=0
m.addConstr(Train_Sample[k]>=y-M*(1-b))
m.addConstr(Train_Sample[k]<=y+M*b)
#add indicator constraints
m.addConstr((b==0)>>(count[k]==1))
m.addConstr((b==1)>>(count[k]==0))with the indicator constraints I mentioned:
for k in range(N):
m.addConstr((count[k] == 0) >> (Train_Sample[k] >= y + 1))
m.addConstr((count[k] == 1) >> (Train_Sample[k] <= y))the code runs without error. The optimal solution value of \( y \) is \( 8 \) and the optimal objective value is \( 64 \). The optimal solution values of the \( \texttt{count} \) variables are:
[1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]
0 -
Thanks for your reply. I just tried as you said. So,ethimg is wrong. Here is my code.
import numpy as np
import math
import pandas as pd
import gurobipy as gp
from gurobipy import GRBdef SP_opt_P(c,r,Train_Sample):
N=len(Train_Sample)
prob=np.array([1/N]*N)
#create model
m=gp.Model('News Vendor')
M=max(Train_Sample)
#create variables
s=m.addMVar(N,name='Sells')
y=m.addVar(name='Optimal Order')
b=m.addVar(vtype=GRB.BINARY,name='b')
count=m.addMVar(N,vtype=GRB.BINARY,name='count')
m.setObjective(gp.quicksum((r*s[k]-c*y)*prob[k] for k in range(N)),GRB.MAXIMIZE) #set objective
#for k in range(N): #model if Train_Sample[k]>y, then b=1, otherwise b=0
#add constraints
m.addConstrs(s[k]<=y for k in range(N))
m.addConstrs(s[k]<=Train_Sample[k] for k in range(N))
for k in range(N):
m.addConstr((count[k] == 0) >> (Train_Sample[k] >= y + 1))
m.addConstr((count[k] == 1) >> (Train_Sample[k] <= y))
m.optimize()
profit=round(m.Objval,3)
Order_amount=round(y.X,3)
sp=[Order_amount,profit]
SP=pd.DataFrame(sp).T
SP.columns=['Order_amount','SP_Tr_P']
print(s.X)
print(count.X)
return SPTrain_Sample=[1,2,3,4,5,6,7,8,9,10]
c=5
p=20
SP_opt_P(c,p,Train_Sample)But I got the prompt. I'm can't find the wrong place. Could you do me a favor? Thanks a lot.
0 -
Train_Sample colud contain float variables, not just integers.
0 -
But I got the prompt. I'm can't find the wrong place. Could you do me a favor? Thanks a lot.
Can you please try upgrading to Gurobi version 10.0.1? This unexpected error is due to a bug in Gurobi 10.0.0.
Train_Sample colud contain float variables, not just integers.
In that case, we only need to change the \( 1 \) in the first indicator constraint to the smallest difference between two unique elements of \(\texttt{Train_Sample}\). This can be done as follows:
eps = min(np.diff(np.unique(Train_Sample)))
for k in range(N):
m.addConstr((count[k] == 0) >> (Train_Sample[k] >= y + eps))
m.addConstr((count[k] == 1) >> (Train_Sample[k] <= y))0 -
Hi Eli Towle, Thanks a lot. It works after I updated the Gurobi version. But I can't tell the difference between your code and mine. I think they are following the logic, though your code is more elegant.
eps = min(np.diff(np.unique(Train_Sample))) for k in range(N): m.addConstr((count[k] == 0) >> (Train_Sample[k] >= y + eps)) m.addConstr((count[k] == 1) >> (Train_Sample[k] <= y))
for k in range(N): #model if Train_Sample[k]>y, then b=1, otherwise b=0 m.addConstr(Train_Sample[k]>=y-M*(1-b)) m.addConstr(Train_Sample[k]<=y+M*b) #add indicator constraints m.addConstr((b==0)>>(count[k]==1)) m.addConstr((b==1)>>(count[k]==0))
0 -
I believe your approach will work if you use a separate binary \( \texttt{b} \) variable for each \( k \).
Also, the first constraint in your loop enforces \(\texttt{Train_Sample[k]} \leq y\) when \( \texttt{count[k]} = 1 \) and \(\texttt{Train_Sample[k]} \geq y\) when \( \texttt{count[k]} = 0 \). This means \(\texttt{count[k]}\) could equal either \( 0 \) or \( 1 \) if \( y = \texttt{Train_Sample[k]}\). To ensure \( \texttt{Train_Sample[k]} \leq y \iff \texttt{count[k]} = 1\), you should add a small positive value to the first constraint in the loop.
Altogether:
b = m.addVars(N, vtype=GRB.BINARY, name="b")
eps = min(np.diff(np.unique(Train_Sample)))
for k in range(N):
m.addConstr(Train_Sample[k] >= y - M * (1 - b[k]) + eps)
m.addConstr(Train_Sample[k] <= y + M * b[k])
m.addConstr((b[k] == 0) >> (count[k] == 1))
m.addConstr((b[k] == 1) >> (count[k] == 0))That said, I don't see a reason to introduce additional \( \texttt{b} \) variables when you can model this same logic using the existing \(\texttt{count}\) variables.
0 -
Hi, Eli Towel. Thanks a lot for your reply. I see your point. Thank you again.
0
Please sign in to leave a comment.
Comments
14 comments