Как правильно использовать минимизацию от scipy с несколькими переменными?
У меня есть пять переменных, которые я хотел бы подчинить scipy.optimize.minimize
чтобы найти решение, которое я ищу с точки зрения A
, B
, C
, D
, а также E
, Во-первых, я импортировал minimize
из scipy
и определили начальные догадки (на основе лабораторных экспериментов, поэтому они должны быть действительными).
from scipy.optimize import minimize
A0 = 1.90
B0 = 6.40
C0 = 11.7
D0 = 3.70
E0 = 2.50
ABCDE0 = [A0, B0, C0, D0, E0]
Во-вторых, я определил отдельные функции, которые составляют целевую функцию, удобно названную Objective
, Я также пытался объединить F
, G
, H
, а также I
в одну функцию без какой-либо удачи, поэтому я решил оставить это пока так.
def F(abcde):
a, b, c, d, e = abcde
return c - (b ** 2) / (a - e)
def G(abcde):
a, b, c, d, e = abcde
return (4 * e * ((a - e) * c - b ** 2)) / (a * c - b ** 2)
def H(abcde):
a, b, c, d, e = abcde
return b / (2 * (a - e))
def I(abcde):
a, b, c, d, e = abcde
return (2 * e * b) / (a * c - b ** 2)
def Objective(abcde):
return (F(abcde) / G(abcde)) / (H(abcde) / I(abcde))
В-третьих, я определил constraint
(т.е. (F/G)/(H/I)=1
) а также границы, названные bnds
быть +/- 10%
из первоначальных догадок ради простоты.
def constraint(x):
F = x[0]
G = x[1]
H = x[2]
I = x[3]
return (F / G) / (H / I) - 1
con = {'type': 'eq', 'fun': constraint1}
min_per = 0.9
max_per = 1.1
bnds = ((A0*min_per, A0*max_per), (B0*min_per, B0*max_per),
(C0*min_per, C0*max_per), (D0*min_per, D0*max_per),
(E0*min_per, E0*max_per))
В-четвертых, и наконец, minimize
предоставил мне решение под названием sol
,
sol = minimize(Objective, ABCDE0, method='SLSQP', bounds=bnds, constraints=con1, options={'disp':True})
Если Sol должен был быть напечатан print(sol)
появится следующее сообщение.
Positive directional derivative for linesearch (Exit mode 8)
Current function value: 1.0
Iterations: 18
Function evaluations: 188
Gradient evaluations: 14
fun: 1.0
jac: array([ 0.00000000e+00, 1.49011612e-08, -7.45058060e-09, 0.00000000e+00,
0.00000000e+00])
message: 'Positive directional derivative for linesearch'
nfev: 188
nit: 18
njev: 14
status: 8
success: False
x: array([ 2.09 , 5.76 , 10.53 , 4.07 , 2.50000277])
На мой взгляд, начинающий constraint
кажется, что проблема, но я не уверен из-за отсутствия опыта minimize
,
- Что я делаю не так?
- Почему он не выполняется успешно?
- Это лучше использовать
root_scalar
как предполагает @fuglede в этом сценарии? - Можно ли объединить все отдельные функции в одну функцию, чтобы избежать путаницы?
Помните, что D0
а также d
не включены ни в одну из функций, но важны в общих схемах вещей как одна из пяти независимых переменных.
1 ответ
Здесь происходит несколько вещей:
Во-первых, по вкусу, я бы, наверное, оставил функции F
..., I
как наличие нескольких входов, чтобы избежать распаковки списка. Целевой функции все же нужен список в качестве аргументов; то есть вы могли бы сделать что-то вроде
def F(a, b, c, d, e):
return c - (b ** 2) / (a - e)
...
def objective(abcde):
return (F(*abcde) / G(*abcde)) / (H(*abcde) / I(*abcde))
Это просто стиль. Что еще более важно, ваш constraint
Метод не совсем делает то, что вы хотите: как я понимаю, вы хотите оценить функции F
..., I
на входах, но этого никогда не происходит; вместо этого значение переменной F
(это неудачное имя, поскольку оно скрывает название функции) a
, Вместо этого вы бы сделали что-то вроде
def constraint(x):
return (F(*x) / G(*x)) / (H(*x) / I(*x)) - 1
В настоящее время, constraint(x)
ничего кроме objective(x) - 1
Таким образом, ваши ограничения в конечном итоге заявив, что ваш objective
должен быть равен 1 при возможном решении. Это означает, что на самом деле оптимизации не происходит вообще: любое возможное решение является оптимальным. Пока априори minimize
действительно будет пытаться найти реальное решение для вас, скорее всего, вам повезет больше, если вы попытаетесь использовать некоторые из корневых возможностей scipy.optimize
как то, что вы делаете, пытается найти корни функции objective - 1
,
Теперь, наконец, это на самом деле оказывается простым: ваша функция objective
равно 1, где бы он ни был определен, поэтому любой ввод является возможным решением: во-первых, записав F = ((a-e)c - (b**2))/(a-e)
, Обратите внимание, что (a*c-b**2) / (4*e*(a-e))
, Затем, (F/G)*I = (2*e*b)/(4*e*(a-e)) = b/(2*(a-e)) = H
, так 1 = (F/G)*(I/H) = (F/G)/(H/I)
,