web-dev-qa-db-ja.com

Python:制約付きの多変量非線形ソルバー

入力ベクトルxを取り、同じ長さのベクトルを返す関数f(x)が与えられた場合、xの関数設定制約の根をどのように見つけることができますか? (例:xの各コンポーネントの範囲。)

驚いたことに、これに関する有用な情報をたくさん見つけることができませんでした。 最適化および求根アルゴリズム のscipyリストには、 brentq などのスカラー関数のオプションがあるようです。しかし、多変量の場合にそのようなオプションをサポートするアルゴリズムは見つかりません。

もちろん、返されたベクトルの各コンポーネントを2乗するような回避策を実行してから、 differential_evolution などの最小化子の1つを使用することもできます(これは私が実際に考える唯一のものです)。ニュートンのアルゴリズムの二次収束を殺してしまうので、これが良い戦略だとは想像できません。また、これは本当に一般的な問題であるに違いないので、これに対するオプションがないように思われることは本当に驚くべきことです。私は何かを逃したことがありますか?

15
Wolpertinger

この問題を回避するための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、および同じ形状の2つの配列lowerupperを渡すことができます。 xのように、すべての次元で下限と上限を指定します。これで、元の関数ではなくこの関数をソルバーに渡して、根を見つけることができます。

継続の急勾配は、境界での符号変更による急なジャンプを防ぐために、現時点での境界値として単純に解釈されます。制約された領域の外側のルートを防ぐために、正/負の境界値にいくつかの小さな値が加算/減算されます。これはこれを処理するための非常に良い方法ではないことに同意しますが、うまくいくようです。

ここに2つの例があります。どちらの場合も、最初の推測は制約領域の外側にありますが、制約領域内の正しいルートが見つかります。

[-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])
8
jotasi

制約付きの最適化を処理したい場合は、scipy.optimizeよりもはるかに簡単な簡単なlirbaryを使用できます。

パッケージへのリンクは次のとおりです。

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

例として、簡単なライブラリを使用する方法は次のとおりです。私がここに書いていることを洗練する必要がありますが、これは一般的なことです。エラーが発生した場合は、どれを教えてください。

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

すでに取り消した可能性のあるものを提案するリスクがありますが、これはscipy.minimizeだけで実現できるはずです。キャッチは、関数には引数が1つだけ必要であるということですその引数はベクトル/リストにすることができます。

したがって、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)]

そして、これをbounds内のminimizeパラメーターとして使用します。

2
Brad Solomon