带有布尔值的 sympy 导数

sympy derivative with boolean

我正在尝试使用 sympy 对包含布尔变量的函数求导。

我的预期结果:

两个不同的导数,取决于布尔值是真还是假(即 1 或 0)。

示例:

import sympy as sy
c, x = sy.symbols("c x", positive=True, real=True)
bo = sy.Function("bo")
fct1 = sy.Function("fct1")
fct2 = sy.Function("fct2")
FOC2 = sy.Function("FOC2")
y = 5
a = 2
b = 4


def fct1(x):
    return -0.004*x**2 + 0.25*x + 4
# the following gives the smaller positive intercept with the x-axis)
# this intercept is the threshold value for the boolean function, bo
min(sy.solve(fct1(x)-y, x))


def bo(x):
    if fct1(x) <= y:
        return 1
    else:
        return 0


def fct2(c, x):
    return a + b*c + bo(x)*c


def FOC2(c, x):
    return sy.diff(fct2(c, x), c)
print(FOC2(c, x))

评论后的最小函数显示 bo 为真或假的 x 阈值为 4.29...,因此是正数和真实数。

输出:

TypeError: cannot determine truth value of Relation

我明白真值取决于x,它是一个符号。因此,不知道 x 就无法确定 bo。

但是我怎样才能得到我的预期结果,其中 bo 是象征性的?

首先,我建议您仔细考虑上面粘贴的代码中发生的事情。您首先定义一些 sympy 函数,例如

fct1 = sy.Function("fct1")

所以在这之后,fct1 是一个未定义的 sympy.Function - 未定义的意思是它既没有指定它的参数是什么,也没有指定函数的样子。

但是,您可以显式定义同名函数,如

def fct1(x):
    return -0.004*x**2 + 0.25*x + 4

但是请注意,此时 fct1 不再是 sympy.Function 或任何 sympy 对象:您覆盖旧定义,它现在只是一个常规 python函数!

这也是您收到错误的原因:当您调用 bo(x) 时,python 尝试计算

-0.004*x**2 + 0.25*x + 4 <= 5

和 return 根据您对 bo() 的定义的值。但是python不知道上面的是不是真的(或者怎么比较),所以报错。

我建议进行 2 处更改:

  1. 而不是 python 函数,如代码中所示,您可以简单地使用 sympy 表达式,例如

    fct1 = -0.004*x**2 + 0.25*x + 4

  2. 为了得到你的条件的真值,我建议使用Heaviside function (wiki), which evaluates to 0 for a negative argument, and to 1 for positive. Its implementation in sympy is sympy.Heaviside。 您的代码可能如下所示:

import sympy as sy
c, x = sy.symbols("c x", positive=True, real=True)
y = 5
a = 2
b = 4


fct1 = -0.004*x**2 + 0.25*x + 4
bo = sy.Heaviside(y - fct1)
fct2 = a + b*c + bo * c

FOC2 = sy.diff(fct2, c)

print(FOC2)

两条评论在线

bo = sy.Heaviside(y - fct1)

(1) 当前实现默认不计算 sympy.Heaviside(0);这是因为周围有不同的定义(有些定义为 1,有些定义为 1/2)。您希望它为 1,以符合 OP 中的(弱)不等式。在 sympy 1.1 中,这可以通过向 Heaviside 传递一个附加参数来实现,即任何你想要 Heaviside(0) 评估为:

bo = sy.Heaviside(y - fct1, 1)

旧版本的 sympy 不支持此功能。

(2) 您将获得您的 FOC2,同样涉及 Heaviside 项。我喜欢这个的一点是,你可以继续使用这个表达式,比如你是否想求二阶导数等等。如果为了可读性,您更喜欢分段表达式 - 没问题。只需将相应的行替换为

bo = sy.Heaviside(y - fct1)._eval_rewrite_as_Piecewise(y-fct1)

这将自动转换为分段函数。 (请注意,在旧版本下,这会自动隐式使用 Heaviside(0) = 0.5 - 最好同时使用 (1) 和 (2):

bo = sy.Heaviside(y - fct1, 1)._eval_rewrite_as_Piecewise(y-fct1)

不幸的是,我现在手头没有可用的 sympy 1.1,只能测试旧代码。

关于 sympy 的分段函数的另一个注意事项:如果使用 sympy 的乳胶打印,通过插入

它们更具可读性
sy.init_printing()

在代码的早期。

(免责声明:我绝不是 sympy 方面的专家,可能还有其他更好的解决方案。只是想提出一个建议!)