Sympy autowrap(cython):定义"助手"对于sympy.Max,sympy.Heaviside

时间:2017-05-30 17:36:48

标签: sympy

我有一个名为J_sym的sympy.Matrix,我想自动装配(最好使用cython后端);相应的符号存储在list_args列表中。

然而,我遇到的问题是显然有些情感功能不受支持,在我的情况下特别是同情.Max和sympy.Heaviside。

具体来说,如果我尝试

J_num_autowrap = autowrap(J_sym, backend="cython", args=list_args)

我得到以下内容:

[...]
wrapped_code_10.c(4): warning C4013: 'Heaviside' undefined; assuming extern returning int
wrapped_code_10.c(4): warning C4013: 'Max' undefined; assuming extern returning int
[...]
: fatal error LNK1120: 2 unresolved externals
[...]
failed with exit status 1120

(当一个lambdifies只使用numpy时会出现类似的问题;但是有一个人可以很容易地为这些函数传递一个字典。)

现在,似乎"助手" autowrap的论据应该提供一个解决方案;但是,我无法确定如何正确使用它。文档字符串本身是绝对神秘的,我发现只有这个链接的例子:
https://github.com/sympy/sympy/issues/10572

我不确定如何在这里正确使用帮助者 - 有人可以帮忙吗?

如果我沿着这些方向尝试:

J_num_autowrap = autowrap(J_sym, backend="cython", args=list_args, helpers=("Max", max(x,y), [x,y]))

如果未声明x,y,我会得到(a)错误 - 如何使用自由变量执行此操作?并且(b)如果我在声明x和y为同情符号之后也这样做,我只会得到错误"关系的真值不能确定"。

(上面的链接提到无论如何都不可能传递多个帮助器 - 即使在我看来这是在1.0中修复的。我仍然得到" ValueError:没有足够的值来解压缩(预计3,得到2)"当试图传递2个(乱码)辅助元组时。不确定状态 - 也许我在这里运气不好。)

如果有任何替代方法可以从我的矩阵中生成C代码,我将非常感谢该方向的任何提示。

1 个答案:

答案 0 :(得分:2)

作为Max问题的解决方法,您可以使用

helpers=("Max", (abs(x+y) + abs(x-y))/2, [x, y])

表达式(abs(x+y) + abs(x-y))/2在数学上等同于max(x, y),并且不会从autowrap生成任何投诉。一个完整的例子:

from sympy import *
from sympy.utilities.autowrap import autowrap
x, y = symbols('x y')
f = autowrap(Max(2*x, y+1), args=[x, y], backend="cython", helpers=("Max", (abs(x+y) + abs(x-y))/2, [x, y]))
print([f(5, 6), f(1, 2)])    # outputs 10 and 3 

同样,Heaviside(x)(x + abs(x))/(2*x)相同 - 除了后者在x = 0时表达为NaN。更加丑陋但安全的版本是(x + abs(x))/(2*abs(x) + 1e-300),其中添加的1e-300几乎不会改变结果。

多个帮助者的问题

你想要帮助 Max和Heaviside。这就是你遇到open issue的地方:autowrap中有一个错误,它使得不可能使用多个助手。 Documentation表示格式类似于

helpers=[("Max", ..., [x, y]), ("Heaviside", ..., [x])]

但是on this line autowrap做了一个方便一个帮助的东西(我们不必把它放在一个列表中),但对于多个(额外的包装层)是致命的:

helpers = [helpers] if helpers else ()

当然,随后的解包for name_h, expr_h, args_h in helpers失败了。

多个助手的解决方法

ufuncify正确处理helpers参数。不幸的是,ufuncify最终会为除NumPy之外的所有后端调用autowrap,因此错误仍然发生在autowrap中。但是如果你愿意使用NumPy后端,这是一个解决方案:

from sympy.utilities.autowrap import ufuncify
x, y = symbols('x y', real=True)
my_helpers = [("Max", abs(x+y)/2 + abs(x-y)/2, [x, y]), ("Heaviside", (x + abs(x)) / (2*abs(x) + 1e-300), [x])]
f = ufuncify([x,y], Max(2*x, y+1) + Heaviside(x-4), backend="numpy", helpers=my_helpers)
print([f(5, 6), f(1, 2)])   $ outputs 11  and  3

减少为一名助手

如果您可以更改表达式以消除Max和Heaviside之一(甚至两者),则可以使用Cython后端。例如,也许您只需要Max(x,0),因此您可以定义Python函数"正面部分":

pos = lambda x: (x+abs(x))/2

然后自动装带对pos没有问题,你只需要帮助Heaviside:

f = autowrap(pos(9-x-y) + Heaviside(x-4), args = [x, y], backend = "cython", helpers=("Heaviside", (x + abs(x)) / (2*abs(x) + 1e-300), [x]))
print([f(5, 6), f(1, 2)])   #  outputs 1 and 6

这种替换的实际程度取决于你的符号表达方式。