Градиент симметричной функции должен иметь одинаковые производные во всех измерениях. numpy.gradient предоставляет различные компоненты.
Вот МВЕ.
import numpy as np
x = (-1,0,1)
y = (-1,0,1)
X,Y = np.meshgrid(x,y)
f = 1/(X*X + Y*Y +1.0)
print(f)
>> [[0.33333333 0.5 0.33333333]
[0.5 1. 0.5 ]
[0.33333333 0.5 0.33333333]]
Это имеет одинаковые значения в обоих измерениях.
Но np.gradient(f) дает
[array([[ 0.16666667, 0.5 , 0.16666667],
[ 0. , 0. , 0. ],
[-0.16666667, -0.5 , -0.16666667]]),
array([[ 0.16666667, 0. , -0.16666667],
[ 0.5 , 0. , -0.5 ],
[ 0.16666667, 0. , -0.16666667]])]
Обе составляющие градиента различны.
Почему так? Что мне не хватает в интерпретации вывода?
@meowgoesthedog Тогда как следует рассматривать такой результат для трехмерного массива?
В 3D вы также должны поддерживать циклический порядок осей при изменении системы координат, т.е. просматривать компонент X в кадре XYZ, Y в YZX и Z в ZXY.
@meowgoesthedog Да, похоже, это так. Я думаю, это должно быть упомянуто в документации numpy.gradient.
@meowgoesthedog Есть ли способ отменить этот «цикл» и получить производные, как для n-мерного массива?
Это простое математическое наблюдение. Зачем математической библиотеке документировать это? И что вы подразумеваете под "отменить"?
Мне нужна производная второго порядка (градиент) скалярной 3D-функции. Применение градиента дважды приводит к искажению результатов. Мне просто нужны значения градиента для всех измерений в соответствующих индексах на выходе.
что вы подразумеваете под производной второго порядка от f:R3->R? Градиент является производной 1-го порядка. Вам нужна дивергенция? Лапласиан?
@meowgoesthedog. Пожалуйста, напишите свой ответ как ответ, чтобы я мог его принять.






Обычно градиенты и якобианы являются операторами функций
Если вам нужен градиент f = 1/(X*X + Y*Y +1.0), вы должны вычислить его символически. Или оцените его с помощью численных методов, использующих эту функцию.
Я не знаю, что такое градиент постоянного трехмерного массива. numpy.gradient — это одномерное понятие.
Python имеет пакет sympy, который может автоматически вычислять якобианы символически.
Если под second order derivative of a scalar 3d field вы подразумеваете лапласиан, то вы можете оценить это с помощью стандартного 4-точечного трафарета.
Давайте пройдемся по этому шаг за шагом. Итак, во-первых, как правильно упомянул meowgoesthedog numpy вычисляет производные в направлении.
Важно отметить, что np.gradient использует значение центральных различий (для простоты мы смотрим только в одном направлении):
grad_f[i] = (f[i+1] - f[i])/2 + (f[i] - f[i-1])/2 = (f[i+1] - f[i-1])/2
На границе numpy вычисляет (в качестве примера возьмем min)
grad_f[min] = f[min+1] - f[min]
grad_f[max] = f[max] - f[max-1]
В вашем случае граница 0 и 2.
Если вы используете более одного измерения, нам необходимо учитывать направление производной. np.gradient вычисляет производные во всех возможных направлениях. Давайте воспроизведем ваши результаты:
Давайте двигаться вдоль столбцов, так что мы считаем с строки векторов
f[1,:] - f[0,:]
Вывод
array([0.16666667, 0.5 , 0.16666667])
это точно первая строка первого элемента вашего градиента.
Строка рассчитывается с центрированными производными, поэтому:
(f[2,:]-f[1,:])/2 + (f[1,:]-f[0,:])/2
Вывод
array([0., 0., 0.])
Третий ряд:
f[2,:] - f[1,:]
Вывод
array([-0.16666667, -0.5 , -0.16666667])
Для другого направления просто поменяйте местами : и числа и помните, что сейчас вы рассчитываете столбцы векторов. Это приводит непосредственно к производной транспонированный в случае симметричной функции, как в вашем случае.
x_ = (-1,0,4)
y_ = (-3,0,1)
z_ = (-1,0,12)
x, y, z = np.meshgrid(x_, y_, z_, indexing='ij')
f = 1/(x**2 + y**2 + z**2 + 1)
np.gradient(f)[1]
Вывод
array([[[ *2.50000000e-01, 4.09090909e-01, 3.97702165e-04*],
[ 8.33333333e-02, 1.21212121e-01, 1.75554093e-04],
[-8.33333333e-02, -1.66666667e-01, -4.65939801e-05]],
[[ **4.09090909e-01, 9.00000000e-01, 4.03045231e-04**],
[ 1.21212121e-01, 2.00000000e-01, 1.77904287e-04],
[-1.66666667e-01, -5.00000000e-01, -4.72366556e-05]],
[[ ***1.85185185e-02, 2.03619910e-02, 3.28827183e-04***],
[ 7.79727096e-03, 8.54700855e-03, 1.45243282e-04],
[-2.92397661e-03, -3.26797386e-03, -3.83406181e-05]]])
Приведенный здесь градиент рассчитывается по строкам (0 будет по матрицам, 1 по строкам, 2 по столбцам).
Это можно рассчитать по
(f[:,1,:] - f[:,0,:])
Вывод
array([[*2.50000000e-01, 4.09090909e-01, 3.97702165e-04*],
[**4.09090909e-01, 9.00000000e-01, 4.03045231e-04**],
[***1.85185185e-02, 2.03619910e-02, 3.28827183e-04***]])
Я добавил звездочки, чтобы было понятно, где найти соответствующие векторы-строки. Поскольку мы рассчитали градиент в направлении 1, нам нужно искать строки векторов.
Если кто-то хочет воспроизвести весь градиент, это делается с помощью
np.stack(((f[:,1,:] - f[:,0,:]), (f[:,2,:] - f[:,0,:])/2, (f[:,2,:] - f[:,1,:])), axis=1)
Мы можем обобщить то, что мы узнали здесь, для вычисления градиентов произвольных функций по направлениям.
def grad_along_axis(f, ax):
f_grad_ind = []
for i in range(f.shape[ax]):
if i == 0:
f_grad_ind.append(np.take(f, i+1, ax) - np.take(f, i, ax))
elif i == f.shape[ax] -1:
f_grad_ind.append(np.take(f, i, ax) - np.take(f, i-1, ax))
else:
f_grad_ind.append((np.take(f, i+1, ax) - np.take(f, i-1, ax))/2)
f_grad = np.stack(f_grad_ind, axis=ax)
return f_grad
где
np.take(f, i, ax) = f[:,...,i,...,:]
и i находится под индексом ax.
Поскольку производные вычисляются вдоль направление, это направление также необходимо учитывать при их сравнении. Компонент Y, просматриваемый по оси Y, идентичен компоненту X, просматриваемому по оси X. то есть вы можете увидеть вращательную симметрию исходной функции, повернув производную функцию Y на 90 градусов по часовой стрелке.