Indicator variable that depends on two other indicator variables
AnsweredHi,
I want to create an indicator that is defined by two other indicators. More specifically, I have the indicators “start” and “finish”, indicating the whether a machine started working at a position i or whether it finished the work (commodity at 0).
I encounter a difficulty to define a new variable restricted[i,t], that indicates when fields are started but not finished.
restricted = 1 only if start[i,t] + finish [i,t] is bigger 0 and smaller 2, otherwise 0. In my binary case I can define restricted = 1 if start + finish = 1
However, when defining such case I get an infeasible model.
How can I define a case like this?
Below you can find code that creates a test case and the output of my IIS file.
Python Code:
from gurobipy import Model, GRB, quicksum
# Define the sets I, Ih, T, H
H = [1]
I = [1]
Ih = [0] + I # I plus the depot 0
T = [0, 1, 2, 3, 4,5]
# Create a new Gurobi model
model = Model("test_model")
# Define variables
commodity = model.addVars(I, range(1), range(1), T, vtype=GRB.CONTINUOUS, name="b", lb=0)
machine_flow = model.addVars(H, Ih, Ih, T, vtype=GRB.CONTINUOUS, name="machine_flow", lb=0)
finish = model.addVars(I, T, vtype=GRB.BINARY, name="finish")
start = model.addVars(I, T, vtype=GRB.BINARY, name="start")
restricted = model.addVars(I, T, vtype=GRB.BINARY, name="restricted")
# Define Values:
# Set artificial commodity values
model.addConstr(commodity[1, 0, 0, 0] == 100)
model.addConstr(commodity[1, 0, 0, 1] == 100)
model.addConstr(commodity[1, 0, 0, 2] == 50)
model.addConstr(commodity[1, 0, 0, 3] == 0)
model.addConstr(commodity[1, 0, 0, 4] == 0)
model.addConstr(commodity[1, 0, 0, 5] == 0)
# Set artificial machine_flow values
model.addConstr(machine_flow[1, 0, 0, 0] == 1)
model.addConstr(machine_flow[1, 0, 1, 1] == 1)
model.addConstr(machine_flow[1, 1, 1, 2] == 1)
model.addConstr(machine_flow[1, 1, 1, 3] == 1)
model.addConstr(machine_flow[1, 1, 0, 4] == 1)
model.addConstr(machine_flow[1, 0, 0, 5] == 1)
# set 0 for all other machine values
for h in H:
for i in Ih:
for j in Ih:
for t in T:
if (h, i, j, t) not in [(1, 0, 0, 0), (1, 0, 1, 1), (1, 1, 1, 2), (1, 1, 1, 3),
(1, 1, 0, 4), (1, 0, 0, 5)]:
model.addConstr(machine_flow[h, i, j, t] == 0)
# Define start variable as an indicator for when a machine has visited a field
for i in I:
for t in T:
model.addGenConstrIndicator(
start[i, t],
1,
quicksum(machine_flow[h, i, i, t_bar] for h in H for t_bar in range(t + 1)) >= 1)
model.addGenConstrIndicator(
start[i, t],
0,
quicksum(machine_flow[h, i, i, t_bar] for h in H for t_bar in range(t + 1)) <= 1e7)
# Ensure the 'finish' variable is 1 when beets are 0 using indicator constraints
for i in I:
for t in T:
model.addGenConstrIndicator(
finish[i, t],
0,
commodity[i, 0, 0, t] >= 1e7)
model.addGenConstrIndicator(
finish[i, t],
1,
commodity[i, 0, 0, t] <= 1e7)
# restricted is 0 when start and finish is 0 (we cant say 0 in gurobi, hence the 1e7)
for i in I:
for t in T:
model.addGenConstrIndicator(
restricted[i, t],
1,
start[i,t] + finish[i,t] == 1)
# restricted is also 0 when start and finish is bigger 2 or smaller 0.5
for i in I:
for t in T:
model.addGenConstrIndicator(
restricted[i, t],
0,
start[i,t] + finish[i,t] >= 2)
for i in I:
for t in T:
model.addGenConstrIndicator(
restricted[i, t],
0,
start[i,t] + finish[i,t] <= 0.5)
# Optimize the model
model.optimize()
# Check if the model is infeasible
if model.status == GRB.INFEASIBLE:
print("Model is infeasible. Computing IIS to find the issue...")
# Compute the Irreducible Inconsistent Subsystem (IIS)
model.computeIIS()
# Write the IIS to a file
model.write("model_iis.ilp")
print("IIS written to file 'model_iis.ilp'")
print("Review the IIS file to identify conflicting constraints.")
# Check and print the results
if model.status == GRB.OPTIMAL:
for i in I:
for t in T:
print(f"start[{i}, {t}] = {start[i, t].x}")
print(f"finish[{i}, {t}] = {finish[i, t].x}")
print(f"restricted[{i}, {t}] = {restricted[i, t].x}")
print(f"commodity[{i}, 0, 0, {t}] = {commodity[i, 0, 0, t].x}")
print(f"machine_flow[1, {i}, {i}, {t}] = {machine_flow[1, i, i, t].x}")
Output:
\ Model test_model_copy
\ LP format  for model browsing. Use MPS format to capture full model detail.
Minimize
0 finish[1,0] + 0 start[1,0] + 0 restricted[1,0]
Subject To
R0: b[1,0,0,0] = 100
R26: machine_flow[1,1,1,0] = 0
GC0: start[1,0] = 1 > machine_flow[1,1,1,0] >= 1
GC13: finish[1,0] = 1 > b[1,0,0,0] <= 1e07
GC24: restricted[1,0] = 1 > finish[1,0] + start[1,0] = 1
GC30: restricted[1,0] = 0 > finish[1,0] + start[1,0] >= 2
Bounds
b[1,0,0,0] free
machine_flow[1,1,1,0] free
Binaries
finish[1,0] start[1,0] restricted[1,0]
End

Hi Ludwig,
Thanks for posting here! I have not fully analyzed your model (we typically try to provide hints that help you take a next step by yourself), but noticed a few things.
 Just to be sure about the meaning of indicator constraints: When calling addGenConstrIndicator(binary, value, expr) then the meaning is "if binary=value then force expr" where some of your comments suggest the reverse "if expr then force binary=value". The latter is not necessarily true.
 For restricted[i, t]=0 you define two consequences. Note that those consequences cannot be true at the same time (lines 8496). From that, it can be concluded restricted[i, t] cannot be 0 ever, so it is forced to be 1. Could this lead to infeasibility?
 The exact meaning of the start/finish/restricted variables is not entirely clear to me. Usually we have variables that mean the following:
 Started[t]=1 when an operation has started at or before time t; this series starts at 0 and at some point switches to 1
 Finished[t]=1 when the operation has finished at or before time t (with discrete time buckets, it would mean finished before bucket t); it has a similar pattern to started
 Busy[t]=1 when the operation is busy (started but not finished); this series starts with 0s, then has 1s when started and becomes 0s when finished
Do you have the same in mind?
Hope this helps; otherwise let us know.
Kind regards,
Ronald0
Please sign in to leave a comment.
Comments
1 comment