Example #1
0
    def invariants_of_degree(self, deg, chi=None, R=None):
        r"""
        Return the (relative) invariants of given degree for this group.

        For this group, compute the invariants of degree ``deg``
        with respect to the group character ``chi``. The method
        is to project each possible monomial of degree ``deg`` via
        the Reynolds operator. Note that if the polynomial ring ``R``
        is specified it's base ring may be extended if the resulting
        invariant is defined over a bigger field.

        INPUT:

        - ``degree`` -- a positive integer

        - ``chi`` -- (default: trivial character) a linear group character of this group

        - ``R`` -- (optional) a polynomial ring

        OUTPUT: list of polynomials

        EXAMPLES::

            sage: Gr = MatrixGroup(SymmetricGroup(2))
            sage: sorted(Gr.invariants_of_degree(3))
            [x0^2*x1 + x0*x1^2, x0^3 + x1^3]
            sage: R.<x,y> = QQ[]
            sage: sorted(Gr.invariants_of_degree(4, R=R))
            [x^2*y^2, x^3*y + x*y^3, x^4 + y^4]

        ::

            sage: R.<x,y,z> = QQ[]
            sage: Gr = MatrixGroup(DihedralGroup(3))
            sage: ct = Gr.character_table()
            sage: chi = Gr.character(ct[0])
            sage: all(f(*(g.matrix()*vector(R.gens()))) == chi(g)*f
            ....: for f in Gr.invariants_of_degree(3, R=R, chi=chi) for g in Gr)
            True

        ::

            sage: i = GF(7)(3)
            sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]])
            sage: G.invariants_of_degree(25)
            []

        ::

            sage: G = MatrixGroup(SymmetricGroup(5))
            sage: R = QQ['x,y']
            sage: G.invariants_of_degree(3, R=R)
            Traceback (most recent call last):
            ...
            TypeError: number of variables in polynomial ring must match size of matrices

        ::

            sage: K.<i> = CyclotomicField(4)
            sage: G =  MatrixGroup(CyclicPermutationGroup(3))
            sage: chi = G.character(G.character_table()[1])
            sage: R.<x,y,z> = K[]
            sage: sorted(G.invariants_of_degree(2, R=R, chi=chi))
            [x*y + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*x*z + (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y*z,
             x^2 + (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y^2 + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*z^2]

        ::

            sage: S3 = MatrixGroup(SymmetricGroup(3))
            sage: chi = S3.character(S3.character_table()[0])
            sage: sorted(S3.invariants_of_degree(5, chi=chi))
            [x0^3*x1^2 - x0^2*x1^3 - x0^3*x2^2 + x1^3*x2^2 + x0^2*x2^3 - x1^2*x2^3,
            x0^4*x1 - x0*x1^4 - x0^4*x2 + x1^4*x2 + x0*x2^4 - x1*x2^4]
        """
        D = self.degree()
        deg = int(deg)
        if deg <= 0:
            raise ValueError("degree must be a positive integer")
        if R is None:
            R = PolynomialRing(self.base_ring(), 'x', D)
        elif R.ngens() != D:
            raise TypeError(
                "number of variables in polynomial ring must match size of matrices"
            )

        ms = self.molien_series(prec=deg + 1, chi=chi)
        if ms[deg].is_zero():
            return []
        inv = set()
        for e in IntegerVectors(deg, D):
            F = self.reynolds_operator(R.monomial(*e), chi=chi)
            if not F.is_zero():
                F = F / F.lc()
                inv.add(F)
                if len(inv) == ms[deg]:
                    break
        return list(inv)
    def invariants_of_degree(self, deg, chi=None, R=None):
        r"""
        Return the (relative) invariants of given degree for this group.

        For this group, compute the invariants of degree ``deg``
        with respect to the group character ``chi``. The method
        is to project each possible monomial of degree ``deg`` via
        the Reynolds operator. Note that if the polynomial ring ``R``
        is specified it's base ring may be extended if the resulting
        invariant is defined over a bigger field.

        INPUT:

        - ``degree`` -- a positive integer

        - ``chi`` -- (default: trivial character) a linear group character of this group

        - ``R`` -- (optional) a polynomial ring

        OUTPUT: list of polynomials

        EXAMPLES::

            sage: Gr = MatrixGroup(SymmetricGroup(2))
            sage: Gr.invariants_of_degree(3)
            [x0^3 + x1^3, x0^2*x1 + x0*x1^2]
            sage: R.<x,y> = QQ[]
            sage: Gr.invariants_of_degree(4, R=R)
            [x^3*y + x*y^3, x^2*y^2, x^4 + y^4]

        ::

            sage: R.<x,y,z> = QQ[]
            sage: Gr = MatrixGroup(DihedralGroup(3))
            sage: ct = Gr.character_table()
            sage: chi = Gr.character(ct[0])
            sage: [f(*(g.matrix()*vector(R.gens()))) == chi(g)*f \
                  for f in Gr.invariants_of_degree(3, R=R, chi=chi) for g in Gr]
            [True, True, True, True, True, True]

        ::

            sage: i = GF(7)(3)
            sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]])
            sage: G.invariants_of_degree(25)
            []

        ::

            sage: G = MatrixGroup(SymmetricGroup(5))
            sage: R = QQ['x,y']
            sage: G.invariants_of_degree(3, R=R)
            Traceback (most recent call last):
            ...
            TypeError: number of variables in polynomial ring must match size of matrices

        ::

            sage: K.<i> = CyclotomicField(4)
            sage: G =  MatrixGroup(CyclicPermutationGroup(3))
            sage: chi = G.character(G.character_table()[1])
            sage: R.<x,y,z> = K[]
            sage: G.invariants_of_degree(2, R=R, chi=chi)
            [x*y + (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*x*z +
             (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y*z,
             x^2 + (-2*izeta3^3 - 3*izeta3^2 - 8*izeta3 - 4)*y^2 +
             (2*izeta3^3 + 3*izeta3^2 + 8*izeta3 + 3)*z^2]

        ::

            sage: S3 = MatrixGroup(SymmetricGroup(3))
            sage: chi = S3.character(S3.character_table()[0])
            sage: S3.invariants_of_degree(5, chi=chi)
            [x0^4*x1 - x0*x1^4 - x0^4*x2 + x1^4*x2 + x0*x2^4 - x1*x2^4,
             x0^3*x1^2 - x0^2*x1^3 - x0^3*x2^2 + x1^3*x2^2 + x0^2*x2^3 - x1^2*x2^3]
        """
        D = self.degree()
        deg = int(deg)
        if deg <= 0:
            raise ValueError("degree must be a positive integer")
        if R is None:
            R = PolynomialRing(self.base_ring(), 'x', D)
        elif R.ngens() != D:
            raise TypeError("number of variables in polynomial ring must match size of matrices")

        ms = self.molien_series(prec=deg+1,chi=chi)
        if ms[deg].is_zero():
            return []
        inv = set()
        for e in IntegerVectors(deg, D):
            F = self.reynolds_operator(R.monomial(*e), chi=chi)
            if not F.is_zero():
                F = F/F.lc()
                inv.add(F)
                if len(inv) == ms[deg]:
                    break
        return list(inv)
class QuantumCliffordAlgebra(CombinatorialFreeModule):
    r"""
    The quantum Clifford algebra.

    The *quantum Clifford algebra*, or `q`-Clifford algebra,
    of rank `n` and twist `k` is the unital associative algebra
    `\mathrm{Cl}_{q}(n, k)` over a field `F` with generators
    `\psi_a, \psi_a^*, \omega_a` for `a = 1, \dotsc, n` that
    satisfy the following relations:

    .. MATH::

        \begin{aligned}
        \omega_a \omega_b & = \omega_b \omega_a,
        & \omega_a^{4k} & = (1 + q^{-2k}) \omega_a^{2k} - q^{-2k},
        \\ \omega_a \psi_b & = q^{\delta_{ab}} \psi_b \omega_a,
        & \omega_a \psi^*_b & = \psi^*_b \omega_a,
        \\ \psi_a \psi_b & + \psi_b \psi_a = 0,
        & \psi^*_a \psi^*_b & + \psi^*_b \psi^*_a = 0,
        \\ \psi_a \psi^*_a & = \frac{q^k \omega_a^{3k} - q^{-k} \omega_a^k}{q^k - q^{-k}},
        & \psi^*_a \psi_a & = \frac{q^{2k} (\omega_a - \omega_a^{3k})}{q^k - q^{-k}},
        \\ \psi_a \psi^*_b & + \psi_b^* \psi_a = 0
        & & \text{if } a \neq b,
        \end{aligned}

    where `q \in F` such that `q^{2k} \neq 1`.

    When `k = 2`, we recover the original definition given by Hayashi in
    [Hayashi1990]_. The `k = 1` version was used in [Kwon2014]_.

    INPUT:

    - ``n`` -- positive integer; the rank
    - ``k`` -- positive integer (default: 1); the twist
    - ``q`` -- (optional) the parameter `q`
    - ``F`` -- (default: `\QQ(q)`) the base field that contains ``q``

    EXAMPLES:

    We construct the rank 3 and twist 1 `q`-Clifford algebra::

        sage: Cl = algebras.QuantumClifford(3)
        sage: Cl
        Quantum Clifford algebra of rank 3 and twist 1 with q=q over
         Fraction Field of Univariate Polynomial Ring in q over Integer Ring
        sage: q = Cl.q()

    Some sample computations::

        sage: p0, p1, p2, d0, d1, d2, w0, w1, w2 = Cl.gens()
        sage: p0 * p1
        psi0*psi1
        sage: p1 * p0
        -psi0*psi1
        sage: p0 * w0 * p1 * d0 * w2
        (1/(q^3-q))*psi1*w2 + (-q/(q^2-1))*psi1*w0^2*w2
        sage: w0^4
        -1/q^2 + ((q^2+1)/q^2)*w0^2

    We construct the homomorphism from `U_q(\mathfrak{sl}_3)` to
    `\mathrm{Cl}(3, 1)` given in (3.17) of [Hayashi1990]_::

        sage: e1 = p0*d1; e2 = p1*d2
        sage: f1 = p1*d0; f2 = p2*d1
        sage: k1 = w0*~w1; k2 = w1*~w2
        sage: k1i = w1*~w0; k2i = w2*~w1
        sage: (e1, e2, f1, f2, k1, k2, k1i, k2i)
        (psi0*psid1, psi1*psid2,
         -psid0*psi1, -psid1*psi2,
         (q^2+1)*w0*w1 - q^2*w0*w1^3, (q^2+1)*w1*w2 - q^2*w1*w2^3,
         (q^2+1)*w0*w1 - q^2*w0^3*w1, (q^2+1)*w1*w2 - q^2*w1^3*w2)

    We check that `k_i` and `k_i^{-1}` are inverses::

        sage: k1 * k1i
        1
        sage: k2 * k2i
        1

    The relations between `e_i`, `f_i`, and `k_i`::

        sage: k1 * f1 == q^-2 * f1 * k1
        True
        sage: k2 * f1 == q^1 * f1 * k2
        True
        sage: k2 * e1 == q^-1 * e1 * k2
        True
        sage: k1 * e1 == q^2 * e1 * k1
        True
        sage: e1 * f1 - f1 * e1 == (k1 - k1i)/(q-q^-1)
        True
        sage: e2 * f1 - f1 * e2
        0

    The `q`-Serre relations::

        sage: e1 * e1 * e2 - (q^1 + q^-1) * e1 * e2 * e1 + e2 * e1 * e1
        0
        sage: f1 * f1 * f2 - (q^1 + q^-1) * f1 * f2 * f1 + f2 * f1 * f1
        0
    """
    @staticmethod
    def __classcall_private__(cls, n, k=1, q=None, F=None):
        r"""
        Standardize input to ensure a unique representation.

        TESTS::

            sage: Cl1 = algebras.QuantumClifford(3)
            sage: q = PolynomialRing(ZZ, 'q').fraction_field().gen()
            sage: Cl2 = algebras.QuantumClifford(3, q=q)
            sage: Cl3 = algebras.QuantumClifford(3, 1, q, q.parent())
            sage: Cl1 is Cl2 and Cl2 is Cl3
            True
        """
        if q is None:
            q = PolynomialRing(ZZ, 'q').fraction_field().gen()
        if F is None:
            F = q.parent()
        q = F(q)
        if F not in Fields():
            raise TypeError("base ring must be a field")
        return super(QuantumCliffordAlgebra, cls).__classcall__(cls, n, k, q, F)

    def __init__(self, n, k, q, F):
        r"""
        Initialize ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(1,2)
            sage: TestSuite(Cl).run(elements=Cl.basis())

            sage: Cl = algebras.QuantumClifford(1,3)
            sage: TestSuite(Cl).run(elements=Cl.basis())  # long time

            sage: Cl = algebras.QuantumClifford(3)  # long time
            sage: elts = Cl.some_elements() + list(Cl.algebra_generators())  # long time
            sage: TestSuite(Cl).run(elements=elts)  # long time

            sage: Cl = algebras.QuantumClifford(2,4)  # long time
            sage: elts = Cl.some_elements() + list(Cl.algebra_generators())  # long time
            sage: TestSuite(Cl).run(elements=elts)  # long time
        """
        self._n = n
        self._k = k
        self._q = q
        self._psi = cartesian_product([(-1,0,1)]*n)
        self._w_poly = PolynomialRing(F, n, 'w')
        indices = [(tuple(psi), tuple(w))
                   for psi in self._psi
                   for w in product(*[list(range((4-2*abs(psi[i]))*k)) for i in range(n)])]
        indices = FiniteEnumeratedSet(indices)

        cat = Algebras(F).FiniteDimensional().WithBasis()
        CombinatorialFreeModule.__init__(self, F, indices, category=cat)
        self._assign_names(self.algebra_generators().keys())

    def _repr_(self):
        r"""
        Return a string representation of ``self``.

        EXAMPLES::

            sage: algebras.QuantumClifford(3)
            Quantum Clifford algebra of rank 3 and twist 1 with q=q over
             Fraction Field of Univariate Polynomial Ring in q over Integer Ring
        """
        return "Quantum Clifford algebra of rank {} and twist {} with q={} over {}".format(
            self._n, self._k, self._q, self.base_ring())

    def _latex_(self):
        r"""
        Return a latex representation of ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3)
            sage: latex(Cl)
            \operatorname{Cl}_{q}(3, 1)
        """
        return "\\operatorname{Cl}_{%s}(%s, %s)" % (self._q, self._n, self._k)

    def _repr_term(self, m):
        r"""
        Return a string representation of the basis element indexed by ``m``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3, 3)
            sage: Cl._repr_term( ((1, 0, -1), (0, -2, 5)) )
            'psi0*psid2*w1^-2*w2^5'
            sage: Cl._repr_term( ((1, 0, -1), (0, 0, 0)) )
            'psi0*psid2'
            sage: Cl._repr_term( ((0, 0, 0), (0, -2, 5)) )
            'w1^-2*w2^5'
            sage: Cl._repr_term( ((0, 0, 0), (0, 0, 0)) )
            '1'

            sage: Cl(5)
            5
        """
        p, v = m
        rp = '*'.join('psi%s'%i if p[i] > 0 else 'psid%s'%i
                      for i in range(self._n) if p[i] != 0)
        gen_str = lambda e: '' if e == 1 else '^%s'%e
        rv = '*'.join('w%s'%i + gen_str(v[i]) for i in range(self._n) if v[i] != 0)
        if rp:
            if rv:
                return rp + '*' + rv
            return rp
        if rv:
            return rv
        return '1'

    def _latex_term(self, m):
        r"""
        Return a latex representation for the basis element indexed by ``m``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3, 3)
            sage: Cl._latex_term( ((1, 0, -1), (0, -2, 5)) )
            '\\psi_{0}\\psi^{\\dagger}_{2}\\omega_{1}^{-2}\\omega_{2}^{5}'
            sage: Cl._latex_term( ((1, 0, -1), (0, 0, 0)) )
            '\\psi_{0}\\psi^{\\dagger}_{2}'
            sage: Cl._latex_term( ((0, 0, 0), (0, -2, 5)) )
            '\\omega_{1}^{-2}\\omega_{2}^{5}'
            sage: Cl._latex_term( ((0, 0, 0), (0, 0, 0)) )
            '1'

            sage: latex(Cl(5))
            5
        """
        p, v = m
        rp = ''.join('\\psi_{%s}'%i if p[i] > 0 else '\\psi^{\\dagger}_{%s}'%i
                     for i in range(self._n) if p[i] != 0)
        gen_str = lambda e: '' if e == 1 else '^{%s}'%e
        rv = ''.join('\\omega_{%s}'%i + gen_str(v[i])
                     for i in range(self._n) if v[i] != 0)
        if not rp and not rv:
            return '1'
        return rp + rv

    def q(self):
        r"""
        Return the `q` of ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3)
            sage: Cl.q()
            q

            sage: Cl = algebras.QuantumClifford(3, q=QQ(-5))
            sage: Cl.q()
            -5
        """
        return self._q

    def twist(self):
        r"""
        Return the twist `k` of ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3, 2)
            sage: Cl.twist()
            2
        """
        return self._k

    def rank(self):
        r"""
        Return the rank `k` of ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3, 2)
            sage: Cl.rank()
            3
        """
        return self._n

    def dimension(self):
        r"""
        Return the dimension of ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3)
            sage: Cl.dimension()
            512

            sage: Cl = algebras.QuantumClifford(4, 2)
            sage: Cl.dimension()
            65536
        """
        return ZZ(8*self._k) ** self._n

    @cached_method
    def algebra_generators(self):
        r"""
        Return the algebra generators of ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3)
            sage: Cl.algebra_generators()
            Finite family {'psi0': psi0, 'psi1': psi1, 'psi2': psi2,
                           'psid0': psid0, 'psid1': psid1, 'psid2': psid2,
                           'w0': w0, 'w1': w1, 'w2': w2}
        """
        one = (0,) * self._n  # one in the corresponding free abelian group
        zero = [0] * self._n
        d = {}
        for i in range(self._n):
            r = list(zero)  # Make a copy
            r[i] = 1
            d['psi%s'%i] = self.monomial( (self._psi(r), one) )
            r[i] = -1
            d['psid%s'%i] = self.monomial( (self._psi(r), one) )
        zero = self._psi(zero)
        for i in range(self._n):
            temp = list(zero)  # Make a copy
            temp[i] = 1
            d['w%s'%i] = self.monomial( (zero, tuple(temp)) )
        return Family(sorted(d), lambda i: d[i])

    @cached_method
    def gens(self):
        r"""
        Return the generators of ``self``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3)
            sage: Cl.gens()
            (psi0, psi1, psi2, psid0, psid1, psid2, w0, w1, w2)
        """
        return tuple(self.algebra_generators())

    @cached_method
    def one_basis(self):
        r"""
        Return the index of the basis element of `1`.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3)
            sage: Cl.one_basis()
            ((0, 0, 0), (0, 0, 0))
        """
        return (self._psi([0]*self._n), (0,)*self._n)

    @cached_method
    def product_on_basis(self, m1, m2):
        r"""
        Return the product of the basis elements indexed by ``m1`` and ``m2``.

        EXAMPLES::

            sage: Cl = algebras.QuantumClifford(3)
            sage: Cl.inject_variables()
            Defining psi0, psi1, psi2, psid0, psid1, psid2, w0, w1, w2
            sage: psi0^2  # indirect doctest
            0
            sage: psid0^2
            0
            sage: w0 * psi0
            q*psi0*w0
            sage: w0 * psid0
            1/q*psid0*w0
            sage: w2 * w0
            w0*w2
            sage: w0^4
            -1/q^2 + ((q^2+1)/q^2)*w0^2
        """
        p1, w1 = m1
        p2, w2 = m2
        # Check for \psi_i^2 == 0 and for the dagger version
        if any(p1[i] != 0 and p1[i] == p2[i] for i in range(self._n)):
            return self.zero()

        # \psi_i is represented by a 1 in p1[i] and p2[i]
        # \psi_i^{\dagger} is represented by a -1 in p1[i] and p2[i]

        k = self._k
        q_power = 0
        sign = 1
        pairings = []
        supported = []
        p = [0] * self._n
        # Move w1 * p2 to q^q_power * p2 * w1
        # Also find pairs \psi_i \psi_i^{\dagger} (or vice versa)
        for i in range(self._n):
            if p2[i] != 0:
                supported.append(i)
                q_power += w1[i] * p2[i]
                if p1[i] != 0:
                    # We make pairings 1-based because we cannot distinguish 0 and -0
                    pairings.append((i+1) * p1[i])
            # we know p1[i] != p2[i] if non-zero, so their sum is -1, 0, 1
            p[i] = p1[i] + p2[i]

        supported.append(self._n-1) # To get between the last support and the end
        # Get the sign of moving \psi_i and \psi_i^{\dagger} into position
        for i in reversed(range(1, len(supported))):
            if i % 2 != 0:
                for j in reversed(range(supported[i-1]+1, supported[i]+1)):
                    if p1[j] != 0:
                        sign = (-1)**i * sign

        # We move the pairs \psi_i \psi_i^{\dagger} (or the reverse) to the
        #   end of the \psi part. This does not change the sign because they
        #   move in pairs.
        # We take the products.
        vp = self._w_poly.gens()
        poly = self._w_poly.one()
        q = self._q
        for i in pairings:
            if i < 0:
                i = -i - 1 # Go back to 0-based
                vpik = -q**(2*k) * vp[i]**(3*k) + (1 + q**(2*k)) * vp[i]**k
                poly *= -(vp[i]**k - vpik) / (q**k - q**(-k))
            else:
                i -= 1 # Go back to 0-based
                vpik = -q**(2*k) * vp[i]**(3*k) + (1 + q**(2*k)) * vp[i]**k
                poly *= (q**k * vp[i]**k - q**(-k) * vpik) / (q**k - q**(-k))

        v = list(w1)
        for i in range(self._n):
            v[i] += w2[i]

        # For all \psi_i v_i^k, convert this to either k = 0, 1
        #   and same for \psi_i^{\dagger}
        for i in range(self._n):
            if p[i] > 0 and v[i] != 0:
                q_power -= 2 * k * (v[i] // (2*k))
                v[i] = v[i] % (2*k)
            if p[i] < 0 and v[i] != 0:
                v[i] = v[i] % (2*k)

        poly *= self._w_poly.monomial(*v)
        poly = poly.reduce([vp[i]**(4*k) - (1 + q**(-2*k)) * vp[i]**(2*k) + q**(-2*k)
                            for i in range(self._n)])
        pdict = poly.dict()
        ret = {(self._psi(p), tuple(e)): pdict[e] * q**q_power * sign
               for e in pdict}

        return self._from_dict(ret)

    class Element(CombinatorialFreeModule.Element):
        def inverse(self):
            r"""
            Return the inverse if ``self`` is a basis element.

            EXAMPLES::

                sage: Cl = algebras.QuantumClifford(3)
                sage: Cl.inject_variables()
                Defining psi0, psi1, psi2, psid0, psid1, psid2, w0, w1, w2
                sage: w0^-1
                (q^2+1)*w0 - q^2*w0^3
                sage: w0^-1 * w0
                1
                sage: w0^-2
                (q^2+1) - q^2*w0^2
                sage: w0^-2 * w0^2
                1
                sage: w0^-2 * w0 == w0^-1
                True
                sage: w = w0 * w1
                sage: w^-1
                (q^4+2*q^2+1)*w0*w1 + (-q^4-q^2)*w0*w1^3
                 + (-q^4-q^2)*w0^3*w1 + q^4*w0^3*w1^3
                sage: w^-1 * w
                1
                sage: w * w^-1
                1

                sage: (w0 + w1)^-1
                Traceback (most recent call last):
                ...
                NotImplementedError: inverse only implemented for basis elements
                sage: (psi0 * w0)^-1
                Traceback (most recent call last):
                ...
                NotImplementedError: inverse only implemented for product of w generators

                sage: Cl = algebras.QuantumClifford(2, 2)
                sage: Cl.inject_variables()
                Defining psi0, psi1, psid0, psid1, w0, w1
                sage: w0^-1
                (q^4+1)*w0^3 - q^4*w0^7
                sage: w0 * w0^-1
                1
            """
            if not self:
                raise ZeroDivisionError
            if len(self) != 1:
                raise NotImplementedError("inverse only implemented for basis elements")
            Cl = self.parent()
            p, w = self.support_of_term()
            if any(p[i] != 0 for i in range(Cl._n)):
                raise NotImplementedError("inverse only implemented for"
                                          " product of w generators")
            poly = Cl._w_poly.monomial(*w)
            wp = Cl._w_poly.gens()
            q = Cl._q
            k = Cl._k
            poly = poly.subs({wi: -q**(2*k) * wi**(4*k-1) + (1 + q**(2*k)) * wi**(2*k-1)
                              for wi in wp})
            poly = poly.reduce([wi**(4*k) - (1 + q**(-2*k)) * wi**(2*k) + q**(-2*k)
                                for wi in wp])
            pdict = poly.dict()
            ret = {(p, tuple(e)): c for e, c in pdict.items()}
            return Cl._from_dict(ret)

        __invert__ = inverse