Lambdify A Parametric Integral

天涯浪子 提交于 2019-12-10 10:07:15

问题


I have the following issue: I want to lambdify a sympy expression containing parametric integrals like Integral(tanh(a*x),(x,0,1)). I tried to do a manual implementation like here.

What we want is essentially that the integral gets converted to something like:

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

where

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

Consider the following 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}] )

Then calling for example K(1) returns the correct value. However L(1) gives

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

Does anyone have an idea how to fix this?

NOTE: Doing it manually is no option, since the expressions I deal with are way more complicated and may contain several different integrals. So I really need to get the lambdify working.


回答1:


I think returning a lambda from integral_as_quad cannot work, because this lambda will never be called, as the Integral object in SymPy is not callable. Instead, the parameter tuple can be passed to quad via its args argument. Another change I made is in the outer lambdification, replacing

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

with

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

The idea is that at this stage we don't need NumPy functions yet, we just want to replace the Integral by our callable. The order of modules list matters: the dictionary comes first to prevent SymPy from keeping Integral as an Integral.

Now L(1) returns the correct amount.

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'])



回答2:


So one possible workaround I have found, but I am unhappy with because it is too slow for my application, is the following:

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

So first, expr gets cast into a lambda function using sympy-functions, e.g. we have

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

and then we use sympy's internal integrator via evalf() (slow!).

Also, don't ask me why theres the double np.array, if one puts the dtype='float64' into the first one, then it returns TypeError: __array__() takes 1 positional argument but 2 were given



来源:https://stackoverflow.com/questions/51173981/lambdify-a-parametric-integral

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!