def BooleanLattice(n, facade=None): """ Return the Boolean lattice containing `2^n` elements. - ``n`` (an integer) -- number of elements will be `2^n` - ``facade`` (boolean) -- whether to make the returned poset a facade poset (see :mod:`sage.categories.facade_sets`); the default behaviour is the same as the default behaviour of the :func:`~sage.combinat.posets.posets.Poset` constructor EXAMPLES:: sage: Posets.BooleanLattice(5) Finite lattice containing 32 elements """ try: n = Integer(n) except TypeError: raise TypeError( "number of elements must be an integer, not {0}".format(n)) if n < 0: raise ValueError( "number of elements must be non-negative, not {0}".format(n)) if n == 0: return LatticePoset(([0], [])) if n == 1: return LatticePoset(([0, 1], [[0, 1]])) return LatticePoset( [[Integer(x | (1 << y)) for y in range(0, n) if x & (1 << y) == 0] for x in range(0, 2**n)], facade=facade)
def order_ideals_lattice(self, as_ideals=True): r""" Returns the lattice of order ideals of a poset `P`, ordered by inclusion. The usual notation is `J(P)`. The underlying set is by default the set of order ideals of `P`. It can be alternatively chosen to be the set of antichains of `P`. INPUT: - ``as_ideals`` -- Boolean, if ``True`` (default) returns a poset on the set of order ideals, otherwise on the set of antichains EXAMPLES:: sage: P = Posets.PentagonPoset(facade = True) sage: P.cover_relations() [[0, 1], [0, 2], [1, 4], [2, 3], [3, 4]] sage: J = P.order_ideals_lattice(); J Finite lattice containing 8 elements sage: list(J) [{}, {0}, {0, 2}, {0, 1}, {0, 1, 2}, {0, 2, 3}, {0, 1, 2, 3}, {0, 1, 2, 3, 4}] As a lattice on antichains:: sage: J2 = P.order_ideals_lattice(False); J2 Finite lattice containing 8 elements sage: list(J2) [(0,), (1, 2), (1, 3), (1,), (2,), (3,), (4,), ()] TESTS:: sage: J = Posets.DiamondPoset(4, facade = True).order_ideals_lattice(); J Finite lattice containing 6 elements sage: list(J) [{}, {0}, {0, 2}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3}] sage: J.cover_relations() [[{}, {0}], [{0}, {0, 2}], [{0}, {0, 1}], [{0, 2}, {0, 1, 2}], [{0, 1}, {0, 1, 2}], [{0, 1, 2}, {0, 1, 2, 3}]] .. NOTE:: we use facade posets in the examples above just to ensure a nicer ordering in the output. """ from sage.combinat.posets.lattices import LatticePoset if as_ideals: from sage.misc.misc import attrcall from sage.sets.set import Set ideals = [Set( self.order_ideal(antichain) ) for antichain in self.antichains()] return LatticePoset((ideals,attrcall("issubset"))) else: from sage.misc.cachefunc import cached_function antichains = [tuple(a) for a in self.antichains()] @cached_function def is_above(a,xb): return any(self.is_lequal(xa,xb) for xa in a) def cmp(a,b): return all(is_above(a,xb) for xb in b) return LatticePoset((antichains,cmp))
def order_ideals_lattice(self): r""" Returns the lattice of order ideals of a poset `P`, ordered by inclusion. The usual notation is `J(P)`. EXAMPLES:: sage: P = Posets.PentagonPoset(facade = True) sage: P.cover_relations() [[0, 1], [0, 2], [1, 4], [2, 3], [3, 4]] sage: J = P.order_ideals_lattice(); J Finite lattice containing 8 elements sage: list(J) [{}, {0}, {0, 2}, {0, 1}, {0, 1, 2}, {0, 2, 3}, {0, 1, 2, 3}, {0, 1, 2, 3, 4}] TESTS:: sage: J = Posets.DiamondPoset(4, facade = True).order_ideals_lattice(); J Finite lattice containing 6 elements sage: list(J) [{}, {0}, {0, 2}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3}] sage: J.cover_relations() [[{}, {0}], [{0}, {0, 2}], [{0}, {0, 1}], [{0, 2}, {0, 1, 2}], [{0, 1}, {0, 1, 2}], [{0, 1, 2}, {0, 1, 2, 3}]] .. note:: we use facade posets in the examples above just to ensure a nicer ordering in the output. """ from sage.misc.misc import attrcall from sage.sets.set import Set from sage.combinat.posets.lattices import LatticePoset ideals = [ Set(self.order_ideal(antichain)) for antichain in self.antichains() ] return LatticePoset((ideals, attrcall("issubset")))
def ChainPoset(n): """ Returns a chain (a totally ordered poset) containing ``n`` elements. EXAMPLES:: sage: C = Posets.ChainPoset(6); C Finite lattice containing 6 elements sage: C.linear_extension() [0, 1, 2, 3, 4, 5] sage: for i in range(5): ... for j in range(5): ... if C.covers(C(i),C(j)) and j != i+1: ... print "TEST FAILED" TESTS: Check that #8422 is solved:: sage: Posets.ChainPoset(0) Finite lattice containing 0 elements sage: C = Posets.ChainPoset(1); C Finite lattice containing 1 elements sage: C.cover_relations() [] sage: C = Posets.ChainPoset(2); C Finite lattice containing 2 elements sage: C.cover_relations() [[0, 1]] """ return LatticePoset((range(n), [[x,x+1] for x in range(n-1)]))
def PentagonPoset(facade=False): """ Returns the "pentagon poset". EXAMPLES:: sage: P = Posets.PentagonPoset(); P Finite lattice containing 5 elements sage: P.cover_relations() [[0, 1], [0, 2], [1, 4], [2, 3], [3, 4]] This lattice and the diamond poset on 5 elements are the two smallest lattices which are not distributive:: sage: P.is_distributive() False sage: Posets.DiamondPoset(5).is_distributive() False """ p = LatticePoset([[1, 2], [4], [3], [4], []], facade=facade) p.hasse_diagram()._pos = { 0: [2, 0], 1: [0, 2], 2: [3, 1], 3: [3, 3], 4: [2, 4] } return p
def DiamondPoset(n, facade=None): """ Return the lattice of rank two containing ``n`` elements. INPUT: - ``n`` - number of vertices, an integer at least 3. - ``facade`` (boolean) -- whether to make the returned poset a facade poset (see :mod:`sage.categories.facade_sets`). The default behaviour is the same as the default behaviour of the :func:`~sage.combinat.posets.posets.Poset` constructor). EXAMPLES:: sage: Posets.DiamondPoset(7) Finite lattice containing 7 elements """ try: n = Integer(n) except TypeError: raise TypeError( "number of elements must be an integer, not {0}".format(n)) if n <= 2: raise ValueError("n must be an integer at least 3") c = [[n - 1] for x in range(n)] c[0] = [x for x in range(1, n - 1)] c[n - 1] = [] return LatticePoset(c, facade=facade)
def face_lattice(self, facade=False): r""" Returns the (big) face lattice. The *(big) face lattice* is the (big) face poset with a top element added. EXAMPLES:: sage: from oriented_matroids import OrientedMatroid sage: C = [ [1,1,1], [1,1,0],[1,1,-1],[1,0,-1],[1,-1,-1],[0,-1,-1], ....: [-1,-1,-1],[0,1,1],[-1,1,1],[-1,0,1],[-1,-1,1],[-1,-1,0], ....: [0,0,0]] sage: M = OrientedMatroid(C, key='covector') sage: M.face_lattice() Finite lattice containing 14 elements """ from sage.combinat.posets.lattices import LatticePoset els = self.covectors() rels = [ (Y, X) for X in els for Y in els if Y.is_conformal_with(X) and Y.support().issubset(X.support()) ] # Add top element for i in els: rels.append((i, 1)) els.append(1) return LatticePoset((els, rels), cover_relations=False, facade=facade)
def PentagonPoset(facade = None): """ Returns the Pentagon poset. INPUT: - ``facade`` (boolean) -- whether to make the returned poset a facade poset (see :mod:`sage.categories.facade_sets`). The default behaviour is the same as the default behaviour of the :func:`~sage.combinat.posets.posets.Poset` constructor). EXAMPLES:: sage: P = Posets.PentagonPoset(); P Finite lattice containing 5 elements sage: P.cover_relations() [[0, 1], [0, 2], [1, 4], [2, 3], [3, 4]] This is smallest lattice that is not modular:: sage: P.is_modular() False This poset and the :meth:`DiamondPoset` are the two smallest lattices which are not distributive:: sage: P.is_distributive() False sage: Posets.DiamondPoset(5).is_distributive() False """ p = LatticePoset([[1,2],[4],[3],[4],[]], facade = facade) p.hasse_diagram()._pos = {0:[2,0],1:[0,2],2:[3,1],3:[3,3],4:[2,4]} return p
def shard_poset(self, side='right'): """ Return the shard intersection order attached to `W`. This is a lattice structure on `W`, introduced in [Reading]_. It contains the noncrossing partition lattice, as the induced lattice on the subset of `c`-sortable elements. The partial order is given by simultaneous inclusion of inversion sets and subgroups attached to every element. The precise description used here can be found in [StThWi]_. Another implementation for the symmetric groups is available as :func:`~sage.combinat.shard_order.shard_poset`. .. SEEALSO:: :meth:`bhz_poset`, :meth:`bruhat_poset`, :meth:`weak_poset` EXAMPLES:: sage: W = CoxeterGroup(['A',3], base_ring=ZZ) sage: SH = W.shard_poset(); SH Finite lattice containing 24 elements sage: SH.is_graded() True sage: SH.characteristic_polynomial() q^3 - 11*q^2 + 23*q - 13 sage: SH.f_polynomial() 34*q^3 + 22*q^2 + q REFERENCES: .. [Reading] Nathan Reading, *Noncrossing partitions and the shard intersection order*, DMTCS Proceedings of FPSAC 2009, 745--756 .. [StThWi] Christian Stump, Hugh Thomas and Nathan Williams, *Cataland: why the fuss?*, :arxiv:`1503.00710` """ from sage.combinat.posets.lattices import LatticePoset data = { w: (frozenset(u.lift() for u in w.covered_reflections_subgroup()), frozenset((~w).inversions_as_reflections())) for w in self } def shard_comparison(u, v): Gu, Nu = data[u] Gv, Nv = data[v] return Gu.issubset(Gv) and Nu.issubset(Nv) return LatticePoset([self, shard_comparison])
def BooleanLattice(n): """ Returns the Boolean lattice containing `2^n` elements. EXAMPLES:: sage: Posets.BooleanLattice(5) Finite lattice containing 32 elements """ return LatticePoset([[Integer(x|(1<<y)) for y in range(0,n) if x&(1<<y)==0] for x in range(0,2**n)])
def lattice(self): r""" Return the lattice of the monotone triangles with `n` rows. EXAMPLES:: sage: M = MonotoneTriangles(3) sage: P = M.lattice() sage: P Finite lattice containing 7 elements """ return LatticePoset(self._lattice_initializer(), cover_relations=True)
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)
def test_composition_series_poset(self): """ Sanity checks for a for regular left/right class EXAMPLES:: sage: M = Semigroups().SetsWithAction().example().algebra(QQ); M Free module generated by Representation of the monoid generated by <2,3> acting on Z/10 Z by multiplication over Rational Field sage: M.test_composition_series_poset() """ S = self.semigroup() R = self.basis().keys() composition_series_poset = self.composition_series_poset() assert composition_series_poset.is_meet_semilattice() #annihilator_poset = self.annihilator_poset() #annihilator_poset_dual = annihilator_poset.dual() #f = lambda J: annihilator_poset_dual(self.annihilator_with_apex(J)) #assert composition_series_poset.is_poset_isomorphism(f, annihilator_poset_dual) # Dimension checks for J in self.composition_series(): assert self.annihilator_with_apex(J).dimension() == S.lr_regular_class(J, side=R.side()).cardinality() assert self.dimension() == sum(S.simple_module_dimension(J) for J in self.composition_series()) # That can't work with the current definition of annihilator_module_with_apex. # j_poset = S.j_poset_on_regular_classes() # f = lambda J: composition_series_poset(self.annihilator_module_with_apex(J).apex()) # assert j_poset.is_poset_morphism(f, composition_series_poset) return assert composition_series_poset.is_lattice() from sage.combinat.posets.lattices import LatticePoset composition_lattice = LatticePoset(composition_series_poset) # annihilator_poset = LatticePoset(annihilator_poset) # Check that self.annihilator_module_with_apex is a # lattice morphism from the composition poset to the # lattice of submodules of self. This is similar to # the above isomorphism test with the # annihilator_poset, except that we enforce that the # meet on submodules is given by intersection. f = lambda J: self.annihilator_module_with_apex(J) L = composition_lattice for J1 in self.composition_series(): for J2 in self.composition_series(): assert f(J1).intersection(f(J2)) == f( L.join(L(J1), L(J2)) )
def BooleanLattice(n): """ Returns the Boolean lattice containing `2^n` elements. EXAMPLES:: sage: Posets.BooleanLattice(5) Finite lattice containing 32 elements """ try: n = Integer(n) except TypeError: raise TypeError( "number of elements must be an integer, not {0}".format(n)) if n < 0: raise ValueError( "number of elements must be non-negative, not {0}".format(n)) if n == 0: return LatticePoset(([0], [])) if n == 1: return LatticePoset(([0, 1], [[0, 1]])) return LatticePoset( [[Integer(x | (1 << y)) for y in range(0, n) if x & (1 << y) == 0] for x in range(0, 2**n)])
def DiamondPoset(n, facade=False): """ Returns the lattice of rank two containing ``n`` elements. EXAMPLES:: sage: Posets.DiamondPoset(7) Finite lattice containing 7 elements """ c = [[n - 1] for x in range(n)] c[0] = [x for x in range(1, n - 1)] c[n - 1] = [] if n > 2: return LatticePoset(c, facade=facade) else: return Poset(c, facade=facade)
def ChainPoset(n, facade=None): """ Return a chain (a totally ordered poset) containing ``n`` elements. - ``n`` (an integer) -- number of elements. - ``facade`` (boolean) -- whether to make the returned poset a facade poset (see :mod:`sage.categories.facade_sets`); the default behaviour is the same as the default behaviour of the :func:`~sage.combinat.posets.posets.Poset` constructor EXAMPLES:: sage: C = Posets.ChainPoset(6); C Finite lattice containing 6 elements sage: C.linear_extension() [0, 1, 2, 3, 4, 5] TESTS:: sage: for i in range(5): ....: for j in range(5): ....: if C.covers(C(i),C(j)) and j != i+1: ....: print("TEST FAILED") Check that :trac:`8422` is solved:: sage: Posets.ChainPoset(0) Finite lattice containing 0 elements sage: C = Posets.ChainPoset(1); C Finite lattice containing 1 elements sage: C.cover_relations() [] sage: C = Posets.ChainPoset(2); C Finite lattice containing 2 elements sage: C.cover_relations() [[0, 1]] """ try: n = Integer(n) except TypeError: raise TypeError( "number of elements must be an integer, not {0}".format(n)) if n < 0: raise ValueError( "number of elements must be non-negative, not {0}".format(n)) return LatticePoset((range(n), [[x, x + 1] for x in range(n - 1)]), facade=facade)
def YoungsLatticePrincipalOrderIdeal(lam): """ Return the principal order ideal of the partition `lam` in Young's Lattice. INPUT: - ``lam`` -- a partition EXAMPLES:: sage: P = Posets.YoungsLatticePrincipalOrderIdeal(Partition([2,2])) sage: P Finite lattice containing 6 elements sage: P.cover_relations() [[[], [1]], [[1], [1, 1]], [[1], [2]], [[1, 1], [2, 1]], [[2], [2, 1]], [[2, 1], [2, 2]]] """ from sage.misc.flatten import flatten from sage.combinat.partition import Partition def lower_covers(l): """ Nested function returning those partitions obtained from the partition `l` by removing a single cell. """ return [l.remove_cell(c[0], c[1]) for c in l.removable_cells()] def contained_partitions(l): """ Nested function returning those partitions contained in the partition `l` """ if l == Partition([]): return l return flatten( [l, [contained_partitions(m) for m in lower_covers(l)]]) ideal = list(set(contained_partitions(lam))) H = DiGraph(dict([[p, lower_covers(p)] for p in ideal])) return LatticePoset(H.reverse())
def __classcall_private__(cls, R, L): """ Normalize input to ensure a unique representation. TESTS:: sage: L1 = posets.BooleanLattice(4) sage: L2 = posets.BooleanLattice(4, facade=False) sage: L1 is L2 False sage: M1 = L1.moebius_algebra(QQ) sage: M2 = L2.moebius_algebra(QQ) sage: M1 is M2 True """ # We force the lattice to not be a facade in order to guarantee # that the ordering of the poset is used (see #21054). L = LatticePoset(L, facade=False) return super(MoebiusAlgebra, cls).__classcall__(cls, R, L)
def SetPartitions(n): r""" Return the lattice of set partitions of the set `\{1,\ldots,n\}` ordered by refinement. INPUT: - ``n`` -- a positive integer EXAMPLES:: sage: Posets.SetPartitions(4) Finite lattice containing 15 elements """ from sage.rings.semirings.non_negative_integer_semiring import NN if n not in NN: raise ValueError('n must be an integer') from sage.combinat.set_partition import SetPartitions S = SetPartitions(n) return LatticePoset((S, S.is_less_than))
def DivisorLattice(n, facade=None): """ Return the divisor lattice of an integer. Elements of the lattice are divisors of `n` and `x < y` in the lattice if `x` divides `y`. INPUT: - ``n`` -- an integer - ``facade`` (boolean) -- whether to make the returned poset a facade poset (see :mod:`sage.categories.facade_sets`); the default behaviour is the same as the default behaviour of the :func:`~sage.combinat.posets.posets.Poset` constructor EXAMPLES:: sage: P = Posets.DivisorLattice(12) sage: sorted(P.cover_relations()) [[1, 2], [1, 3], [2, 4], [2, 6], [3, 6], [4, 12], [6, 12]] sage: P = Posets.DivisorLattice(10, facade=False) sage: P(2) < P(5) False TESTS:: sage: Posets.DivisorLattice(1) Finite lattice containing 1 elements """ from sage.arith.misc import divisors try: n = Integer(n) except TypeError: raise TypeError( "number of elements must be an integer, not {0}".format(n)) if n <= 0: raise ValueError("n must be a positive integer") return LatticePoset((divisors(n), lambda x, y: y % x == 0), facade=facade, linear_extension=True)
def ChainPoset(n): """ Returns a chain (a totally ordered poset) containing ``n`` elements. EXAMPLES:: sage: C = Posets.ChainPoset(6); C Finite lattice containing 6 elements sage: C.linear_extension() [0, 1, 2, 3, 4, 5] TESTS:: sage: for i in range(5): ....: for j in range(5): ....: if C.covers(C(i),C(j)) and j != i+1: ....: print "TEST FAILED" Check that :trac:`8422` is solved:: sage: Posets.ChainPoset(0) Finite lattice containing 0 elements sage: C = Posets.ChainPoset(1); C Finite lattice containing 1 elements sage: C.cover_relations() [] sage: C = Posets.ChainPoset(2); C Finite lattice containing 2 elements sage: C.cover_relations() [[0, 1]] """ try: n = Integer(n) except TypeError: raise TypeError( "number of elements must be an integer, not {0}".format(n)) if n < 0: raise ValueError( "number of elements must be non-negative, not {0}".format(n)) return LatticePoset((range(n), [[x, x + 1] for x in range(n - 1)]))
def IntegerPartitionsDominanceOrder(n): r""" Return the poset of integer partitions on the integer `n` ordered by dominance. That is, if `p=(p_1,\ldots,p_i)` and `q=(q_1,\ldots,q_j)` are integer partitions of `n`, then `p` is greater than `q` if and only if `p_1+\cdots+p_k > q_1+\cdots+q_k` for all `k`. INPUT: - ``n`` -- a positive integer EXAMPLES:: sage: P = Posets.IntegerPartitionsDominanceOrder(6); P Finite lattice containing 11 elements sage: P.cover_relations() [[[1, 1, 1, 1, 1, 1], [2, 1, 1, 1, 1]], [[2, 1, 1, 1, 1], [2, 2, 1, 1]], [[2, 2, 1, 1], [2, 2, 2]], [[2, 2, 1, 1], [3, 1, 1, 1]], [[2, 2, 2], [3, 2, 1]], [[3, 1, 1, 1], [3, 2, 1]], [[3, 2, 1], [3, 3]], [[3, 2, 1], [4, 1, 1]], [[3, 3], [4, 2]], [[4, 1, 1], [4, 2]], [[4, 2], [5, 1]], [[5, 1], [6]]] """ from sage.rings.semirings.non_negative_integer_semiring import NN if n not in NN: raise ValueError('n must be an integer') from sage.combinat.partition import Partitions, Partition return LatticePoset((Partitions(n), Partition.dominates)).dual()
def support_lattice(self): from sage.combinat.posets.lattices import LatticePoset return LatticePoset(self.j_poset())
def weak_poset(self, side = "right", facade = False): """ INPUT: - ``side`` -- "left", "right", or "twosided" (default: "right") - ``facade`` -- a boolean (default: ``False``) Returns the left (resp. right) poset for weak order. In this poset, `u` is smaller than `v` if some reduced word of `u` is a right (resp. left) factor of some reduced word of `v`. EXAMPLES:: sage: W = WeylGroup(["A", 2]) sage: P = W.weak_poset() sage: P Finite lattice containing 6 elements sage: P.show() This poset is in fact a lattice:: sage: W = WeylGroup(["B", 3]) sage: P = W.weak_poset(side = "left") sage: P.is_lattice() True so this method has an alias :meth:`weak_lattice`:: sage: W.weak_lattice(side = "left") is W.weak_poset(side = "left") True As a bonus feature, one can create the left-right weak poset:: sage: W = WeylGroup(["A",2]) sage: P = W.weak_poset(side = "twosided") sage: P.show() sage: len(P.hasse_diagram().edges()) 8 This is the transitive closure of the union of left and right order. In this poset, `u` is smaller than `v` if some reduced word of `u` is a factor of some reduced word of `v`. Note that this is not a lattice:: sage: P.is_lattice() False By default, the elements of `P` are aware of that they belong to `P`:: sage: P.an_element().parent() Finite poset containing 6 elements If instead one wants the elements to be plain elements of the Coxeter group, one can use the ``facade`` option:: sage: P = W.weak_poset(facade = True) sage: P.an_element().parent() Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space) .. see also:: :func:`Poset` for more on posets and facade posets. TESTS:: sage: [len(WeylGroup(["A", n]).weak_poset(side = "right").cover_relations()) for n in [1,2,3]] [1, 6, 36] sage: [len(WeylGroup(["A", n]).weak_poset(side = "left" ).cover_relations()) for n in [1,2,3]] [1, 6, 36] .. todo:: - Use the symmetric group in the examples (for nicer output), and print the edges for a stronger test. - The constructed poset should be lazy, in order to handle large / infinite Coxeter groups. """ from sage.combinat.posets.posets import Poset from sage.combinat.posets.lattices import LatticePoset if side == "twosided": covers = tuple([u, v] for u in self for v in u.upper_covers(side="left")+u.upper_covers(side="right") ) return Poset((self, covers), cover_relations = True, facade = facade) else: covers = tuple([u, v] for u in self for v in u.upper_covers(side=side) ) return LatticePoset((self, covers), cover_relations = True, facade = facade)
def test_finite_lattice(L): """ Test several functions on a given finite lattice. The function contains tests of different kinds: - Implications of Boolean properties. Examples: a distributive lattice is modular, a dismantlable and distributive lattice is planar, a simple lattice can not be constructible by Day's doublings. - Dual and self-dual properties. Examples: Dual of a modular lattice is modular, dual of an atomic lattice is co-atomic. - Certificate tests. Example: certificate for a non-complemented lattice must be an element without a complement. - Verification of some property by known property or by a random test. Examples: A lattice is distributive iff join-primes are exactly join-irreducibles and an interval of a relatively complemented lattice is complemented. - Set inclusions. Example: Every co-atom must be meet-irreducible. - And several other tests. Example: The skeleton of a pseudocomplemented lattice must be Boolean. EXAMPLES:: sage: from sage.tests.finite_poset import test_finite_lattice sage: L = posets.RandomLattice(10, 0.98) sage: test_finite_lattice(L) is None # Long time True """ from sage.combinat.posets.lattices import LatticePoset from sage.sets.set import Set from sage.combinat.subset import Subsets from sage.misc.prandom import randint from sage.misc.flatten import flatten from sage.misc.misc import attrcall from sage.misc.sageinspect import sage_getargspec if L.cardinality() < 4: # Special cases should be tested in specific TESTS-sections. return None all_props = set(list(implications) + flatten(implications.values())) P = {x: test_attrcall('is_' + x, L) for x in all_props} ### Relations between boolean-valued properties ### # Direct one-property implications for prop1 in implications: if P[prop1]: for prop2 in implications[prop1]: if not P[prop2]: raise ValueError("error: %s should implicate %s" % (prop1, prop2)) # Impossible combinations for p1, p2 in mutually_exclusive: if P[p1] and P[p2]: raise ValueError( "error: %s and %s should be impossible combination" % (p1, p2)) # Two-property implications for p1, p2, p3 in two_to_one: if P[p1] and P[p2] and not P[p3]: raise ValueError("error: %s and %s, so should be %s" % (p1, p2, p3)) Ldual = L.dual() # Selfdual properties for p in selfdual_properties: if P[p] != test_attrcall('is_' + p, Ldual): raise ValueError("selfdual property %s error" % p) # Dual properties and elements for p1, p2 in dual_properties: if P[p1] != test_attrcall('is_' + p2, Ldual): raise ValueError("dual properties error %s" % p1) for e1, e2 in dual_elements: if set(attrcall(e1)(L)) != set(attrcall(e2)(Ldual)): raise ValueError("dual elements error %s" % e1) ### Certificates ### # Return value must be a pair with correct result as first element. for p_ in all_props: # Dirty fix first if p_[:9] == 'doubling_' or p_[:5] == 'uniq_': continue p = "is_" + p_ if 'certificate' in sage_getargspec(getattr(L, p)).args: res = attrcall(p, certificate=True)(L) if type(res) != type((1, 2)) or len(res) != 2: raise ValueError( "certificate-option does not return a pair in %s" % p) if P[p_] != res[0]: raise ValueError("certificate-option changes result in %s" % p) # Test for "yes"-certificates if P['supersolvable']: a = L.is_supersolvable(certificate=True)[1] S = Subsets(L).random_element() if L.is_chain_of_poset(S): if not L.sublattice(a + list(S)).is_distributive(): raise ValueError("certificate error in is_supersolvable") if P['dismantlable']: elms = L.is_dismantlable(certificate=True)[1] if len(elms) != L.cardinality(): raise ValueError("certificate error 1 in is_dismantlable") elms = elms[:randint(0, len(elms) - 1)] L_ = L.sublattice([x for x in L if x not in elms]) if L_.cardinality() != L.cardinality() - len(elms): raise ValueError("certificate error 2 in is_dismantlable") if P['vertically_decomposable']: c = L.is_vertically_decomposable(certificate=True)[1] if c == L.bottom() or c == L.top(): raise ValueError( "certificate error 1 in is_vertically_decomposable") e = L.random_element() if L.compare_elements(c, e) is None: raise ValueError( "certificate error 2 in is_vertically_decomposable") # Test for "no"-certificates if not P['atomic']: a = L.is_atomic(certificate=True)[1] if a in L.atoms() or a not in L.join_irreducibles(): raise ValueError("certificate error in is_atomic") if not P['coatomic']: a = L.is_coatomic(certificate=True)[1] if a in L.coatoms() or a not in L.meet_irreducibles(): raise ValueError("certificate error in is_coatomic") if not P['complemented']: a = L.is_complemented(certificate=True)[1] if L.complements(a) != []: raise ValueError("compl. error 1") if not P['sectionally_complemented']: a, b = L.is_sectionally_complemented(certificate=True)[1] L_ = L.sublattice(L.interval(L.bottom(), a)) if L_.is_complemented(): raise ValueError("sec. compl. error 1") if len(L_.complements(b)) > 0: raise ValueError("sec. compl. error 2") if not P['cosectionally_complemented']: a, b = L.is_cosectionally_complemented(certificate=True)[1] L_ = L.sublattice(L.interval(a, L.top())) if L_.is_complemented(): raise ValueError("cosec. compl. error 1") if len(L_.complements(b)) > 0: raise ValueError("cosec. compl. error 2") if not P['relatively_complemented']: a, b, c = L.is_relatively_complemented(certificate=True)[1] I = L.interval(a, c) if len(I) != 3 or b not in I: raise ValueError("rel. compl. error 1") if not P['upper_semimodular']: a, b = L.is_upper_semimodular(certificate=True)[1] if not set(L.lower_covers(a)).intersection(set( L.lower_covers(b))) or set(L.upper_covers(a)).intersection( set(L.upper_covers(b))): raise ValueError("certificate error in is_upper_semimodular") if not P['lower_semimodular']: a, b = L.is_lower_semimodular(certificate=True)[1] if set(L.lower_covers(a)).intersection(set( L.lower_covers(b))) or not set(L.upper_covers(a)).intersection( set(L.upper_covers(b))): raise ValueError("certificate error in is_lower_semimodular") if not P['distributive']: x, y, z = L.is_distributive(certificate=True)[1] if L.meet(x, L.join(y, z)) == L.join(L.meet(x, y), L.meet(x, z)): raise ValueError("certificate error in is_distributive") if not P['modular']: x, a, b = L.is_modular(certificate=True)[1] if not L.is_less_than(x, b) or L.join(x, L.meet(a, b)) == L.meet( L.join(x, a), b): raise ValueError("certificate error in is_modular") if not P['pseudocomplemented']: a = L.is_pseudocomplemented(certificate=True)[1] L_ = L.subposet([e for e in L if L.meet(e, a) == L.bottom()]) if L_.has_top(): raise ValueError("certificate error in is_pseudocomplemented") if not P['join_pseudocomplemented']: a = L.is_join_pseudocomplemented(certificate=True)[1] L_ = L.subposet([e for e in L if L.join(e, a) == L.top()]) if L_.has_bottom(): raise ValueError("certificate error in is_join_pseudocomplemented") if not P['join_semidistributive']: e, x, y = L.is_join_semidistributive(certificate=True)[1] if L.join(e, x) != L.join(e, y) or L.join(e, x) == L.join( e, L.meet(x, y)): raise ValueError("certificate error in is_join_semidistributive") if not P['meet_semidistributive']: e, x, y = L.is_meet_semidistributive(certificate=True)[1] if L.meet(e, x) != L.meet(e, y) or L.meet(e, x) == L.meet( e, L.join(x, y)): raise ValueError("certificate error in is_meet_semidistributive") if not P['simple']: c = L.is_simple(certificate=True)[1] if len(L.congruence([c[randint(0, len(c) - 1)]])) == 1: raise ValueError("certificate error in is_simple") if not P['isoform']: c = L.is_isoform(certificate=True)[1] if len(c) == 1: raise ValueError("certificate error in is_isoform") if all( L.subposet(c[i]).is_isomorphic(L.subposet(c[i + 1])) for i in range(len(c) - 1)): raise ValueError("certificate error in is_isoform") if not P['uniform']: c = L.is_uniform(certificate=True)[1] if len(c) == 1: raise ValueError("certificate error in is_uniform") if all(len(c[i]) == len(c[i + 1]) for i in range(len(c) - 1)): raise ValueError("certificate error in is_uniform") if not P['regular']: c = L.is_regular(certificate=True)[1] if len(c[0]) == 1: raise ValueError("certificate error 1 in is_regular") if Set(c[1]) not in c[0]: raise ValueError("certificate error 2 in is_regular") if L.congruence([c[1]]) == c[0]: raise ValueError("certificate error 3 in is_regular") if not P['subdirectly_reducible']: x, y = L.is_subdirectly_reducible(certificate=True)[1] a = L.random_element() b = L.random_element() c = L.congruence([[a, b]]) if len(c) != L.cardinality(): for c_ in c: if x in c_: if y not in c_: raise ValueError( "certificate error 1 in is_subdirectly_reducible") break else: raise ValueError( "certificate error 2 in is_subdirectly_reducible") if not P['join_distributive']: a = L.is_join_distributive(certificate=True)[1] L_ = L.sublattice(L.interval(a, L.join(L.upper_covers(a)))) if L_.is_distributive(): raise ValueError("certificate error in is_join_distributive") if not P['meet_distributive']: a = L.is_meet_distributive(certificate=True)[1] L_ = L.sublattice(L.interval(L.meet(L.lower_covers(a)), a)) if L_.is_distributive(): raise ValueError("certificate error in is_meet_distributive") ### Other ### # Other ways to recognize some boolean property if P['distributive'] != (set(L.join_primes()) == set( L.join_irreducibles())): raise ValueError( "every join-irreducible of a distributive lattice should be join-prime" ) if P['distributive'] != (set(L.meet_primes()) == set( L.meet_irreducibles())): raise ValueError( "every meet-irreducible of a distributive lattice should be meet-prime" ) if P['join_semidistributive'] != all( L.canonical_joinands(e) is not None for e in L): raise ValueError( "every element of join-semidistributive lattice should have canonical joinands" ) if P['meet_semidistributive'] != all( L.canonical_meetands(e) is not None for e in L): raise ValueError( "every element of meet-semidistributive lattice should have canonical meetands" ) # Random verification of a Boolean property if P['relatively_complemented']: a = L.random_element() b = L.random_element() if not L.sublattice(L.interval(a, b)).is_complemented(): raise ValueError("rel. compl. error 3") if P['sectionally_complemented']: a = L.random_element() if not L.sublattice(L.interval(L.bottom(), a)).is_complemented(): raise ValueError("sec. compl. error 3") if P['cosectionally_complemented']: a = L.random_element() if not L.sublattice(L.interval(a, L.top())).is_complemented(): raise ValueError("cosec. compl. error 2") # Element set inclusions for s1, s2 in set_inclusions: if not set(attrcall(s1)(L)).issubset(set(attrcall(s2)(L))): raise ValueError("%s should be a subset of %s" % (s1, s2)) # Sublattice-closed properties L_ = L.sublattice(Subsets(L).random_element()) for p in sublattice_closed: if P[p] and not test_attrcall('is_' + p, L_): raise ValueError("property %s should apply to sublattices" % p) # Some sublattices L_ = L.center() # Center is a Boolean lattice if not L_.is_atomic() or not L_.is_distributive(): raise ValueError("error in center") if P['pseudocomplemented']: L_ = L.skeleton() # Skeleton is a Boolean lattice if not L_.is_atomic() or not L_.is_distributive(): raise ValueError("error in skeleton") L_ = L.frattini_sublattice() S = Subsets(L).random_element() if L.sublattice(S) == L and L.sublattice([e for e in S if e not in L_]) != L: raise ValueError("error in Frattini sublattice") L_ = L.maximal_sublattices() L_ = L_[randint(0, len(L_) - 1)] e = L.random_element() if e not in L_ and L.sublattice(list(L_) + [e]) != L: raise ValueError("error in maximal_sublattices") # Reverse functions: vertical composition and decomposition L_ = reduce(lambda a, b: a.vertical_composition(b), L.vertical_decomposition(), LatticePoset()) if not L.is_isomorphic(L_): raise ValueError("error in vertical [de]composition") # Meet and join a = L.random_element() b = L.random_element() m = L.meet(a, b) j = L.join(a, b) m_ = L.subposet([ e for e in L.principal_lower_set(a) if e in L.principal_lower_set(b) ]).top() j_ = L.subposet([ e for e in L.principal_upper_set(a) if e in L.principal_upper_set(b) ]).bottom() if m != m_ or m != Ldual.join(a, b): raise ValueError("error in meet") if j != j_ or j != Ldual.meet(a, b): raise ValueError("error in join") # Misc misc e = L.neutral_elements() e = e[randint(0, len(e) - 1)] a = L.random_element() b = L.random_element() if not L.sublattice([e, a, b]).is_distributive(): raise ValueError("error in neutral_elements")
def m_cambrian_lattice(self, c, m=1, on_roots=False): """ Return the `m`-Cambrian lattice on `m`-delta sequences. See :arxiv:`1503.00710` and :arxiv:`math/0611106`. The `m`-delta sequences are certain `m`-colored minimal factorizations of `c` into reflections. INPUT: - `c` -- a Coxeter element of ``self`` (as a tuple, or as an element of ``self``) - `m` -- a positive integer (optional, default 1) - ``on_roots`` (optional, default ``False``) -- if ``on_roots`` is ``True``, the lattice is realized on roots rather than on reflections. In order for this to work, the ElementMethod ``reflection_to_root`` must be available. EXAMPLES:: sage: CoxeterGroup(["A",2]).m_cambrian_lattice((1,2)) Finite lattice containing 5 elements sage: CoxeterGroup(["A",2]).m_cambrian_lattice((1,2),2) Finite lattice containing 12 elements """ from sage.combinat.posets.lattices import LatticePoset if hasattr(c, "reduced_word"): c = c.reduced_word() c = list(c) sorting_word = self.long_element().coxeter_sorting_word(c) if on_roots: if not hasattr(self.long_element(), "reflection_to_root"): raise ValueError("The parameter 'on_root=True' needs " "the ElementMethod 'reflection_to_root'") inv_woc = [ t.reflection_to_root() for t in self.inversion_sequence(sorting_word) ] S = [s.reflection_to_root() for s in self.simple_reflections()] PhiP = [t.reflection_to_root() for t in self.reflections()] else: inv_woc = self.inversion_sequence(sorting_word) S = self.simple_reflections() T = self.reflections_from_w0() Twords = {t: t.reduced_word() for t in T} elements = set() covers = [] bottom_elt = frozenset((s, 0) for s in S) new = set([bottom_elt]) while new: new_element = new.pop() elements.add(new_element) for t in new_element: if t[1] < m: cov_element = [s for s in new_element if s != t] cov_element.append((t[0], t[1] + 1)) idx_t0 = inv_woc.index(t[0]) for t_conj in [(i, t[1]) for i in inv_woc[idx_t0:]] + [ (i, t[1] + 1) for i in inv_woc[:idx_t0] ]: if t_conj in cov_element: cov_element.remove(t_conj) if on_roots: tmp = t_conj[0].weyl_action( t[0].associated_reflection()) if tmp in PhiP: cov_element.append((tmp, t_conj[1])) else: cov_element.append( (-tmp, t_conj[1] - 1)) else: tmp = t[0] * t_conj[0] * t[0] invs = self.inversion_sequence( Twords[t[0]] + Twords[t_conj[0]]) plus_or_minus = invs.count(tmp) if plus_or_minus % 2: cov_element.append((tmp, t_conj[1])) else: cov_element.append( (tmp, t_conj[1] - 1)) cov_element = frozenset(cov_element) if cov_element not in elements: new.add(cov_element) covers.append((new_element, cov_element)) return LatticePoset([elements, covers], cover_relations=True)
def NuTamariLattice(v): r""" This function returns the `\\nu`-Tamari lattice indexed by ``v`` INPUT: - ``v`` -- directed path (canopy) as a list of 0 (east step) and 1 (north step) OUTPUT: the `\\nu`-Tamari lattice indexed by ``v`` """ def extend_path(elem, cur, toextend, x, y): """ Extending the path (cur) step by step, within the limit of v indicated by lvl. The parameter (toextend) is the remaining steps to add. When a path is fully generated, it gets added to (elem). """ if toextend == 0: elem.append(tuple(cur)) return if y < (len(lvl) - 1): cur.append(1) extend_path(elem, cur, toextend - 1, x, y + 1) cur.pop() if x < lvl[y]: cur.append(0) extend_path(elem, cur, toextend - 1, x + 1, y) cur.pop() return def generate_elements(): elem = [] extend_path(elem, [], len(v), 0, 0) return elem def swap(e, p): """ Given a valley p, this function does the v-Tamari switch to obtain a covering element. """ # Initial coordinates and horizontal distance x = [e[:p + 1].count(0), e[:p + 1].count(1)] hdist = lvl[x[1]] - x[0] # Find the next point with the same horizontal distance for j in range(p + 1, len(e)): x[e[j]] += 1 if hdist == (lvl[x[1]] - x[0]): break # Construct a new path with the portion switched e1 = list(e) for i in range(p, j): e1[i] = e1[i + 1] e1[j] = 0 return tuple(e1) def get_cover_elem(e): return [ swap(e, x) for x in range(len(v) - 1) if (e[x] == 0) and (e[x + 1] == 1) ] checkpath(v) lvl = get_level_list(v) return LatticePoset( dict((e, get_cover_elem(e)) for e in generate_elements()))
def order_ideals_lattice(self, as_ideals=True): r""" Return the lattice of order ideals of a poset ``self``, ordered by inclusion. The lattice of order ideals of a poset `P` is usually denoted by `J(P)`. Its underlying set is the set of order ideals of `P`, and its partial order is given by inclusion. The order ideals of `P` are in a canonical bijection with the antichains of `P`. The bijection maps every order ideal to the antichain formed by its maximal elements. By setting the ``as_ideals`` keyword variable to ``False``, one can make this method apply this bijection before returning the lattice. INPUT: - ``as_ideals`` -- Boolean, if ``True`` (default) returns a poset on the set of order ideals, otherwise on the set of antichains EXAMPLES:: sage: P = Posets.PentagonPoset(facade = True) sage: P.cover_relations() [[0, 1], [0, 2], [1, 4], [2, 3], [3, 4]] sage: J = P.order_ideals_lattice(); J Finite lattice containing 8 elements sage: list(J) [{}, {0}, {0, 2}, {0, 1}, {0, 1, 2}, {0, 2, 3}, {0, 1, 2, 3}, {0, 1, 2, 3, 4}] As a lattice on antichains:: sage: J2 = P.order_ideals_lattice(False); J2 Finite lattice containing 8 elements sage: list(J2) [(0,), (1, 2), (1, 3), (1,), (2,), (3,), (4,), ()] TESTS:: sage: J = Posets.DiamondPoset(4, facade = True).order_ideals_lattice(); J Finite lattice containing 6 elements sage: list(J) [{}, {0}, {0, 2}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3}] sage: J.cover_relations() [[{}, {0}], [{0}, {0, 2}], [{0}, {0, 1}], [{0, 2}, {0, 1, 2}], [{0, 1}, {0, 1, 2}], [{0, 1, 2}, {0, 1, 2, 3}]] .. NOTE:: we use facade posets in the examples above just to ensure a nicer ordering in the output. """ from sage.combinat.posets.lattices import LatticePoset if as_ideals: from sage.misc.misc import attrcall from sage.sets.set import Set ideals = [ Set(self.order_ideal(antichain)) for antichain in self.antichains() ] return LatticePoset((ideals, attrcall("issubset"))) else: from sage.misc.cachefunc import cached_function antichains = [tuple(a) for a in self.antichains()] @cached_function def is_above(a, xb): return any(self.is_lequal(xa, xb) for xa in a) def cmp(a, b): return all(is_above(a, xb) for xb in b) return LatticePoset((antichains, cmp))
def GeneralizedTamariLattice(a,b,m=1): r""" Returns the `(a,b)`-Tamari lattice of parameter `m`. INPUT: - `a` and `b` coprime integers with `a \geq b` - `m` a nonnegative integer such that `a \geq b \times m` OUTPUT: - a finite lattice (the lattice property is only conjectural in general) The elements of the lattice are :func:`Dyck paths<sage.combinat.dyck_word.DyckWord>` in the `(a \times b)`-rectangle. The parameter `m` (slope) is used only to define the covering relations. When the slope `m` is `0`, two paths are comparable if and only if one is always above the other. The usual :wikipedia:`Tamari lattice<Tamari lattice>` of index `b` is the special case `a=b+1` and `m=1`. Other special cases give the `m`-Tamari lattices studied in [BMFPR]_. EXAMPLES:: sage: from sage.combinat.tamari_lattices import GeneralizedTamariLattice sage: GeneralizedTamariLattice(3,2) Finite lattice containing 2 elements sage: GeneralizedTamariLattice(4,3) Finite lattice containing 5 elements sage: GeneralizedTamariLattice(4,4) Traceback (most recent call last): ... ValueError: The numbers a and b must be coprime with a>=b. sage: GeneralizedTamariLattice(7,5,2) Traceback (most recent call last): ... ValueError: The condition a>=b*m does not hold. sage: P = GeneralizedTamariLattice(5,3);P Finite lattice containing 7 elements TESTS:: sage: P.coxeter_transformation()**18 == 1 True REFERENCES: .. [BMFPR] M. Bousquet-Melou, E. Fusy, L.-F. Preville Ratelle. "The number of intervals in the m-Tamari lattices". arXiv:1106.1498 """ if not(gcd(a,b)==1 and a>=b): raise ValueError("The numbers a and b must be coprime with a>=b.") if not(a>=b*m): raise ValueError("The condition a>=b*m does not hold.") def covers(p): return [swap(p,i,m) for i in range(len(p)-1) if p[i]==0 and p[i+1]==1] return LatticePoset(dict([[p,covers(p)] for p in paths_in_triangle(a,b,a,b)]))