найти список стационарных точек, их значений и местоположений, а также того, являются ли они минимумами или максимумами.
Это вообще неразрешимая проблема. Для этого подходит метод 1 (символический), но для сложных функций не существует символического решения для стационарных точек (нет метода для решения общей системы двух уравнений символически).
Символическое решение с SymPy
Для простых функций, таких как ваш пример, SymPy будет работать нормально. Вот полный пример нахождения стационарных точек и их классификации по собственным значениям гессиана.
import sympy as sym
x, y = sym.symbols("x y")
f = sym.cos(x*10)**2 + sym.sin(y*10)**2
gradient = sym.derive_by_array(f, (x, y))
hessian = sym.Matrix(2, 2, sym.derive_by_array(gradient, (x, y)))
Пока что гессиан является символической матрицей 2 на 2: [[200*sin(10*x)**2 - 200*cos(10*x)**2, 0], [0, -200*sin(10*y)**2 + 200*cos(10*y)**2]]
. Затем мы находим стационарные точки, приравнивая gradient
к нулю, и вставляем их в гессиан по одному.
stationary_points = sym.solve(gradient, (x, y))
for p in stationary_points:
value = f.subs({x: p[0], y: p[1]})
hess = hessian.subs({x: p[0], y: p[1]})
eigenvals = hess.eigenvals()
if all(ev > 0 for ev in eigenvals):
print("Local minimum at {} with value {}".format(p, value))
elif all(ev < 0 for ev in eigenvals):
print("Local maximum at {} with value {}".format(p, value))
elif any(ev > 0 for ev in eigenvals) and any(ev < 0 for ev in eigenvals):
print("Saddle point at {} with value {}".format(p, value))
else:
print("Could not classify the stationary point at {} with value {}".format(p, value))
Последнее предложение необходимо, потому что, когда гессиан является только полу определенным, мы не можем сказать, какой тип стационарной точки это то, что (x**2 + y**4
и x**2 - y**4
имеют один и тот же гессиан в (0, 0) ) но другое поведение). Выход:
Saddle point at (0, 0) with value 1
Local maximum at (0, pi/20) with value 2
Saddle point at (0, pi/10) with value 1
Local maximum at (0, 3*pi/20) with value 2
Local minimum at (pi/20, 0) with value 0
Saddle point at (pi/20, pi/20) with value 1
Local minimum at (pi/20, pi/10) with value 0
Saddle point at (pi/20, 3*pi/20) with value 1
Saddle point at (pi/10, 0) with value 1
Local maximum at (pi/10, pi/20) with value 2
Saddle point at (pi/10, pi/10) with value 1
Local maximum at (pi/10, 3*pi/20) with value 2
Local minimum at (3*pi/20, 0) with value 0
Saddle point at (3*pi/20, pi/20) with value 1
Local minimum at (3*pi/20, pi/10) with value 0
Saddle point at (3*pi/20, 3*pi/20) with value 1
Очевидно, solve
не нашел всех решений (их бесконечно много). Рассмотрим solve vs solveset , но в любом случае обрабатывать бесконечно много решений сложно.
Числовая оптимизация с SciPy
SciPy предлагает множество процедур числовой минимизации , включая грубую силу (это ваш метод 2; как правило, он очень и очень медленный). Это мощные методы, но рассмотрим эти моменты.
- Каждый прогон найдет только один минимум.
- Заменив f на -f, вы также можете найти максимум.
- Изменение начальной точки поиска (аргумент x0
minimize
) может привести к другому максимуму или минимуму. Тем не менее, вы никогда не узнаете, есть ли другие экстремумы, которые вы еще не видели.
- Ничего из этого не найдет седловые точки.
Смешанная стратегия
Используя lambdify
можно превратить символическое выражение в функцию Python, которую можно передавать в числовые решатели SciPy.
from scipy.optimize import fsolve
grad = sym.lambdify((x, y), gradient)
fsolve(lambda v: grad(v[0], v[1]), (1, 2))
Возвращает некоторую стационарную точку, [0.9424778 , 2.04203522]
в этом примере. Какой это пункт, зависит от первоначального предположения, которое было (1, 2). Обычно (но не всегда) вы получите решение, близкое к первоначальному предположению.
Это имеет преимущество перед подходом прямой минимизации в том, что седловые точки также могут быть обнаружены. Тем не менее, будет сложно найти все решения, поскольку каждый запуск fsolve
вызывает только одно.