#optimization #mathematical-optimization #linear-programming #pulp #integer-programming
#оптимизация #математическая оптимизация #линейное программирование #pulp #целочисленное программирование
Вопрос:
Простая задача оптимизации: найти оптимальную последовательность управления для холодильника на основе стоимости энергии. Единственное ограничение — оставаться ниже порогового значения температуры, а целевая функция пытается минимизировать стоимость используемой энергии. Эта проблема упрощается, поэтому элемент управления представляет собой просто двоичный массив, т.Е.. [0, 1, 0, 1, 0], где 1 означает использование электричества для охлаждения холодильника, а 0 означает включение механизма охлаждения (что означает отсутствие затрат на этот период, но температура будет повышаться). Мы можем предположить, что каждый период является фиксированным периодом времени и имеет постоянное изменение температуры в зависимости от его состояния включения / выключения.
Вот пример значений:
Cost of energy (for our example 5 periods): [466, 426, 423, 442, 494]
Minimum cooling periods (just as a test): 3
Starting temperature: 0
Temperature threshold(must be less than or equal): 1
Temperature change per period of cooling: -1
Temperature change per period of warming (when control input is 0): 2
И вот код в PuLP
from pulp import LpProblem, LpMinimize, LpVariable, lpSum, LpStatus, value
from itertools import accumulate
l = list(range(5))
costy = [466, 426, 423, 442, 494]
cost = dict(zip(l, costy))
min_cooling_periods = 3
prob = LpProblem("Fridge", LpMinimize)
si = LpVariable.dicts("time_step", l, lowBound=0, upBound=1, cat='Integer')
prob = lpSum([cost[i]*si[i] for i in l]) # cost function to minimize
prob = lpSum([si[i] for i in l]) >= min_cooling_periods # how many values must be positive
prob.solve()
Оптимизация, похоже, работает, прежде чем я попытаюсь учесть пороговое значение температуры. Используя только функцию стоимости, она возвращает массив из 0, что действительно минимизирует стоимость (дух). С первым ограничением (сколько значений должно быть положительным) он выбирает самые дешевые 3 периода охлаждения и правильно вычисляет общую стоимость.
obj = value(prob.objective)
print(f'Solution is {LpStatus[prob.status]}nThe total cost of this regime is: {obj}n')
for v in prob.variables():
print(f'{v.name} = {v.varValue}')
output:
Solution is Optimal
The total cost of this regime is: 1291.0
time_step_0 = 0.0
time_step_1 = 1.0
time_step_2 = 1.0
time_step_3 = 1.0
time_step_4 = 0.0
Итак, если наша управляющая последовательность [0, 1, 1, 1, 0], температура будет выглядеть примерно так в конце каждого периода охлаждения / потепления: [2, 1, 0, -1, 1]. Температура повышается на 2 всякий раз, когда управляющий вход равен 1, и снижается на 1 всякий раз, когда управляющий вход равен 1. Эта последовательность примеров является допустимым ответом, но ее придется изменить, если мы добавим пороговое значение максимальной температуры, равное 1, что будет означать, что первое значение должно быть равно 1, иначе холодильник нагреется до температуры 2.
Однако я получаю неправильные результаты при попытке указать последовательное ограничение на пребывание в пределах пороговых значений температуры с условием:
up_temp_thresh = 1
down = -1
up = 2
# here is where I try to ensure that the control sequence would never cause the temperature to
# surpass the threshold. In practice I would like a lower and upper threshold but for now
# let us focus only on the upper threshold.
prob = lpSum([e <= up_temp_thresh for e in accumulate([down if si[i] == 1. else up for i in l])]) >= len(l)
В этом случае ответ получается таким же, как и раньше, я явно неправильно формулирую его как последовательность [0, 1, 1, 1, 0] превзошел бы порог.
Я пытаюсь закодировать «температура в конце каждой управляющей последовательности должна быть меньше порогового значения». Я делаю это, превращая управляющую последовательность в массив изменений температуры, поэтому управляющая последовательность [0, 1, 1, 1, 0] дает нам изменения температуры [2, -1, -1, -1, 2]. Затем, используя accumulate
функцию, она вычисляет совокупную сумму, равную температуре холодильника после каждого шага, которая равна [2, 1, 0, -1, 1]. Я хотел бы просто проверить, меньше ли максимальное значение этого массива порогового значения, но с помощью lpSum я проверяю, что сумма значений в массиве меньше порогового значения равна длине массива, что должно быть то же самое.
Однако я явно неправильно формулирую этот шаг. Как написано, это последнее ограничение не влияет на результат, а небольшие изменения дают другие неправильные ответы. Кажется, ответ должен быть [1, 1, 1, 0, 0], который дает приемлемый температурный ряд [-1, -2, -3, -1, 1]. Как я могу указать последовательный характер управляющего ввода с помощью PuLP или другой бесплатной библиотеки оптимизации python?
Ответ №1:
Самым простым и наименее подверженным ошибкам подходом было бы создать новый набор вспомогательных переменных вашей проблемы, которые отслеживают температуру холодильника в каждом интервале. Это не «первичные переменные решения», потому что вы не можете напрямую их выбирать — скорее их значение ограничено переменными решения о включении / выключении для холодильника.
Затем вы должны добавить ограничения на эти переменные температурного состояния для представления динамики. Итак, в непроверенном коде:
l_plus_1 = list(range(6))
fridge_temp = LpVariable.dicts("fridge_temp", l_plus_1, cat='Continuos')
fridge_temp[0] = init_temp # initial temperature of fridge - a known value
for i in l:
prob = fridge_temp[i 1] == fridge_temp[i] 2 - 3*s[i]
Затем вы можете отправить минимальные / максимальные температурные ограничения для этих новых fridge_temp
переменных.
Обратите внимание, что в приведенном выше я предположил, что переменные температуры холодильника определяются с большим интервалом, чем решения о включении / выключении для холодильника. Переменные температуры холодильника представляют температуру в начале интервала, а наличие одной дополнительной означает, что мы можем гарантировать, что конечная температура холодильника приемлема.
Комментарии:
1. Хороший ответ. Можно изменить
cat
наInteger
иs[i]
наsi[i]
, а затем добавить временное ограничение, и оно работает. Спасибо