From 63787c4e7599520e64fd6eadc55e0bccfe9a496b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 14 Jul 2022 13:15:36 +0900 Subject: [PATCH] Bringing more to the GB strategy classes. --- src/sage/algebras/clifford_algebra.py | 30 +++++++-- .../algebras/clifford_algebra_element.pyx | 11 ++-- .../algebras/exterior_algebra_groebner.pxd | 4 ++ .../algebras/exterior_algebra_groebner.pyx | 64 +++++++++++++------ 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 5e3be412ce5..ee589a65076 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -2405,7 +2405,6 @@ def __init__(self, ring, gens, coerce=True, side="twosided"): """ Initialize ``self``. """ - self._groebner_basis = None self._groebner_strategy = None self._homogeneous = all(x.is_super_homogeneous() for x in gens) if self._homogeneous: @@ -2416,7 +2415,24 @@ def reduce(self, f): """ Reduce ``f`` modulo ``self``. """ - return f.reduce(self) + if self._groebner_strategy is None: + self.groebner_basis() + R = self.ring() + return self._groebner_strategy.reduce(R(f)) + + def _contains_(self, f): + r""" + Return ``True`` if ``f`` is in this ideal, + ``False`` otherwise. + + EXAMPLES:: + + .. NOTE:: + + Requires computation of a Groebner basis, which can be a very + expensive operation. + """ + return self.reduce(f).is_zero() def groebner_basis(self, term_order="neglex"): r""" @@ -2484,9 +2500,9 @@ def groebner_basis(self, term_order="neglex"): from sage.algebras.exterior_algebra_groebner import GroebnerStrategyDegLex as strategy else: raise ValueError("invalid term order") - if strategy == self._groebner_strategy: - return self._groebner_basis - self._groebner_strategy = strategy - self._groebner_basis = self._groebner_strategy(self).compute_groebner() - return self._groebner_basis + if strategy == type(self._groebner_strategy): + return self._groebner_strategy.groebner_basis + self._groebner_strategy = strategy(self) + self._groebner_strategy.compute_groebner() + return self._groebner_strategy.groebner_basis diff --git a/src/sage/algebras/clifford_algebra_element.pyx b/src/sage/algebras/clifford_algebra_element.pyx index 93568ebd1d1..0c35b57d983 100644 --- a/src/sage/algebras/clifford_algebra_element.pyx +++ b/src/sage/algebras/clifford_algebra_element.pyx @@ -390,18 +390,19 @@ cdef class ExteriorAlgebraElement(CliffordAlgebraElement): INPUT: - ``I`` -- a list of exterior algebra elements or an ideal - - ``side`` -- the side, ignored if ``I`` is an ideal + - ``left`` -- boolean; if reduce as a left ideal (``True``) + or right ideal (``False``), ignored if ``I`` is an ideal """ from sage.algebras.clifford_algebra import ExteriorAlgebraIdeal if isinstance(I, ExteriorAlgebraIdeal): - left = (I.side() == "left") - I = I.groebner_basis() + return I.reduce(self) f = self E = self._parent - from sage.algebras.exterior_algebra_cython import leading_support + + cdef FrozenBitset lm, s for g in I: - lm = leading_support(g) + lm = g.leading_support() reduction = True while reduction: supp = f.support() diff --git a/src/sage/algebras/exterior_algebra_groebner.pxd b/src/sage/algebras/exterior_algebra_groebner.pxd index 664d70202fe..4e235dae6ef 100644 --- a/src/sage/algebras/exterior_algebra_groebner.pxd +++ b/src/sage/algebras/exterior_algebra_groebner.pxd @@ -17,6 +17,7 @@ cdef class GroebnerStrategy: cdef int side cdef MonoidElement ideal cdef bint homogeneous + cdef public tuple groebner_basis cdef inline bint build_S_poly(self, CliffordAlgebraElement f, CliffordAlgebraElement g) @@ -26,6 +27,9 @@ cdef class GroebnerStrategy: cdef set preprocessing(self, list P, list G) cdef list reduction(self, list P, list G) + cpdef CliffordAlgebraElement reduce(self, CliffordAlgebraElement f) + cdef CliffordAlgebraElement reduce_single(self, CliffordAlgebraElement f, CliffordAlgebraElement g) + # These are the methods that determine the ordering of the monomials. # These must be implemented in subclasses. Declare them as "inline" there. cdef Integer bitset_to_int(self, FrozenBitset X) diff --git a/src/sage/algebras/exterior_algebra_groebner.pyx b/src/sage/algebras/exterior_algebra_groebner.pyx index be35add50f7..cb2dc49c8bf 100644 --- a/src/sage/algebras/exterior_algebra_groebner.pyx +++ b/src/sage/algebras/exterior_algebra_groebner.pyx @@ -51,6 +51,7 @@ cdef class GroebnerStrategy: Initialize ``self``. """ self.ideal = I + self.groebner_basis = (None,) self.E = I.ring() self.homogeneous = all(x.is_super_homogeneous() for x in I.gens()) if self.homogeneous or I.side() == "left": @@ -221,31 +222,14 @@ cdef class GroebnerStrategy: # Now that we have a Gröbner basis, we make this into a reduced Gröbner basis cdef set pairs = set((i, j) for i in range(n) for j in range(n) if i != j) - cdef list supp + cdef tuple supp cdef bint did_reduction cdef FrozenBitset lm, s while pairs: i,j = pairs.pop() # We perform the classical reduction algorithm here on each pair - # TODO: Make this faster by using the previous technique - f = G[i] - g = G[j] - lm = self.leading_supp(g) - did_reduction = True - while did_reduction: - supp = f.support() - did_reduction = False - for s in supp: - if lm <= s: - did_reduction = True - mon = self.E.monomial(s - lm) - if self.side == 0: - gp = mon * g - f = f - f[s] / gp[s] * gp - else: - gp = g * mon - f = f - f[s] / gp[s] * gp - break + # TODO: Make this faster by using the previous technique? + f = self.reduce_single(G[i], G[j]) if G[i] != f: G[i] = f if not f: @@ -253,7 +237,45 @@ cdef class GroebnerStrategy: else: pairs.update((k, i) for k in range(n) if k != i) - return tuple([~f[self.leading_supp(f)] * f for f in G if f]) + self.groebner_basis = tuple([~f[self.leading_supp(f)] * f for f in G if f]) + + cpdef CliffordAlgebraElement reduce(self, CliffordAlgebraElement f): + """ + Reduce ``f`` modulo the ideal with Gröbner basis ``G``. + """ + for g in self.groebner_basis: + f = self.reduce_single(f, g) + return f + + cdef CliffordAlgebraElement reduce_single(self, CliffordAlgebraElement f, CliffordAlgebraElement g): + """ + Reduce ``f`` by ``g``. + + .. TODO:: + + Optimize this by doing it in-place and changing the underlying dict of ``f``. + """ + cdef FrozenBitset lm, s + cdef tuple supp + + lm = self.leading_supp(g) + did_reduction = True + while did_reduction: + supp = tuple(f._monomial_coefficients) + did_reduction = False + for s in supp: + if lm <= s: + did_reduction = True + mon = self.E.monomial(s - lm) + if self.side == 0: + gp = mon * g + f -= f[s] / gp[s] * gp + else: + gp = g * mon + f -= f[s] / gp[s] * gp + break + return f + cdef Integer bitset_to_int(self, FrozenBitset X): raise NotImplementedError