通过Gurobi Python添加变量和约束而无需枚举所有元素的有效方法

时间:2018-09-21 22:55:15

标签: python loops mathematical-optimization linear-programming gurobi

我正在学习通过Gurobi python构建优化模型,但是在寻找定义决策变量和约束的pythonic方法时遇到了一些问题:

假设我有这些设置:

time={morning, afternoon, evening};
interval={early,late};
food={burger, banana, apple, orange};

我的决策变量是二进制进餐[时间,间隔,食物]。但是,我只有如下定义的一组可能的选项,不能枚举我的集合中的所有元素:

time      interval  food  number  value
morning   early   banana   2      500
morning   early   apple    3      600
afternoon early   burger   1      800
evening    late   orane    2      400

所以我的eat变量只能是以下内容:

eat[morning,early,banana]
eat[morning,early,apple]
eat[afternoon,early,burger]
eat[evening,late,orange]

我做不到:

eat = m.addVars(time, interval, food, name = "Eat", vtype=GRB.BINARY)

我可以做类似的事情:

eat = {}
for row in input.to_dict('records'):
         key = (row['time'], row['interval'],row['food'])
         eat[key] =  m.addVar(name = "Eat", vtype=GRB.BINARY)

但是我仍然很难定义我的目标,那就是数量和价值相乘并进食,我正在寻找一种更一致,更优雅的方法:

obj = quicksum(number[i,j,k]*value[i, j, k] * eat[i, j, k] for i in time 
for j in interval for k in food)

上面将列举所有错误的内容,而我尝试了如下操作:

obj = quicksum(number[key]*value[key] * eat[key] \
                 for key in eat)

将其限制为仅在字典中定义的组合,但是当我不得不像下面这样分隔字典的元素时,我就在约束中苦苦挣扎:

m.addConstrs(quicksum(eat[i,j,k] for k in food)==1 for i in time for j in interval)

或类似的

m.addConstrs(quicksum(eat[morning,j,banana] ==1) for j in interval)

很长的问题很抱歉。来自优化/ Python专家的任何帮助都将非常有用。

1 个答案:

答案 0 :(得分:3)

它可以帮助您利用Gurobi Python API必须存储的变量tupledict。它具有一些方便的方法,使您可以轻松地对变量求和,乘法或切片。我在下面提供了一个完整的示例。

from gurobipy import GRB, Model
import numpy as np

tuples = [('morning', 'early', 'banana'),('morning', 'early', 'apple'), 
('afternoon', 'early', 'burger'), ('evening', 'late', 'orane')]

numbers, values = [2, 3, 1, 2], [500, 600, 800, 400]

m = Model('SO52451928')

eat = m.addVars(tuples, name='eat', vtype=GRB.BINARY)

coeffs = np.array(numbers) * np.array(values) # Can be made with regular lists as well
coeffs = dict(zip(tuples, coeffs))

obj = eat.prod(coeffs)
m.setObjective(obj)

# This structure holds the unique combinations of (time, interval) that 
# appear in the data. They are necessary, because they form the set over which
# our constraints are defined
time_intervals = set(zip(*zip(*tuples)[:2]))

constrs = m.addConstrs((
    eat.sum(i, j, '*') == 1 for i, j in time_intervals), name='one_food')

m.write(m.ModelName+'.lp')
m.optimize()

if m.SolCount > 0:
    print(zip(m.getAttr(
    'VarName', m.getVars()), m.getAttr('x', m.getVars())))

我希望这会有所帮助!