diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 81c225d8842..4b145344edb 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -324,6 +324,7 @@ Comprehensive module list sage/combinat/posets/linear_extensions sage/combinat/posets/mobile sage/combinat/posets/moebius_algebra + sage/combinat/posets/sashes sage/combinat/posets/all sage/combinat/posets/hasse_cython sage/combinat/posets/hasse_cython_flint diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index c01a2ab88f8..bc803ff60f8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -910,7 +910,7 @@ REFERENCES: Ann. Math. (2) 192, No. 3, 821-891 (2020). :arxiv:`1902.03719`, :doi:`10.4007/annals.2020.192.3.4`. -.. [Bru2014] Erwan Brugalle and Kristin Shaw. *A bit of tropical geometry*. +.. [Bru2014] Erwan Brugallé and Kristin Shaw. *A bit of tropical geometry*. Amer. Math. Monthly, 121(7):563-589, 2014. .. [BHNR2004] \S. Brlek, S. Hamel, M. Nivat, C. Reutenauer, On the @@ -1428,6 +1428,10 @@ REFERENCES: The Open Book Series, Vol. 2 (2019), No. 1, pp. 155-171, https://msp.org/obs/2019/2-1/p10.xhtml +.. [BTTM2024] \L. Bossinger, M. L. Telek, H. Tillmann-Morris, + *Binary Geometries From Pellytopes*, + :arxiv:`2410.08002` + .. [BUVO2007] Johannes Buchmann, Ullrich Vollmer: Binary Quadratic Forms, An Algorithmic Approach, Algorithms and Computation in Mathematics, Volume 20, Springer (2007) @@ -4427,6 +4431,9 @@ REFERENCES: modular forms*, LMS J. of Comput. Math. 14 (2011), 214-231. +.. [Law2014] Shirley Law, *Combinatorial realization of the Hopf algebra + of sashes*, Proceedings of FPSAC 2014, DMTCS, :arxiv:`1407.4073` + .. [Laz1992] Daniel Lazard, *Solving Zero-dimensional Algebraic Systems*, in Journal of Symbolic Computation (1992) vol\. 13, pp\. 117-131 diff --git a/src/sage/combinat/posets/meson.build b/src/sage/combinat/posets/meson.build index 32c323c9dd0..5605d705ba9 100644 --- a/src/sage/combinat/posets/meson.build +++ b/src/sage/combinat/posets/meson.build @@ -18,6 +18,7 @@ py.install_sources( 'moebius_algebra.py', 'poset_examples.py', 'posets.py', + 'sashes.py', subdir: 'sage/combinat/posets', ) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index d8bb880d360..134d454f682 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -52,6 +52,7 @@ :meth:`~Posets.RandomPoset` | Return a random poset on `n` elements. :meth:`~Posets.RibbonPoset` | Return a ribbon on `n` elements with descents at `descents`. :meth:`~Posets.RestrictedIntegerPartitions` | Return the poset of integer partitions of `n`, ordered by restricted refinement. + :meth:`~Posets.Sashes` | Return the lattice of sashes of `n`. :meth:`~Posets.SetPartitions` | Return the poset of set partitions of the set `\{1,\dots,n\}`. :meth:`~Posets.ShardPoset` | Return the shard intersection order. :meth:`~Posets.ShufflePoset` | Return the Shuffle lattice for `(m,n)`. @@ -102,7 +103,7 @@ import sage.categories.posets from sage.combinat.permutation import Permutations, Permutation, to_standard from sage.combinat.posets.posets import Poset, FinitePoset, FinitePosets_n -from sage.combinat.posets import bubble_shuffle, hochschild_lattice +from sage.combinat.posets import bubble_shuffle, hochschild_lattice, sashes from sage.combinat.posets.d_complete import DCompletePoset from sage.combinat.posets.mobile import MobilePoset as Mobile from sage.combinat.posets.lattices import (LatticePoset, MeetSemilattice, @@ -290,6 +291,8 @@ def BooleanLattice(n, facade=None, use_subsets=False): HochschildLattice = staticmethod(hochschild_lattice.hochschild_lattice) + Sashes = staticmethod(sashes.lattice_of_sashes) + @staticmethod def ChainPoset(n, facade=None): r""" diff --git a/src/sage/combinat/posets/sashes.py b/src/sage/combinat/posets/sashes.py new file mode 100644 index 00000000000..06007786734 --- /dev/null +++ b/src/sage/combinat/posets/sashes.py @@ -0,0 +1,219 @@ +r""" +Lattices of sashes + +These lattices were introduced by S. Law in [Law2014]_. They are +lattice quotients of the weak order on the symmetric groups. This +implies that they are congruence-uniform. + +There is a lattice of sashes `\Sigma_n` for every integer `n \geq 1`. +The underlying set of `\Sigma_n` is the set of words of length `n` +in the letters ``□``, ``▨`` and ``■■`` of respective lengths `1,1` and `2`. + +The cardinalities of the lattices $\Sigma_n$ are therefore given by +the Pell numbers (:oeis:`A000129`). + +The implementation describes sashes as tuples of strings. + +REFERENCES: + +- [Law2014]_ + +""" +from itertools import pairwise +from typing import Iterator + +from sage.categories.finite_lattice_posets import FiniteLatticePosets +from sage.combinat.posets.lattices import LatticePoset +from sage.geometry.cone import Cone +from sage.geometry.fan import Fan +from sage.geometry.polyhedron.constructor import Polyhedron +from sage.misc.cachefunc import cached_function +from sage.modules.free_module_element import vector + +B, N, BB = "□", "▨", "■■" + + +def sashes(n: int) -> Iterator[tuple[str, ...]]: + """ + Iterate over the sashes of length `n`. + + INPUT: + + - ``n`` -- nonnegative integer + + EXAMPLES:: + + sage: from sage.combinat.posets.sashes import sashes + sage: [''.join(s) for s in sashes(2)] + ['□□', '□▨', '▨□', '▨▨', '■■'] + + TESTS:: + + sage: [''.join(s) for s in sashes(0)] + [''] + sage: [''.join(s) for s in sashes(1)] + ['□', '▨'] + sage: list(sashes(-1)) + Traceback (most recent call last): + ... + ValueError: n must be nonnegative + """ + if n < 0: + raise ValueError("n must be nonnegative") + if n == 0: + yield () + return + if n == 1: + yield (B,) + yield (N,) + return + for s in sashes(n - 1): + yield s + (B,) + yield s + (N,) + for s in sashes(n - 2): + yield s + (BB,) + + +def cover_relations(s: tuple[str, ...]) -> Iterator[tuple[str, ...]]: + """ + Iterate over the cover relations of the given sash. + + INPUT: + + - ``s`` -- a sash, as a tuple of strings + + EXAMPLES:: + + sage: from sage.combinat.posets.sashes import cover_relations + sage: s = ('□', '▨', '▨') + sage: [''.join(t) for t in cover_relations(s)] + ['□□▨', '□▨□'] + sage: s = ('□', '■■', '▨') + sage: [''.join(t) for t in cover_relations(s)] + ['□□□▨', '□■■□'] + """ + for i, letter in enumerate(s): + if letter == BB: + yield s[:i] + (B, B) + s[i + 1:] + for i, (l1, l2) in enumerate(pairwise(s)): + if l1 == N: + if l2 == B: + yield s[:i] + (BB,) + s[i + 2:] + else: + yield s[:i] + (B,) + s[i + 1:] + if s[-1] == N: + yield s[:-1] + (B,) + + +def lattice_of_sashes(n: int) -> LatticePoset: + """ + Return the lattice of sashes of length `n`. + + INPUT: + + - ``n`` -- positive integer + + EXAMPLES:: + + sage: L = posets.Sashes(4); L + Finite lattice containing 29 elements + sage: L.hasse_diagram().to_undirected().is_regular(4) + True + + TESTS:: + + sage: posets.Sashes(0) + Traceback (most recent call last): + ... + ValueError: n must be positive + """ + if n <= 0: + raise ValueError("n must be positive") + cat = FiniteLatticePosets().CongruenceUniform() + return LatticePoset({s: list(cover_relations(s)) for s in sashes(n)}, + cover_relations=True, check=False, + category=cat) + + +@cached_function +def pellytope_fan(n: int) -> Fan: + """ + Return the fan of the pellytope of dimension `n`. + + This is defined by induction. + + INPUT: + + - ``n`` -- integer + + EXAMPLES:: + + sage: from sage.combinat.posets.sashes import pellytope_fan + sage: pellytope_fan(3).f_vector() + (1, 8, 18, 12) + + TESTS:: + + sage: pellytope_fan(1) + Rational polyhedral fan in 1-d lattice N + sage: pellytope_fan(0) + Traceback (most recent call last): + ... + ValueError: n must be positive + + REFERENCES: + + - [BTTM2024]_ + """ + if n <= 0: + raise ValueError("n must be positive") + dim_one = Fan([Cone([[-1]]), Cone([[1]])]) + if n == 1: + return dim_one + G = pellytope_fan(n - 1).cartesian_product(dim_one) + v = vector([0] * (n - 2) + [-1, 1]) + return G.subdivide(new_rays=[v]) + + +def pellytope(n: int) -> Polyhedron: + """ + Return the pellytope of dimension `n`. + + This is defined as a Minkowski sum. + + INPUT: + + - ``n`` -- integer + + EXAMPLES:: + + sage: from sage.combinat.posets.sashes import pellytope + sage: P3 = pellytope(3); P3 + A 3-dimensional polyhedron in ZZ^3 defined as + the convex hull of 12 vertices + sage: P3.f_vector() + (1, 12, 18, 8, 1) + + TESTS:: + + sage: pellytope(0) + Traceback (most recent call last): + ... + ValueError: n must be positive + + REFERENCES: + + - [BTTM2024]_ + """ + if n <= 0: + raise ValueError("n must be positive") + v = [vector([1 if i == j else 0 for i in range(n)]) + for j in range(n)] + zero = [0] * n + + resu = [Polyhedron(vertices=[zero, v[i]]) for i in range(n)] + + resu.extend(Polyhedron(vertices=[zero, v[j], v[j] + v[j + 1]]) + for j in range(n - 1)) + + return sum(resu)