Добавление двоичной переменной в Gurobi

#python #linear-programming #gurobi #integer-programming

#python #линейное программирование #gurobi #целочисленное программирование

Вопрос:

Итак, я хочу добавить двоичную переменную, z где z[i, j] = 1 , когда расстояние между i и j меньше или равно 150 и z[i, j] = 0 в противном случае. У меня есть список, c где каждая c[i][j] представляет расстояние между i и j . Я, конечно, не могу настроить z как обычную двоичную переменную ниже:

 y = m.addVars(I, J, vtype=GRB.BINARY, name="assign")
  

И я хочу добавить ограничения:

 # One day mailing
m.addConstrs(
    z[i,j] <= y[i,j] for i in I for j in J,
    "Serve")

# Coverage Constraint
m.addConstr(
   quicksum(h[i] * z[i, j] for i in I for j in J) <= 
        0.9 * quicksum(h[i] * y[i, j] for i in I for j in J),
        "Cover")
  

где h — список с целыми числами. Как мне настроить z ?

Ответ №1:

Сначала вам нужно добавить z в качестве двоичной переменной:

 z = m.addVars(I, J, vtype=GRB.BINARY, name="z")
  

Тогда вам нужны ограничения, гарантирующие, что z[i, j] = 1 тогда и только тогда, когда c[i, j] <= 150 .
Один из способов сделать это — использовать ограничения индикатора:

 z = 1 -> c <= 150
z = 0 -> c >= 150
  

Это эквивалентно

 c > 150 -> z = 0
c < 150 -> z = 1
  

Вы добавляете их следующим образом:

 m.addConstrs((z[i, j] == 1) >> (c[i][j] <= 150) for i in I for j in J)
m.addConstrs((z[i, j] == 0) >> (c[i][j] >= 150) for i in I for j in J)
  

Вы также можете смоделировать это самостоятельно явно:
Если у вас есть верхняя и нижняя границы M и m для значения c[i][j] - 150 (т.е. M >= c[i][j] - 150 >= m для всех i, j ), вы можете использовать следующие ограничения:

 M * (1-z) >= c - 150
m * z <= c - 150
  

Если c > 150 , то правые части обоих неравенств будут положительными. Затем первая вызывает 1 - z = 1 и, следовательно z = 0 . Второе неравенство будет тривиально выполнено.

Если c < 150 , то правые стороны отрицательны. Первое неравенство становится тривиальным, в то время как второе вызывает z = 1 .

Для M максимальной записи в c будет достаточно, поскольку m вы можете выбрать, -150 если все c[i][j] значения неотрицательны.

Вы добавляете эти ограничения следующим образом:

 m.addConstrs( M * (1 - z[i, j]) >= c[i][j] - 150 for i in I for j in J )
m.addConstrs( m * z[i,j] <= c[i][j] - 150 for i in I for j in J )
  

Обратите внимание, что я пренебрег случаем, когда c = 150 . Это связано с тем, что для чисел с плавающей запятой равенства всегда считаются выполняемыми только в пределах допусков, и, следовательно, не существует простого способа отличить строгие неравенства от нестрогих. Вы можете приблизить это с помощью epsilon, например:

 z = 0 -> c >= 150   epsilon