class AlternatingSignMatrices(Parent, UniqueRepresentation): r""" Class of all `n \times n` alternating sign matrices. An alternating sign matrix of size `n` is an `n \times n` matrix of `0`s, `1`s and `-1`s such that the sum of each row and column is `1` and the non-zero entries in each row and column alternate in sign. Alternating sign matrices of size `n` are in bijection with :class:`monotone triangles <MonotoneTriangles>` with `n` rows. INPUT: - `n` -- an integer, the size of the matrices. - ``use_monotone_triangle`` -- (Default: ``True``) If ``True``, the generation of the matrices uses monotone triangles, else it will use the earlier and now obsolete contre-tableaux implementation; must be ``True`` to generate a lattice (with the ``lattice`` method) EXAMPLES: This will create an instance to manipulate the alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: A Alternating sign matrices of size 3 sage: A.cardinality() 7 Notably, this implementation allows to make a lattice of it:: sage: L = A.lattice() sage: L Finite lattice containing 7 elements sage: L.category() Category of facade finite lattice posets """ def __init__(self, n, use_monotone_triangles=True): r""" Initialize ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: TestSuite(A).run() sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) False """ self._n = n self._matrix_space = MatrixSpace(ZZ, n) self._umt = use_monotone_triangles Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): r""" Return a string representation of ``self``. TESTS:: sage: A = AlternatingSignMatrices(4); A Alternating sign matrices of size 4 """ return "Alternating sign matrices of size %s" % self._n def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. See :meth:`sage.structure.parent._repr_option` for details. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A._repr_option('element_ascii_art') True """ return self._matrix_space._repr_option(key) def __contains__(self, asm): """ Check if ``asm`` is in ``self``. TESTS:: sage: A = AlternatingSignMatrices(3) sage: [[0,1,0],[1,0,0],[0,0,1]] in A True sage: [[0,1,0],[1,-1,1],[0,1,0]] in A True sage: [[0, 1],[1,0]] in A False sage: [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]] in A False sage: [[-1, 1, 1],[1,-1,1],[1,1,-1]] in A False """ if isinstance(asm, AlternatingSignMatrix): return asm._matrix.nrows() == self._n try: asm = self._matrix_space(asm) except (TypeError, ValueError): return False for row in asm: pos = False for val in row: if val > 0: if pos: return False else: pos = True elif val < 0: if pos: pos = False else: return False if not pos: return False if any(sum(row) != 1 for row in asm.columns()): return False return True def _element_constructor_(self, asm): """ Construct an element of ``self``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]); elt [1 0 0] [0 1 0] [0 0 1] sage: elt.parent() is A True sage: A([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] """ if isinstance(asm, AlternatingSignMatrix): if asm.parent() is self: return asm raise ValueError("Cannot convert between alternating sign matrices of different sizes") if asm in MonotoneTriangles(self._n): return self.from_monotone_triangle(asm) return self.element_class(self, self._matrix_space(asm)) Element = AlternatingSignMatrix def _an_element_(self): """ Return an element of ``self``. """ return self.element_class(self, self._matrix_space.identity_matrix()) def from_monotone_triangle(self, triangle): r""" Return an alternating sign matrix from a monotone triangle. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.from_monotone_triangle([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] sage: A.from_monotone_triangle([[3, 2, 1], [3, 2], [3]]) [0 0 1] [0 1 0] [1 0 0] """ n = len(triangle) if n != self._n: raise ValueError("Incorrect size") asm = [] prev = [0]*n for line in reversed(triangle): v = [1 if j+1 in reversed(line) else 0 for j in range(n)] row = [a-b for (a, b) in zip(v, prev)] asm.append(row) prev = v return self.element_class(self, self._matrix_space(asm)) def size(self): r""" Return the size of the matrices in ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: A.size() 4 """ return self._n def cardinality(self): r""" Return the cardinality of ``self``. The number of `n \times n` alternating sign matrices is equal to .. MATH:: \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} EXAMPLES:: sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] """ return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) for k in range(self._n)] )) def matrix_space(self): """ Return the underlying matrix space. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.matrix_space() Full MatrixSpace of 3 by 3 dense matrices over Integer Ring """ return self._matrix_space def __iter__(self): r""" Iterator on the alternating sign matrices of size `n`. If defined using ``use_monotone_triangles``, this iterator will use the iteration on the monotone triangles. Else, it will use the iteration on contre-tableaux. TESTS:: sage: A = AlternatingSignMatrices(4) sage: len(list(A)) 42 """ if self._umt: for t in MonotoneTriangles(self._n): yield self.from_monotone_triangle(t) else: for c in ContreTableaux(self._n): yield from_contre_tableau(c) def _lattice_initializer(self): r""" Return a 2-tuple to use in argument of ``LatticePoset``. For more details about the cover relations, see ``MonotoneTriangles``. Notice that the returned matrices are made immutable to ensure their hashability required by ``LatticePoset``. EXAMPLES: Proof of the lattice property for alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: P = Poset(A._lattice_initializer()) sage: P.is_lattice() True """ assert(self._umt) (mts, rels) = MonotoneTriangles(self._n)._lattice_initializer() bij = dict((t, self.from_monotone_triangle(t)) for t in mts) asms, rels = bij.itervalues(), [(bij[a], bij[b]) for (a,b) in rels] return (asms, rels) def cover_relations(self): r""" Iterate on the cover relations between the alternating sign matrices. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: for (a,b) in A.cover_relations(): ... eval('a, b') ... ( [1 0 0] [0 1 0] [0 1 0] [1 0 0] [0 0 1], [0 0 1] ) ( [1 0 0] [1 0 0] [0 1 0] [0 0 1] [0 0 1], [0 1 0] ) ( [0 1 0] [ 0 1 0] [1 0 0] [ 1 -1 1] [0 0 1], [ 0 1 0] ) ( [1 0 0] [ 0 1 0] [0 0 1] [ 1 -1 1] [0 1 0], [ 0 1 0] ) ( [ 0 1 0] [0 0 1] [ 1 -1 1] [1 0 0] [ 0 1 0], [0 1 0] ) ( [ 0 1 0] [0 1 0] [ 1 -1 1] [0 0 1] [ 0 1 0], [1 0 0] ) ( [0 0 1] [0 0 1] [1 0 0] [0 1 0] [0 1 0], [1 0 0] ) ( [0 1 0] [0 0 1] [0 0 1] [0 1 0] [1 0 0], [1 0 0] ) """ (_, rels) = self._lattice_initializer() return (_ for _ in rels) def lattice(self): r""" Return the lattice of the alternating sign matrices of size `n`, created by ``LatticePoset``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: L = A.lattice() sage: L Finite lattice containing 7 elements """ return LatticePoset(self._lattice_initializer(), cover_relations=True)
class AlternatingSignMatrices(Parent, UniqueRepresentation): r""" Class of all `n \times n` alternating sign matrices. An alternating sign matrix of size `n` is an `n \times n` matrix of `0`'s, `1`'s and `-1`'s such that the sum of each row and column is `1` and the non-zero entries in each row and column alternate in sign. Alternating sign matrices of size `n` are in bijection with :class:`monotone triangles <MonotoneTriangles>` with `n` rows. INPUT: - `n` -- an integer, the size of the matrices. - ``use_monotone_triangle`` -- (Default: ``True``) If ``True``, the generation of the matrices uses monotone triangles, else it will use the earlier and now obsolete contre-tableaux implementation; must be ``True`` to generate a lattice (with the ``lattice`` method) EXAMPLES: This will create an instance to manipulate the alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: A Alternating sign matrices of size 3 sage: A.cardinality() 7 Notably, this implementation allows to make a lattice of it:: sage: L = A.lattice() sage: L Finite lattice containing 7 elements sage: L.category() Category of facade finite lattice posets """ def __init__(self, n, use_monotone_triangles=True): r""" Initialize ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: TestSuite(A).run() sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) False """ self._n = n self._matrix_space = MatrixSpace(ZZ, n) self._umt = use_monotone_triangles Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): r""" Return a string representation of ``self``. TESTS:: sage: A = AlternatingSignMatrices(4); A Alternating sign matrices of size 4 """ return "Alternating sign matrices of size %s" % self._n def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. See :meth:`sage.structure.parent._repr_option` for details. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A._repr_option('element_ascii_art') True """ return self._matrix_space._repr_option(key) def __contains__(self, asm): """ Check if ``asm`` is in ``self``. TESTS:: sage: A = AlternatingSignMatrices(3) sage: [[0,1,0],[1,0,0],[0,0,1]] in A True sage: [[0,1,0],[1,-1,1],[0,1,0]] in A True sage: [[0, 1],[1,0]] in A False sage: [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]] in A False sage: [[-1, 1, 1],[1,-1,1],[1,1,-1]] in A False """ if isinstance(asm, AlternatingSignMatrix): return asm._matrix.nrows() == self._n try: asm = self._matrix_space(asm) except (TypeError, ValueError): return False for row in asm: pos = False for val in row: if val > 0: if pos: return False else: pos = True elif val < 0: if pos: pos = False else: return False if not pos: return False if any(sum(row) != 1 for row in asm.columns()): return False return True def _element_constructor_(self, asm): """ Construct an element of ``self``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]); elt [1 0 0] [0 1 0] [0 0 1] sage: elt.parent() is A True sage: A([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] """ if isinstance(asm, AlternatingSignMatrix): if asm.parent() is self: return asm raise ValueError("Cannot convert between alternating sign matrices of different sizes") if asm in MonotoneTriangles(self._n): return self.from_monotone_triangle(asm) return self.element_class(self, self._matrix_space(asm)) Element = AlternatingSignMatrix def _an_element_(self): """ Return an element of ``self``. """ return self.element_class(self, self._matrix_space.identity_matrix()) def from_monotone_triangle(self, triangle): r""" Return an alternating sign matrix from a monotone triangle. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.from_monotone_triangle([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] sage: A.from_monotone_triangle([[3, 2, 1], [3, 2], [3]]) [0 0 1] [0 1 0] [1 0 0] """ n = len(triangle) if n != self._n: raise ValueError("Incorrect size") asm = [] prev = [0]*n for line in reversed(triangle): v = [1 if j+1 in reversed(line) else 0 for j in range(n)] row = [a-b for (a, b) in zip(v, prev)] asm.append(row) prev = v return self.element_class(self, self._matrix_space(asm)) def size(self): r""" Return the size of the matrices in ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: A.size() 4 """ return self._n def cardinality(self): r""" Return the cardinality of ``self``. The number of `n \times n` alternating sign matrices is equal to .. MATH:: \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} EXAMPLES:: sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] """ return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) for k in range(self._n)] )) def matrix_space(self): """ Return the underlying matrix space. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.matrix_space() Full MatrixSpace of 3 by 3 dense matrices over Integer Ring """ return self._matrix_space def __iter__(self): r""" Iterator on the alternating sign matrices of size `n`. If defined using ``use_monotone_triangles``, this iterator will use the iteration on the monotone triangles. Else, it will use the iteration on contre-tableaux. TESTS:: sage: A = AlternatingSignMatrices(4) sage: len(list(A)) 42 """ if self._umt: for t in MonotoneTriangles(self._n): yield self.from_monotone_triangle(t) else: for c in ContreTableaux(self._n): yield from_contre_tableau(c) def _lattice_initializer(self): r""" Return a 2-tuple to use in argument of ``LatticePoset``. For more details about the cover relations, see ``MonotoneTriangles``. Notice that the returned matrices are made immutable to ensure their hashability required by ``LatticePoset``. EXAMPLES: Proof of the lattice property for alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: P = Poset(A._lattice_initializer()) sage: P.is_lattice() True """ assert(self._umt) (mts, rels) = MonotoneTriangles(self._n)._lattice_initializer() bij = dict((t, self.from_monotone_triangle(t)) for t in mts) asms, rels = bij.itervalues(), [(bij[a], bij[b]) for (a,b) in rels] return (asms, rels) def cover_relations(self): r""" Iterate on the cover relations between the alternating sign matrices. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: for (a,b) in A.cover_relations(): ... eval('a, b') ... ( [1 0 0] [0 1 0] [0 1 0] [1 0 0] [0 0 1], [0 0 1] ) ( [1 0 0] [1 0 0] [0 1 0] [0 0 1] [0 0 1], [0 1 0] ) ( [0 1 0] [ 0 1 0] [1 0 0] [ 1 -1 1] [0 0 1], [ 0 1 0] ) ( [1 0 0] [ 0 1 0] [0 0 1] [ 1 -1 1] [0 1 0], [ 0 1 0] ) ( [ 0 1 0] [0 0 1] [ 1 -1 1] [1 0 0] [ 0 1 0], [0 1 0] ) ( [ 0 1 0] [0 1 0] [ 1 -1 1] [0 0 1] [ 0 1 0], [1 0 0] ) ( [0 0 1] [0 0 1] [1 0 0] [0 1 0] [0 1 0], [1 0 0] ) ( [0 1 0] [0 0 1] [0 0 1] [0 1 0] [1 0 0], [1 0 0] ) """ (_, rels) = self._lattice_initializer() return (_ for _ in rels) def lattice(self): r""" Return the lattice of the alternating sign matrices of size `n`, created by ``LatticePoset``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: L = A.lattice() sage: L Finite lattice containing 7 elements """ return LatticePoset(self._lattice_initializer(), cover_relations=True)