Я изучаю пригодность SymPy для некоторых из моих проектов и столкнулся с проблемой взаимодействия между lambdify и IndexedBase.
Короче говоря, мои приложения интенсивно используют функции, которые используют структуры удвоенных суммированных массивов. Мне нужно вычислить как функцию, так и производную с первой по третью по отношению к элементу массива.
Мои вопросы таковы:
Упрощенный пример:
from sympy import IndexedBase, Idx, lambdify, Sum, diff
from numpy import array
i = Idx("i", range=(0,1))
j = Idx("j", range=(0,1))
n = IndexedBase("n")
coefficients = IndexedBase("C")
double_sum = Sum(Sum(n[i]*n[j]*coefficients[i,j],(i,i.lower,i.upper)),(j,j.lower,j.upper))
first_derivative = diff(double_sum, n[i])
second_derivative = diff(first_derivative, n[j])
test_function_1 = lambdify((n,coefficients),double_sum)
test_function_2 = lambdify((n,coefficients,i),first_derivative)
test_function_3 = lambdify((n,coefficients,i,j),second_derivative)
test_vector = array([1, 2])
test_coefficients = array([[1,1],[2,3]])
test_value_1 = test_function_1(test_vector,test_coefficients)
print(test_value_1)
test_value_2 = test_function_2(test_vector,test_coefficients,1)
print(test_value_2)
test_value_3 = test_function_3(test_vector,test_coefficients)
print(test_value_3)
Выполнение этого кода дает ошибку:
File "<lambdifygenerated-2>", line 9, in _lambdifygenerated
File "<lambdifygenerated-2>", line 9, in <genexpr>
NameError: name 'KroneckerDelta' is not defined
Хм. хорошо - так что, если этот подход не подходит, есть ли разумная альтернатива в экосистеме sympy, которая позволила бы мне выполнять эту работу в sympy, или я действительно застрял в том, чтобы делать производные вручную в 2018 году?
Индексированные выражения полезны, но их производные иногда сложнее, чем они должны быть, и у них, как правило, возникают проблемы с lambdify
. Вот примерно эквивалентный код, который не использует индексированные. Разница в том, что размер массива объявляется заранее, что позволяет создавать явные массивы нормальных (неиндексированных) символов с помощью symarray
, манипулировать ими и лямбдифицировать выражения. Я обозначил их таким образом, чтобы первые производные возвращались как матрица с 1 столбцом, а вторые производные - как квадратная матрица (альтернативный тип возвращаемого значения указан ниже).
from sympy import symarray, Add, lambdify, Matrix
from numpy import array
i_upper = 2
j_upper = 2
n = symarray("n", i_upper)
coefficients = symarray("C", (i_upper, j_upper))
double_sum = Add(*[n[i]*n[j]*coefficients[i, j] for i in range(i_upper) for j in range(j_upper)])
first_derivative = Matrix(i_upper, 1, lambda i, j: double_sum.diff(n[i]))
second_derivative = Matrix(i_upper, j_upper, lambda i, j: double_sum.diff(n[i], n[j]))
params = list(n) + list(coefficients.ravel())
test_function_1 = lambdify(params, double_sum)
test_function_2 = lambdify(params, first_derivative)
test_function_3 = lambdify(params, second_derivative)
test_vector = array([1, 2])
test_coefficients = array([[1, 1], [2, 3]])
test_params = list(test_vector) + list(test_coefficients.ravel())
test_value_1 = test_function_1(*test_params)
test_value_2 = test_function_2(*test_params)
test_value_3 = test_function_3(*test_params)
Значения теста - 19
, [[8], [15]]
и [[2, 3], [3, 6]]
соответственно.
В качестве альтернативы функции могут возвращать вложенные списки:
first_derivative = [double_sum.diff(n[i]) for i in range(i_upper)]
second_derivative = [[double_sum.diff(n[i], n[j]) for j in range(i_upper)] for i in range(i_upper)]
или массивы NumPy:
first_derivative = array([double_sum.diff(n[i]) for i in range(i_upper)])
second_derivative = array([[double_sum.diff(n[i], n[j]) for j in range(i_upper)] for i in range(i_upper)])
Ограничения этого подхода: (а) необходимо знать количество символов во время формирования выражений; (б) не может принимать индексы i
или j
в качестве параметров лямбдифицированной функции.
Эй, это фантастика, именно то, что мне нужно - работает! На практике было бы неплохо использовать объекты indexedbase, поскольку этот подход не работает в случаях, которые можно упростить, используя представления индексированными суммами, но это работает для моей цели :).
Индексированные + производные + lambdify => вряд ли сработает. Это может ответить на ваш вопрос о пригодности.