Как правильно использовать минимизацию от 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),

Другие вопросы по тегам