Search code examples
pythonpython-2.7sympymathematical-expressionscommutativity

Non-commutative sympify (or simplify)


I would like to be able to simplify mathematical expressions from a string in Python. There are several "commutative" ways of doing it. Is there a non-commutative function for that?

I know that sympify from sympy can do some non-commutative jobs, here you have an example:

from sympy import *
x=Symbol('x',commutative=False)
y=Symbol('y',commutative=False)

print sympify(3*x*y - y*x - 2*x*y)

it will print xy -yx, however if we apply sympify to the string, that is,

print sympify('3*x*y - y*x - 2*x*y')

The result is 0.

Is there a way of simplifying the above string to preserve non-commutativity of x and y?

I found that someone has already asked about it here http://osdir.com/ml/python-sympy/2012-02/msg00250.html and someone has answered http://osdir.com/ml/python-sympy/2012-02/msg00255.html, however the solution seems not to work in general.

I preferred to ask first, if there is no immediate solution I guess that I will have to code it myself.


Solution

  • You still need to tell Sympy that there are constraints on the symbols x and y. To do this, still create Symbol instances for them, and then just pass those parameters in as locals to sympify:

    In [120]: x = sympy.Symbol('x', commutative=False)
    
    In [121]: y = sympy.Symbol('y', commutative=False)
    
    In [122]: sympy.sympify('3*x*y - y*x - 2*x*y', locals={'x':x, 'y':y})
    Out[122]: x*y - y*x
    

    To do it programmatically, SymPy provides some nice parsing tools for extracting symbols from a string expression. The key idea is that you have to suppress evaluation since normal evaluation will make commutativity assumptions that ruin your ability to extract what you need:

    In [155]: s = sympy.parsing.sympy_parser.parse_expr('3*x*y - y*x - 2*x*y', evaluate=False)
    
    In [156]: s.atoms(sympy.Symbol)
    Out[156]: {x, y}
    

    It does not appear that there is a direct way to mutate the assumption state of an already-created Symbol, which is unfortunate. But you can iterate through these symbols, and make a new collection of symbols with the same names and the non-commutative assumption, and use that for locals in sympify.

    def non_commutative_sympify(expr_string):
        parsed_expr = sympy.parsing.sympy_parser.parse_expr(
            expr_string, 
            evaluate=False
        )
    
        new_locals = {sym.name:sympy.Symbol(sym.name, commutative=False)
                      for sym in parsed_expr.atoms(sympy.Symbol)}
    
        return sympy.sympify(expr_string, locals=new_locals)
    

    Which gives, e.g.:

    In [184]: non_commutative_sympify('3*x*y - y*x - 2*x*y')
    Out[184]: x*y - y*x
    
    In [185]: non_commutative_sympify('x*y*z - y*z*x - 2*x*y*z + z*y*x')
    Out[185]: -x*y*z - y*z*x + z*y*x