Python:具有约束的多变量非线性求解器

Wol*_*ger 15 python algorithm optimization

给定一个f(x)带有输入向量x并返回相同长度向量的函数,如何找到函数设置约束的根x?(例如,每个组成部分的范围x.)

令我惊讶的是,我找不到很多关于此的有用信息.在优化和根查找算法的scipy列表中,似乎有一些标量函数的选项,如brentq.我找不到任何支持多变量情况的选项的算法.

当然,人们可以像处理返回向量的每个组件一样进行解决,然后使用其中一个最小化器,例如differential_evolution(这是我认为的唯一一个).我无法想象这是一个很好的策略,因为它杀死了牛顿算法的二次收敛.此外,我发现真的令人惊讶的是,似乎没有这个选项,因为它一定是一个非常普遍的问题.我错过了什么吗?

Que*_*ski 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"
Run Code Online (Sandbox Code Playgroud)


jot*_*asi 8

解决这个问题的一个(不是特别好但很有希望工作)选项是为求解器提供一个仅在约束区域具有根的函数,并且这种函数以确保求解器被推回到适当区域的方式继续(有点像这里,但在多个方面).

实现这一目标(至少对于矩形约束)可以做的是实现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)
Run Code Online (Sandbox Code Playgroud)

您可以向此函数传递一个x值,f您想要继续的函数,以及两个数组lowerupper相同的形状,例如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.])))
Run Code Online (Sandbox Code Playgroud)

得到:

    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])
Run Code Online (Sandbox Code Playgroud)

这也适用于非对角函数:

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.])))
Run Code Online (Sandbox Code Playgroud)

得到:

    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])
Run Code Online (Sandbox Code Playgroud)


Bra*_*mon 5

冒着提出一些你可能已经划掉的东西的风险,我相信这应该是可行的scipy.minimize。问题是该函数必须只有一个参数,该参数可以是向量/列表。

所以 f(x, y) 就变成了 f(z),其中 z = [x, y]。

这里有一个很好的例子,如果您没有遇到过,您可能会发现它很有用。

如果您想对 2x1 向量施加边界,正如您所提到的,您可以使用:

# Specify a (lower, upper) tuple for each component of the vector    
bnds = [(0., 1.) for i in len(x)]
Run Code Online (Sandbox Code Playgroud)

并使用它作为bounds中的参数minimize

  • 我*明确地*在问题中说我不希望这样,因为它破坏了牛顿算法的二次收敛性...... (2认同)