Дискретная оптимизация (ограничение SOS1) - GEKKO - PullRequest
2 голосов
/ 23 апреля 2020

Я пытаюсь определить проблему оптимизации с GEKKO в Python, и я хочу использовать некоторые переменные проекта с предопределенным списком вариантов. Кроме того, у каждого выбора есть связанная стоимость, и ограничение будет заключаться в том, что общая стоимость должна быть ниже указанного предела.

Ниже приведен типичный пример gekko (найден здесь ) с модификацией, что x1 и x2 равны sos1. Также с индексом выбранных значений x1 и x2 я нахожу связанные с ними затраты из другого списка, и их сумма должна быть меньше определенного значения (ограничения).

from gekko import GEKKO
def test(x1,x2,x3,x4):
    res = x1*x4*(x1+x2+x3)+x3
    return res

def check(x1,x2):
    tt = [1,2,3,4,5]
    cost = [10,10,10,2,1]
    if x1.value in tt:
        y1 = tt.index(x1.value)
        y2 = tt.index(x2.value)
        C = cost[y1]+cost[y2]
        return C
    return 10

m = GEKKO() # Initialize gekko
m.options.SOLVER=1  # APOPT is an MINLP solver

# optional solver settings with APOPT
m.solver_options = ['minlp_maximum_iterations 500', \
                    # minlp iterations with integer solution
                    'minlp_max_iter_with_int_sol 10', \
                    # treat minlp as nlp
                    'minlp_as_nlp 0', \
                    # nlp sub-problem max iterations
                    'nlp_maximum_iterations 50', \
                    # 1 = depth first, 2 = breadth first
                    'minlp_branch_method 1', \
                    # maximum deviation from whole number
                    'minlp_integer_tol 0.05', \
                    # covergence tolerance
                    'minlp_gap_tol 0.01']

# Integer constraints for x3 and x4
x3 = m.Var(value=1,lb=1,ub=5,integer=True)
x4 = m.Var(value=2,lb=1,ub=5,integer=True)
x1 = m.sos1([1,2,3,4,5])
x2 = m.sos1([1,2,3,4,5])

# Equations
m.Equation(x1*x2*x3*x4>=25)
m.Equation(x1**2+x2**2+x3**2+x4**2==40)
m.Equation(check(x1,x2)<=5)
m.Obj(test(x1,x2,x3,x4)) # Objective

m.solve(disp=False) # Solve
print('Results')
print('x1: ' + str(x1.value))
print('x2: ' + str(x2.value))
print('x3: ' + str(x3.value))
print('x4: ' + str(x4.value))
print('Objective: ' + str(m.options.objfcnval))

Примечание. Мне пришлось добавить блок if в функцию check в качестве начального значения x1 и x2, по-видимому, равно нулю.

Этот код не не работает, и я получаю следующую ошибку.

> Exception has occurred: Exception
 @error: Equation Definition
 Equation without an equality (=) or inequality (>,<)
 true
 STOPPING...

Я не знаю, что вызывает эту ошибку. Как мне переформулировать мою модель, чтобы получить желаемый результат?

Редактировать: этот пример кода является лишь моей попыткой воссоздать ошибку. Мое настоящее приложение - это проектирование инженерной системы. Например, предположим, что система имеет 2 компонента - батарею и лампочку. У меня есть два варианта батареи: батарея А весит 10 кг и ее надежность составляет 0,97, а батарея В весит 6 кг и ее надежность составляет 0,75. Точно так же есть разные варианты лампочки. Мне нужно выбрать вариант для батареи и лампы таким образом, чтобы общая надежность системы была как можно выше (объективная), а общий вес был меньше «х» кг (ограничение). В приведенном выше коде представьте значения x1 и x2 в качестве выбранных вариантов для компонентов, и я нахожу их индекс для получения их соответствующего веса / стоимости (если были выбраны батарея A и лампочка B, я получу их веса, чтобы проверить, общий вес меньше допустимого предела). Теперь моя действительная система имеет n компонентов и m вариантов для каждого компонента. И каждый выбор имеет вес, стоимость, надежность и т. Д. c. Я пытаюсь найти оптимальную комбинацию, чтобы максимизировать надежность системы с ограничениями по весу, стоимости и т. Д. c

Ответы [ 2 ]

3 голосов
/ 24 апреля 2020

В дополнение к хорошему ответу от пользователя7440787, вам нужно найти несколько значений из предопределенного набора дискретных проектных переменных. Вместо использования предопределенной функции m.SOS1() вы можете использовать что-то вроде следующего для t ie одного двоичного массива переменных решения для нескольких корреляций или переменных.

from gekko import GEKKO
m = GEKKO(remote=False)
# design variable
y = m.Var(lb=1,ub=5)
# options
n = 4
# weight
weight=[19.05-y, 25.0-0.1*y**2, 29.3-0.02*y**3, 30.2]
# cost
cost = [3.2+y,2.4+0.01*y**2,1.6+y+0.001*y**3,5.2]
# SOS1 with binary variables
b = m.Array(m.Var,n,lb=0,ub=1,integer=True)
m.Equation(m.sum(b)==1) # only select one
# cost x weight
cxw = m.sum([b[i]*cost[i]*weight[i] for i in range(4)])
# minimize cost x weight
m.Minimize(cxw)
# change to APOPT solver
m.options.SOLVER = 1
m.solve(disp=False)
print('Design Variable: ' + str(y.value[0]))
print('Option: ' + str(b))

В этом примере у вас есть один дизайн переменная y и различные уравнения для cost и weight, основанные на расчетной переменной. Общая цель - минимизировать произведение cost и weight при настройке y.

2 голосов
/ 24 апреля 2020

Я построил простую модель на основе вашего примера описания.

from gekko import GEKKO
import numpy as np

m = GEKKO() # Initialize gekko
m.options.SOLVER=1  # APOPT is an MINLP solver

# optional solver settings with APOPT
m.solver_options = ['minlp_maximum_iterations 500', \
                    # minlp iterations with integer solution
                    'minlp_max_iter_with_int_sol 10', \
                    # treat minlp as nlp
                    'minlp_as_nlp 0', \
                    # nlp sub-problem max iterations
                    'nlp_maximum_iterations 50', \
                    # 1 = depth first, 2 = breadth first
                    'minlp_branch_method 1', \
                    # maximum deviation from whole number
                    'minlp_integer_tol 0.05', \
                    # covergence tolerance
                    'minlp_gap_tol 0.01']


x1 = m.Array(m.Var, 5, **{'value':0,'lb':0,'ub':1, 'integer': True}) # battery options
print(f'x1_initial: {x1}')
x2 = m.Array(m.Var, 5, **{'value':0,'lb':0,'ub':1, 'integer': True}) # bulb options
print(f'x2_initial: {x2}')
bat_cost = np.array([ 10, 2, 3, 4, 5])  # battery costs
bat_weigh = np.array([ 1, 25, 20, 19, 20])  # battery weighs
bulb_cost = np.array([ 2, 5, 33, 24, 5])  # bulb costs
bulb_weigh = np.array([ 6, 10, 2, 10, 20])  # bulb weighs
m.Equation( sum(bat_weigh * x1) + sum(bulb_weigh * x2) <= 25)  # limit total weigh 
m.Equation(m.sum(x1) == 1)  # restrict choice to a single battery 
m.Equation(m.sum(x2) == 1)  # restrict choice to a single bulb
m.Obj( sum(bat_cost * x1) + sum(bulb_cost * x2) ) # Objective

m.solve(disp=False) # Solve
print('Results:')
print(f'x1: {x1}')
print(f'x2: {x2}')
print(f'battery cost: {sum(np.array([i[0] for i in x1]) * bat_cost)}')
print(f'battery weigh: {sum(np.array([i[0] for i in x1]) * bat_weigh)}')
print(f'bulb cost: {sum(np.array([i[0] for i in x2]) * bulb_cost)}')
print(f'bulb weigh: {sum(np.array([i[0] for i in x2]) * bulb_weigh)}')
print('Objective value: ' + str(m.options.objfcnval))

В результате получилось следующее:

x1_initial: [0 0 0 0 0]
x2_initial: [0 0 0 0 0]
Results:
x1: [[0.0] [0.0] [0.0] [1.0] [0.0]]
x2: [[1.0] [0.0] [0.0] [0.0] [0.0]]
battery cost: 4.0
battery weigh: 19.0
bulb cost: 2.0
bulb weigh: 6.0
Objective value: 6.0

Это очень простой пример, демонстрирующий представление представления Информация о батарее и лампочке. Это можно сделать более сложным, но мне нужно больше подробностей и понять, почему у вас есть полиномиальные уравнения, что они представляют.

И еще раз повторюсь, ошибка, которую вы получаете, имеет отношение к строке:

m.Equation(check(x1,x2)<=5)
...