扩展python中的代数能力(sympy)

时间:2013-01-10 18:06:46

标签: python sympy

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

谢谢!

5 个答案:

答案 0 :(得分:5)

没有直接的支持。 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有点像黑客,所以请注意它是脆弱的。有些函数会重新评估表达式,将其转换回Pow。其他函数会因为一些预期的不变量会被evaluate = False表达式打破而破坏(例如,我怀疑factor()会正常工作)。“

答案 1 :(得分:1)

似乎没有这样的事情,它只是反过来。

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

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

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

答案 2 :(得分:1)

对于简单表达式,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)等内容。但是如果你扩展表达式的分子和分母并单独处理它们,这可能就是你需要的。如果基数总是符号,那么这个符号 - hackery可能会更好地解决这个问题:

>>> 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)

答案 3 :(得分:0)

跟随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,方法是将**kwargs传递给expr.func调用。这是你有兴趣做的事情,还是对大多数用户来说这是不必要的复杂? (或者我误解了你上面的评论并且有更简单的方法吗?)

答案 4 :(得分:0)

其他答案不处理-x**2因此我使用正则表达式来代替只有2的幂。我明白这有点hacky但它​​对我有用。

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