У меня проблема с оптимизацией, и я решаю ее с помощью scipy и модуля минимизации. Я использую SLSQP как метод, потому что он единственный, который подходит для моей проблемы. Функция для оптимизации — это функция стоимости, где «x» представляет собой список процентов. У меня есть некоторые ограничения, которые необходимо соблюдать:
Ниже вы можете увидеть модель моей попытки. Проблема заключается в функции ограничения. Я получаю решения, в которых сумма «prop» больше, чем «probertyMax».
import numpy as np
from scipy.optimize import minimize
class objects:
def __init__(self, percentOfInput, min, max, cost, proberty1, proberty2):
self.percentOfInput = percentOfInput
self.min = min
self.max = max
self.cost = cost
self.proberty1 = proberty1
self.proberty2 = proberty2
class data:
def __init__(self):
self.objectList = list()
self.objectList.append(objects(10, 0, 20, 200, 2, 7))
self.objectList.append(objects(20, 5, 30, 230, 4, 2))
self.objectList.append(objects(30, 10, 40, 270, 5, 9))
self.objectList.append(objects(15, 0, 30, 120, 2, 2))
self.objectList.append(objects(25, 10, 40, 160, 3, 5))
self.proberty1Max = 1
self.proberty2Max = 6
D = data()
def optiFunction(x):
for index, obj in enumerate(D.objectList):
obj.percentOfInput = x[1]
costSum = 0
for obj in D.objectList:
costSum += obj.cost * obj.percentOfInput
return costSum
def PercentSum(x):
y = np.sum(x) -100
return y
def constraint(x, val):
for index, obj in enumerate(D.objectList):
obj.percentOfInput = x[1]
prop = 0
if val == 1:
for obj in D.objectList:
prop += obj.proberty1 * obj.percentOfInput
return D.proberty1Max -prop
else:
for obj in D.objectList:
prop += obj.proberty2 * obj.percentOfInput
return D.proberty2Max -prop
def checkConstrainOK(cons, x):
for con in cons:
y = con['fun'](x)
if con['type'] == 'eq' and y != 0:
print("eq constrain not respected y= ", y)
return False
elif con['type'] == 'ineq' and y <0:
print("ineq constrain not respected y= ", y)
return False
return True
initialGuess = []
b = []
for obj in D.objectList:
initialGuess.append(obj.percentOfInput)
b.append((obj.min, obj.max))
bnds = tuple(b)
cons = list()
cons.append({'type': 'eq', 'fun': PercentSum})
cons.append({'type': 'ineq', 'fun': lambda x, val=1 :constraint(x, val) })
cons.append({'type': 'ineq', 'fun': lambda x, val=2 :constraint(x, val) })
solution = minimize(optiFunction,initialGuess,method='SLSQP',\
bounds=bnds,constraints=cons,options = {'eps':0.001,'disp':True})
print('status ' + str(solution.status))
print('message ' + str(solution.message))
checkConstrainOK(cons, solution.x)
Нет способа найти решение, но вывод такой:
Positive directional derivative for linesearch (Exit mode 8)
Current function value: 4900.000012746761
Iterations: 7
Function evaluations: 21
Gradient evaluations: 3
status 8
message Positive directional derivative for linesearch
Где моя вина? В данном случае он заканчивается модой 8, потому что пример очень маленький. С большими данными алгоритм заканчивается режимом 0. Но я думаю, что он должен заканчиваться намеком на то, что ограничение не может быть выполнено.
Не имеет значения, установлено ли для proberty1Max значение 4 или 1. Но в случае, если оно равно 1, правильного решения быть не может.
PS: Я многое изменил в этом вопросе... Теперь код исполняемый.
Обновлено: 1. Хорошо, я узнал, что неравное ограничение принимается, если результат положительный (> 0). В прошлом я думаю, что <0 также будет принято. Из-за этого функция ограничения теперь немного короче.
Я думаю, что это может быть связано с проблемой tux007, упомянутой в комментариях. ссылка на выпуск Я думаю, что оптимизатор работает неправильно, если первоначальная догадка не является правильным решением. Линейное программирование не годится для переопределенных уравнений. Моя проблема не имеет единственного решения, это приближение.
Эй, Эрвин, я только что обновил вопрос. Надеюсь информации достаточно.
conMax = [{'type': 'ineq', 'fun': MaxLimit(x)}]
вы передаете не вызываемый MaxLimit
в качестве ограничения, а постоянное значение, возвращаемое MaxLimit(x)
. Это специально?
Эй, Дион, хорошая мысль! Я обновил код. Это резюме... В реальном коде я использую лямбда-функцию, потому что MaxLimit() имеет более одного параметра, и я добавляю некоторое ограничение с помощью цикла.
возможно это связано с этим багом github.com/stevenj/nlopt/issues/254
Может быть полезно, если вы опубликуете либо алгебраическое, либо псевдоалгебраическое представление вашей задачи оптимизации. Мне кажется, вы пытаетесь максимизировать sum_i cost[i] * percentOfInput[i]
, с учетом sum_i percentOfInput[i] == 1
и... чего-то еще? Можете ли вы подтвердить или исправить то, что я написал, и заполнить «что-то еще»? У меня такое ощущение, что вашу проблему можно сформулировать как линейную программу, которую вы могли бы решить с помощью LP-решателя вместо чего-то вроде SLSQP.
Как упоминалось в комментарии, я думаю, что это проблема: Вводящий в заблуждение вывод из ....
Если вы посмотрите на последние изменения, ограничение не выполняется, но алгоритм говорит: «Положительная производная по направлению для линейного поиска».
SLSQP предполагает наличие непрерывных и дифференцируемых (гладких) функций. Я думаю, что вы нарушаете это, что может привести к проблемам. (Хотя вы говорите, что
max
иtat
— постоянные данные, что странно).