【问题标题】:Expanding algebraic powers in python (sympy)在 python 中扩展代数幂(sympy)
【发布时间】:2017-08-30 17:31:19
【问题描述】:

我只是想知道在 python 的 Sympy 模块中是否存在将代数幂(例如 x**2)扩展到它们的乘法形式(即 x**2 -> x*x)的现有方法?

谢谢!

【问题讨论】:

    标签: python sympy


    【解决方案1】:

    对此没有直接支持。 SymPy 自动组合乘法中的常用项以求幂。避免这种情况发生的唯一方法是使用evaluate=False 机制。例如

    >>> Mul(x, x, evaluate=False)
    x*x
    

    不久前在 SymPy 邮件列表上讨论了这个确切的问题 (https://groups.google.com/d/topic/sympy/qaJGesRbX_0/discussion)。我在那里发布了一些代码可以做到这一点。我在这里重复一遍:

    def pow_to_mul(expr):
        """
        Convert integer powers in an expression to Muls, like a**2 => a*a.
        """
        pows = list(expr.atoms(Pow))
        if any(not e.is_Integer for b, e in (i.as_base_exp() for i in pows)):
    
            raise ValueError("A power contains a non-integer exponent")
        repl = zip(pows, (Mul(*[b]*e,evaluate=False) for b,e in (i.as_base_exp() for i in pows)))
        return expr.subs(repl)
    

    这是它的工作原理

    >>> a = Symbol('a')
    >>> exp = a**2
    >>> print(exp)
    a**2
    >>> print(pow_to_mul(exp))
    a*a
    

    我将在邮件列表中提出同样的警告:“evaluate=False 有点像 hack,所以请注意它很脆弱。一些函数会重新计算表达式,将其转换回 Pow。其他函数会中断,因为某些预期的不变量会被 evaluate=False 表达式破坏(例如,我怀疑 factor() 会正常工作)。"

    【讨论】:

    • 这很简洁,并且在某些时候有效。但是,正如您所说, evaluate kwarg 很脆弱,经常被忽视,甚至经常在最后的 expr.subs(repl) 行中。转换后我只想打印到 C 字符串(即我不会对表达式执行任何操作),那么有没有办法强制 subs 方法不评估转换后的表达式?
    • 好的,看来我已经解决了这个问题,方法是破解 xreplace 的副本,以便在重新生成子表达式时在内部使用 evaluate=False。你能看出这种方法有什么问题吗?
    • 应该可以编写一个版本的 xreplace 来做到这一点而无需破解任何东西,即,就像一个函数,将表达式和替换作为输入并返回替换为 evaluate=False 的输出。
    • 这在以下情况下不起作用:exp = a**2*(-1) 它仍然打印指数
    • 同样,这不会完全扩展像 (1 + x**2)**2 这样的东西。运行两次对于像我的示例这样的双指数有效,但这不是灵丹妙药。
    【解决方案2】:

    好像没有这样的事,只是反其道而行之。

    sympy 总是以最简单的方式显示输出,所以它总是会说:

    (x**2).expand() -> x**2
    
    simplify(x**2) -> x**2
    

    【讨论】:

    • 是否可以选择性地关闭该功能?
    • 我还没有看到任何实际显示 x*x 的选项。但是,谁知道呢,这是一个相当庞大的图书馆……
    【解决方案3】:

    replace 方法非常适合简单表达式的这项任务:

    >>> expr = (x**2 + 1)/(x**3 - 2*x)
    >>> expr.replace(
    ... lambda x: x.is_Pow and x.exp > 0,
    ... lambda x: Mul(*[x.base]*x.exp, evaluate=False))
    (x*x + 1)/(-2*x + x*x*x)
    

    需要调整以处理1/x**3x**2*(1 + x**2) 之类的事情。但是,如果您扩展表达式的分子和分母并分别处理它们,这可能会满足您的需求。如果基础总是符号,那么这个符号黑客可能会做得更好:

    >>> def sack(expr):
    ...     return expr.replace(
    ...     lambda x: x.is_Pow and x.exp > 0,
    ...     lambda x: Symbol('*'.join([x.base.name]*x.exp)))
    ...
    >>> sack(-x**2)
    -x*x
    >>> sack(x**2*(1 + x**3)
    x*x*(x*x*x + 1)
    

    【讨论】:

      【解决方案4】:

      根据 Aaron 接受的答案和我对此的评论,这是我使用的 xreplace 版本,而不是最后的 subs 行,以避免评估子表达式(从而失去权力的扩展到一个乘法链)。

      def non_eval_xreplace(expr, rule):
          """
          Duplicate of sympy's xreplace but with non-evaluate statement included
          """
          if expr in rule:
              return rule[expr]
          elif rule:
              args = []
              altered = False
              for a in expr.args:
                  try:
                      new_a = non_eval_xreplace(a, rule)
                  except AttributeError:
                      new_a = a
                  if new_a != a:
                      altered = True
                  args.append(new_a)
              args = tuple(args)
              if altered:
                  return expr.func(*args, evaluate=False)
          return expr
      

      我在想这个功能可以添加到 SymPy 库中现有的xreplace 中,方法是让它接受传递给expr.func 调用的**kwargs。这是您有兴趣做的事情,还是对于大多数用户来说这会变得不必要地复杂? (或者我是否误解了您上面的评论,有没有更简单的方法可以做到这一点?)

      【讨论】:

        【解决方案5】:

        其他答案无法处理-x**2,因此我使用正则表达式仅解决 2 的幂。我知道这有点笨拙,但它对我有用。

        from sympy.printing import ccode
        import re
        CPOW = re.compile(r'pow\((?P<var>[A-Za-z_]\w*)\s*,\s*2\s*\)')
        
        def to_c_code(expr):
            code = ccode(expr)
            # sympy has a hard time unsimplifying x**2 to x*x
            # replace all pow(var,2) with var*var
            code = re.sub(CPOW, r'\g<var>*\g<var>', code)
            return code
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-12-05
          • 1970-01-01
          • 1970-01-01
          • 2015-09-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多