Lambdify параметрический интеграл - PullRequest
0 голосов
/ 04 июля 2018

У меня следующая проблема: я хочу lambdify sympy выражение, содержащее параметрические интегралы, такие как Integral(tanh(a*x),(x,0,1)). Я пытался сделать ручную реализацию как здесь.

По сути, мы хотим, чтобы интеграл преобразовывался во что-то вроде:

lambda theta: quad(lambda x: g(x,theta), a,b)[0]

, где

g = sp.lambdify((x,param), f, modules='numpy'))

Рассмотрим следующее MWE:

import sympy as sp
import numpy as np
from scipy.integrate import quad

def integral_as_quad(function, limits):
    x, a, b = limits
    param = function.free_symbols - {x}
    f = sp.lambdify( (x,*param), function, modules='numpy')
    return lambda y: quad(lambda x: f(x,y), a,b)[0]

a, x = sp.symbols('a,x')
I = sp.Integral(sp.tanh(a*x),(x,0,1))
K = integral_as_quad(sp.tanh(a*x),(x,0,1))
L = sp.lambdify(a, I, modules=['numpy', {'Integral':integral_as_quad}] )

Затем, например, вызов K(1) возвращает правильное значение. Однако L(1) дает

AttributeError: 'Mul' object has no attribute 'tanh'

У кого-нибудь есть идеи как это исправить?

ПРИМЕЧАНИЕ: Делать это вручную нельзя, поскольку выражения, с которыми я имею дело, более сложны и могут содержать несколько различных интегралов. Так что мне действительно нужно, чтобы lambdify работал.

Ответы [ 2 ]

0 голосов
/ 04 июля 2018

Я думаю, что возврат лямбды из integral_as_quad не может работать, потому что эта лямбда никогда не будет вызываться, так как объект Integral в SymPy не вызывается. Вместо этого, кортеж параметра может быть передан в quad через его аргумент args. Другое изменение, которое я сделал, во внешнем lambdification, заменяя

modules=['numpy', {'Integral':integral_as_quad}] 

с

modules=[{'Integral': integral_as_quad}, 'sympy'] 

Идея состоит в том, что на данном этапе нам пока не нужны функции NumPy, мы просто хотим заменить Integral нашим вызываемым. Порядок списка modules имеет значение: словарь стоит первым, чтобы SymPy не мог оставить Integral как Integral.

Теперь L (1) возвращает правильную сумму.

import sympy as sp
import numpy as np
from scipy.integrate import quad

def integral_as_quad(function, limits):
    x, a, b = limits
    param = tuple(function.free_symbols - {x})
    f = sp.lambdify((x, *param), function, modules=['numpy'])
    return quad(f, a, b, args=param)[0]

a, x = sp.symbols('a,x')
I = sp.Integral(sp.tanh(a*x), (x,0,1))
L = sp.lambdify(a, I, modules=[{'Integral': integral_as_quad}, 'sympy'])
0 голосов
/ 04 июля 2018

Итак, я нашел один возможный обходной путь, но я недоволен, потому что он слишком медленный для моего приложения, это:

def to_lambda(expr, param):
    # Preprocessing
    expr = expr.evalf()
    f = sp.lambdify([param], expr, modules='sympy')
    fun = lambda x: np.array(np.array(f(x).evalf()), dtype='float64')
    return fun

Итак, во-первых, expr преобразуется в лямбда-функцию с использованием sympy -функций, например, у нас есть

f = lambda a: Integral(tanh(a*x),(x,0,1))

и затем мы используем внутренний интегратор sympy через evalf() (медленно!).

Кроме того, не спрашивайте меня, почему есть двойное np.array, если кто-то помещает dtype='float64' в первое, то возвращается TypeError: __array__() takes 1 positional argument but 2 were given

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