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

eblbsuwk  于 2023-10-14  发布在  Python
关注(0)|答案(3)|浏览(142)

给定一个函数f(x),它接受一个输入向量x并返回一个相同长度的向量,你如何找到这个函数在x上设置约束的根?(例如x的每个分量的范围。
令我惊讶的是,我找不到很多有用的信息。在优化和根查找算法的scipy列表中,似乎有一些标量函数的选项,如brentq。我找不到任何算法,支持这样一个选项的多元情况下,虽然。
当然,我们可以做一个变通的方法,比如对返回向量的每个分量求平方,然后使用一个最小化器,比如differential_evolution(这是我实际上认为的唯一一个)。我不能想象这是一个好的策略,因为它杀死了牛顿算法的二次收敛。此外,我觉得这真的很令人惊讶,似乎没有一个选项,因为它必须是一个真正的共同问题。我错过了什么吗?

ajsxfq5m

ajsxfq5m1#

解决这个问题的一个(不是特别好,但希望有效)选择是给予求解器一个函数,该函数只在约束区域中具有根,并且以确保求解器被推回适当区域的方式继续(有点像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,以及两个形状相同的数组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.])))

给出:

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

w8f9ii692#

如果您想处理带有约束的优化,可以使用facile库,它比scipy.optimize容易使用得多
这里是包的链接:https://pypi.python.org/pypi/facile
下面介绍如何在示例中使用facile库。你需要完善我在这里写的东西,这只是一般性的。如果你提出了错误,告诉我是哪一个。

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"
mpgws1up

mpgws1up3#

冒着暗示你可能已经划掉的东西的风险,我相信这应该是可行的,只要scipy.minimize。这个函数必须只有一个参数,* 但是 * 这个参数可以是一个vector/list。
所以f(x,y)就变成了f(z),其中z = [x,y]。
这里有一个很好的例子,如果你没有遇到过的话,你可能会觉得很有用。
如果你想对一个2x 1的向量施加边界,你可以使用用途:

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

并将其用作minimize中的bounds参数。

相关问题