#python #numpy #sympy #symbolic-math #lambdify
Вопрос:
У меня есть функция, определенная в numpy, которую я хотел бы преобразовать в sympy, чтобы я мог применить ее к символьным переменным sympy. Попытка прямого применения функции numpy к переменной sympy завершается неудачей:
import numpy as np
import sympy as sp
def np_fun(a):
return np.array([np.sin(a), np.cos(a)])
x = sp.symbols('x')
sp_fun = np_fun(x)
Я получаю ошибку
AttributeError: 'Symbol' object has no attribute 'sin'
Моей следующей мыслью было преобразовать функцию numpy в sympy, но я не мог найти способ сделать это. Я знаю, что мог бы заставить этот код работать, просто определив функцию как выражение симпатии:
sp_fun = sp.Array([sp.sin(x), sp.cos(x)])
Но я использую функцию синуса/косинуса в качестве простого примера. Фактическая функция, которую я использую, уже определена в numpy и намного сложнее, поэтому переписывать ее было бы очень утомительно.
Комментарии:
1.
sympy
имеет возможность генерироватьnumpy
выражения из своих собственных выражений. Ноsympy
уже работает с символами. Но вашаnp_fun
функция-это функция Python, даже если она вызываетnumpy
функции. В.py
файле это строка, но в запущенном сеансе она уже анализируется интерпретатором. Вы можете получить строковую версию только сinspect
помощью инструментов.2.
np.fun(x)
звонитnp.sin(x)
, что становитсяnp.sin(np.array(x))
.np.array(x)
представляет собой массив 0d с элементом объекта,Symbol
. Столкнувшись с таким типом массива,np.sin()
пытается это сделатьx.sin()
. Это источник вашей ошибки атрибута.sympy
символы можно использовать в некоторыхnumpy
математических задачах, но не в таких, какnp.sin
.
Ответ №1:
В принципе, вы можете напрямую изменить ast («абстрактное синтаксическое дерево») функции, хотя на практике это может оказаться довольно сложным. В любом случае, вот как это сделать для вашего простого примера:
Это создает из источника ast и выводится из NodeTransformer
класса для изменения ast на месте. Преобразователь узлов имеет универсальный метод посещения, который пересекает узел и его поддерево, делегируя его конкретным посетителям узла в производных классах. Здесь мы меняем все имена np
на sp
, а затем меняем эти атрибуты на прежние np
теперь sp
, которые пишутся по-другому. Вам придется добавить все эти различия в translate
диктант.
Наконец, мы компилируем обратно из ast в объект кода и выполняем его, чтобы сделать измененную функцию доступной.
import ast, inspect
import numpy as np
import sympy as sp
def f(a):
return np.array([np.sin(a), np.cos(a)])
z = ast.parse(inspect.getsource(f))
translate = {'array': 'Array'}
class np_to_sp(ast.NodeTransformer):
def visit_Name(self, node):
if node.id=='np':
node = ast.copy_location(ast.Name(id='sp', ctx=node.ctx), node)
return node
def visit_Attribute(self, node):
self.generic_visit(node)
if node.value.id=='sp' and node.attr in translate:
fields = {k: getattr(node, k) for k in node._fields}
fields['attr'] = translate[node.attr]
node = ast.copy_location(ast.Attribute(**fields), node)
return node
np_to_sp().visit(z)
exec(compile(z, '', 'exec'))
x = sp.Symbol('x')
print(f(x))
Выход:
[sin(x), cos(x)]
ОБНОВЛЕНИЕ простого улучшения: изменение функций, вызываемых функцией:
import ast, inspect
import numpy as np
import sympy as sp
def f(a):
return np.array([np.sin(a), np.cos(a)])
def f2(a):
return np.array([1, np.sin(a)])
def f3(a):
return f(a) f2(a)
translate = {'array': 'Array'}
class np_to_sp(ast.NodeTransformer):
def visit_Name(self, node):
if node.id=='np':
node = ast.copy_location(ast.Name(id='sp', ctx=node.ctx), node)
return node
def visit_Attribute(self, node):
self.generic_visit(node)
if node.value.id=='sp' and node.attr in translate:
fields = {k: getattr(node, k) for k in node._fields}
fields['attr'] = translate[node.attr]
node = ast.copy_location(ast.Attribute(**fields), node)
return node
from types import FunctionType
for fn in f3.__code__.co_names:
fo = globals()[fn]
if not isinstance(fo, FunctionType):
continue
z = ast.parse(inspect.getsource(fo))
np_to_sp().visit(z)
exec(compile(z, '', 'exec'))
x = sp.Symbol('x')
print(f3(x))
С принтами:
[sin(x) 1, sin(x) cos(x)]
Комментарии:
1. Спасибо, это интересный подход. Знаете ли вы, как я мог бы изменить это, чтобы оно работало для композиций функций. Например, если я определю
def f2(a): return np.array([1, np.sin(a)])
иdef f3(a): return f(a) f2(a)
, и я хотел преобразоватьf3
вместоf
2. @wxyz Вроде того, смотрите обновленный пост. Не очень надежный/общий, хотя, но вы понимаете принцип.
Ответ №2:
Я бы рекомендовал использовать Find и Replace, чтобы преобразовать вашу функцию numpy в выражение sympy. Вы можете сделать это на python, используя str.replace()
и определяя правила замены текста в соответствии с вашей функцией. Если вы опубликуете свою функцию, будет проще предоставить более подробную информацию.
Комментарии:
1. Спасибо. Итак, вы предлагаете мне использовать поиск и замену для создания дополнительного кода python (например, в новом файле .py), в котором определены новые функции sympy?