I have an objective function that has a if
conditional in it. I am having trouble implementing it in Gurobi Python.
Background
There are s
suppliers and p
plants. x[s][p]
is a variable that indicates the number of items that flow from supplier-x
to plant-p
. c[s][p]
indicates the cost of supplying one item from a supplier to a center.
Additionally, there is a fixed cost t[s]
for each supplier. If a supplier supplies to any center, this fixed cost is incurred (this fixed cost does not depend on the number of items).
I want to minimize the cost using an objective function like -
The first part is easy to model like sum(x[s, p] * spc[s, p] for s in range(num_suppliers) for p in range(num_center))
.
For the second term, how can I model it? (The second part basically means that add the supplier's fixed cost only if the supplier is actually supplier anything to any plant).
Edit
This is the code I have now. Note: This does not produce the minimum value -
from gurobipy import *
supplier_capacity = [
5, 10
]
plant_demand = [
2, 4
]
num_suppliers = len(supplier_capacity)
num_plants = len(plant_demand)
t = [
100, 1
]
c = {
(0, 0): 1,
(0, 1): 4,
(1, 0): 4,
(1, 1): 2
}
x = {} # flow between each supplier to plant
m = Model()
xl = [(s, p) for s in range(num_suppliers) for p in range(num_plants)]
x = m.addVars(xl, vtype=GRB.INTEGER, lb=0, name='flow')
for s in range(num_suppliers):
m.addConstr(x.sum(s, '*') <= supplier_capacity[s])
for p in range(num_plants):
m.addConstr(x.sum('*', p) >= plant_demand[p])
m.setObjective(
(
sum(x[s, p] * c[s, p] for s in range(num_suppliers) for p in range(num_plants)) +
sum(t[s] for s in range(num_suppliers) if x.sum(s, '*') >= 0)
), GRB.MINIMIZE
)
m.update()
m.optimize()
if m.status == GRB.Status.OPTIMAL:
print('==== RESULTS ====')
print('Min Cost: {}'.format(m.ObjVal))
for v in m.getVars():
print('{} = {}'.format(v.VarName, v.X))
else:
print('Infeasible model')
Since x is a decision variable, you can not use it with a standard python if statement. Instead, you need to add a binary indicator variable (y_s) that will be forced to the value 1 whenever any of the shipment variables (x_sp) at non-zero. Then you add the indicator variable to the objective function with the coefficient t_s.
y = [m.addVar(vtype='B', obj=t_s) for t_s in t]
for s, y_s in enumerate(y):
for p in range(num_plants):
big_M = min(supplier_capacity[s], plant_demand[p])
m.addConstr(big_M * y_s >= x[(s, p)]
The constraints force each supplier to be "on" if it ships anything to any plant. The big_M value is an upper bound on the amount a supplier could ship to the plant. Since y is a binary variable, it must be 1 if any of the relevant x variables are non-zero. Conversely, if y is 1, then any or all of the relevant x variables will be effectively unconstrained. Since the coefficient on the y variables are all positive and you are minimizing you don't need an explicit constraint that y be 0 if all the x's are zero.