"""Minimal Grassmann algebra with sympy coefficients, used by the Section 3 checks.""" # Minimal Grassmann algebra with sympy coefficients, for checking conventions. import sympy as sp, itertools class G: def __init__(self, d=None): self.d = {k: sp.sympify(v) for k, v in (d or {}).items() if sp.simplify(v) != 0} @staticmethod def gen(i): return G({(i,): 1}) @staticmethod def const(c): return G({(): c}) def __add__(self, o): o = o if isinstance(o, G) else G.const(o) d = dict(self.d) for k, v in o.d.items(): d[k] = d.get(k, 0) + v return G(d) __radd__ = __add__ def __neg__(self): return G({k: -v for k, v in self.d.items()}) def __sub__(self, o): return self + (-(o if isinstance(o, G) else G.const(o))) def __rsub__(self, o): return (-self) + o def __mul__(self, o): o = o if isinstance(o, G) else G.const(o) d = {} for k1, v1 in self.d.items(): for k2, v2 in o.d.items(): if set(k1) & set(k2): continue seq = list(k1) + list(k2) # sign of sorting permutation sign = 1 arr = seq[:] for i in range(len(arr)): for j in range(len(arr) - 1 - i): if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j]; sign = -sign k = tuple(arr) d[k] = d.get(k, 0) + sign * v1 * v2 return G(d) def __rmul__(self, o): return G.const(o) * self def body(self): return self.d.get((), sp.Integer(0)) def coeff(self, mono): return self.d.get(tuple(mono), sp.Integer(0)) def soul(self): return self - G.const(self.body()) def apply(self, fn): # coefficientwise sympy operation (e.g. differentiate in x) return G({k: fn(v) for k, v in self.d.items()}) def dxi(self, i): # left derivative with respect to generator i d = {} for k, v in self.d.items(): if i in k: pos = k.index(i) d[tuple(m for m in k if m != i)] = d.get(tuple(m for m in k if m != i), 0) + (-1) ** pos * v return G(d) def simplify(self): return G({k: sp.simplify(v) for k, v in self.d.items()}) def func_even(f, a, var, order=6): # f(a) for even a = a0 + n, f a sympy expression in var a0 = a.body(); n = a.soul() res = G(); p = G.const(1) for j in range(order + 1): res = res + p * (sp.diff(f, var, j).subs(var, a0) / sp.factorial(j)) p = p * n return res def det(M): n = len(M) if n == 1: return M[0][0] res = G() for j in range(n): minor = [row[:j] + row[j + 1:] for row in M[1:]] res = res + ((-1) ** j) * M[0][j] * det(minor) return res def inv_even(a, order=6): # inverse of an even element with invertible body t = sp.Symbol('t') return func_even(1 / t, a, t, order)