Python Sympy:lambdify,这样数组上的操作总是会产生数组,也会产生常量?

Python Sympy:lambdify,这样数组上的操作总是会产生数组,也会产生常量?,python,numpy,sympy,Python,Numpy,Sympy,我需要计算用户在许多点上给出的函数(f')的导数。点位于列表中(或numpy.array、pandas.Series…)。当f'依赖于同向变量时,我得到了期望值,但当f'是常数时,我得不到期望值: 将sympy作为sp导入 f1=sp.sympify('1') f2=sp.sympify('t') lamb1=sp.lambdify('t',f1) lamb2=sp.lambdify('t',f2) 打印(lamb1([1,2,3])) 打印(lamb2([1,2,3])) 我获得: 1 [1

我需要计算用户在许多点上给出的函数(f')的导数。点位于列表中(或numpy.array、pandas.Series…)。当f'依赖于同向变量时,我得到了期望值,但当f'是常数时,我得不到期望值:

将sympy作为sp导入
f1=sp.sympify('1')
f2=sp.sympify('t')
lamb1=sp.lambdify('t',f1)
lamb2=sp.lambdify('t',f2)
打印(lamb1([1,2,3]))
打印(lamb2([1,2,3]))
我获得:

1
[1, 2, 3]
第二个是可以的,但是我希望第一个是一个列表


这些函数位于矩阵中,是辛运算(如求导数)的最终结果。f1和f2的确切形式因问题而异。

lamb1
是一个返回常量1:
def lamb1(x):返回1
的函数

lamb2
是一个返回其参数的函数:
def lamb2(x):返回x

因此,输出非常符合预期

这里有一个可能有效的方法。我将
f2
的测试函数改为
t*t
,因为这在我的测试中更烦人(处理Pow(t,2))

将sympy作为sp导入
将numpy作为np导入
f1=sp.sympify('1')
f2=sp.sympify('t*t')
def np_lambdify(变量名,函数):
lamb=sp.lambdify(varname,func,modules=['numpy'])
如果函数是常数():
返回lambda t:np.full_-like(t,lamb(t))
其他:
返回lambda t:lamb(np.数组(t))
lamb1=np_lambdify('t',f1)
lamb2=np_lambdify('t',f2)
印刷品(λ1(1))
打印(lamb1([1,2,3]))
印刷品(2(2))
打印(lamb2([1,2,3]))
产出:

1
[1 1 1]
4
[1 4 9]

我经常使用技巧
t*0+1
创建一个与输入长度相同的零向量,然后将其每个元素加1。它与NumPy合作;检查它是否与Symphy一起工作

我从不使用lambdify,所以我对它的工作方式不能太挑剔。但是,您似乎需要愚弄它,给它一个表达式,它不会简化为标量,当用数字计算时,它会减少到所需的值:

>>> import numpy as np
>>> lambdify('t','(1+t)*t-t**2-t+42','numpy')(np.array([1,2,3]))
array([42, 42, 42])

通过
isympy/ipython
内省:

In [28]: lamb2??                                                                                 
Signature: lamb2(t)
Docstring:
Created with lambdify. Signature:

func(arg_0)

Expression:

t

Source code:

def _lambdifygenerated(t):
    return (t)
首先:

In [29]: lamb1??                                                                                 
Signature: lamb1(t)
Docstring:
Created with lambdify. Signature:

func(arg_0)

Expression:

1

Source code:

def _lambdifygenerated(t):
    return (1)
因此返回输入参数;另一个只返回常量,而不考虑输入
lambdify
sympy
进行相当简单的词汇翻译,将其转换为
numpy
Python

编辑 将函数放入
sp.Matrix

In [55]: lamb3 = lambdify('t',Matrix([f1,f2]))                                                   

In [56]: lamb3??                                                                                 
...
def _lambdifygenerated(t):
    return (array([[1], [t]]))
...

In [57]: lamb3(np.arange(3))                                                                     
Out[57]: 
array([[1],
       [array([0, 1, 2])]], dtype=object)
这将返回一个numpy数组;但由于形状的混合,结果是对象数据类型,而不是2d

我们可以通过直接阵列生成看到这一点:

In [53]: np.array([[1],[1,2,3]])                                                                 
Out[53]: array([list([1]), list([1, 2, 3])], dtype=object)

In [54]: np.array([np.ones(3,int),[1,2,3]])                                                      
Out[54]: 
array([[1, 1, 1],
       [1, 2, 3]])
sympy
np.array
都不会试图“广播”该常量。有一些numpy构造可以做到这一点,比如乘法和加法,但是这个简单的sympy函数和lambdify不能做到

编辑
frompyfunc
是一种将数组传递给只使用标量输入的函数的方法。虽然
lamb2
与数组输入一起工作,但您对
lamb1
情况不满意,或者可能对
lamb3
不满意

In [60]: np.frompyfunc(lamb1,1,1)([1,2,3])                                                       
Out[60]: array([1, 1, 1], dtype=object)

In [61]: np.frompyfunc(lamb2,1,1)([1,2,3])                                                       
Out[61]: array([1, 2, 3], dtype=object)
这[61]比简单的
lamb2([1,2,3])
要慢,因为它可以有效地进行迭代

In [62]: np.frompyfunc(lamb3,1,1)([1,2,3])                                                       
Out[62]: 
array([array([[1],
       [1]]), array([[1],
       [2]]),
       array([[1],
       [3]])], dtype=object)
在这种矩阵情况下,结果是数组的数组。但由于形状匹配,它们可以组合成一个数组(以各种方式):


通常情况下,
lambdify
返回常量实际上不是问题,因为NumPy的广播语义会自动将常量视为具有适当形状的常量数组

如果这是一个问题,您可以使用像

def broadcast(fun):
    return lambda *x: numpy.broadcast_arrays(fun(*x), *x)[0]
(这是从对这个问题有更多讨论的


请注意,使用
broadcast
比JohanC的回答中的
full_like
更好,因为广播的常量数组实际上不会占用更多内存,而
full_like
会复制内存中的常量来制作数组。

我明白你的意思,谢谢。然而,在我使用它的脚本中,正如我所提到的,这两个求值是在一个矩阵中进行的(函数是另一个函数的梯度)。因此,当我求值时,我得到类似于[1,1,2,3]的东西,但我需要[1,1,1],[1,2,3],,是否有任何方法来识别它们,并在lamb1的情况下使用类似于[1]*len([1,2,3])的东西来获得所需的[1,1,1]可能有用。它不会很快,但对于您的一般情况,它可能是最干净的解决方案。您的解决方案很有用,但我更喜欢@JohanC的解决方案(我不知道哪个是最好的还是最快的)f1和f2是用户给出的函数的导数,如我所说,使用Symphy的目的是避免用户必须派生和定义函数。此外,导数的数量取决于问题,你的解不是通解。f1和f2是由symphy.diff计算的(我在问题中省略了它),我不能修改它们。
def broadcast(fun):
    return lambda *x: numpy.broadcast_arrays(fun(*x), *x)[0]