Можно ли создать символы в SymPy, которые являются антикоммутирующими?
Мне нужно реализовать некоторые переменные Грассмана в Python (то есть, антикоммутирующие переменные). Другими словами, я хотел бы что-то с поведением следующим образом
>>> from sympy import *
>>> x, y = symbols('x y')
>>> y*x
-x*y
>>> y*y
0
Еще одна особенность, которая мне понадобится, - это возможность дать канонический порядок моим переменным. Когда я набрал >>> y*x
, конечно, было бы справедливо также выводить y*x
над -x*y
, Но я хотел бы иметь возможность выбрать это x
должно появиться слева от y
(возможно, только после вызова функции simplify(y*x)
).
Есть ли у SymPy или какой-либо другой библиотеки такая возможность? Если нет, то каков наилучший способ реализовать это сам (например, я сам должен создать символическую библиотеку, расширить SymPy и т. Д.)?
1 ответ
Вы можете сделать новый класс, наследующий от Symbol
и изменить свое поведение при умножении (__mul__
) к желаемому. Чтобы сделать это полезным, вам все равно нужен канонический порядок, который должен быть таким же, как у SymPy (который, на первый взгляд, выглядит по имени, т.е. Symbol.name
) чтобы избежать проблем.
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
Теперь это все еще не решает сложные продукты, такие как x*y*x*y
правильно. Для этого мы можем написать функцию, которая сортирует произвольный продукт (используя пузырьковую сортировку):
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
Наконец, мы можем написать функцию, которая сортирует все продукты в выражении, перебирая дерево выражений и применяя sort_product
к каждому продукту, с которым он сталкивается:
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)
Обратите внимание, что я все еще не учел все возможные варианты.
Ответ Wrzlprmft - отличное начало, поэтому я добавлю следующий логический шаг. Поскольку вы просите, чтобы антикоммутирующие символы обрабатывались системой компьютерной алгебры, разумно предположить, что вы хотите иметь возможность дифференцировать их. Для этого потребуется функция для перезаписи правила продукта sympy.
from sympy import Add, Mul, prod
from sympy.ntheory.multinomial import multinomial_coefficients_iterator
def AnticomDeriv(ptr, s, n):
args = ptr.args
m = len(args)
terms = []
factor = S.One
if isinstance(s, AnticomSym):
if n > 1:
return S.Zero
args = list(args)
for i in range(len(args)):
d = args[i].diff(s)
terms.append(factor * reduce(lambda x, y: x*y, (args[:i] + [d] + args[i + 1:]), S.One))
if isinstance(args[i], AnticomSym):
factor *= -1
return Add.fromiter(terms)
for kvals, c in multinomial_coefficients_iterator(m, n):
p = prod([arg.diff((s, k)) for k, arg in zip(kvals, args)])
terms.append(c * p)
return Add(*terms)
Mul._eval_derivative_n_times = AnticomDeriv
Это даст следующее (правильное) поведение.
>>> x = AnticomSym('x')
>>> y = AnticomSym('y')
>>> expr = x*y
>>> expr.diff(x)
y
>>> expr.diff(y)
-x