【问题标题】:Derivative of a parsed SymPy expression is always 0已解析 SymPy 表达式的导数始终为 0
【发布时间】:2017-04-22 02:43:18
【问题描述】:

我正在编写一个要求用户输入表达式的程序。此表达式作为字符串输入,并使用parse_expr 转换为 Sympy 表达式。然后我需要对用户输入的那个表达式进行偏导数。但是,diff 在我测试的每个表达式中都返回 0。

例如,如果用户输入a*exp(-b*(x-c)**(2)),使用以下代码,diff 在应该(据我所知diff)返回2*a*b*(c - x)*exp(-b*(x - c)**2) 时返回2*a*b*(c - x)*exp(-b*(x - c)**2)x

a, b, c, x = symbols('a b c x', real=True)
str_expr = "a*exp(-b*(x-c)**(2))"
parsed_expr = parse_expr(str_expr)
result = diff(parsed_expr, x)
print(result) # prints 0

我做错了什么?

【问题讨论】:

    标签: python math sympy


    【解决方案1】:

    底线:使用parse_expr(str_expr,locals())

    如果表达式可能使用任何未导入本地命名空间且无法访问的实体 with the default from sympy import *,则也添加 global_dict=<dict of allowed entities to use>


    根据Calculus — SymPy Tutorial - SymPy 1.0.1.dev documentation,您将符号表达式按原样键入diff() 参数。由于字母是Symbol 对象(带有重写的运算符)这一事实,Python 在计算参数时被欺骗构造对应于表达式的 SymPy 对象!

    因此,如果你将它作为一个字符串,你 eval 它会触发相同的行为:

    <...>
    >>> s="a*exp(-b*(x-c)**(2))"
    >>> diff(eval(s), x)
    −ab(−2c+2x)e−b(−c+x)2
    

    eval 与不受信任的输入一起使用会带来安全隐患,因为它接受任意 Python 代码。

    这就是像parse_expr 这样的替代品发挥作用的地方。但是,由于解析表达式的方式,如上所述,它需要访问表达式中使用的外部实体 - 例如用于变量的 Symbol 对象和使用的命名函数的函数对象 - 通过 local_dict 和 @987654335 @参数。

    否则,它会即时创建Symbol 对象。这意味着,它为表达式中的x 创建的Symbol 对象与变量x 不同!难怪它的导数是0!

    <...>
    >>> ps=parse_expr(s)
    >>> ps.free_symbols
    {a,b,c,x}
    >>> x in _
    False
    >>> diff(ps,x)
    0
    
    >>> ps=parse_expr(s,locals())
    >>> x in ps.free_symbols
    True
    >>> diff(ps,x)
    -ab(−2c+2x)e−b(−c+x)2
    

    【讨论】:

      【解决方案2】:

      正在努力使sympifyeval 更安全。最好使用以下内容:

      from sympy import *
      
      var ('a b c x')
      
      str_expr = "a*exp(-b*(x-c)**(2))"
      parsed_expr = sympify(str_expr)
      result = diff(parsed_expr, x)
      print(result) 
      

      结果:

      -a*b*(-2*c + 2*x)*exp(-b*(-c + x)**2)
      

      【讨论】:

      • sympifyparse_expr 有什么区别?他们似乎在做同样的事情。 sympify 似乎更不安全,因为它似乎只是试图清理字符串,而 parse_expr 是一个成熟的专业解析器。
      • 我在评论您答案的第一个版本,将sympifyeval 进行比较。另外,请注意我写的工作是ongoing。我在 sympy 聊天中询问是否可以使 sympify 更安全,并为此付出了一些努力。
      【解决方案3】:

      a, b, c, x = symbols('a b c x', real=True) 替换为:

      a = Symbol('a')
      b = Symbol('b')
      c = Symbol('c')
      x = Symbol('x')
      

      【讨论】:

      • 不是symbols->Symbol 的替代品使它工作。这是real=True的遗漏。 Symbol('x') 恰好等于 xSymbol 对象,parse_expr 在未传入参数时隐式创建,如 my answer 中所述。
      • 所以,你很幸运,这不是正确的做法。
      【解决方案4】:

      不同假设的符号比较不相等:

      >>> Symbol('x') == Symbol('x', real=True)
      False
      

      当您使用sympifyparse_expr 时,它会将未知变量解析为符号而无需假设。在您的情况下,这将创建 Symbol('x'),这与您已经创建的 Symbol('x', real=True) 不同。

      解决方案是删除假设,或者在解析时包含 locals() 字典,以便它将名称 x 识别为您已经定义的 Symbol('x', real=True),例如

      parse_expr(str_expr,locals())
      

      sympify(str_expr, locals())
      

      【讨论】:

        猜你喜欢
        • 2018-01-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多