Python – Is it possible to build anti-commute symbols in SymPy?

Is it possible to build anti-commute symbols in SymPy?… here is a solution to the problem.

Is it possible to build anti-commute symbols in SymPy?

I need to implement some Grassmann variables (i.e. anti-commute variables) in python. In other words, I want some behavior as follows

>>> from sympy import *
>>> x, y = symbols('x y')
>>> y*x
-x*y
>>> y*y
0

Another feature I need is the ability to canonical sort my variables. When I enter >>> y*x, it is definitely valid to output y*x on -x*y. However, I would like to be able to choose which x should appear to the left of y (perhaps only when calling the function simplify(y*x)).

Does SymPy or some other library have this capability? If not, what is the best way for me to implement it myself (e.g. should I create a symbol library myself, extend SymPy, etc)?

Solution

You can create a new class that inherits from Symbol and change its multiplication (__mul__) behavior to the desired behavior.
To make it useful, you need a canonical sort anyway, which should be the same as SymPy’s ordering (a quick glance seems to be named by name, i.e. Symbol.name) to avoid problems.

from sympy import Symbol, S

class AnticomSym(Symbol):
    def __new__(cls,*args,**kwargs):
        return super().__new__(cls,*args,**kwargs,commutative=False)

def __mul__(self,other):
        if isinstance(other,AnticomSym):
            if other==self:
                return S.Zero
            elif other.name<self.name:
                return -Symbol.__mul__(other,self)

return super().__mul__(other)

def __pow__(self,exponent):
        if exponent>=2:
            return S.Zero
        else:
            return super().__pow__(exponent)

x = AnticomSym("x")
y = AnticomSym("y")

assert y*x == -x*y
assert y*y == 0
assert y**2 == 0
assert y**1 == y
assert ((x+y)**2).expand() == 0
assert x*y-y*x == 2*x*y

Now, this still doesn’t parse complex products like x*y*x*y correctly.
To do this, we can write a function that sorts arbitrary products (using bubbling sorting):

from sympy import Mul

def sort_product(product):
    while True:
        if not isinstance(product,Mul):
            return product

arglist = list(product.args)
        i = 0
        while i < len(arglist)-1:
            slice_prod = arglist[i]*arglist[i+1]
            is_mul = isinstance(slice_prod,Mul)
            arglist[i:i+2] = slice_prod.args if is_mul else [slice_prod]
            i += 1

new_product = Mul(*arglist)
        if product == new_product:
            return new_product
        product = new_product

z = AnticomSym("z")
assert sort_product(y*(-x)) == x*y
assert sort_product(x*y*x*y) == 0
assert sort_product(z*y*x) == -x*y*z

Finally, we can write a function that sorts all products in the expression by traversing the expression tree and applying an sort_product to each product we encounter:

def sort_products(expr):
    if expr.is_Atom:
        return expr
    else:
        simplified_args = (sort_products(arg) for arg in expr.args)
        if isinstance(expr,Mul):
            return sort_product(Mul(*simplified_args))
        else:
            return expr.func(*simplified_args)

from sympy import exp
assert sort_products(exp(y*(-x))) == exp(x*y)
assert sort_products(exp(x*y*x*y)-exp(z*y*z*x)) == 0
assert sort_products(exp(z*y*x)) == exp(-x*y*z)

Note that I may still not have taken into account all possible scenarios.

Related Problems and Solutions