简体   繁体   English

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

[英]Python: multivariate non-linear solver with constraints

Given a function f(x) that takes an input vector x and returns a vector of the same length, how can you find the roots of the function setting constraints on x ? 给定一个函数f(x) ,它取一个输入向量x并返回一个相同长度的向量,你如何在x上找到函数设置约束的根? (Eg a range for each component of x .) (例如x每个分量的范围。)

To my surprise I could not find a lot of useful information about this. 令我惊讶的是,我找不到很多关于此的有用信息。 In the scipy list for Optimization and Root finding algorithms there seem to be some options for scalar functions such as brentq . 优化和根查找算法的scipy列表中,似乎有一些标量函数的选项,如brentq I can not find any algorithms that supports such an option for the multivariate case though. 我找不到任何支持多变量情况的选项的算法。

Of course one could do a work-around like squaring each component of the returned vector and then use one of the minimizers such as differential_evolution (this is the only one I think actually). 当然,人们可以像处理返回向量的每个组件一样进行解决,然后使用其中一个最小化器,例如differential_evolution (这是我认为的唯一一个)。 I can not imagine that this is a good strategy though, since it kills the quadratic convergence of Newton's algorithm. 我无法想象这是一个很好的策略,因为它杀死了牛顿算法的二次收敛。 Also I find it really surprising that there does not seem to be an option for this, since it must be a really common problem. 此外,我发现真的令人惊讶的是,似乎没有这个选项,因为它一定是一个非常普遍的问题。 Have I missed something? 我错过了什么吗?

If you want to handle an optimization with constraints, you can use the facile lirbary, which is a lot easier than scipy.optimize 如果你想用约束来处理优化,你可以使用轻松的lirbary,这比scipy.optimize容易得多。

Here is the link to the package : 这是包的链接:

https://pypi.python.org/pypi/facile/1.2 https://pypi.python.org/pypi/facile/1.2

Here's how to use the facile library for your example. 以下是如何使用easyile库作为示例。 You will need to refine what I write here, which is only general. 你需要改进我在这里写的东西,这只是一般性的。 If you have Errors raised, tell me which. 如果你有错误,请告诉我哪个。

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"

One (not particularly nice but hopefully working) option to work around this problem would be to give the solver a function that only has roots in the constrained region and that is continued in a way ensuring that the solver is pushed back in the proper region (a little bit like here but in multiple dimensions). 解决这个问题的一个(不是特别好但很有希望工作)选项是为求解器提供一个仅在约束区域具有根的函数,并且这种函数以确保求解器被推回到适当区域的方式继续(有点像这里,但在多个方面)。

What one might do to achieve this (at least for rectangular constraints) is to implement a constrainedFunction that is linearly continued starting from the border value of your function: 实现这一点(至少对于矩形约束)可能做的是实现一个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)

You can pass this function an x value, the function f that you want to continue, as well as two arrays lower and upper of the same shape like x giving the lower and upper bounds in all dimensions. 您可以将此函数传递给x值,即要继续的函数f ,以及相同形状的lowerupper的两个数组,如x给出所有维度的下限和上限。 Now you can pass this function rather than your original function to the solver to find the roots. 现在您可以将此函数而不是原始函数传递给求解器以查找根。

The steepness of the continuation is simply taken as the border value at the moment to prevent steep jumps for sign changes at the border. 延续的陡度简单地被视为此时的边界值,以防止边界处的符号变化的急剧跳跃。 To prevent roots outside the constrained region, some small value is added/substracted to positive/negative boundary values. 为了防止受约束区域之外的根,将一些小值加到/减去正/负边界值。 I agree that this is not a very nice way to handle this, but it seems to work. 我同意这不是一个非常好的方法来处理这个,但它似乎工作。

Here are two examples. 这是两个例子。 For both the initial guess is outside the constrained region but a correct root in the constrained region is found. 对于两者,初始猜测都在约束区域之外,但是在约束区域中找到正确的根。

Finding the roots of a multidimensional cosine constrained to [-2, -1]x[1, 2] gives: 找到约束为[-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.])))

gives: 得到:

    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])

This also works for functions that are not diagonal: 这也适用于非对角函数:

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.])))

gives: 得到:

    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])

At the risk of suggesting something you might've already crossed off, I believe this should feasible with just scipy.minimize . 有可能提出你可能已经scipy.minimize事情,我相信这应该是可行的,只需scipy.minimize The catch is that the function must have only one argument, but that argument can be a vector/list. 问题是函数必须只有一个参数, 该参数可以是向量/列表。

So f(x, y) becomes just f(z) where z = [x, y]. 因此f(x,y)变为f(z),其中z = [x,y]。

A good example that you might find useful if you haven't come across is here . 如果您没有遇到过,您可能会觉得有用的一个很好的例子就在这里

If you want to impose bounds, as you mentioned, for a 2x1 vector, you could use: 如果你想强加边界,如你所提到的,对于2x1向量,你可以使用:

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

And use this as the bounds parameter within minimize . 并将其用作minimize bounds内的bounds参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM