Ответ 1
Один (не очень приятный, но, надеюсь, рабочий) вариант для решения этой проблемы заключается в том, чтобы дать решателю функцию, которая имеет только корни в ограниченной области и которая продолжается таким образом, чтобы решатель был отброшен в (немного похож на здесь, но в нескольких измерениях).
Что можно сделать для достижения этого (по крайней мере для прямоугольных ограничений) - реализовать constrainedFunction
, который линейно продолжается, начиная с граничного значения вашей функции:
import numpy as np
def constrainedFunction(x, f, lower, upper, minIncr=0.001):
x = np.asarray(x)
lower = np.asarray(lower)
upper = np.asarray(upper)
xBorder = np.where(x<lower, lower, x)
xBorder = np.where(x>upper, upper, xBorder)
fBorder = f(xBorder)
distFromBorder = (np.sum(np.where(x<lower, lower-x, 0.))
+np.sum(np.where(x>upper, x-upper, 0.)))
return (fBorder + (fBorder
+np.where(fBorder>0, minIncr, -minIncr))*distFromBorder)
Вы можете передать этой функции значение x
, функцию f
, которую вы хотите продолжить, а также два массива lower
и upper
той же формы, что и x
, что дает нижнюю и верхнюю границ во всех измерениях. Теперь вы можете передать эту функцию, а не свою оригинальную функцию, в решатель, чтобы найти корни.
Крутизна продолжения просто берется как значение границы на данный момент, чтобы предотвратить крутые скачки для изменения знака на границе. Чтобы предотвратить появление корней за пределами ограниченной области, некоторое небольшое значение добавляется/вычитается на положительные/отрицательные граничные значения. Я согласен с тем, что это не очень хороший способ справиться с этим, но, похоже, он работает.
Вот два примера. Ибо исходное предположение находится за пределами ограниченной области, но найден правильный корень в ограниченной области.
Поиск корней многомерного косинуса, ограниченного [-2, -1] x [1, 2], дает:
from scipy import optimize as opt
opt.root(constrainedFunction, x0=np.zeros(2),
args=(np.cos, np.asarray([-2., 1.]), np.asarray([-1, 2.])))
дает:
fjac: array([[ -9.99999975e-01, 2.22992740e-04],
[ 2.22992740e-04, 9.99999975e-01]])
fun: array([ 6.12323400e-17, 6.12323400e-17])
message: 'The solution converged.'
nfev: 11
qtf: array([ -2.50050470e-10, -1.98160617e-11])
r: array([-1.00281376, 0.03518108, -0.9971942 ])
status: 1
success: True
x: array([-1.57079633, 1.57079633])
Это также работает для функций, которые не диагональны:
def f(x):
return np.asarray([0., np.cos(x.sum())])
opt.root(constrainedFunction, x0=np.zeros(2),
args=(f, np.asarray([-2., 2.]), np.asarray([-1, 4.])))
дает:
fjac: array([[ 0.00254922, 0.99999675],
[-0.99999675, 0.00254922]])
fun: array([ 0.00000000e+00, 6.12323400e-17])
message: 'The solution converged.'
nfev: 11
qtf: array([ 1.63189544e-11, 4.16007911e-14])
r: array([-0.75738638, -0.99212138, -0.00246647])
status: 1
success: True
x: array([-1.65863336, 3.22942968])