Подтвердить что ты не робот

Python: многомерный нелинейный решатель с ограничениями

Для функции f(x), которая принимает входной вектор x и возвращает вектор той же длины, как вы можете найти корни ограничений установки функции на x? (Например, диапазон для каждого компонента x.)

К моему удивлению, я не смог найти много полезной информации об этом. В списке scipy для алгоритмов оптимизации и корневого поиска, похоже, есть некоторые опции для скалярных функций, таких как brentq. Я не могу найти алгоритмы, которые поддерживают такой вариант для многомерного случая.

Конечно, можно было бы работать, например, возводить в квадрат каждый компонент возвращаемого вектора, а затем использовать один из минимизаторов, например differential_evolution (это единственное, что я думаю на самом деле). Я не могу себе представить, что это хорошая стратегия, поскольку она убивает квадратичную сходимость алгоритма Ньютона. Также я нахожу, что это действительно удивительно, что, похоже, для этого нет выбора, так как это должна быть действительно общая проблема. Я что-то пропустил?

4b9b3361

Ответ 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, которую вы хотите продолжить, а также два массива 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

Если вы хотите обработать оптимизацию с ограничениями, вы можете использовать простой лирбари, что намного проще, чем scipy.optimize

Вот ссылка на пакет:

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"

Ответ 3

Рискуя предложить что-то, что вы, возможно, уже перешли, я считаю, что это возможно только с помощью 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)]

И используйте это как параметр bounds в minimize.