Вот попытка реализации подхода к хохолку , описанного Эрвином Кальвелагеном .
Сначала построим полином с корнями в 0, 15, 30 и 50
который является положительным в нужном регионе:
In [123]: x = np.linspace(-1, 51, 100)
In [124]: plt.plot(x,-(x-0)*(x-15)*(x-30)*(x-50))
Out[124]: [<matplotlib.lines.Line2D at 0x7fa01d65b748>]
In [125]: plt.axhline(color='red')
Out[145]: <matplotlib.lines.Line2D at 0x7fa01d6620b8>
In [146]: plt.show()
Теперь вы можете использовать этот многочлен в качестве ограничения:
import numpy as np
import scipy.optimize as optimize
cons = (
{'type': 'ineq', 'fun': lambda x: -(x-0)*(x-15)*(x-30)*(x-50)}, )
def f(x):
return (x-20)**2
res = optimize.basinhopping(f, [40], minimizer_kwargs={'method':'SLSQP', 'constraints': cons}, niter=10, stepsize=20)
print(res)
выходы
message: 'Optimization terminated successfully.'
nfev: 13
nit: 4
njev: 4
status: 0
success: True
x: array([15.])
message: ['requested number of basinhopping iterations completed successfully']
minimization_failures: 0
nfev: 178
nit: 10
njev: 56
x: array([15.])
Обратите внимание, что первоначальное предположение было на 40
, и все же optimize.basinhopping
удалось найти минимум в другом несмежном интервале на x = 15
.
Использование размера шага, порядка порядка расстояния между двумя интервалами, важно, чтобы дать basinhopping
возможность выборки из обоих интервалов.
Без обхода бассейна, optimize.minimize
с использованием SLSQP с невыпуклым ограничением может не дать выборку из всех допустимых интервалов. Например,
import scipy.optimize as optimize
cons = ({'type': 'ineq', 'fun': lambda x: -(x-0)*(x-15)*(x-30)*(x-50)}, )
def f(x):
return (x-20)**2
res = optimize.minimize(
f, [40], method='SLSQP', constraints=cons)
print(res.x)
# [30.]
res = optimize.minimize(
f, [5], method='SLSQP', constraints=cons)
print(res.x)
# [15.]
показывает, что вам придется запускать optimize.minimize
дважды с предположением в каждом интервале
чтобы найти истинный минимум.