Векторизованная частичная производная многомерной функции в Python

Был феноменальный ответ, посланный alko для вычисления частичной производной многомерной функции численно в этом потоке .

У меня теперь есть вопрос о повышении этой функции, чтобы принять массив входных значений. У меня есть код, в котором я перебираю большой длинный список n-мерных точек, вычисляя частные производные по каждой переменной, и это становится довольно дорогостоящим.

Достаточно легко векторизовать рассматриваемую функцию с помощью np.vectorize , но она вызывает проблемы с partial_derivative wrapper:

 from scipy.misc import derivative import numpy as np def foo(x, y): return(x**2 + y**3) def partial_derivative(func, var=0, point=[]): args = point[:] def wraps(x): args[var] = x return func(*args) return derivative(wraps, point[var], dx=1e-6) vfoo = np.vectorize(foo) >>>foo(3,1) >>>10 >>>vfoo([3,3], [1,1]) >>>array([10,10]) >>>partial_derivative(foo,0,[3,1]) >>>6.0 >>>partial_derivative(vfoo,0,[[3,3], [1,1]]) >>>TypeError: can only concatenate list (not "float") to list 

Последняя строка должна идеально возвращать [6.0, 6.0] . В этом случае два массива, подаваемых на векторизованную функцию vfoo , по существу vfoo на место попарно, поэтому ([3,3], [1,1]) преобразуется в две точки [3,1] и [3,1] . Кажется, что это искажается, когда оно переходит к wraps функции. Точка, в которой он заканчивается, переходит к derivative функции [3,3] . Кроме того, очевидно, что TypeError выбрасывается.

Есть ли у кого-нибудь рекомендации или предложения? Кто-нибудь когда-либо имел необходимость сделать что-то подобное?

редактировать

Иногда я думаю, что размещение на SO – это то, что нужно, чтобы сломать ментальный блок. Я думаю, что у меня это работает для всех, кто может быть заинтересован:

 vfoo = np.vectorize(foo) foo(3,1) X = np.array([3,3]) Y = np.array([1,1]) vfoo(X, Y) partial_derivative(foo,0,[3,1]) partial_derivative(vfoo,0,[X, Y]) 

И последняя строка теперь возвращает array([ 6., 6.])

У меня есть небольшая проблема с args [var] = x: это может навсегда изменить args [var], и все значения были переданы по ссылке, но малые ваши изменения. Таким образом, вы не можете получить точный ответ, который вы ищете. Вот пример:

 In[67]: a = np.arange(9).reshape(3,3) In[68]: b = a[:] In[69]: b[0,0]=42 In[70]: a Out[70]: array([[42, 1, 2], [ 3, 4, 5], [ 6, 7, 8]]) 

вам нужно исправить это, например:

 def wraps(x): tmp = args[var] args[var] = x ret= func(*args) args[var] = tmp return ret 

Кроме того, вы можете использовать numdifftools . Кажется, они знают, что делают. Это будет делать все частные производные:

 import numpy as np import numdifftools as nd def partial_function(f___,input,pos,value): tmp = input[pos] input[pos] = value ret = f___(*input) input[pos] = tmp return ret def partial_derivative(f,input): ret = np.empty(len(input)) for i in range(len(input)): fg = lambda x:partial_function(f,input,i,x) ret[i] = nd.Derivative(fg)(input[i]) return ret if __name__ == "__main__": f = lambda x,y: x*x*x+y*y input = np.array([1.0,1.0]) print ('partial_derivative of f() at: '+str(input)) print (partial_derivative(f,input)) 

Наконец: если вы хотите, чтобы ваша функция принимала массив параметров, например:

 f = lambda x: x[0]*x[0]*x[0]+x[1]*x[1] 

затем замените соответствующую строку (удалите «*»)

 ret = f___(input)