如何在Gurobi Python目标函数中对`if`条件进行建模?

时间:2019-06-24 14:36:13

标签: python mathematical-optimization linear-programming gurobi

我有一个目标函数,其中包含一个if条件。我无法在Gurobi Python中实现它。

背景

s个供应商和p个工厂。 x[s][p]是一个变量,指示从supplier-xplant-p的项数。 c[s][p]表示从供应商向中心供应一件商品的成本。

此外,每个供应商都有固定成本t[s]。如果供应商向任何中心供应产品,则将产生此固定成本(该固定成本与项目数无关)。

我想使用-

这样的目标函数将成本降到最低

Objective Function

第一部分很容易像sum(x[s, p] * spc[s, p] for s in range(num_suppliers) for p in range(num_center))那样建模。

对于第二个学期,我该如何建模? (第二部分基本上意味着仅当供应商实际上是任何工厂的供应商时,才增加供应商的固定成本。)

修改

这是我现在拥有的代码。注意:这不会产生最小值-

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')

2 个答案:

答案 0 :(得分:2)

由于x是决策变量,因此您可以not与标准python if语句一起使用它。相反,您需要添加一个二进制indicator变量(y_s),只要任何装运变量(x_sp)为非零,该变量都将被强制为值1。然后将指标变量的系数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)]

如果将任何产品运到任何工厂,这些约束将迫使每个供应商处于“启用”状态。 big_M值是供应商可以运送到工厂的数量的上限。由于y是二进制变量,因此如果任何相关的x变量都不为零,则必须为1。相反,如果y为1,则任何或所有相关的x变量将实际上不受约束。由于y变量的系数全部为正,并且您将其最小化,因此,如果所有x均为零,则不需要明确约束y为0。

答案 1 :(得分:0)

您是否正在寻找类似的东西?在这里,您只需要看起来像这样的数组的第一个和最后一个元素。因此,只有第一行和最后一行的每一列的总和> = 1。

array([[ 0,  1,  2,  3],
       [ 4,  -5,  -6,  -7],
       [ 8,  9, 10, 11]])
num_suppliers, num_center = 3, 4

t = [1,2,3]
x = {
     (0, 0): 0,
     (0, 1): 1,
     (0, 2): 2,
     (0, 3): 3,
     (1, 0): 4,
     (1, 1): -5,
     (1, 2): -6,
     (1, 3): -7,
     (2, 0): 8,
     (2, 1): 9,
     (2, 2): 10,
     (2, 3): 11
     }

sum(t[s] for s in range(num_suppliers) if sum(x[s, p] for p in range(num_center)) >= 1)

输出:4