python SymPy Lambdify可以转换Add和穆尔等核心函数吗?

fdx2calv  于 2023-06-04  发布在  Python
关注(0)|答案(2)|浏览(201)

我的目标是用我自己定制的乘法和加法实现来计算一个基本的符号方程,比如ad(b + c)
我试图使用lambdify来翻译两个核心SymPy函数(AddMul)与我自己的函数,但我不能让他们识别。
在这个阶段,我只是想让Add工作。代码在下面。

from sympy import *
import numpy as np
x, y = symbols('x y')
A = [1,1]
B = [2,2]

def addVectors(inA, inB):
    print("running addVectors")
    return np.add(inA, inB)

# Test vector addition
print(addVectors(A,B))

# Now using lambdify
f = lambdify([x, y], x + y, {"add":addVectors})
print(f(A, B)) # <------- expect [3,3] and addVectors to be run a second time

# but I get the same as this
print(A + B)

从而产生

running addVectors
[3 3]
[1, 1, 2, 2]
[1, 1, 2, 2]

我期望使用自定义的addVectors函数来计算表达式中的+运算符。这意味着结果看起来像这样。

running addVectors
[3 3]
running addVectors
[3 3]
[1, 1, 2, 2]

我尝试了几种不同配置的lambdify线,这些都给予了相同的原始结果。

f = lambdify([x, y], x + y, {"add":addVectors})
f = lambdify([x, y], x + y, {"Add":addVectors})
f = lambdify([x, y], x + y, {"+":addVectors})
f = lambdify([x, y], Add(x,y), {"Add":addVectors})
f = lambdify([x, y], x + y)

为了确认语法是否正确,我使用了一个更接近文档的示例,并将符号cos函数替换为sin实现。

from sympy import *
import numpy as np
x = symbols('x')
    
def mysin(x):
    print('taking the sin of', x)
    return np.sin(x)

print(mysin(1))

f = lambdify(x, cos(x), {'cos': mysin})
f(1)

其如预期的那样工作并且产生

taking the sin of 1
0.8414709848078965
taking the sin of 1
0.8414709848078965

是否可以使用lambdify实现我自己的AddMul函数?
我怀疑我的问题是Add(和Mul)不是SymPy“函数”。文档将它们称为“表达式”,这意味着它们在lambdify进程中不会被识别为替换。
我阅读过的一些链接:SymPy cosSymPy AddSymPy Lambdify
任何指针将不胜感激。谢谢你阅读到这里。

编辑:得到一个更一般的情况下工作

它使用lambdifyreplace函数的组合来替换AddMul。然后,此示例计算ad(b + c)的表达式,这就是目标。

from sympy import *
import numpy as np
 
w, x, y, z = symbols('w x y z')
A = [3,3]
B = [2,2]
C = [1,1]
D = [4,4]
 
def addVectors(*args):
    result = args[0]
    for arg in args[1:]:
        result = np.add(result, arg)
    return result
 
def mulVectors(*args):
    result = args[0]
    for arg in args[1:]:
        result = np.multiply(result, arg)
    return result
 
expr = w*z*(x + y)
print(expr)
expr = expr.replace(Add, lambda *args: lerchphi(*args))
expr = expr.replace(Mul, lambda *args: Max(*args))
print(expr)
 
f = lambdify([w, x, y, z], expr, {"lerchphi":addVectors, "Max":mulVectors})
print(f(A, B, C, D))
 
print(mulVectors(A,D,addVectors(B,C)))

从而产生

w*z*(x + y)
Max(w, z, lerchphi(x, y))
[36 36]
[36 36]

使用此解决方案需要注意的几点:
1.使用replace函数,你可以用一个函数(type -> func)替换一个类型。参见文档。
1.我替换类型的函数必须接受多个输入,因为表达式中的每个类型可能有两个以上的参数(如上面示例中的multiply)。我只找到了3个接受*args作为输入的函数。它们是MinMaxlerchphi

  1. SymPy简化了MinMax的功能,因为Max(x, Min(x, y)) = x。这意味着我不能同时使用MinMax。所以我使用了lerchphiMax。这些函数是任意的,因为我将在下一步中将它们的实现转换为自定义函数。这意味着我只能替换两个。
    1.最后一步是将lerchphiMax转换为自定义函数。
icomxhvb

icomxhvb1#

在sympy中,加法是一种运算。因此,我不确定是否可以通过传入自定义modules来实现您的目标…
但是,lambdify的核心是打印模块。本质上,lambdify使用某种打印机来生成要计算的表达式的字符串表示。如果你看一下lambdify的签名,你会发现它可以传递一个自定义打印机。
给定一个打印机类,与+的加法由_print_Add方法执行。实现目标的一种方法是修改NumPyPrinter的这个方法。

from sympy.printing.lambdarepr import NumPyPrinter
import inspect

class MyNumPyPrinter(NumPyPrinter):
    def _print_Add(self, expr, **kwargs):
        str_args = [self.doprint(t) for t in expr.args]
        return "add(*[%s])" % ", ".join(str_args)

f = lambdify([x, y], x + y, printer=MyNumPyPrinter)
print(inspect.getsource(f))
# def _lambdifygenerated(x, y):
#    return add(*[x, y])

print(f(A, B))
# [3 3]

请注意,我不知道这可能会产生什么影响。那是你自己去发现的。。

8zzbczxx

8zzbczxx2#

第一个f的帮助是:

In [26]: help(f)
Help on function _lambdifygenerated:

_lambdifygenerated(x, y)
    Created with lambdify. Signature:
    
    func(x, y)
    
    Expression:
    
    x + y
    
    Source code:
    
    def _lambdifygenerated(x, y):
        return x + y

在mysin情况下它产生

def _lambdifygenerated(x):
    return cos(x)

sympy +生成一个Add对象

In [20]: expr = x+y
In [21]: expr
Out[21]: 
x + y
In [22]: type(expr)
Out[22]: sympy.core.add.Add

但我不知道这些信息是否可以在lambdify中使用。我从其他SO知道,lambdify对sympy做了相当肤浅的词汇翻译。它没有显示出对目标模块(numpy)有“深入”理解的迹象。
lambdify的文档相当长。我自己还没有全部读过,但我已经回答了一些关于它的问题:(

相关问题