преобразовать функцию, определенную в numpy, в sympy - PullRequest
0 голосов
/ 22 марта 2019

У меня есть функция, определенная в 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, но я не мог найти способ сделать это. Я знаю, что мог бы заставить этот код работать, просто определив функцию как выражение sympy:

sp_fun = sp.Array([sp.sin(x), sp.cos(x)])

Но я использую функцию синуса / косинуса в качестве простого примера. Фактическая функция, которую я использую, уже была определена в numpy, и она намного сложнее, поэтому было бы очень утомительно ее переписывать.

Ответы [ 2 ]

0 голосов
/ 23 марта 2019

В принципе, вы можете напрямую изменить ast («абстрактное синтаксическое дерево») функции, хотя на практике это может быть довольно проблематично. В любом случае, вот как это сделать для вашего простого примера:

Это создает из источника ast и происходит от класса NodeTransformer для модификации ast на месте. Преобразователь узла имеет общий метод посещения, который пересекает узел и его поддерево, делегирующее узлам конкретных посетителей в производных классах. Здесь мы меняем все имена np на sp, а затем меняем эти атрибуты на прежние np, теперь sp, которые пишутся по-разному. Вы должны добавить все такие различия к translate dict.

Наконец, мы компилируем обратно из 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)]
0 голосов
/ 23 марта 2019

Я бы порекомендовал использовать Find and Replace, чтобы преобразовать вашу функцию numpy в выражение sympy. Вы можете сделать это в Python, используя str.replace() и определяя правила для замены текста в соответствии с вашей функцией. Если вы опубликуете свою функцию, было бы легче предоставить больше подробностей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...