diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py index 6a8b8d90c1c..8d63153f1cd 100644 --- a/src/sage/categories/crystals.py +++ b/src/sage/categories/crystals.py @@ -248,6 +248,7 @@ def an_element(self): """ return self.first() + @cached_method def weight_lattice_realization(self): """ Return the weight lattice realization used to express weights diff --git a/src/sage/combinat/crystals/letters.pxd b/src/sage/combinat/crystals/letters.pxd new file mode 100644 index 00000000000..4b9598127cd --- /dev/null +++ b/src/sage/combinat/crystals/letters.pxd @@ -0,0 +1,78 @@ +from sage.structure.element cimport Element + +cdef class Letter(Element): + cdef readonly int value + +cdef class EmptyLetter(Element): + cdef readonly str value + cpdef e(self, int i) + cpdef f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class Crystal_of_letters_type_A_element(Letter): + cpdef Letter e(self, int i) + cpdef Letter f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class Crystal_of_letters_type_B_element(Letter): + cpdef Letter e(self, int i) + cpdef Letter f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class Crystal_of_letters_type_C_element(Letter): + cpdef Letter e(self, int i) + cpdef Letter f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class Crystal_of_letters_type_D_element(Letter): + cpdef Letter e(self, int i) + cpdef Letter f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class Crystal_of_letters_type_G_element(Letter): + cpdef Letter e(self, int i) + cpdef Letter f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class LetterTuple(Element): + cdef readonly tuple value + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class Crystal_of_letters_type_E6_element(LetterTuple): + cpdef LetterTuple e(self, int i) + cpdef LetterTuple f(self, int i) + +cdef class Crystal_of_letters_type_E6_element_dual(LetterTuple): + cpdef LetterTuple lift(self) + cpdef LetterTuple retract(self, LetterTuple p) + cpdef LetterTuple e(self, int i) + cpdef LetterTuple f(self, int i) + +cdef class Crystal_of_letters_type_E7_element(LetterTuple): + cpdef LetterTuple e(self, int i) + cpdef LetterTuple f(self, int i) + +cdef class BKKLetter(Letter): + cpdef Letter e(self, int i) + cpdef Letter f(self, int i) + +cdef class QueerLetter_element(Letter): + cpdef Letter e(self, int i) + cpdef Letter f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class LetterWrapped(Element): + cdef readonly Element value + cpdef tuple _to_tuple(self) + cpdef LetterWrapped e(self, int i) + cpdef LetterWrapped f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index b6724ef785f..69cbe8ff9bf 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -354,8 +354,6 @@ cdef class Letter(Element): sage: C(1) != C(-1) True """ - cdef readonly int value - def __init__(self, parent, int value): """ EXAMPLES:: @@ -504,11 +502,11 @@ cdef class Letter(Element): if op == Py_LT: return self._parent.lt_elements(self, x) if op == Py_GT: - return x.parent().lt_elements(x, self) + return x._parent.lt_elements(x, self) if op == Py_LE: return self.value == x.value or self._parent.lt_elements(self, x) if op == Py_GE: - return self.value == x.value or x.parent().lt_elements(x, self) + return self.value == x.value or x._parent.lt_elements(x, self) return False cdef class EmptyLetter(Element): @@ -522,8 +520,6 @@ cdef class EmptyLetter(Element): Used in the rigged configuration bijections. """ - cdef readonly str value - def __init__(self, parent): """ Initialize ``self``. @@ -623,7 +619,7 @@ cdef class EmptyLetter(Element): sage: C('E').weight() (0, 0, 0) """ - return self.parent().weight_lattice_realization().zero() + return self._parent.weight_lattice_realization().zero() cpdef e(self, int i): """ @@ -1304,8 +1300,6 @@ cdef class LetterTuple(Element): """ Abstract class for type `E` letters. """ - cdef readonly tuple value - def __init__(self, parent, tuple value): """ Initialize ``self``. @@ -2785,8 +2779,6 @@ cdef class LetterWrapped(Element): Element which uses another crystal implementation and converts those elements to a tuple with `\pm i`. """ - cdef readonly Element value - def __init__(self, parent, Element value): """ Initialize ``self``. @@ -2940,7 +2932,7 @@ cdef class LetterWrapped(Element): cdef Element ret = self.value.e(i) if ret is None: return None - return type(self)(self.parent(), ret) + return type(self)(self._parent, ret) cpdef LetterWrapped f(self, int i): r""" @@ -2956,7 +2948,7 @@ cdef class LetterWrapped(Element): cdef Element ret = self.value.f(i) if ret is None: return None - return type(self)(self.parent(), ret) + return type(self)(self._parent, ret) cpdef int epsilon(self, int i): r""" diff --git a/src/sage/combinat/crystals/spins.pxd b/src/sage/combinat/crystals/spins.pxd new file mode 100644 index 00000000000..4ca5f8a7082 --- /dev/null +++ b/src/sage/combinat/crystals/spins.pxd @@ -0,0 +1,21 @@ +from sage.structure.element cimport Element + +cdef class Spin(Element): + cdef bint* _value + cdef int _n + cdef long _hash + + cdef Spin _new_c(self, bint* value) + +cdef class Spin_crystal_type_B_element(Spin): + cpdef Spin e(self, int i) + cpdef Spin f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + +cdef class Spin_crystal_type_D_element(Spin): + cpdef Spin e(self, int i) + cpdef Spin f(self, int i) + cpdef int epsilon(self, int i) + cpdef int phi(self, int i) + diff --git a/src/sage/combinat/crystals/spins.py b/src/sage/combinat/crystals/spins.py deleted file mode 100644 index 78e87fa6ea4..00000000000 --- a/src/sage/combinat/crystals/spins.py +++ /dev/null @@ -1,527 +0,0 @@ -r""" -Spin Crystals - -These are the crystals associated with the three spin -representations: the spin representations of odd orthogonal groups -(or rather their double covers); and the `+` and `-` spin -representations of the even orthogonal groups. - -We follow Kashiwara and Nakashima (Journal of Algebra 165, 1994) in -representing the elements of the spin crystal by sequences of signs -`\pm`. -""" -#TODO: Do we want the following two representations? -# -#Two other representations are available as attributes -#:meth:`Spin.internal_repn` and :meth:`Spin.signature` of the crystal element. -# -#- A numerical internal representation, an integer `n` such that if `n-1` -# is written in binary and the `1`'s are replaced by ``-``, the `0`'s by -# ``+`` -# -#- The signature, which is a list in which ``+`` is replaced by `+1` and -# ``-`` by `-1`. - - -#***************************************************************************** -# Copyright (C) 2007 Anne Schilling -# Nicolas Thiery -# Daniel Bump -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# -# http://www.gnu.org/licenses/ -#**************************************************************************** -from __future__ import print_function - -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent -from sage.categories.classical_crystals import ClassicalCrystals -from sage.combinat.crystals.letters import LetterTuple -from sage.combinat.root_system.cartan_type import CartanType -from sage.combinat.tableau import Tableau - - -######################### -# Type B spin -######################### - -def CrystalOfSpins(ct): - r""" - Return the spin crystal of the given type `B`. - - This is a combinatorial model for the crystal with highest weight - `Lambda_n` (the `n`-th fundamental weight). It has - `2^n` elements, here called Spins. See also - :func:`~sage.combinat.crystals.letters.CrystalOfLetters`, - :func:`~sage.combinat.crystals.spins.CrystalOfSpinsPlus`, - and :func:`~sage.combinat.crystals.spins.CrystalOfSpinsMinus`. - - INPUT: - - - ``['B', n]`` - A Cartan type `B_n`. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: C.list() - [+++, ++-, +-+, -++, +--, -+-, --+, ---] - sage: C.cartan_type() - ['B', 3] - - :: - - sage: [x.signature() for x in C] - ['+++', '++-', '+-+', '-++', '+--', '-+-', '--+', '---'] - - TESTS:: - - sage: crystals.TensorProduct(C,C,generators=[[C.list()[0],C.list()[0]]]).cardinality() - 35 - """ - ct = CartanType(ct) - if ct[0] == 'B': - return GenericCrystalOfSpins(ct, Spin_crystal_type_B_element, "spins") - else: - raise NotImplementedError - -######################### -# Type D spins -######################### - -def CrystalOfSpinsPlus(ct): - r""" - Return the plus spin crystal of the given type D. - - This is the crystal with highest weight `Lambda_n` (the - `n`-th fundamental weight). - - INPUT: - - - ``['D', n]`` - A Cartan type `D_n`. - - EXAMPLES:: - - sage: D = crystals.SpinsPlus(['D',4]) - sage: D.list() - [++++, ++--, +-+-, -++-, +--+, -+-+, --++, ----] - - :: - - sage: [x.signature() for x in D] - ['++++', '++--', '+-+-', '-++-', '+--+', '-+-+', '--++', '----'] - - TESTS:: - - sage: TestSuite(D).run() - """ - ct = CartanType(ct) - if ct[0] == 'D': - return GenericCrystalOfSpins(ct, Spin_crystal_type_D_element, "plus") - else: - raise NotImplementedError - -def CrystalOfSpinsMinus(ct): - r""" - Return the minus spin crystal of the given type D. - - This is the crystal with highest weight `Lambda_{n-1}` - (the `(n-1)`-st fundamental weight). - - INPUT: - - - ``['D', n]`` - A Cartan type `D_n`. - - EXAMPLES:: - - sage: E = crystals.SpinsMinus(['D',4]) - sage: E.list() - [+++-, ++-+, +-++, -+++, +---, -+--, --+-, ---+] - sage: [x.signature() for x in E] - ['+++-', '++-+', '+-++', '-+++', '+---', '-+--', '--+-', '---+'] - - TESTS:: - - sage: len(crystals.TensorProduct(E,E,generators=[[E[0],E[0]]]).list()) - 35 - sage: D = crystals.SpinsPlus(['D',4]) - sage: len(crystals.TensorProduct(D,E,generators=[[D.list()[0],E.list()[0]]]).list()) - 56 - """ - ct = CartanType(ct) - if ct[0] == 'D': - return GenericCrystalOfSpins(ct, Spin_crystal_type_D_element, "minus") - else: - raise NotImplementedError - -class GenericCrystalOfSpins(UniqueRepresentation, Parent): - """ - A generic crystal of spins. - """ - def __init__(self, ct, element_class, case): - """ - EXAMPLES:: - - sage: E = crystals.SpinsMinus(['D',4]) - sage: TestSuite(E).run() - """ - self._cartan_type = CartanType(ct) - if case == "spins": - self.rename("The crystal of spins for type %s"%ct) - elif case == "plus": - self.rename("The plus crystal of spins for type %s"%ct) - else: - self.rename("The minus crystal of spins for type %s"%ct) - - self.Element = element_class - Parent.__init__(self, category = ClassicalCrystals()) - - if case == "minus": - generator = [1]*(ct[1]-1) - generator.append(-1) - else: - generator = [1]*ct[1] - self.module_generators = (self.element_class(self, tuple(generator)),) - - def _element_constructor_(self, value): - """ - Construct an element of ``self`` from ``value``. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: x = C((1,1,1)); x - +++ - sage: y = C([1,1,1]); y - +++ - sage: x == y - True - """ - return self.element_class(self, tuple(value)) - - def digraph(self): - """ - Return the directed graph associated to ``self``. - - EXAMPLES:: - - sage: crystals.Spins(['B',3]).digraph() - Digraph on 8 vertices - """ - try: - return self._digraph - except AttributeError: - pass - self._digraph = super(GenericCrystalOfSpins, self).digraph() - self._digraph.copy(immutable=True) - return self._digraph - - def lt_elements(self, x,y): - r""" - Return ``True`` if and only if there is a path from ``x`` to ``y`` - in the crystal graph. - - Because the crystal graph is classical, it is a directed acyclic - graph which can be interpreted as a poset. This function implements - the comparison function of this poset. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: x = C([1,1,1]) - sage: y = C([-1,-1,-1]) - sage: C.lt_elements(x,y) - True - sage: C.lt_elements(y,x) - False - sage: C.lt_elements(x,x) - False - """ - if x.parent() is not self or y.parent() is not self: - raise ValueError("both elements must be in this crystal") - try: - GC = self._digraph_closure - except AttributeError: - GC = self.digraph().transitive_closure() - self._digraph_closure = GC - if GC.has_edge(x,y): - return True - return False - -class Spin(LetterTuple): - """ - A spin letter in the crystal of spins. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: c = C([1,1,1]) - sage: TestSuite(c).run() - - sage: C([1,1,1]).parent() - The crystal of spins for type ['B', 3] - - sage: c = C([1,1,1]) - sage: c._repr_() - '+++' - - sage: D = crystals.Spins(['B',4]) - sage: a = C([1,1,1]) - sage: b = C([-1,-1,-1]) - sage: c = D([1,1,1,1]) - sage: a == a - True - sage: a == b - False - sage: b == c - False - """ - def signature(self): - """ - Return the signature of ``self``. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: C([1,1,1]).signature() - '+++' - sage: C([1,1,-1]).signature() - '++-' - """ - sword = "" - for x in range(self.parent().cartan_type().n): - sword += "+" if self.value[x] == 1 else "-" - return sword - - def _repr_(self): - """ - Represents the spin elements in terms of its signature. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: b = C([1,1,-1]) - sage: b - ++- - sage: b._repr_() - '++-' - """ - return self.signature() - - def _repr_diagram(self): - """ - Return a representation of ``self`` as a diagram. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: b = C([1,1,-1]) - sage: print(b._repr_diagram()) - + - + - - - """ - return '\n'.join(self.signature()) - - def pp(self): - """ - Pretty print ``self`` as a column. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: b = C([1,1,-1]) - sage: b.pp() - + - + - - - """ - print(self._repr_diagram()) - - def _latex_(self): - r""" - Gives the latex output of a spin column. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: b = C([1,1,-1]) - sage: print(b._latex_()) - {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} - \raisebox{-.6ex}{$\begin{array}[b]{*{1}c}\cline{1-1} - \lr{-}\\\cline{1-1} - \lr{+}\\\cline{1-1} - \lr{+}\\\cline{1-1} - \end{array}$} - } - """ - return Tableau([[i] for i in reversed(self.signature())])._latex_() - - def epsilon(self, i): - r""" - Return `\varepsilon_i` of ``self``. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: [[C[m].epsilon(i) for i in range(1,4)] for m in range(8)] - [[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0], - [0, 0, 1], [1, 0, 1], [0, 1, 0], [0, 0, 1]] - """ - if self.e(i) is None: - return 0 - return 1 - - def phi(self, i): - r""" - Return `\varphi_i` of ``self``. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: [[C[m].phi(i) for i in range(1,4)] for m in range(8)] - [[0, 0, 1], [0, 1, 0], [1, 0, 1], [0, 0, 1], - [1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]] - """ - if self.f(i) is None: - return 0 - return 1 - -class Spin_crystal_type_B_element(Spin): - r""" - Type B spin representation crystal element - """ - def e(self, i): - r""" - Returns the action of `e_i` on self. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: [[C[m].e(i) for i in range(1,4)] for m in range(8)] - [[None, None, None], [None, None, +++], [None, ++-, None], [+-+, None, None], - [None, None, +-+], [+--, None, -++], [None, -+-, None], [None, None, --+]] - """ - assert i in self.index_set() - rank = self.parent().cartan_type().n - if i < rank: - if self.value[i-1] == -1 and self.value[i] == 1: - ret = [self.value[x] for x in range(rank)] - ret[i-1] = 1 - ret[i] = -1 - return self.__class__(self.parent(), tuple(ret)) - elif i == rank: - if self.value[i-1] == -1: - ret = [self.value[x] for x in range(rank)] - ret[i-1] = 1 - return self.__class__(self.parent(), tuple(ret)) - - return None - - def f(self, i): - r""" - Returns the action of `f_i` on self. - - EXAMPLES:: - - sage: C = crystals.Spins(['B',3]) - sage: [[C[m].f(i) for i in range(1,4)] for m in range(8)] - [[None, None, ++-], [None, +-+, None], [-++, None, +--], [None, None, -+-], - [-+-, None, None], [None, --+, None], [None, None, ---], [None, None, None]] - """ - assert i in self.index_set() - rank = self.parent().cartan_type().n - if i < rank: - if self.value[i-1] == 1 and self.value[i] == -1: - ret = [self.value[x] for x in range(rank)] - ret[i-1] = -1 - ret[i] = 1 - return self.__class__(self.parent(), tuple(ret)) - elif i == rank: - if self.value[i-1] == 1: - ret = [self.value[x] for x in range(rank)] - ret[i-1] = -1 - return self.__class__(self.parent(), tuple(ret)) - - return None - -class Spin_crystal_type_D_element(Spin): - r""" - Type D spin representation crystal element - """ - def e(self, i): - r""" - Returns the action of `e_i` on self. - - EXAMPLES:: - - sage: D = crystals.SpinsPlus(['D',4]) - sage: [[D.list()[m].e(i) for i in range(1,4)] for m in range(8)] - [[None, None, None], [None, None, None], [None, ++--, None], [+-+-, None, None], - [None, None, +-+-], [+--+, None, -++-], [None, -+-+, None], [None, None, None]] - - :: - - sage: E = crystals.SpinsMinus(['D',4]) - sage: [[E[m].e(i) for i in range(1,4)] for m in range(8)] - [[None, None, None], [None, None, +++-], [None, ++-+, None], [+-++, None, None], - [None, None, None], [+---, None, None], [None, -+--, None], [None, None, --+-]] - """ - assert i in self.index_set() - rank = self.parent().cartan_type().n - if i < rank: - if self.value[i-1] == -1 and self.value[i] == 1: - ret = [self.value[x] for x in range(rank)] - ret[i-1] = 1 - ret[i] = -1 - return self.__class__(self.parent(), tuple(ret)) - elif i == rank: - if self.value[i-2] == -1 and self.value[i-1] == -1: - ret = [self.value[x] for x in range(rank)] - ret[i-2] = 1 - ret[i-1] = 1 - return self.__class__(self.parent(), tuple(ret)) - - return None - - def f(self, i): - r""" - Returns the action of `f_i` on self. - - EXAMPLES:: - - sage: D = crystals.SpinsPlus(['D',4]) - sage: [[D.list()[m].f(i) for i in range(1,4)] for m in range(8)] - [[None, None, None], [None, +-+-, None], [-++-, None, +--+], [None, None, -+-+], - [-+-+, None, None], [None, --++, None], [None, None, None], [None, None, None]] - - :: - - sage: E = crystals.SpinsMinus(['D',4]) - sage: [[E[m].f(i) for i in range(1,4)] for m in range(8)] - [[None, None, ++-+], [None, +-++, None], [-+++, None, None], [None, None, None], - [-+--, None, None], [None, --+-, None], [None, None, ---+], [None, None, None]] - """ - assert i in self.index_set() - rank = self.parent().cartan_type().n - if i < rank: - if self.value[i-1] == 1 and self.value[i] == -1: - ret = [self.value[x] for x in range(rank)] - ret[i-1] = -1 - ret[i] = 1 - return self.__class__(self.parent(), tuple(ret)) - elif i == rank: - if self.value[i-2] == 1 and self.value[i-1] == 1: - ret = [self.value[x] for x in range(rank)] - ret[i-2] = -1 - ret[i-1] = -1 - return self.__class__(self.parent(), tuple(ret)) - - return None diff --git a/src/sage/combinat/crystals/spins.pyx b/src/sage/combinat/crystals/spins.pyx new file mode 100644 index 00000000000..6c6d57aa4bf --- /dev/null +++ b/src/sage/combinat/crystals/spins.pyx @@ -0,0 +1,754 @@ +# -*- coding: utf-8 -*- +r""" +Spin Crystals + +These are the crystals associated with the three spin +representations: the spin representations of odd orthogonal groups +(or rather their double covers); and the `+` and `-` spin +representations of the even orthogonal groups. + +We follow Kashiwara and Nakashima (Journal of Algebra 165, 1994) in +representing the elements of the spin crystal by sequences of signs +`\pm`. +""" +#TODO: Do we want the following two representations? +# +#Two other representations are available as attributes +#:meth:`Spin.internal_repn` and :meth:`Spin.signature` of the crystal element. +# +#- A numerical internal representation, an integer `n` such that if `n-1` +# is written in binary and the `1`'s are replaced by ``-``, the `0`'s by +# ``+`` +# +#- The signature, which is a list in which ``+`` is replaced by `+1` and +# ``-`` by `-1`. + + +#***************************************************************************** +# Copyright (C) 2007 Anne Schilling +# Nicolas Thiery +# Daniel Bump +# 2019 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#**************************************************************************** +from __future__ import print_function + +from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT +from cysignals.memory cimport sig_malloc, sig_free +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent cimport Parent +from sage.structure.element cimport Element, parent +from sage.categories.classical_crystals import ClassicalCrystals +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.tableau import Tableau +from sage.rings.integer_ring import ZZ +from sage.typeset.ascii_art import AsciiArt +from sage.typeset.unicode_art import UnicodeArt + + +######################### +# Type B spin +######################### + +def CrystalOfSpins(ct): + r""" + Return the spin crystal of the given type `B`. + + This is a combinatorial model for the crystal with highest weight + `Lambda_n` (the `n`-th fundamental weight). It has + `2^n` elements, here called Spins. See also + :func:`~sage.combinat.crystals.letters.CrystalOfLetters`, + :func:`~sage.combinat.crystals.spins.CrystalOfSpinsPlus`, + and :func:`~sage.combinat.crystals.spins.CrystalOfSpinsMinus`. + + INPUT: + + - ``['B', n]`` - A Cartan type `B_n`. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: C.list() + [+++, ++-, +-+, -++, +--, -+-, --+, ---] + sage: C.cartan_type() + ['B', 3] + + :: + + sage: [x.signature() for x in C] + ['+++', '++-', '+-+', '-++', '+--', '-+-', '--+', '---'] + + TESTS:: + + sage: crystals.TensorProduct(C,C,generators=[[C.list()[0],C.list()[0]]]).cardinality() + 35 + """ + ct = CartanType(ct) + if ct[0] == 'B': + return GenericCrystalOfSpins(ct, Spin_crystal_type_B_element, "spins") + else: + raise NotImplementedError + +######################### +# Type D spins +######################### + +def CrystalOfSpinsPlus(ct): + r""" + Return the plus spin crystal of the given type D. + + This is the crystal with highest weight `Lambda_n` (the + `n`-th fundamental weight). + + INPUT: + + - ``['D', n]`` - A Cartan type `D_n`. + + EXAMPLES:: + + sage: D = crystals.SpinsPlus(['D',4]) + sage: D.list() + [++++, ++--, +-+-, -++-, +--+, -+-+, --++, ----] + + :: + + sage: [x.signature() for x in D] + ['++++', '++--', '+-+-', '-++-', '+--+', '-+-+', '--++', '----'] + + TESTS:: + + sage: TestSuite(D).run() + """ + ct = CartanType(ct) + if ct[0] == 'D': + return GenericCrystalOfSpins(ct, Spin_crystal_type_D_element, "plus") + else: + raise NotImplementedError + +def CrystalOfSpinsMinus(ct): + r""" + Return the minus spin crystal of the given type D. + + This is the crystal with highest weight `Lambda_{n-1}` + (the `(n-1)`-st fundamental weight). + + INPUT: + + - ``['D', n]`` - A Cartan type `D_n`. + + EXAMPLES:: + + sage: E = crystals.SpinsMinus(['D',4]) + sage: E.list() + [+++-, ++-+, +-++, -+++, +---, -+--, --+-, ---+] + sage: [x.signature() for x in E] + ['+++-', '++-+', '+-++', '-+++', '+---', '-+--', '--+-', '---+'] + + TESTS:: + + sage: len(crystals.TensorProduct(E,E,generators=[[E[0],E[0]]]).list()) + 35 + sage: D = crystals.SpinsPlus(['D',4]) + sage: len(crystals.TensorProduct(D,E,generators=[[D.list()[0],E.list()[0]]]).list()) + 56 + """ + ct = CartanType(ct) + if ct[0] == 'D': + return GenericCrystalOfSpins(ct, Spin_crystal_type_D_element, "minus") + else: + raise NotImplementedError + +class GenericCrystalOfSpins(UniqueRepresentation, Parent): + """ + A generic crystal of spins. + """ + def __init__(self, ct, element_class, case): + """ + EXAMPLES:: + + sage: E = crystals.SpinsMinus(['D',4]) + sage: TestSuite(E).run() + """ + self._cartan_type = CartanType(ct) + if case == "spins": + self.rename("The crystal of spins for type %s"%ct) + elif case == "plus": + self.rename("The plus crystal of spins for type %s"%ct) + else: + self.rename("The minus crystal of spins for type %s"%ct) + + self.Element = element_class + Parent.__init__(self, category=ClassicalCrystals()) + + if case == "minus": + generator = [1]*(ct[1]-1) + generator.append(-1) + else: + generator = [1]*ct[1] + self.module_generators = (self.element_class(self, tuple(generator)),) + + def _element_constructor_(self, value): + """ + Construct an element of ``self`` from ``value``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: x = C((1,1,1)); x + +++ + sage: y = C([1,1,1]); y + +++ + sage: x == y + True + """ + return self.element_class(self, tuple(value)) + + @lazy_attribute + def _digraph_closure(self): + """ + The transitive closure of the digraph associated to ``self``. + + EXAMPLES:: + + sage: crystals.Spins(['B',4])._digraph_closure + Transitive closure of : Digraph on 16 vertices + """ + return self.digraph().transitive_closure() + + def lt_elements(self, x, y): + r""" + Return ``True`` if and only if there is a path from ``x`` to ``y`` + in the crystal graph. + + Because the crystal graph is classical, it is a directed acyclic + graph which can be interpreted as a poset. This function implements + the comparison function of this poset. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: x = C([1,1,1]) + sage: y = C([-1,-1,-1]) + sage: C.lt_elements(x, y) + True + sage: C.lt_elements(y, x) + False + sage: C.lt_elements(x, x) + False + """ + if parent(x) is not self or parent(y) is not self: + raise ValueError("both elements must be in this crystal") + return self._digraph_closure.has_edge(x, y) + +cdef class Spin(Element): + """ + A spin letter in the crystal of spins. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: c = C([1,1,1]) + sage: c + +++ + sage: c.parent() + The crystal of spins for type ['B', 3] + + sage: D = crystals.Spins(['B',4]) + sage: a = C([1,1,1]) + sage: b = C([-1,-1,-1]) + sage: c = D([1,1,1,1]) + sage: a == a + True + sage: a == b + False + sage: b == c + False + """ + # cdef bint* self._value # A + is a 0/False and a - is a 1/True + + def __init__(self, parent, tuple val): + """ + Initialize ``self``. + + TESTS:: + + sage: C = crystals.Spins(['B',3]) + sage: c = C([1,1,1]) + sage: TestSuite(c).run() + """ + cdef int i + self._n = parent.cartan_type().rank() + self._value = sig_malloc(self._n*sizeof(bint)) + for i in range(self._n): + self._value[i] = (val[i] != 1) + Element.__init__(self, parent) + + cdef Spin _new_c(self, bint* value): + r""" + Fast creation of a spin element. + """ + cdef Spin ret = type(self).__new__(type(self)) + ret._parent = self._parent + ret._n = self._n + ret._value = value + ret._hash = 0 + return ret + + def __dealloc__(self): + """ + Deallocate ``self``. + + TESTS:: + + sage: C = crystals.Spins(['B',3]) + sage: c = C([1,1,1]) + sage: del c + """ + sig_free(self._value) + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: C = crystals.Spins(['B',3]) + sage: len(set(C)) == len(set([hash(x) for x in C])) + True + """ + cdef int i + if self._hash == 0: + self._hash = hash(tuple([-1 if self._value[i] else 1 for i in range(self._n)])) + return self._hash + + def __reduce__(self): + r""" + Used to pickle ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: a = C([1,-1,1]) + sage: a.__reduce__() + (The crystal of spins for type ['B', 3], ((1, -1, 1),)) + """ + tup = tuple([-1 if self._value[i] else 1 for i in range(self._n)]) + return (self._parent, (tup,)) + + cpdef _richcmp_(left, right, int op): + """ + Return ``True`` if ``left`` compares with ``right`` based on ``op``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: x = C([1,1,1]) + sage: y = C([-1,-1,-1]) + sage: x < y + True + sage: x >= y + False + sage: x < x + False + sage: x <= x + True + sage: x != y + True + sage: x == y + False + """ + cdef Spin self, x + cdef int i + self = left + x = right + if op == Py_EQ: + for i in range(self._n): + if self._value[i] != x._value[i]: + return False + return True + if op == Py_NE: + for i in range(self._n): + if self._value[i] != x._value[i]: + return True + return False + if op == Py_LT: + return self._parent._digraph_closure.has_edge(self, x) + if op == Py_GT: + return x._parent._digraph_closure.has_edge(x, self) + if op == Py_LE: + return self == x or self._parent._digraph_closure.has_edge(self, x) + if op == Py_GE: + return self == x or x._parent._digraph_closure.has_edge(x, self) + return False + + @property + def value(self): + r""" + Return ``self`` as a tuple with `+1` and `-1`. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: C([1,1,1]).value + (1, 1, 1) + sage: C([1,1,-1]).value + (1, 1, -1) + """ + cdef int i + one = ZZ.one() + return tuple([-one if self._value[i] else one for i in range(self._n)]) + + def signature(self): + """ + Return the signature of ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: C([1,1,1]).signature() + '+++' + sage: C([1,1,-1]).signature() + '++-' + """ + cdef int i + cdef str sword = "" + for i in range(self._n): + sword += "+" if self._value[i] != 1 else "-" + return sword + + _repr_ = signature + + def _repr_diagram(self): + """ + Return a representation of ``self`` as a diagram. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: b = C([1,1,-1]) + sage: print(b._repr_diagram()) + + + + + - + """ + return '\n'.join(self.signature()) + + def _ascii_art_(self): + """ + Return an ascii art representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: b = C([1,1,-1]) + sage: ascii_art(b) + + + + + - + """ + return AsciiArt(list(self.signature())) + + def _unicode_art_(self): + """ + Return a unicode art representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: b = C([1,1,-1]) + sage: unicode_art(b) + + + + + - + """ + return UnicodeArt(list(self.signature())) + + def pp(self): + """ + Pretty print ``self`` as a column. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: b = C([1,1,-1]) + sage: b.pp() + + + + + - + """ + print(self._repr_diagram()) + + def _latex_(self): + r""" + Gives the latex output of a spin column. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: b = C([1,1,-1]) + sage: print(b._latex_()) + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{1}c}\cline{1-1} + \lr{-}\\\cline{1-1} + \lr{+}\\\cline{1-1} + \lr{+}\\\cline{1-1} + \end{array}$} + } + """ + return Tableau([[i] for i in reversed(self.signature())])._latex_() + + def weight(self): + """ + Return the weight of ``self``. + + EXAMPLES:: + + sage: [v.weight() for v in crystals.Spins(['B',3])] + [(1/2, 1/2, 1/2), (1/2, 1/2, -1/2), + (1/2, -1/2, 1/2), (-1/2, 1/2, 1/2), + (1/2, -1/2, -1/2), (-1/2, 1/2, -1/2), + (-1/2, -1/2, 1/2), (-1/2, -1/2, -1/2)] + """ + WLR = self._parent.weight_lattice_realization() + cdef int i + mone = -WLR.base_ring().one() + # The ambient space is indexed by 0,...,n-1 + return WLR._from_dict({i: mone**int(self._value[i]) / 2 for i in range(self._n)}, + remove_zeros=False, coerce=False) + +cdef class Spin_crystal_type_B_element(Spin): + r""" + Type B spin representation crystal element + """ + cpdef Spin e(self, int i): + r""" + Return the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: [[C[m].e(i) for i in range(1,4)] for m in range(8)] + [[None, None, None], [None, None, +++], [None, ++-, None], [+-+, None, None], + [None, None, +-+], [+--, None, -++], [None, -+-, None], [None, None, --+]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + cdef int j + cdef bint* ret + if i == self._n: + if self._value[i-1]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = False + return self._new_c(ret) + return None + + if self._value[i-1] and not self._value[i]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = False + ret[i] = True + return self._new_c(ret) + return None + + cpdef Spin f(self, int i): + r""" + Return the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: [[C[m].f(i) for i in range(1,4)] for m in range(8)] + [[None, None, ++-], [None, +-+, None], [-++, None, +--], [None, None, -+-], + [-+-, None, None], [None, --+, None], [None, None, ---], [None, None, None]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + cdef int j + cdef bint* ret + if i == self._n: + if not self._value[i-1]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = True + return self._new_c(ret) + return None + + if self._value[i] and not self._value[i-1]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = True + ret[i] = False + return self._new_c(ret) + return None + + cpdef int epsilon(self, int i): + r""" + Return `\varepsilon_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: [[C[m].epsilon(i) for i in range(1,4)] for m in range(8)] + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0], + [0, 0, 1], [1, 0, 1], [0, 1, 0], [0, 0, 1]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + if i == self._n: + return self._value[i-1] + return self._value[i-1] and not self._value[i] + + cpdef int phi(self, int i): + r""" + Return `\varphi_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.Spins(['B',3]) + sage: [[C[m].phi(i) for i in range(1,4)] for m in range(8)] + [[0, 0, 1], [0, 1, 0], [1, 0, 1], [0, 0, 1], + [1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + if i == self._n: + return not self._value[i-1] + return self._value[i] and not self._value[i-1] + +cdef class Spin_crystal_type_D_element(Spin): + r""" + Type D spin representation crystal element + """ + cpdef Spin e(self, int i): + r""" + Return the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: D = crystals.SpinsPlus(['D',4]) + sage: [[D.list()[m].e(i) for i in range(1,4)] for m in range(8)] + [[None, None, None], [None, None, None], [None, ++--, None], [+-+-, None, None], + [None, None, +-+-], [+--+, None, -++-], [None, -+-+, None], [None, None, None]] + + :: + + sage: E = crystals.SpinsMinus(['D',4]) + sage: [[E[m].e(i) for i in range(1,4)] for m in range(8)] + [[None, None, None], [None, None, +++-], [None, ++-+, None], [+-++, None, None], + [None, None, None], [+---, None, None], [None, -+--, None], [None, None, --+-]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + cdef int j + cdef bint* ret + if i == self._n: + if self._value[i-1] and self._value[i-2]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = False + ret[i-2] = False + return self._new_c(ret) + return None + + if self._value[i-1] and not self._value[i]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = False + ret[i] = True + return self._new_c(ret) + return None + + cpdef Spin f(self, int i): + r""" + Return the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: D = crystals.SpinsPlus(['D',4]) + sage: [[D.list()[m].f(i) for i in range(1,4)] for m in range(8)] + [[None, None, None], [None, +-+-, None], [-++-, None, +--+], [None, None, -+-+], + [-+-+, None, None], [None, --++, None], [None, None, None], [None, None, None]] + + :: + + sage: E = crystals.SpinsMinus(['D',4]) + sage: [[E[m].f(i) for i in range(1,4)] for m in range(8)] + [[None, None, ++-+], [None, +-++, None], [-+++, None, None], [None, None, None], + [-+--, None, None], [None, --+-, None], [None, None, ---+], [None, None, None]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + cdef int j + cdef bint* ret + if i == self._n: + if not self._value[i-1] and not self._value[i-2]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = True + ret[i-2] = True + return self._new_c(ret) + return None + + if self._value[i] and not self._value[i-1]: + ret = sig_malloc(self._n*sizeof(bint)) + for j in range(self._n): + ret[j] = self._value[j] + ret[i-1] = True + ret[i] = False + return self._new_c(ret) + return None + + cpdef int epsilon(self, int i): + r""" + Return `\varepsilon_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.SpinsMinus(['D',4]) + sage: [[C[m].epsilon(i) for i in C.index_set()] for m in range(8)] + [[0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], + [0, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + if i == self._n: + return self._value[i-1] and self._value[i-2] + return self._value[i-1] and not self._value[i] + + cpdef int phi(self, int i): + r""" + Return `\varphi_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.SpinsPlus(['D',4]) + sage: [[C[m].phi(i) for i in C.index_set()] for m in range(8)] + [[0, 0, 0, 1], [0, 1, 0, 0], [1, 0, 1, 0], [0, 0, 1, 0], + [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0]] + """ + if i < 1 or i > self._n: + raise ValueError("i is not in the index set") + if i == self._n: + return not self._value[i-1] and not self._value[i-2] + return self._value[i] and not self._value[i-1] +