给定一个函数f(x)
,它接受一个输入向量x
并返回一个相同长度的向量,你怎么能找到x
上函数设置约束的根? (例如x
的每个组成部分的范围。)
令我惊讶的是,我找不到很多关于此的有用信息。在Optimization and Root finding algorithms的scipy列表中,似乎有一些标量函数的选项,例如brentq。我找不到任何支持多变量情况的算法的算法。
当然,人们可以像处理返回向量的每个组件一样进行解决,然后使用其中一个最小化器,例如differential_evolution(这是我认为的唯一一个)。我无法想象这是一个很好的策略,因为它杀死了牛顿算法的二次收敛。此外,我发现真的令人惊讶的是,似乎没有这个选项,因为它一定是一个非常普遍的问题。我错过了什么吗?
答案 0 :(得分:8)
如果你想使用约束来处理优化,你可以使用轻松的lirbary,这比scipy.optimize
容易得多。以下是该软件包的链接:
https://pypi.python.org/pypi/facile/1.2
以下是如何使用easyile库作为示例。你需要改进我在这里写的东西,这只是一般性的。如果您有错误,请告诉我哪个。
import facile
# Your vector x
x = [ facile.variable('name', min, max) for i in range(Size) ]
# I give an example here of your vector being ordered and each component in a range
# You could as well put in the range where declaring variables
for i in range(len(x)-1):
facile.constraint( x[i] < x[i+1])
facile.constraint( range[i,0] < x[i] < range[i,1] ) #Supposed you have a 'range' array where you store the range for each variable
def function(...)
# Define here the function you want to find roots of
# Add as constraint that you want the vector to be a root of function
facile.constraint(function(x) == 0)
# Use facile solver
if facile.solve(x):
print [x[i].value() for i in range(len(x))]
else:
print "Impossible to find roots"
答案 1 :(得分:7)
解决这个问题的一个(不是特别好但有希望工作)选项是为求解器提供一个只在受约束区域中具有根的函数,并且继续以确保求解器被推回到适当的区域(有点像here,但有多个维度)。
实现这一点(至少对于矩形约束)可能会做的是实现从函数的边界值开始线性延续的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])
答案 2 :(得分:2)
冒着建议你可能已经克服的风险,我认为只有scipy.minimize
才能实现这一点。问题是该函数必须只有一个参数,但是该参数可以是一个矢量/列表。
因此f(x,y)变为f(z),其中z = [x,y]。
如果您没有找到有用的好例子是here。
如果你想强加一个2x1向量的边界,你可以使用:
# Specify a (lower, upper) tuple for each component of the vector
bnds = [(0., 1.) for i in len(x)]
并将其用作bounds
中的minimize
参数。