passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,3908 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ r"""
3
+ Tamari Interval-posets
4
+
5
+ This module implements Tamari interval-posets: combinatorial objects which
6
+ represent intervals of the Tamari order. They have been introduced in
7
+ [CP2015]_ and allow for many combinatorial operations on Tamari intervals.
8
+ In particular, they are linked to :class:`DyckWords` and :class:`BinaryTrees`.
9
+ An introduction into Tamari interval-posets is given in Chapter 7
10
+ of [Pons2013]_.
11
+
12
+ The Tamari lattice can be defined as a lattice structure on either of several
13
+ classes of Catalan objects, especially binary trees and Dyck paths
14
+ [Tam1962]_ [HT1972]_ [Sta-EC2]_. An interval can be seen as
15
+ a pair of comparable elements. The number of intervals has been given in
16
+ [Cha2008]_.
17
+
18
+ AUTHORS:
19
+
20
+ - Viviane Pons 2014: initial implementation
21
+ - Frédéric Chapoton 2014: review
22
+ - Darij Grinberg 2014: review
23
+ - Travis Scrimshaw 2014: review
24
+ """
25
+ # ****************************************************************************
26
+ # Copyright (C) 2013 Viviane Pons <viviane.pons@univie.ac.at>,
27
+ #
28
+ # Distributed under the terms of the GNU General Public License (GPL)
29
+ # as published by the Free Software Foundation; either version 2 of
30
+ # the License, or (at your option) any later version.
31
+ # https://www.gnu.org/licenses/
32
+ # ****************************************************************************
33
+ from __future__ import annotations
34
+ from collections.abc import Iterator
35
+
36
+ from sage.categories.enumerated_sets import EnumeratedSets
37
+ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
38
+ from sage.categories.posets import Posets
39
+ from sage.categories.monoids import Monoids
40
+ from sage.combinat.posets.posets import Poset, FinitePoset
41
+ from sage.categories.finite_posets import FinitePosets
42
+ from sage.combinat.binary_tree import BinaryTrees
43
+ from sage.combinat.binary_tree import LabelledBinaryTrees, LabelledBinaryTree
44
+ from sage.combinat.permutation import Permutation
45
+ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
46
+ from sage.misc.cachefunc import cached_method
47
+ from sage.misc.latex import latex
48
+ from sage.misc.lazy_attribute import lazy_attribute
49
+ from sage.misc.lazy_import import lazy_import
50
+ from sage.rings.integer import Integer
51
+ from sage.rings.integer_ring import ZZ
52
+ from sage.rings.semirings.non_negative_integer_semiring import NN
53
+ from sage.sets.non_negative_integers import NonNegativeIntegers
54
+ from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets
55
+ from sage.sets.family import Family
56
+ from sage.structure.element import Element
57
+ from sage.structure.global_options import GlobalOptions
58
+ from sage.structure.parent import Parent
59
+ from sage.structure.richcmp import richcmp, op_NE, op_EQ
60
+ from sage.structure.unique_representation import UniqueRepresentation
61
+ from sage.graphs.digraph import DiGraph
62
+
63
+ lazy_import('sage.combinat.dyck_word', 'DyckWords')
64
+
65
+
66
+ class TamariIntervalPoset(Element,
67
+ metaclass=InheritComparisonClasscallMetaclass):
68
+ r"""
69
+ The class of Tamari interval-posets.
70
+
71
+ An interval-poset is a labelled poset of size `n`, with labels
72
+ `1, 2, \ldots, n`, satisfying the following conditions:
73
+
74
+ - if `a < c` (as integers) and `a` precedes `c` in the poset, then,
75
+ for all `b` such that `a < b < c`, `b` precedes `c`,
76
+
77
+ - if `a < c` (as integers) and `c` precedes `a` in the poset, then,
78
+ for all `b` such that `a < b < c`, `b` precedes `a`.
79
+
80
+ We use the word "precedes" here to distinguish the poset order and
81
+ the natural order on numbers. "Precedes" means "is smaller than
82
+ with respect to the poset structure"; this does not imply a
83
+ covering relation.
84
+
85
+ Interval-posets of size `n` are in bijection with intervals of
86
+ the Tamari lattice of binary trees of size `n`. Specifically, if
87
+ `P` is an interval-poset of size `n`, then the set of linear
88
+ extensions of `P` (as permutations in `S_n`) is an interval in the
89
+ right weak order (see
90
+ :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal`),
91
+ and is in fact the preimage of an interval in the Tamari lattice (of
92
+ binary trees of size `n`) under the operation which sends a
93
+ permutation to its right-to-left binary search tree
94
+ (:meth:`~sage.combinat.permutation.Permutation.binary_search_tree`
95
+ with the ``left_to_right`` variable set to ``False``)
96
+ without its labelling.
97
+
98
+ INPUT:
99
+
100
+ - ``size`` -- integer; the size of the interval-posets (number of
101
+ vertices)
102
+
103
+ - ``relations`` -- list (or tuple) of pairs ``(a,b)`` (themselves
104
+ lists or tuples), each representing a relation of the form
105
+ '`a` precedes `b`' in the poset
106
+
107
+ - ``check`` -- boolean (default: ``True``); whether to check the
108
+ interval-poset condition or not
109
+
110
+ .. WARNING::
111
+
112
+ The ``relations`` input can be a list or tuple, but not an
113
+ iterator (nor should its entries be iterators).
114
+
115
+ NOTATION:
116
+
117
+ Here and in the following, the signs `<` and `>` always refer to
118
+ the natural ordering on integers, whereas the word "precedes" refers
119
+ to the order of the interval-poset. "Minimal" and "maximal" refer
120
+ to the natural ordering on integers.
121
+
122
+ The *increasing relations* of an interval-poset `P` mean the pairs
123
+ `(a, b)` of elements of `P` such that `a < b` as integers and `a`
124
+ precedes `b` in `P`. The *initial forest* of `P` is the poset
125
+ obtained by imposing (only) the increasing relations on the ground
126
+ set of `P`. It is a sub-interval poset of `P`, and is a forest with
127
+ its roots on top. This forest is usually given the structure of a
128
+ planar forest by ordering brother nodes by their labels; it then has
129
+ the property that if its nodes are traversed in post-order
130
+ (see :meth:`~sage.combinat.abstract_tree.AbstractTree.post_order_traversal`,
131
+ and traverse the trees of the forest from left to right as well),
132
+ then the labels encountered are `1, 2, \ldots, n` in this order.
133
+
134
+ The *decreasing relations* of an interval-poset `P` mean the pairs
135
+ `(a, b)` of elements of `P` such that `b < a` as integers and `a`
136
+ precedes `b` in `P`. The *final forest* of `P` is the poset
137
+ obtained by imposing (only) the decreasing relations on the ground
138
+ set of `P`. It is a sub-interval poset of `P`, and is a forest with
139
+ its roots on top. This forest is usually given the structure of a
140
+ planar forest by ordering brother nodes by their labels; it then has
141
+ the property that if its nodes are traversed in pre-order
142
+ (see :meth:`~sage.combinat.abstract_tree.AbstractTree.pre_order_traversal`,
143
+ and traverse the trees of the forest from left to right as well),
144
+ then the labels encountered are `1, 2, \ldots, n` in this order.
145
+
146
+ EXAMPLES::
147
+
148
+ sage: TamariIntervalPoset(0,[])
149
+ The Tamari interval of size 0 induced by relations []
150
+ sage: TamariIntervalPoset(3,[])
151
+ The Tamari interval of size 3 induced by relations []
152
+ sage: TamariIntervalPoset(3,[(1,2)])
153
+ The Tamari interval of size 3 induced by relations [(1, 2)]
154
+ sage: TamariIntervalPoset(3,[(1,2),(2,3)])
155
+ The Tamari interval of size 3 induced by relations [(1, 2), (2, 3)]
156
+ sage: TamariIntervalPoset(3,[(1,2),(2,3),(1,3)])
157
+ The Tamari interval of size 3 induced by relations [(1, 2), (2, 3)]
158
+ sage: TamariIntervalPoset(3,[(1,2),(3,2)])
159
+ The Tamari interval of size 3 induced by relations [(1, 2), (3, 2)]
160
+ sage: TamariIntervalPoset(3,[[1,2],[2,3]])
161
+ The Tamari interval of size 3 induced by relations [(1, 2), (2, 3)]
162
+ sage: TamariIntervalPoset(3,[[1,2],[2,3],[1,2],[1,3]])
163
+ The Tamari interval of size 3 induced by relations [(1, 2), (2, 3)]
164
+
165
+ sage: TamariIntervalPoset(3,[(3,4)])
166
+ Traceback (most recent call last):
167
+ ...
168
+ ValueError: the relations do not correspond to the size of the poset
169
+
170
+ sage: TamariIntervalPoset(2,[(2,1),(1,2)])
171
+ Traceback (most recent call last):
172
+ ...
173
+ ValueError: The graph is not directed acyclic
174
+
175
+ sage: TamariIntervalPoset(3,[(1,3)])
176
+ Traceback (most recent call last):
177
+ ...
178
+ ValueError: this does not satisfy the Tamari interval-poset condition
179
+
180
+ It is also possible to transform a poset directly into an interval-poset::
181
+
182
+ sage: TIP = TamariIntervalPosets()
183
+ sage: p = Poset(([1,2,3], [(1,2)]))
184
+ sage: TIP(p)
185
+ The Tamari interval of size 3 induced by relations [(1, 2)]
186
+ sage: TIP(Poset({1: []}))
187
+ The Tamari interval of size 1 induced by relations []
188
+ sage: TIP(Poset({}))
189
+ The Tamari interval of size 0 induced by relations []
190
+ """
191
+ @staticmethod
192
+ def __classcall_private__(cls, *args, **opts) -> TIP:
193
+ r"""
194
+ Ensure that interval-posets created by the enumerated sets and
195
+ directly are the same and that they are instances of
196
+ :class:`TamariIntervalPoset`.
197
+
198
+ TESTS::
199
+
200
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
201
+ sage: ip.parent()
202
+ Interval-posets
203
+ sage: type(ip)
204
+ <class 'sage.combinat.interval_posets.TamariIntervalPosets_all_with_category.element_class'>
205
+
206
+ sage: ip2 = TamariIntervalPosets()(4,[(2,4),(3,4),(2,1),(3,1)])
207
+ sage: ip2.parent() is ip.parent()
208
+ True
209
+ sage: type(ip) is type(ip2)
210
+ True
211
+
212
+ sage: ip3 = TamariIntervalPosets(4)([(2,4),(3,4),(2,1),(3,1)])
213
+ sage: ip3.parent() is ip.parent()
214
+ False
215
+ sage: type(ip3) is type(ip)
216
+ True
217
+ """
218
+ P = TamariIntervalPosets_all()
219
+ return P.element_class(P, *args, **opts)
220
+
221
+ def __init__(self, parent, size, relations=None, check=True):
222
+ r"""
223
+ TESTS::
224
+
225
+ sage: TamariIntervalPoset(3,[(1,2),(3,2)]).parent()
226
+ Interval-posets
227
+ sage: P = Poset(DiGraph([(4,1),(3,1),(2,1)]))
228
+ sage: TamariIntervalPoset(P).parent()
229
+ Interval-posets
230
+ """
231
+ if relations is None:
232
+ relations = []
233
+ if isinstance(size, FinitePoset):
234
+ # first argument is a poset
235
+ self._poset = size
236
+ self._size = size.cardinality()
237
+ else:
238
+ # arguments are size and relations
239
+ self._size = size
240
+ self._poset = Poset((list(range(1, size + 1)), relations))
241
+ if self._poset.cardinality() != size:
242
+ # This can happen as the Poset constructor automatically adds
243
+ # in elements from the relations.
244
+ raise ValueError("the relations do not correspond to the size of the poset")
245
+
246
+ if check and not TamariIntervalPosets.check_poset(self._poset):
247
+ raise ValueError("this does not satisfy the Tamari interval-poset condition")
248
+
249
+ Element.__init__(self, parent)
250
+
251
+ self._cover_relations = tuple(self._poset.cover_relations())
252
+ self._latex_options = {}
253
+
254
+ def set_latex_options(self, D):
255
+ r"""
256
+ Set the latex options for use in the ``_latex_`` function.
257
+
258
+ The default values are set in the ``__init__`` function.
259
+
260
+ - ``tikz_scale`` -- (default: 1) scale for use with the tikz package
261
+
262
+ - ``line_width`` -- (default: 1 * ``tikz_scale``) value representing the
263
+ line width
264
+
265
+ - ``color_decreasing`` -- (default: red) the color for decreasing
266
+ relations
267
+
268
+ - ``color_increasing`` -- (default: blue) the color for increasing
269
+ relations
270
+
271
+ - ``hspace`` -- (default: 1) the difference between horizontal
272
+ coordinates of adjacent vertices
273
+
274
+ - ``vspace`` -- (default: 1) the difference between vertical
275
+ coordinates of adjacent vertices
276
+
277
+ INPUT:
278
+
279
+ - ``D`` -- dictionary with a list of latex parameters to change
280
+
281
+ EXAMPLES::
282
+
283
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
284
+ sage: ip.latex_options()["color_decreasing"]
285
+ 'red'
286
+ sage: ip.set_latex_options({"color_decreasing":'green'})
287
+ sage: ip.latex_options()["color_decreasing"]
288
+ 'green'
289
+ sage: ip.set_latex_options({"color_increasing":'black'})
290
+ sage: ip.latex_options()["color_increasing"]
291
+ 'black'
292
+
293
+ To change the default options for all interval-posets, use the
294
+ parent's latex options::
295
+
296
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
297
+ sage: ip2 = TamariIntervalPoset(4,[(1,2),(2,3)])
298
+ sage: ip.latex_options()["color_decreasing"]
299
+ 'red'
300
+ sage: ip2.latex_options()["color_decreasing"]
301
+ 'red'
302
+ sage: TamariIntervalPosets.options(latex_color_decreasing='green')
303
+ sage: ip.latex_options()["color_decreasing"]
304
+ 'green'
305
+ sage: ip2.latex_options()["color_decreasing"]
306
+ 'green'
307
+
308
+ Next we set a local latex option and show the global option does not
309
+ override it::
310
+
311
+ sage: ip.set_latex_options({"color_decreasing": 'black'})
312
+ sage: ip.latex_options()["color_decreasing"]
313
+ 'black'
314
+ sage: TamariIntervalPosets.options(latex_color_decreasing='blue')
315
+ sage: ip.latex_options()["color_decreasing"]
316
+ 'black'
317
+ sage: ip2.latex_options()["color_decreasing"]
318
+ 'blue'
319
+ sage: TamariIntervalPosets.options._reset()
320
+ """
321
+ for opt in D:
322
+ self._latex_options[opt] = D[opt]
323
+
324
+ def latex_options(self) -> dict:
325
+ r"""
326
+ Return the latex options for use in the ``_latex_`` function as a
327
+ dictionary.
328
+
329
+ The default values are set using the options.
330
+
331
+ - ``tikz_scale`` -- (default: 1) scale for use with the tikz package
332
+
333
+ - ``line_width`` -- (default: 1) value representing the line width
334
+ (additionally scaled by ``tikz_scale``)
335
+
336
+ - ``color_decreasing`` -- (default: ``'red'``) the color for
337
+ decreasing relations
338
+
339
+ - ``color_increasing`` -- (default: ``'blue'``) the color for
340
+ increasing relations
341
+
342
+ - ``hspace`` -- (default: 1) the difference between horizontal
343
+ coordinates of adjacent vertices
344
+
345
+ - ``vspace`` -- (default: 1) the difference between vertical
346
+ coordinates of adjacent vertices
347
+
348
+ EXAMPLES::
349
+
350
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
351
+ sage: ip.latex_options()['color_decreasing']
352
+ 'red'
353
+ sage: ip.latex_options()['hspace']
354
+ 1
355
+ """
356
+ d = self._latex_options.copy()
357
+ if "tikz_scale" not in d:
358
+ d["tikz_scale"] = self.parent().options["latex_tikz_scale"]
359
+ if "line_width" not in d:
360
+ d["line_width"] = self.parent().options["latex_line_width_scalar"] * d["tikz_scale"]
361
+ if "color_decreasing" not in d:
362
+ d["color_decreasing"] = self.parent().options["latex_color_decreasing"]
363
+ if "color_increasing" not in d:
364
+ d["color_increasing"] = self.parent().options["latex_color_increasing"]
365
+ if "hspace" not in d:
366
+ d["hspace"] = self.parent().options["latex_hspace"]
367
+ if "vspace" not in d:
368
+ d["vspace"] = self.parent().options["latex_vspace"]
369
+ return d
370
+
371
+ def _find_node_positions(self, hspace=1, vspace=1) -> dict[int, list]:
372
+ """
373
+ Compute a nice embedding.
374
+
375
+ If `x` precedes `y`, then `y` will always be placed on top of `x`
376
+ and/or to the right of `x`.
377
+ Decreasing relations tend to be drawn vertically and increasing
378
+ relations horizontally.
379
+ The algorithm tries to avoid superposition but on big
380
+ interval-posets, it might happen.
381
+
382
+ OUTPUT:
383
+
384
+ a dictionary {vertex: (x,y)}
385
+
386
+ EXAMPLES::
387
+
388
+ sage: ti = TamariIntervalPosets(4)[2]
389
+ sage: list(ti._find_node_positions().values())
390
+ [[0, 0], [0, -1], [0, -2], [1, -2]]
391
+ """
392
+ node_positions = {}
393
+
394
+ to_draw = [(1, 0)]
395
+ current_parent = [self.increasing_parent(1)]
396
+ parenty = [0]
397
+ x = 0
398
+ y = 0
399
+ for i in range(2, self.size() + 1):
400
+ decreasing_parent = self.decreasing_parent(i)
401
+ increasing_parent = self.increasing_parent(i)
402
+ while to_draw and (decreasing_parent is None or
403
+ decreasing_parent < to_draw[-1][0]):
404
+ n = to_draw.pop()
405
+ node_positions[n[0]] = [x, n[1]]
406
+ if i != current_parent[-1]:
407
+ if (not self.le(i, i - 1) and decreasing_parent is not None):
408
+ x += hspace
409
+ if current_parent[-1] is not None:
410
+ y -= vspace
411
+ else:
412
+ y -= vspace
413
+ if increasing_parent != current_parent[-1]:
414
+ current_parent.append(increasing_parent)
415
+ parenty.append(y)
416
+ nodey = y
417
+ else:
418
+ current_parent.pop()
419
+ x += hspace
420
+ nodey = parenty.pop()
421
+ if not current_parent or increasing_parent != current_parent[-1]:
422
+ current_parent.append(increasing_parent)
423
+ parenty.append(nodey)
424
+ to_draw.append((i, nodey))
425
+
426
+ for n in to_draw:
427
+ node_positions[n[0]] = [x, n[1]]
428
+ return node_positions
429
+
430
+ def plot(self, **kwds):
431
+ """
432
+ Return a picture.
433
+
434
+ The picture represents the Hasse diagram, where the covers are
435
+ colored in blue if they are increasing and in red if they are
436
+ decreasing.
437
+
438
+ This uses the same coordinates as the latex view.
439
+
440
+ EXAMPLES::
441
+
442
+ sage: ti = TamariIntervalPosets(4)[2]
443
+ sage: ti.plot() # needs sage.plot
444
+ Graphics object consisting of 6 graphics primitives
445
+
446
+ TESTS::
447
+
448
+ sage: ti = TamariIntervalPoset(3, [[2,1], [2,3]])
449
+ sage: ti.plot() # needs sage.plot
450
+ Graphics object consisting of 6 graphics primitives
451
+ """
452
+ c0 = 'blue' # self.latex_options()["color_increasing"]
453
+ c1 = 'red' # self.latex_options()["color_decreasing"]
454
+ G = self.poset().hasse_diagram()
455
+ G.set_pos(self._find_node_positions())
456
+ for a, b in G.edges(sort=False, labels=False):
457
+ if a < b:
458
+ G.set_edge_label(a, b, 0)
459
+ else:
460
+ G.set_edge_label(a, b, 1)
461
+ return G.plot(color_by_label={0: c0, 1: c1}, **kwds)
462
+
463
+ def _latex_(self) -> str:
464
+ r"""
465
+ A latex representation of ``self`` using the tikzpicture package.
466
+
467
+ This picture shows the union of the Hasse diagrams of the
468
+ initial and final forests.
469
+
470
+ If `x` precedes `y`, then `y` will always be placed on top of `x`
471
+ and/or to the right of `x`.
472
+ Decreasing relations tend to be drawn vertically and increasing
473
+ relations horizontally.
474
+ The algorithm tries to avoid superposition but on big
475
+ interval-posets, it might happen.
476
+
477
+ You can use ``self.set_latex_options()`` to change default latex
478
+ options. Or you can use the parent's options.
479
+
480
+ EXAMPLES::
481
+
482
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
483
+ sage: latex(ip)
484
+ \begin{tikzpicture}[scale=1]
485
+ \node(T1) at (1,0) {1};
486
+ \node(T2) at (0,-1) {2};
487
+ \node(T3) at (1,-2) {3};
488
+ \node(T4) at (2,-1) {4};
489
+ \draw[line width = 0.5, color=red] (T3) -- (T1);
490
+ \draw[line width = 0.5, color=red] (T2) -- (T1);
491
+ \draw[line width = 0.5, color=blue] (T2) -- (T4);
492
+ \draw[line width = 0.5, color=blue] (T3) -- (T4);
493
+ \end{tikzpicture}
494
+
495
+ TESTS::
496
+
497
+ sage: ip = TamariIntervalPoset(0,[])
498
+ sage: latex(ip)
499
+ \begin{tikzpicture}[scale=1]
500
+ \node(T0) at (0,0){$\emptyset$};\end{tikzpicture}
501
+ """
502
+ latex.add_package_to_preamble_if_available("tikz")
503
+ latex_options = self.latex_options()
504
+ start = "\\begin{tikzpicture}[scale=" + str(latex_options['tikz_scale']) + "]\n"
505
+ end = "\\end{tikzpicture}"
506
+ vspace = latex_options["vspace"]
507
+ hspace = latex_options["hspace"]
508
+
509
+ def draw_node(j, x, y) -> str:
510
+ r"""
511
+ Internal method to draw vertices
512
+ """
513
+ return "\\node(T" + str(j) + ") at (" + str(x) + "," + str(y) + ") {" + str(j) + "};\n"
514
+
515
+ def draw_increasing(i, j) -> str:
516
+ r"""
517
+ Internal method to draw increasing relations
518
+ """
519
+ return "\\draw[line width = " + str(latex_options["line_width"]) + ", color=" + latex_options["color_increasing"] + "] (T" + str(i) + ") -- (T" + str(j) + ");\n"
520
+
521
+ def draw_decreasing(i, j) -> str:
522
+ r"""
523
+ Internal method to draw decreasing relations
524
+ """
525
+ return "\\draw[line width = " + str(latex_options["line_width"]) + ", color=" + latex_options["color_decreasing"] + "] (T" + str(i) + ") -- (T" + str(j) + ");\n"
526
+
527
+ if self.size() == 0:
528
+ nodes = "\\node(T0) at (0,0){$\\emptyset$};"
529
+ relations = ""
530
+ else:
531
+ positions = self._find_node_positions(hspace, vspace)
532
+ nodes = "" # latex for node declarations
533
+ relations = "" # latex for drawing relations
534
+ for i in range(1, self.size() + 1):
535
+ nodes += draw_node(i, *positions[i])
536
+ for i, j in self.decreasing_cover_relations():
537
+ relations += draw_decreasing(i, j)
538
+ for i, j in self.increasing_cover_relations():
539
+ relations += draw_increasing(i, j)
540
+
541
+ return start + nodes + relations + end
542
+
543
+ def poset(self) -> FinitePoset:
544
+ r"""
545
+ Return ``self`` as a labelled poset.
546
+
547
+ An interval-poset is indeed constructed from a labelled poset which
548
+ is stored internally. This method allows to access the poset and
549
+ all the associated methods.
550
+
551
+ EXAMPLES::
552
+
553
+ sage: ip = TamariIntervalPoset(4,[(1,2),(3,2),(2,4),(3,4)])
554
+ sage: pos = ip.poset(); pos
555
+ Finite poset containing 4 elements
556
+ sage: pos.maximal_chains()
557
+ [[3, 2, 4], [1, 2, 4]]
558
+ sage: pos.maximal_elements()
559
+ [4]
560
+ sage: pos.is_lattice()
561
+ False
562
+ """
563
+ return self._poset
564
+
565
+ def _mul_(self, other: TIP) -> TIP:
566
+ """
567
+ Return the associative product of ``self`` and ``other``.
568
+
569
+ This is defined by taking the disjoint union of the relations
570
+ of ``self`` with the relations of ``other`` shifted by `n`,
571
+ where `n` is the size of ``self``.
572
+
573
+ EXAMPLES::
574
+
575
+ sage: T1 = TamariIntervalPoset(1,[])
576
+ sage: T2 = TamariIntervalPoset(2,[[1,2]])
577
+ sage: T1*T1
578
+ The Tamari interval of size 2 induced by relations []
579
+ sage: T2*T1
580
+ The Tamari interval of size 3 induced by relations [(1, 2)]
581
+ sage: T1*T2
582
+ The Tamari interval of size 3 induced by relations [(2, 3)]
583
+ sage: T2*T2
584
+ The Tamari interval of size 4 induced by relations [(1, 2), (3, 4)]
585
+
586
+ TESTS::
587
+
588
+ sage: U = TamariIntervalPoset(0,[])
589
+ sage: U*T1 == T1
590
+ True
591
+ sage: T2*U == T2
592
+ True
593
+ """
594
+ n = self._size
595
+ m = other.size()
596
+ relations = self._poset.cover_relations()
597
+ relations.extend((i + n, j + n)
598
+ for i, j in other._poset.cover_relations_iterator())
599
+ P = FinitePoset(DiGraph([list(range(1, n + m + 1)), relations],
600
+ format='vertices_and_edges')) # type:ignore
601
+ return TamariIntervalPoset(P, check=False) # type:ignore
602
+
603
+ def factor(self) -> list[TamariIntervalPoset]:
604
+ """
605
+ Return the unique decomposition as a list of connected components.
606
+
607
+ EXAMPLES::
608
+
609
+ sage: factor(TamariIntervalPoset(2,[])) # indirect doctest
610
+ [The Tamari interval of size 1 induced by relations [],
611
+ The Tamari interval of size 1 induced by relations []]
612
+
613
+ .. SEEALSO:: :meth:`is_connected`
614
+
615
+ TESTS::
616
+
617
+ sage: # needs sage.combinat
618
+ sage: T = TamariIntervalPosets(20).random_element()
619
+ sage: facs = factor(T)
620
+ sage: all(U.is_connected() for U in facs)
621
+ True
622
+ sage: T == prod(facs)
623
+ True
624
+ """
625
+ hasse = self.poset().hasse_diagram()
626
+ cc = hasse.connected_components_subgraphs()
627
+ resu = []
628
+ for comp in sorted(cc, key=min):
629
+ shift = 1 - min(comp)
630
+ comp.relabel(lambda i: i + shift)
631
+ resu.append(TamariIntervalPoset(len(comp),
632
+ comp.edges(sort=False, labels=False)))
633
+ return resu
634
+
635
+ def __hash__(self):
636
+ """
637
+ Return the hash of ``self``.
638
+
639
+ EXAMPLES::
640
+
641
+ sage: len(set(hash(u) for u in TamariIntervalPosets(4)))
642
+ 68
643
+ """
644
+ pair = (self.size(), tuple(tuple(e) for e in self._cover_relations))
645
+ return hash(pair)
646
+
647
+ @cached_method
648
+ def increasing_cover_relations(self) -> list[tuple[int, int]]:
649
+ r"""
650
+ Return the cover relations of the initial forest of ``self``.
651
+
652
+ This is the poset formed by keeping only the relations of the form
653
+ `a` precedes `b` with `a < b`.
654
+
655
+ The initial forest of ``self`` is a forest with its roots
656
+ being on top. It is also called the increasing poset of ``self``.
657
+
658
+ .. WARNING::
659
+
660
+ This method computes the cover relations of the initial
661
+ forest. This is not identical with the cover relations of
662
+ ``self`` which happen to be increasing!
663
+
664
+ .. SEEALSO::
665
+
666
+ :meth:`initial_forest`
667
+
668
+ EXAMPLES::
669
+
670
+ sage: TamariIntervalPoset(4,[(1,2),(3,2),(2,4),(3,4)]).increasing_cover_relations()
671
+ [(1, 2), (2, 4), (3, 4)]
672
+ sage: TamariIntervalPoset(3,[(1,2),(1,3),(2,3)]).increasing_cover_relations()
673
+ [(1, 2), (2, 3)]
674
+ """
675
+ relations = []
676
+ size = self.size()
677
+ for i in range(1, size):
678
+ for j in range(i + 1, size + 1):
679
+ if self.le(i, j):
680
+ relations.append((i, j))
681
+ break
682
+ return relations
683
+
684
+ def increasing_roots(self) -> list[int]:
685
+ r"""
686
+ Return the root vertices of the initial forest of ``self``.
687
+
688
+ These are the vertices `a` of ``self`` such that there is no
689
+ `b > a` with `a` precedes `b`.
690
+
691
+ OUTPUT:
692
+
693
+ The list of all roots of the initial forest of ``self``, in
694
+ decreasing order.
695
+
696
+ EXAMPLES::
697
+
698
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(3,5),(4,5)]); ip
699
+ The Tamari interval of size 6 induced by relations [(1, 2), (3, 5), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
700
+ sage: ip.increasing_roots()
701
+ [6, 5, 2]
702
+ sage: ip.initial_forest().increasing_roots()
703
+ [6, 5, 2]
704
+
705
+ TESTS::
706
+
707
+ sage: TamariIntervalPoset(0,[]).increasing_roots()
708
+ []
709
+ """
710
+ size = self.size()
711
+ if size == 0:
712
+ return []
713
+ roots = [size]
714
+ root = size
715
+ for i in range(size - 1, 0, -1):
716
+ if not self.le(i, root):
717
+ roots.append(i)
718
+ root = i
719
+ return roots
720
+
721
+ def increasing_children(self, v) -> list[int]:
722
+ r"""
723
+ Return the children of ``v`` in the initial forest of ``self``.
724
+
725
+ INPUT:
726
+
727
+ - ``v`` -- integer representing a vertex of ``self``
728
+ (between 1 and ``size``)
729
+
730
+ OUTPUT:
731
+
732
+ The list of all children of ``v`` in the initial forest of
733
+ ``self``, in decreasing order.
734
+
735
+ EXAMPLES::
736
+
737
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(3,5),(4,5)]); ip
738
+ The Tamari interval of size 6 induced by relations [(1, 2), (3, 5), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
739
+ sage: ip.increasing_children(2)
740
+ [1]
741
+ sage: ip.increasing_children(5)
742
+ [4, 3]
743
+ sage: ip.increasing_children(1)
744
+ []
745
+ """
746
+ children = []
747
+ root = None
748
+ for i in range(v - 1, 0, -1):
749
+ if not self.le(i, v):
750
+ break
751
+ if root is None or not self.le(i, root):
752
+ children.append(i)
753
+ root = i
754
+ return children
755
+
756
+ def increasing_parent(self, v) -> None | int:
757
+ r"""
758
+ Return the vertex parent of ``v`` in the initial forest of ``self``.
759
+
760
+ This is the lowest (as integer!) vertex `b > v` such that `v`
761
+ precedes `b`. If there is no such vertex (that is, `v` is an
762
+ increasing root), then ``None`` is returned.
763
+
764
+ INPUT:
765
+
766
+ - ``v`` -- integer representing a vertex of ``self``
767
+ (between 1 and ``size``)
768
+
769
+ EXAMPLES::
770
+
771
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(3,5),(4,5)]); ip
772
+ The Tamari interval of size 6 induced by relations [(1, 2), (3, 5), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
773
+ sage: ip.increasing_parent(1)
774
+ 2
775
+ sage: ip.increasing_parent(3)
776
+ 5
777
+ sage: ip.increasing_parent(4)
778
+ 5
779
+ sage: ip.increasing_parent(5) is None
780
+ True
781
+ """
782
+ parent = None
783
+ for i in range(self.size(), v, -1):
784
+ if self.le(v, i):
785
+ parent = i
786
+ return parent
787
+
788
+ @cached_method
789
+ def decreasing_cover_relations(self) -> list[tuple[int, int]]:
790
+ r"""
791
+ Return the cover relations of the final forest of ``self``.
792
+
793
+ This is the poset formed by keeping only the relations of the form
794
+ `a` precedes `b` with `a > b`.
795
+
796
+ The final forest of ``self`` is a forest with its roots
797
+ being on top. It is also called the decreasing poset of ``self``.
798
+
799
+ .. WARNING::
800
+
801
+ This method computes the cover relations of the final
802
+ forest. This is not identical with the cover relations of
803
+ ``self`` which happen to be decreasing!
804
+
805
+ .. SEEALSO::
806
+
807
+ :meth:`final_forest`
808
+
809
+ EXAMPLES::
810
+
811
+ sage: TamariIntervalPoset(4,[(2,1),(3,2),(3,4),(4,2)]).decreasing_cover_relations()
812
+ [(4, 2), (3, 2), (2, 1)]
813
+ sage: TamariIntervalPoset(4,[(2,1),(4,3),(2,3)]).decreasing_cover_relations()
814
+ [(4, 3), (2, 1)]
815
+ sage: TamariIntervalPoset(3,[(2,1),(3,1),(3,2)]).decreasing_cover_relations()
816
+ [(3, 2), (2, 1)]
817
+ """
818
+ relations = []
819
+ for i in range(self.size(), 1, -1):
820
+ for j in range(i - 1, 0, -1):
821
+ if self.le(i, j):
822
+ relations.append((i, j))
823
+ break
824
+ return relations
825
+
826
+ def decreasing_roots(self) -> list[int]:
827
+ r"""
828
+ Return the root vertices of the final forest of ``self``.
829
+
830
+ These are the vertices `b` such that there is no `a < b` with `b`
831
+ preceding `a`.
832
+
833
+ OUTPUT:
834
+
835
+ The list of all roots of the final forest of ``self``, in
836
+ increasing order.
837
+
838
+ EXAMPLES::
839
+
840
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(3,5),(4,5)]); ip
841
+ The Tamari interval of size 6 induced by relations [(1, 2), (3, 5), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
842
+ sage: ip.decreasing_roots()
843
+ [1, 2]
844
+ sage: ip.final_forest().decreasing_roots()
845
+ [1, 2]
846
+ """
847
+ if self.size() == 0:
848
+ return []
849
+ roots = [1]
850
+ root = 1
851
+ for i in range(2, self.size() + 1):
852
+ if not self.le(i, root):
853
+ roots.append(i)
854
+ root = i
855
+ return roots
856
+
857
+ def decreasing_children(self, v) -> list[int]:
858
+ r"""
859
+ Return the children of ``v`` in the final forest of ``self``.
860
+
861
+ INPUT:
862
+
863
+ - ``v`` -- integer representing a vertex of ``self``
864
+ (between 1 and ``size``)
865
+
866
+ OUTPUT:
867
+
868
+ The list of all children of ``v`` in the final forest of ``self``,
869
+ in increasing order.
870
+
871
+ EXAMPLES::
872
+
873
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(3,5),(4,5)]); ip
874
+ The Tamari interval of size 6 induced by relations [(1, 2), (3, 5), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
875
+ sage: ip.decreasing_children(2)
876
+ [3, 5]
877
+ sage: ip.decreasing_children(3)
878
+ [4]
879
+ sage: ip.decreasing_children(1)
880
+ []
881
+ """
882
+ children = []
883
+ root = None
884
+ for i in range(v + 1, self.size() + 1):
885
+ if not self.le(i, v):
886
+ break
887
+ if root is None or not self.le(i, root):
888
+ children.append(i)
889
+ root = i
890
+ return children
891
+
892
+ def decreasing_parent(self, v) -> None | int:
893
+ r"""
894
+ Return the vertex parent of ``v`` in the final forest of ``self``.
895
+
896
+ This is the highest (as integer!) vertex `a < v` such that ``v``
897
+ precedes ``a``. If there is no such vertex (that is, `v` is a
898
+ decreasing root), then ``None`` is returned.
899
+
900
+ INPUT:
901
+
902
+ - ``v`` -- integer representing a vertex of ``self`` (between
903
+ 1 and ``size``)
904
+
905
+ EXAMPLES::
906
+
907
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(3,5),(4,5)]); ip
908
+ The Tamari interval of size 6 induced by relations [(1, 2), (3, 5), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
909
+ sage: ip.decreasing_parent(4)
910
+ 3
911
+ sage: ip.decreasing_parent(3)
912
+ 2
913
+ sage: ip.decreasing_parent(5)
914
+ 2
915
+ sage: ip.decreasing_parent(2) is None
916
+ True
917
+ """
918
+ parent = None
919
+ for i in range(1, v):
920
+ if self.le(v, i):
921
+ parent = i
922
+ return parent
923
+
924
+ def le(self, e1, e2) -> bool:
925
+ r"""
926
+ Return whether ``e1`` precedes or equals ``e2`` in ``self``.
927
+
928
+ EXAMPLES::
929
+
930
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3)])
931
+ sage: ip.le(1,2)
932
+ True
933
+ sage: ip.le(1,3)
934
+ True
935
+ sage: ip.le(2,3)
936
+ True
937
+ sage: ip.le(3,4)
938
+ False
939
+ sage: ip.le(1,1)
940
+ True
941
+ """
942
+ return self._poset.le(e1, e2)
943
+
944
+ def lt(self, e1, e2) -> bool:
945
+ r"""
946
+ Return whether ``e1`` strictly precedes ``e2`` in ``self``.
947
+
948
+ EXAMPLES::
949
+
950
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3)])
951
+ sage: ip.lt(1,2)
952
+ True
953
+ sage: ip.lt(1,3)
954
+ True
955
+ sage: ip.lt(2,3)
956
+ True
957
+ sage: ip.lt(3,4)
958
+ False
959
+ sage: ip.lt(1,1)
960
+ False
961
+ """
962
+ return self._poset.lt(e1, e2)
963
+
964
+ def ge(self, e1, e2) -> bool:
965
+ r"""
966
+ Return whether ``e2`` precedes or equals ``e1`` in ``self``.
967
+
968
+ EXAMPLES::
969
+
970
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3)])
971
+ sage: ip.ge(2,1)
972
+ True
973
+ sage: ip.ge(3,1)
974
+ True
975
+ sage: ip.ge(3,2)
976
+ True
977
+ sage: ip.ge(4,3)
978
+ False
979
+ sage: ip.ge(1,1)
980
+ True
981
+ """
982
+ return self._poset.ge(e1, e2)
983
+
984
+ def gt(self, e1, e2) -> bool:
985
+ r"""
986
+ Return whether ``e2`` strictly precedes ``e1`` in ``self``.
987
+
988
+ EXAMPLES::
989
+
990
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3)])
991
+ sage: ip.gt(2,1)
992
+ True
993
+ sage: ip.gt(3,1)
994
+ True
995
+ sage: ip.gt(3,2)
996
+ True
997
+ sage: ip.gt(4,3)
998
+ False
999
+ sage: ip.gt(1,1)
1000
+ False
1001
+ """
1002
+ return self._poset.gt(e1, e2)
1003
+
1004
+ def size(self) -> Integer:
1005
+ r"""
1006
+ Return the size (number of vertices) of the interval-poset.
1007
+
1008
+ EXAMPLES::
1009
+
1010
+ sage: TamariIntervalPoset(3,[(2,1),(3,1)]).size()
1011
+ 3
1012
+ """
1013
+ return self._size
1014
+
1015
+ @cached_method
1016
+ def cubical_coordinates(self) -> tuple[int, ...]:
1017
+ """
1018
+ Return the cubical coordinates of ``self``.
1019
+
1020
+ This provides a fast and natural way to order
1021
+ the set of interval-posets of a given size.
1022
+
1023
+ EXAMPLES::
1024
+
1025
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3)])
1026
+ sage: ip.cubical_coordinates()
1027
+ (-1, -2, 0)
1028
+
1029
+ TESTS::
1030
+
1031
+ sage: ip = TamariIntervalPoset(1,[])
1032
+ sage: ip.cubical_coordinates()
1033
+ ()
1034
+ sage: ip = TamariIntervalPoset(3,[])
1035
+ sage: ip.cubical_coordinates()
1036
+ (0, 0)
1037
+ sage: ip = TamariIntervalPosets(10).random_element() # needs sage.combinat
1038
+ sage: len(ip.cubical_coordinates()) # needs sage.combinat
1039
+ 9
1040
+ sage: sorted(ip.cubical_coordinates() for ip in TamariIntervalPosets(2)) # needs sage.combinat
1041
+ [(-1,), (0,), (1,)]
1042
+
1043
+ REFERENCES:
1044
+
1045
+ - [Com2019]_
1046
+ """
1047
+ tup = [0] * (self.size() - 1)
1048
+ for i, j in self._poset.relations_iterator(strict=True):
1049
+ if i < j:
1050
+ tup[j - 2] -= 1
1051
+ else:
1052
+ tup[j - 1] += 1
1053
+ return tuple(tup)
1054
+
1055
+ def complement(self) -> TIP:
1056
+ r"""
1057
+ Return the complement of the interval-poset ``self``.
1058
+
1059
+ If `P` is a Tamari interval-poset of size `n`, then the
1060
+ *complement* of `P` is defined as the interval-poset `Q` whose
1061
+ base set is `[n] = \{1, 2, \ldots, n\}` (just as for `P`), but
1062
+ whose order relation has `a` precede `b` if and only if
1063
+ `n + 1 - a` precedes `n + 1 - b` in `P`.
1064
+
1065
+ In terms of the Tamari lattice, the *complement* is the symmetric
1066
+ of ``self``. It is formed from the left-right symmetrized of
1067
+ the binary trees of the interval (switching left and right
1068
+ subtrees, see
1069
+ :meth:`~sage.combinat.binary_tree.BinaryTree.left_right_symmetry`).
1070
+ In particular, initial intervals are sent to final intervals and
1071
+ vice-versa.
1072
+
1073
+ EXAMPLES::
1074
+
1075
+ sage: TamariIntervalPoset(3, [(2, 1), (3, 1)]).complement()
1076
+ The Tamari interval of size 3 induced by relations [(1, 3), (2, 3)]
1077
+ sage: TamariIntervalPoset(0, []).complement()
1078
+ The Tamari interval of size 0 induced by relations []
1079
+ sage: ip = TamariIntervalPoset(4, [(1, 2), (2, 4), (3, 4)])
1080
+ sage: ip.complement() == TamariIntervalPoset(4, [(2, 1), (3, 1), (4, 3)])
1081
+ True
1082
+ sage: ip.lower_binary_tree() == ip.complement().upper_binary_tree().left_right_symmetry()
1083
+ True
1084
+ sage: ip.upper_binary_tree() == ip.complement().lower_binary_tree().left_right_symmetry()
1085
+ True
1086
+ sage: ip.is_initial_interval()
1087
+ True
1088
+ sage: ip.complement().is_final_interval()
1089
+ True
1090
+ """
1091
+ N = self._size + 1
1092
+ new_covers = [[N - i, N - j]
1093
+ for i, j in self._poset.cover_relations_iterator()]
1094
+ P = FinitePoset(DiGraph([list(range(1, N)), new_covers],
1095
+ format='vertices_and_edges')) # type:ignore
1096
+ return TamariIntervalPoset(P, check=False) # type:ignore
1097
+
1098
+ def left_branch_involution(self) -> TIP:
1099
+ """
1100
+ Return the image of ``self`` by the left-branch involution.
1101
+
1102
+ OUTPUT: an interval-poset
1103
+
1104
+ .. SEEALSO:: :meth:`rise_contact_involution`
1105
+
1106
+ EXAMPLES::
1107
+
1108
+ sage: tip = TamariIntervalPoset(8, [(1,2), (2,4), (3,4), (6,7), (3,2), (5,4), (6,4), (8,7)])
1109
+ sage: t = tip.left_branch_involution(); t
1110
+ The Tamari interval of size 8 induced by relations [(1, 6), (2, 6),
1111
+ (3, 5), (4, 5), (5, 6), (6, 8), (7, 8), (7, 6), (4, 3), (3, 1),
1112
+ (2, 1)]
1113
+ sage: t.left_branch_involution() == tip
1114
+ True
1115
+
1116
+ REFERENCES:
1117
+
1118
+ - [Pons2018]_
1119
+ """
1120
+ gt = self.grafting_tree().left_border_symmetry()
1121
+ return TamariIntervalPosets.from_grafting_tree(gt)
1122
+
1123
+ def rise_contact_involution(self) -> TIP:
1124
+ """
1125
+ Return the image of ``self`` by the rise-contact involution.
1126
+
1127
+ OUTPUT: an interval-poset
1128
+
1129
+ This is defined by conjugating the complement involution
1130
+ by the left-branch involution.
1131
+
1132
+ .. SEEALSO:: :meth:`left_branch_involution`, :meth:`complement`
1133
+
1134
+ EXAMPLES::
1135
+
1136
+ sage: tip = TamariIntervalPoset(8, [(1,2), (2,4), (3,4), (6,7),
1137
+ ....: (3,2), (5,4), (6,4), (8,7)])
1138
+ sage: t = tip.rise_contact_involution(); t
1139
+ The Tamari interval of size 8 induced by relations [(2, 8), (3, 8),
1140
+ (4, 5), (5, 7), (6, 7), (7, 8), (8, 1), (7, 2), (6, 2), (5, 3),
1141
+ (4, 3), (3, 2), (2, 1)]
1142
+ sage: t.rise_contact_involution() == tip
1143
+ True
1144
+ sage: (tip.lower_dyck_word().number_of_touch_points() # needs sage.combinat
1145
+ ....: == t.upper_dyck_word().number_of_initial_rises())
1146
+ True
1147
+ sage: tip.number_of_tamari_inversions() == t.number_of_tamari_inversions()
1148
+ True
1149
+
1150
+ REFERENCES:
1151
+
1152
+ - [Pons2018]_
1153
+ """
1154
+ t = self.left_branch_involution().complement()
1155
+ return t.left_branch_involution()
1156
+
1157
+ def insertion(self, i) -> TIP:
1158
+ r"""
1159
+ Return the Tamari insertion of an integer `i` into the
1160
+ interval-poset ``self``.
1161
+
1162
+ If `P` is a Tamari interval-poset of size `n` and `i` is an
1163
+ integer with `1 \leq i \leq n+1`, then the Tamari insertion of
1164
+ `i` into `P` is defined as the Tamari interval-poset of size
1165
+ `n+1` which corresponds to the interval `[C_1, C_2]` on the
1166
+ Tamari lattice, where the binary trees `C_1` and `C_2` are
1167
+ defined as follows: We write the interval-poset `P` as
1168
+ `[B_1, B_2]` for two binary trees `B_1` and `B_2`. We label
1169
+ the vertices of each of these two trees with the integers
1170
+ `1, 2, \ldots, i-1, i+1, i+2, \ldots, n+1` in such a way that
1171
+ the trees are binary search trees (this labelling is unique).
1172
+ Then, we insert `i` into each of these trees (in the way as
1173
+ explained in
1174
+ :meth:`~sage.combinat.binary_tree.LabelledBinaryTree.binary_search_insert`).
1175
+ The shapes of the resulting two trees are denoted `C_1` and
1176
+ `C_2`.
1177
+
1178
+ An alternative way to construct the insertion of `i` into
1179
+ `P` is by relabeling each vertex `u` of `P` satisfying
1180
+ `u \geq i` (as integers) as `u+1`, and then adding a vertex
1181
+ `i` which should precede `i-1` and `i+1`.
1182
+
1183
+ .. TODO::
1184
+
1185
+ To study this, it would be more natural to define
1186
+ interval-posets on arbitrary ordered sets rather than just
1187
+ on `\{1, 2, \ldots, n\}`.
1188
+
1189
+ EXAMPLES::
1190
+
1191
+ sage: ip = TamariIntervalPoset(4, [(2, 3), (4, 3)]); ip
1192
+ The Tamari interval of size 4 induced by relations [(2, 3), (4, 3)]
1193
+ sage: ip.insertion(1)
1194
+ The Tamari interval of size 5 induced by relations [(1, 2), (3, 4), (5, 4)]
1195
+ sage: ip.insertion(2)
1196
+ The Tamari interval of size 5 induced by relations [(2, 3), (3, 4), (5, 4), (2, 1)]
1197
+ sage: ip.insertion(3)
1198
+ The Tamari interval of size 5 induced by relations [(2, 4), (3, 4), (5, 4), (3, 2)]
1199
+ sage: ip.insertion(4)
1200
+ The Tamari interval of size 5 induced by relations [(2, 3), (4, 5), (5, 3), (4, 3)]
1201
+ sage: ip.insertion(5)
1202
+ The Tamari interval of size 5 induced by relations [(2, 3), (5, 4), (4, 3)]
1203
+
1204
+ sage: ip = TamariIntervalPoset(0, [])
1205
+ sage: ip.insertion(1)
1206
+ The Tamari interval of size 1 induced by relations []
1207
+
1208
+ sage: ip = TamariIntervalPoset(1, [])
1209
+ sage: ip.insertion(1)
1210
+ The Tamari interval of size 2 induced by relations [(1, 2)]
1211
+ sage: ip.insertion(2)
1212
+ The Tamari interval of size 2 induced by relations [(2, 1)]
1213
+
1214
+ TESTS:
1215
+
1216
+ Verifying that the two ways of computing insertion are
1217
+ equivalent::
1218
+
1219
+ sage: def insert_alternative(T, i):
1220
+ ....: # Just another way to compute the insertion of i into T.
1221
+ ....: from sage.combinat.binary_tree import LabelledBinaryTree
1222
+ ....: B1 = T.lower_binary_tree().canonical_labelling()
1223
+ ....: B2 = T.upper_binary_tree().canonical_labelling()
1224
+ ....: C1 = B1.binary_search_insert(i)
1225
+ ....: C2 = B2.binary_search_insert(i)
1226
+ ....: return TamariIntervalPosets.from_binary_trees(C1, C2)
1227
+
1228
+ We should have relabelled the trees to "make space" for a label i,
1229
+ but we did not, because it does not make a difference: The
1230
+ binary search insertion will go precisely the same, because
1231
+ an integer equal to the label of the root gets sent onto
1232
+ the left branch.
1233
+
1234
+ sage: def test_equivalence(n):
1235
+ ....: for T in TamariIntervalPosets(n):
1236
+ ....: for i in range(1, n + 2):
1237
+ ....: if insert_alternative(T, i) != T.insertion(i):
1238
+ ....: print(T, i)
1239
+ ....: return False
1240
+ ....: return True
1241
+ sage: test_equivalence(3) # needs sage.combinat
1242
+ True
1243
+
1244
+ sage: ti = TamariIntervalPosets(3).an_element()
1245
+ sage: ti.insertion(6)
1246
+ Traceback (most recent call last):
1247
+ ...
1248
+ ValueError: integer to be inserted not in the appropriate interval
1249
+ """
1250
+ n = self._size
1251
+ if not 0 < i <= n + 1:
1252
+ raise ValueError("integer to be inserted not "
1253
+ "in the appropriate interval")
1254
+
1255
+ def add1(u):
1256
+ if u >= i:
1257
+ return u + 1
1258
+ return u
1259
+ rels = [(add1(a), add1(b))
1260
+ for (a, b) in self.decreasing_cover_relations()]
1261
+ rels += [(add1(a), add1(b))
1262
+ for (a, b) in self.increasing_cover_relations()]
1263
+ rels += [(k, k - 1) for k in [i] if i > 1]
1264
+ rels += [(k, k + 1) for k in [i] if i <= n]
1265
+ return TamariIntervalPoset(n + 1, rels)
1266
+
1267
+ def _repr_(self) -> str:
1268
+ r"""
1269
+ Return a string representation of ``self``.
1270
+
1271
+ TESTS::
1272
+
1273
+ sage: TamariIntervalPoset(3,[(2,1),(3,1)])
1274
+ The Tamari interval of size 3 induced by relations [(3, 1), (2, 1)]
1275
+ sage: TamariIntervalPoset(3,[(3,1),(2,1)])
1276
+ The Tamari interval of size 3 induced by relations [(3, 1), (2, 1)]
1277
+ sage: TamariIntervalPoset(3,[(2,3),(2,1)])
1278
+ The Tamari interval of size 3 induced by relations [(2, 3), (2, 1)]
1279
+ """
1280
+ msg = "The Tamari interval of size {} induced by relations {}"
1281
+ return msg.format(self.size(),
1282
+ self.increasing_cover_relations() +
1283
+ self.decreasing_cover_relations())
1284
+
1285
+ def _ascii_art_(self):
1286
+ """
1287
+ Return an ascii art picture of ``self``.
1288
+
1289
+ This is a picture of the Hasse diagram. Vertices from `1` to `n`
1290
+ are placed on the diagonal from top-left to bottom-right.
1291
+ Then increasing covers are drawn above the diagonal
1292
+ and decreasing covers are drawn below the diagonal.
1293
+
1294
+ EXAMPLES::
1295
+
1296
+ sage: T = TamariIntervalPosets(5)[56]
1297
+ sage: ascii_art(T)
1298
+ O-----------+
1299
+ O--------+
1300
+ +--O--+ |
1301
+ O--+
1302
+ O
1303
+ sage: T.poset().cover_relations()
1304
+ [[3, 4], [3, 2], [4, 5], [2, 5], [1, 5]]
1305
+ """
1306
+ n = self.size()
1307
+ M = [[' O ' if i == j else ' ' for i in range(n)] for j in range(n)]
1308
+
1309
+ def superpose(x, y, b):
1310
+ # put symbol b at position x, y
1311
+ # on top of existing symbols there
1312
+ i = x - 1
1313
+ j = y - 1
1314
+ a = M[i][j]
1315
+ if a == ' ':
1316
+ M[i][j] = b
1317
+ elif a == '-+ ':
1318
+ if b == a:
1319
+ pass
1320
+ elif b == '---':
1321
+ M[i][j] = '-+-'
1322
+ elif b == ' | ':
1323
+ M[i][j] = '-+ '
1324
+ elif a == ' +-':
1325
+ if b == a:
1326
+ pass
1327
+ elif b == '---':
1328
+ M[i][j] = '-+-'
1329
+ elif b == ' | ':
1330
+ M[i][j] = ' +-'
1331
+ elif a == '---':
1332
+ if b == a:
1333
+ pass
1334
+ elif b == '-+ ':
1335
+ M[i][j] = '-+-'
1336
+ elif b == ' +-':
1337
+ M[i][j] = '-+-'
1338
+ elif a == ' | ':
1339
+ if b == a:
1340
+ pass
1341
+ elif b == '-+ ':
1342
+ M[i][j] = '-+ '
1343
+ elif b == ' +-':
1344
+ M[i][j] = ' +-'
1345
+
1346
+ def superpose_node(i, right=True):
1347
+ i -= 1 # for indexing
1348
+ if M[i][i] == ' O ':
1349
+ if right:
1350
+ M[i][i] = ' O-'
1351
+ else:
1352
+ M[i][i] = '-O '
1353
+ elif M[i][i] == ' O-' and not right:
1354
+ M[i][i] = '-O-'
1355
+ elif M[i][i] == '-O ' and right:
1356
+ M[i][i] = '-O-'
1357
+
1358
+ for i, j in self.poset().hasse_diagram().edges(sort=True, labels=False):
1359
+ if i > j:
1360
+ superpose_node(i, False)
1361
+ superpose(i, j, ' +-')
1362
+ for k in range(j + 1, i):
1363
+ superpose(k, j, ' | ')
1364
+ superpose(i, k, '---')
1365
+ else:
1366
+ superpose_node(i, True)
1367
+ superpose(i, j, '-+ ')
1368
+ for k in range(i + 1, j):
1369
+ superpose(i, k, '---')
1370
+ superpose(k, j, ' | ')
1371
+
1372
+ from sage.typeset.ascii_art import AsciiArt
1373
+ return AsciiArt([''.join(ligne) for ligne in M])
1374
+
1375
+ def _unicode_art_(self):
1376
+ """
1377
+ Return an unicode picture of ``self``.
1378
+
1379
+ This is a picture of the Hasse diagram. Vertices from `1` to `n` are
1380
+ placed on the diagonal from top-left to bottom-right.
1381
+ Then increasing covers are drawn above the diagonal
1382
+ and decreasing covers are drawn below the diagonal.
1383
+
1384
+ EXAMPLES::
1385
+
1386
+ sage: T = TamariIntervalPosets(5)[56]
1387
+ sage: unicode_art(T)
1388
+ o───╮
1389
+ o──┤
1390
+ ╰o╮│
1391
+ o┤
1392
+ o
1393
+ sage: T.poset().cover_relations()
1394
+ [[3, 4], [3, 2], [4, 5], [2, 5], [1, 5]]
1395
+ """
1396
+ n = self.size()
1397
+ M = [['o' if i == j else ' ' for i in range(n)] for j in range(n)]
1398
+
1399
+ def superpose(x, y, b):
1400
+ # put symbol b at position x, y
1401
+ # on top of existing symbols there
1402
+ i = x - 1
1403
+ j = y - 1
1404
+ a = M[i][j]
1405
+ if a == ' ':
1406
+ M[i][j] = b
1407
+ elif a == '╮':
1408
+ if b == a:
1409
+ pass
1410
+ elif b == '─':
1411
+ M[i][j] = '┬'
1412
+ elif b == '│':
1413
+ M[i][j] = '┤'
1414
+ elif a == '╰':
1415
+ if b == a:
1416
+ pass
1417
+ elif b == '─':
1418
+ M[i][j] = '┴'
1419
+ elif b == '│':
1420
+ M[i][j] = '├'
1421
+ elif a == '─':
1422
+ if b == a:
1423
+ pass
1424
+ elif b == '╮':
1425
+ M[i][j] = '┬'
1426
+ elif b == '╰':
1427
+ M[i][j] = '┴'
1428
+ elif a == '│':
1429
+ if b == a:
1430
+ pass
1431
+ elif b == '╮':
1432
+ M[i][j] = '┤'
1433
+ elif b == '╰':
1434
+ M[i][j] = '├'
1435
+
1436
+ for i, j in self.poset().hasse_diagram().edges(sort=True, labels=False):
1437
+ if i > j:
1438
+ superpose(i, j, '╰')
1439
+ for k in range(j + 1, i):
1440
+ superpose(k, j, '│')
1441
+ superpose(i, k, '─')
1442
+ else:
1443
+ superpose(i, j, '╮')
1444
+ for k in range(i + 1, j):
1445
+ superpose(i, k, '─')
1446
+ superpose(k, j, '│')
1447
+
1448
+ from sage.typeset.unicode_art import UnicodeArt
1449
+ return UnicodeArt([''.join(ligne) for ligne in M])
1450
+
1451
+ def _richcmp_(self, other, op) -> bool:
1452
+ r"""
1453
+ Comparison.
1454
+
1455
+ The comparison is first by size, then
1456
+ using cubical coordinates.
1457
+
1458
+ .. SEEALSO:: :meth:`cubical_coordinates`
1459
+
1460
+ TESTS::
1461
+
1462
+ sage: TamariIntervalPoset(0,[]) == TamariIntervalPoset(0,[])
1463
+ True
1464
+ sage: TamariIntervalPoset(1,[]) == TamariIntervalPoset(0,[])
1465
+ False
1466
+ sage: TamariIntervalPoset(3,[(1,2),(3,2)]) == TamariIntervalPoset(3,[(3,2),(1,2)])
1467
+ True
1468
+ sage: TamariIntervalPoset(3,[(1,2),(3,2)]) == TamariIntervalPoset(3,[(1,2)])
1469
+ False
1470
+ sage: TamariIntervalPoset(3,[(1,2),(3,2)]) != TamariIntervalPoset(3,[(3,2),(1,2)])
1471
+ False
1472
+
1473
+ sage: ip1 = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
1474
+ sage: ip2 = TamariIntervalPoset(4,[(1,2),(2,3)])
1475
+ sage: ip1 <= ip2
1476
+ False
1477
+ sage: ip1 <= ip1
1478
+ True
1479
+ sage: ip2 <= ip1
1480
+ True
1481
+
1482
+ sage: ip1 != 33
1483
+ True
1484
+ """
1485
+ if not isinstance(other, TamariIntervalPoset):
1486
+ return NotImplemented
1487
+ if op == op_EQ:
1488
+ return (self.size() == other.size() and
1489
+ self._cover_relations == other._cover_relations)
1490
+ if op == op_NE:
1491
+ return not (self.size() == other.size() and
1492
+ self._cover_relations == other._cover_relations)
1493
+ return richcmp((self.size(), self.cubical_coordinates()),
1494
+ (other.size(), other.cubical_coordinates()), op)
1495
+
1496
+ def __iter__(self) -> Iterator[int]:
1497
+ r"""
1498
+ Iterate through the vertices of ``self``.
1499
+
1500
+ EXAMPLES::
1501
+
1502
+ sage: ip = TamariIntervalPoset(4,[(1,2),(3,2)])
1503
+ sage: [i for i in ip]
1504
+ [1, 2, 3, 4]
1505
+ """
1506
+ return iter(range(1, self.size() + 1))
1507
+
1508
+ def contains_interval(self, other) -> bool:
1509
+ r"""
1510
+ Return whether the interval represented by ``other`` is contained
1511
+ in ``self`` as an interval of the Tamari lattice.
1512
+
1513
+ In terms of interval-posets, it means that all relations of ``self``
1514
+ are relations of ``other``.
1515
+
1516
+ INPUT:
1517
+
1518
+ - ``other`` -- an interval-poset
1519
+
1520
+ EXAMPLES::
1521
+
1522
+ sage: ip1 = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
1523
+ sage: ip2 = TamariIntervalPoset(4,[(2,3)])
1524
+ sage: ip2.contains_interval(ip1)
1525
+ True
1526
+ sage: ip3 = TamariIntervalPoset(4,[(2,1)])
1527
+ sage: ip2.contains_interval(ip3)
1528
+ False
1529
+ sage: ip4 = TamariIntervalPoset(3,[(2,3)])
1530
+ sage: ip2.contains_interval(ip4)
1531
+ False
1532
+ """
1533
+ if other.size() != self.size():
1534
+ return False
1535
+ return all(other.le(i, j) for (i, j) in self._cover_relations)
1536
+
1537
+ def lower_contains_interval(self, other) -> bool:
1538
+ r"""
1539
+ Return whether the interval represented by ``other`` is contained
1540
+ in ``self`` as an interval of the Tamari lattice and if they share
1541
+ the same lower bound.
1542
+
1543
+ As interval-posets, it means that ``other`` contains the relations
1544
+ of ``self`` plus some extra increasing relations.
1545
+
1546
+ INPUT:
1547
+
1548
+ - ``other`` -- an interval-poset
1549
+
1550
+ EXAMPLES::
1551
+
1552
+ sage: ip1 = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
1553
+ sage: ip2 = TamariIntervalPoset(4,[(4,3)])
1554
+ sage: ip2.lower_contains_interval(ip1)
1555
+ True
1556
+ sage: ip2.contains_interval(ip1) and ip2.lower_binary_tree() == ip1.lower_binary_tree()
1557
+ True
1558
+ sage: ip3 = TamariIntervalPoset(4,[(4,3),(2,1)])
1559
+ sage: ip2.contains_interval(ip3)
1560
+ True
1561
+ sage: ip2.lower_binary_tree() == ip3.lower_binary_tree()
1562
+ False
1563
+ sage: ip2.lower_contains_interval(ip3)
1564
+ False
1565
+
1566
+ TESTS::
1567
+
1568
+ sage: ip1 = TamariIntervalPoset(3,[(1,2),(2,3)])
1569
+ sage: ip2 = TamariIntervalPoset(3,[(2,1),(3,2)])
1570
+ sage: ip2.lower_contains_interval(ip1)
1571
+ False
1572
+ """
1573
+ if not self.contains_interval(other):
1574
+ return False
1575
+ return all(self.le(i, j)
1576
+ for (i, j) in other.decreasing_cover_relations())
1577
+
1578
+ def upper_contains_interval(self, other) -> bool:
1579
+ r"""
1580
+ Return whether the interval represented by ``other`` is contained
1581
+ in ``self`` as an interval of the Tamari lattice and if they share
1582
+ the same upper bound.
1583
+
1584
+ As interval-posets, it means that ``other`` contains the relations
1585
+ of ``self`` plus some extra decreasing relations.
1586
+
1587
+ INPUT:
1588
+
1589
+ - ``other`` -- an interval-poset
1590
+
1591
+ EXAMPLES::
1592
+
1593
+ sage: ip1 = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
1594
+ sage: ip2 = TamariIntervalPoset(4,[(1,2),(2,3)])
1595
+ sage: ip2.upper_contains_interval(ip1)
1596
+ True
1597
+ sage: ip2.contains_interval(ip1) and ip2.upper_binary_tree() == ip1.upper_binary_tree()
1598
+ True
1599
+ sage: ip3 = TamariIntervalPoset(4,[(1,2),(2,3),(3,4)])
1600
+ sage: ip2.upper_contains_interval(ip3)
1601
+ False
1602
+ sage: ip2.contains_interval(ip3)
1603
+ True
1604
+ sage: ip2.upper_binary_tree() == ip3.upper_binary_tree()
1605
+ False
1606
+
1607
+ TESTS::
1608
+
1609
+ sage: ip1 = TamariIntervalPoset(3,[(1,2),(2,3)])
1610
+ sage: ip2 = TamariIntervalPoset(3,[(2,1),(3,2)])
1611
+ sage: ip2.lower_contains_interval(ip1)
1612
+ False
1613
+ """
1614
+ if not self.contains_interval(other):
1615
+ return False
1616
+ return all(self.le(i, j)
1617
+ for (i, j) in other.increasing_cover_relations())
1618
+
1619
+ def is_linear_extension(self, perm) -> bool:
1620
+ r"""
1621
+ Return whether the permutation ``perm`` is a linear extension
1622
+ of ``self``.
1623
+
1624
+ INPUT:
1625
+
1626
+ - ``perm`` -- a permutation of the size of ``self``
1627
+
1628
+ EXAMPLES::
1629
+
1630
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
1631
+ sage: ip.is_linear_extension([1,4,2,3])
1632
+ True
1633
+ sage: ip.is_linear_extension(Permutation([1,4,2,3]))
1634
+ True
1635
+ sage: ip.is_linear_extension(Permutation([1,4,3,2]))
1636
+ False
1637
+ """
1638
+ return self._poset.is_linear_extension(perm)
1639
+
1640
+ def contains_binary_tree(self, binary_tree) -> bool:
1641
+ r"""
1642
+ Return whether the interval represented by ``self`` contains
1643
+ the binary tree ``binary_tree``.
1644
+
1645
+ INPUT:
1646
+
1647
+ - ``binary_tree`` -- a binary tree
1648
+
1649
+ .. SEEALSO:: :meth:`contains_dyck_word`
1650
+
1651
+ EXAMPLES::
1652
+
1653
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
1654
+ sage: ip.contains_binary_tree(BinaryTree([[None,[None,[]]],None]))
1655
+ True
1656
+ sage: ip.contains_binary_tree(BinaryTree([None,[[[],None],None]]))
1657
+ True
1658
+ sage: ip.contains_binary_tree(BinaryTree([[],[[],None]]))
1659
+ False
1660
+ sage: ip.contains_binary_tree(ip.lower_binary_tree())
1661
+ True
1662
+ sage: ip.contains_binary_tree(ip.upper_binary_tree())
1663
+ True
1664
+ sage: all(ip.contains_binary_tree(bt) for bt in ip.binary_trees())
1665
+ True
1666
+ """
1667
+ return self.is_linear_extension(binary_tree.to_132_avoiding_permutation())
1668
+
1669
+ def contains_dyck_word(self, dyck_word) -> bool:
1670
+ r"""
1671
+ Return whether the interval represented by ``self`` contains
1672
+ the Dyck word ``dyck_word``.
1673
+
1674
+ INPUT:
1675
+
1676
+ - ``dyck_word`` -- a Dyck word
1677
+
1678
+ .. SEEALSO:: :meth:`contains_binary_tree`
1679
+
1680
+ EXAMPLES::
1681
+
1682
+ sage: # needs sage.combinat
1683
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
1684
+ sage: ip.contains_dyck_word(DyckWord([1,1,1,0,0,0,1,0]))
1685
+ True
1686
+ sage: ip.contains_dyck_word(DyckWord([1,1,0,1,0,1,0,0]))
1687
+ True
1688
+ sage: ip.contains_dyck_word(DyckWord([1,0,1,1,0,1,0,0]))
1689
+ False
1690
+ sage: ip.contains_dyck_word(ip.lower_dyck_word())
1691
+ True
1692
+ sage: ip.contains_dyck_word(ip.upper_dyck_word())
1693
+ True
1694
+ sage: all(ip.contains_dyck_word(bt) for bt in ip.dyck_words())
1695
+ True
1696
+ """
1697
+ return self.contains_binary_tree(dyck_word.to_binary_tree_tamari())
1698
+
1699
+ def intersection(self, other: TIP) -> TIP:
1700
+ r"""
1701
+ Return the interval-poset formed by combining the relations from
1702
+ both ``self`` and ``other``. It corresponds to the intersection
1703
+ of the two corresponding intervals of the Tamari lattice.
1704
+
1705
+ INPUT:
1706
+
1707
+ - ``other`` -- an interval-poset of the same size as ``self``
1708
+
1709
+ EXAMPLES::
1710
+
1711
+ sage: ip1 = TamariIntervalPoset(4,[(1,2),(2,3)])
1712
+ sage: ip2 = TamariIntervalPoset(4,[(4,3)])
1713
+ sage: ip1.intersection(ip2)
1714
+ The Tamari interval of size 4 induced by relations [(1, 2), (2, 3), (4, 3)]
1715
+ sage: ip3 = TamariIntervalPoset(4,[(2,1)])
1716
+ sage: ip1.intersection(ip3)
1717
+ Traceback (most recent call last):
1718
+ ...
1719
+ ValueError: this intersection is empty, it does not correspond to an interval-poset
1720
+ sage: ip4 = TamariIntervalPoset(3,[(2,3)])
1721
+ sage: ip2.intersection(ip4)
1722
+ Traceback (most recent call last):
1723
+ ...
1724
+ ValueError: intersections are only possible on interval-posets of the same size
1725
+ """
1726
+ if other.size() != self.size():
1727
+ raise ValueError("intersections are only possible on interval-posets of the same size")
1728
+ try:
1729
+ return TamariIntervalPoset(self.size(), self._cover_relations + other._cover_relations)
1730
+ except ValueError:
1731
+ raise ValueError("this intersection is empty, it does not correspond to an interval-poset")
1732
+
1733
+ def initial_forest(self) -> TIP:
1734
+ r"""
1735
+ Return the initial forest of ``self``, i.e., the interval-poset
1736
+ formed from only the increasing relations of ``self``.
1737
+
1738
+ .. SEEALSO:: :meth:`final_forest`
1739
+
1740
+ EXAMPLES::
1741
+
1742
+ sage: TamariIntervalPoset(4,[(1,2),(3,2),(2,4),(3,4)]).initial_forest()
1743
+ The Tamari interval of size 4 induced by relations [(1, 2), (2, 4), (3, 4)]
1744
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3)])
1745
+ sage: ip.initial_forest() == ip
1746
+ True
1747
+ """
1748
+ relations = self.increasing_cover_relations()
1749
+ P = FinitePoset(DiGraph([list(range(1, self._size + 1)), relations],
1750
+ format='vertices_and_edges')) # type:ignore
1751
+ return TamariIntervalPoset(P, check=False) # type:ignore
1752
+
1753
+ def final_forest(self) -> TIP:
1754
+ r"""
1755
+ Return the final forest of ``self``, i.e., the interval-poset
1756
+ formed with only the decreasing relations of ``self``.
1757
+
1758
+ .. SEEALSO:: :meth:`initial_forest`
1759
+
1760
+ EXAMPLES::
1761
+
1762
+ sage: TamariIntervalPoset(4,[(2,1),(3,2),(3,4),(4,2)]).final_forest()
1763
+ The Tamari interval of size 4 induced by relations [(4, 2), (3, 2), (2, 1)]
1764
+ sage: ip = TamariIntervalPoset(3,[(2,1),(3,1)])
1765
+ sage: ip.final_forest() == ip
1766
+ True
1767
+ """
1768
+ relations = self.decreasing_cover_relations()
1769
+ P = FinitePoset(DiGraph([list(range(1, self._size + 1)), relations],
1770
+ format='vertices_and_edges')) # type:ignore
1771
+ return TamariIntervalPoset(P, check=False) # type:ignore
1772
+
1773
+ def is_initial_interval(self) -> bool:
1774
+ r"""
1775
+ Return if ``self`` corresponds to an initial interval of the Tamari
1776
+ lattice.
1777
+
1778
+ This means that its lower end is the smallest element of the lattice.
1779
+ It consists of checking that ``self`` does not contain any decreasing
1780
+ relations.
1781
+
1782
+ .. SEEALSO:: :meth:`is_final_interval`
1783
+
1784
+ EXAMPLES::
1785
+
1786
+ sage: ip = TamariIntervalPoset(4, [(1, 2), (2, 4), (3, 4)])
1787
+ sage: ip.is_initial_interval()
1788
+ True
1789
+ sage: ip.lower_dyck_word() # needs sage.combinat
1790
+ [1, 0, 1, 0, 1, 0, 1, 0]
1791
+ sage: ip = TamariIntervalPoset(4, [(1, 2), (2, 4), (3, 4), (3, 2)])
1792
+ sage: ip.is_initial_interval()
1793
+ False
1794
+ sage: ip.lower_dyck_word() # needs sage.combinat
1795
+ [1, 0, 1, 1, 0, 0, 1, 0]
1796
+ sage: all(DyckWord([1,0,1,0,1,0]).tamari_interval(dw) # needs sage.combinat
1797
+ ....: .is_initial_interval()
1798
+ ....: for dw in DyckWords(3))
1799
+ True
1800
+ """
1801
+ return not self.decreasing_cover_relations()
1802
+
1803
+ def is_final_interval(self) -> bool:
1804
+ r"""
1805
+ Return if ``self`` corresponds to a final interval of the Tamari
1806
+ lattice.
1807
+
1808
+ This means that its upper end is the largest element of the lattice.
1809
+ It consists of checking that ``self`` does not contain any increasing
1810
+ relations.
1811
+
1812
+ .. SEEALSO:: :meth:`is_initial_interval`
1813
+
1814
+ EXAMPLES::
1815
+
1816
+ sage: ip = TamariIntervalPoset(4, [(4, 3), (3, 1), (2, 1)])
1817
+ sage: ip.is_final_interval()
1818
+ True
1819
+ sage: ip.upper_dyck_word() # needs sage.combinat
1820
+ [1, 1, 1, 1, 0, 0, 0, 0]
1821
+ sage: ip = TamariIntervalPoset(4, [(4, 3), (3, 1), (2, 1), (2, 3)])
1822
+ sage: ip.is_final_interval()
1823
+ False
1824
+ sage: ip.upper_dyck_word() # needs sage.combinat
1825
+ [1, 1, 0, 1, 1, 0, 0, 0]
1826
+ sage: all(dw.tamari_interval(DyckWord([1, 1, 1, 0, 0, 0])) # needs sage.combinat
1827
+ ....: .is_final_interval()
1828
+ ....: for dw in DyckWords(3))
1829
+ True
1830
+ """
1831
+ return not self.increasing_cover_relations()
1832
+
1833
+ def lower_binary_tree(self):
1834
+ r"""
1835
+ Return the lowest binary tree in the interval of the Tamari
1836
+ lattice represented by ``self``.
1837
+
1838
+ This is a binary tree. It is the shape of the unique binary
1839
+ search tree whose left-branch ordered forest (i.e., the result
1840
+ of applying
1841
+ :meth:`~sage.combinat.binary_tree.BinaryTree.to_ordered_tree_left_branch`
1842
+ and cutting off the root) is the final forest of ``self``.
1843
+
1844
+ .. SEEALSO:: :meth:`lower_dyck_word`
1845
+
1846
+ EXAMPLES::
1847
+
1848
+ sage: ip = TamariIntervalPoset(6, [(3,2),(4,3),(5,2),(6,5),(1,2),(4,5)]); ip
1849
+ The Tamari interval of size 6 induced by relations
1850
+ [(1, 2), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
1851
+ sage: ip.lower_binary_tree()
1852
+ [[., .], [[., [., .]], [., .]]]
1853
+ sage: ff = TamariIntervalPosets.final_forest(ip.lower_binary_tree())
1854
+ sage: ff == ip.final_forest()
1855
+ True
1856
+ sage: ip == TamariIntervalPosets.from_binary_trees(ip.lower_binary_tree(),
1857
+ ....: ip.upper_binary_tree())
1858
+ True
1859
+ """
1860
+ return self.min_linear_extension().binary_search_tree_shape(left_to_right=False)
1861
+
1862
+ def lower_dyck_word(self):
1863
+ r"""
1864
+ Return the lowest Dyck word in the interval of the Tamari lattice
1865
+ represented by ``self``.
1866
+
1867
+ .. SEEALSO:: :meth:`lower_binary_tree`
1868
+
1869
+ EXAMPLES::
1870
+
1871
+ sage: # needs sage.combinat
1872
+ sage: ip = TamariIntervalPoset(6, [(3,2),(4,3),(5,2),(6,5),(1,2),(4,5)]); ip
1873
+ The Tamari interval of size 6 induced by relations
1874
+ [(1, 2), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
1875
+ sage: ip.lower_dyck_word()
1876
+ [1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
1877
+ sage: ldw_ff = TamariIntervalPosets.final_forest(ip.lower_dyck_word())
1878
+ sage: ldw_ff == ip.final_forest()
1879
+ True
1880
+ sage: ip == TamariIntervalPosets.from_dyck_words(ip.lower_dyck_word(),
1881
+ ....: ip.upper_dyck_word())
1882
+ True
1883
+ """
1884
+ return self.lower_binary_tree().to_dyck_word_tamari()
1885
+
1886
+ def upper_binary_tree(self):
1887
+ r"""
1888
+ Return the highest binary tree in the interval of the Tamari
1889
+ lattice represented by ``self``.
1890
+
1891
+ This is a binary tree. It is the shape of the unique binary
1892
+ search tree whose right-branch ordered forest (i.e., the result
1893
+ of applying
1894
+ :meth:`~sage.combinat.binary_tree.BinaryTree.to_ordered_tree_right_branch`
1895
+ and cutting off the root) is the initial forest of ``self``.
1896
+
1897
+ .. SEEALSO:: :meth:`upper_dyck_word`
1898
+
1899
+ EXAMPLES::
1900
+
1901
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(4,5)]); ip
1902
+ The Tamari interval of size 6 induced by relations
1903
+ [(1, 2), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
1904
+ sage: ip.upper_binary_tree()
1905
+ [[., .], [., [[., .], [., .]]]]
1906
+
1907
+ sage: TamariIntervalPosets.initial_forest(ip.upper_binary_tree()) == ip.initial_forest()
1908
+ True
1909
+ sage: ip == TamariIntervalPosets.from_binary_trees(ip.lower_binary_tree(),ip.upper_binary_tree())
1910
+ True
1911
+ """
1912
+ return self.max_linear_extension().binary_search_tree_shape(left_to_right=False)
1913
+
1914
+ def upper_dyck_word(self):
1915
+ r"""
1916
+ Return the highest Dyck word in the interval of the Tamari lattice
1917
+ represented by ``self``.
1918
+
1919
+ .. SEEALSO:: :meth:`upper_binary_tree`
1920
+
1921
+ EXAMPLES::
1922
+
1923
+ sage: # needs sage.combinat
1924
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(4,5)]); ip
1925
+ The Tamari interval of size 6 induced by relations
1926
+ [(1, 2), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
1927
+ sage: ip.upper_dyck_word()
1928
+ [1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0]
1929
+ sage: udw_if = TamariIntervalPosets.initial_forest(ip.upper_dyck_word())
1930
+ sage: udw_if == ip.initial_forest()
1931
+ True
1932
+ sage: ip == TamariIntervalPosets.from_dyck_words(ip.lower_dyck_word(),
1933
+ ....: ip.upper_dyck_word())
1934
+ True
1935
+ """
1936
+ return self.upper_binary_tree().to_dyck_word_tamari()
1937
+
1938
+ def subposet(self, start, end) -> TIP:
1939
+ r"""
1940
+ Return the renormalized subposet of ``self`` consisting solely
1941
+ of integers from ``start`` (inclusive) to ``end`` (not inclusive).
1942
+
1943
+ "Renormalized" means that these integers are relabelled
1944
+ `1,2,\ldots,k` in the obvious way (i.e., by subtracting
1945
+ ``start - 1``).
1946
+
1947
+ INPUT:
1948
+
1949
+ - ``start`` -- integer; the starting vertex (inclusive)
1950
+ - ``end`` -- integer; the ending vertex (not inclusive)
1951
+
1952
+ EXAMPLES::
1953
+
1954
+ sage: ip = TamariIntervalPoset(6, [(3,2),(4,3),(5,2),(6,5),(1,2),(3,5),(4,5)]); ip
1955
+ The Tamari interval of size 6 induced by relations
1956
+ [(1, 2), (3, 5), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
1957
+ sage: ip.subposet(1,3)
1958
+ The Tamari interval of size 2 induced by relations [(1, 2)]
1959
+ sage: ip.subposet(1,4)
1960
+ The Tamari interval of size 3 induced by relations [(1, 2), (3, 2)]
1961
+ sage: ip.subposet(1,5)
1962
+ The Tamari interval of size 4 induced by relations [(1, 2), (4, 3), (3, 2)]
1963
+ sage: ip.subposet(1,7) == ip
1964
+ True
1965
+ sage: ip.subposet(1,1)
1966
+ The Tamari interval of size 0 induced by relations []
1967
+
1968
+ TESTS::
1969
+
1970
+ sage: ip.sub_poset(1,1)
1971
+ The Tamari interval of size 0 induced by relations []
1972
+ sage: ip = TamariIntervalPosets(4).an_element()
1973
+ sage: ip.subposet(2,9)
1974
+ Traceback (most recent call last):
1975
+ ...
1976
+ ValueError: invalid starting or ending value
1977
+ """
1978
+ if start < 1 or start > end or end > self.size() + 1:
1979
+ raise ValueError("invalid starting or ending value")
1980
+ if start == end:
1981
+ return TamariIntervalPoset(0, [])
1982
+ relations = [(i - start + 1, j - start + 1)
1983
+ for (i, j) in self.increasing_cover_relations()
1984
+ if i >= start and j < end]
1985
+ relations.extend((j - start + 1, i - start + 1)
1986
+ for (j, i) in self.decreasing_cover_relations()
1987
+ if i >= start and j < end)
1988
+ return TamariIntervalPoset(end - start, relations, check=False)
1989
+
1990
+ sub_poset = subposet
1991
+
1992
+ def min_linear_extension(self) -> Permutation:
1993
+ r"""
1994
+ Return the minimal permutation for the right weak order which is
1995
+ a linear extension of ``self``.
1996
+
1997
+ This is also the minimal permutation in the sylvester
1998
+ class of ``self.lower_binary_tree()`` and is a 312-avoiding
1999
+ permutation.
2000
+
2001
+ The right weak order is also known as the right permutohedron
2002
+ order. See
2003
+ :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal`
2004
+ for its definition.
2005
+
2006
+ EXAMPLES::
2007
+
2008
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
2009
+ sage: ip.min_linear_extension()
2010
+ [1, 2, 4, 3]
2011
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(4,5)])
2012
+ sage: ip.min_linear_extension()
2013
+ [1, 4, 3, 6, 5, 2]
2014
+ sage: ip = TamariIntervalPoset(0,[])
2015
+ sage: ip.min_linear_extension()
2016
+ []
2017
+ sage: ip = TamariIntervalPoset(5, [(1, 4), (2, 4), (3, 4), (5, 4)]); ip
2018
+ The Tamari interval of size 5 induced by relations [(1, 4), (2, 4), (3, 4), (5, 4)]
2019
+ sage: ip.min_linear_extension()
2020
+ [1, 2, 3, 5, 4]
2021
+ """
2022
+ # The min linear extension is built by postfix-reading the
2023
+ # final forest of ``self``.
2024
+ final_forest = DiGraph([list(self),
2025
+ self.decreasing_cover_relations()],
2026
+ format='vertices_and_edges')
2027
+
2028
+ def add(perm: list, i):
2029
+ r"""
2030
+ Internal recursive method to compute the min linear extension.
2031
+ """
2032
+ for j in sorted(final_forest.neighbors_in(i)):
2033
+ add(perm, j)
2034
+ perm.append(i)
2035
+ perm: list[int] = []
2036
+ for i in sorted(final_forest.sinks()):
2037
+ add(perm, i)
2038
+ return Permutation(perm) # type:ignore
2039
+
2040
+ def max_linear_extension(self) -> Permutation:
2041
+ r"""
2042
+ Return the maximal permutation for the right weak order which is
2043
+ a linear extension of ``self``.
2044
+
2045
+ This is also the maximal permutation in the sylvester
2046
+ class of ``self.upper_binary_tree()`` and is a 132-avoiding
2047
+ permutation.
2048
+
2049
+ The right weak order is also known as the right permutohedron
2050
+ order. See
2051
+ :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal`
2052
+ for its definition.
2053
+
2054
+ EXAMPLES::
2055
+
2056
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
2057
+ sage: ip.max_linear_extension()
2058
+ [4, 1, 2, 3]
2059
+ sage: ip = TamariIntervalPoset(6,[(3,2),(4,3),(5,2),(6,5),(1,2),(4,5)]); ip
2060
+ The Tamari interval of size 6 induced by relations [(1, 2), (4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
2061
+ sage: ip.max_linear_extension()
2062
+ [6, 4, 5, 3, 1, 2]
2063
+ sage: ip = TamariIntervalPoset(0,[]); ip
2064
+ The Tamari interval of size 0 induced by relations []
2065
+ sage: ip.max_linear_extension()
2066
+ []
2067
+ sage: ip = TamariIntervalPoset(5, [(1, 4), (2, 4), (3, 4), (5, 4)]); ip
2068
+ The Tamari interval of size 5 induced by relations [(1, 4), (2, 4), (3, 4), (5, 4)]
2069
+ sage: ip.max_linear_extension()
2070
+ [5, 3, 2, 1, 4]
2071
+ """
2072
+ # The max linear extension is built by right-to-left
2073
+ # postfix-reading the initial forest of ``self``.
2074
+ initial_forest = DiGraph([list(self),
2075
+ self.increasing_cover_relations()],
2076
+ format='vertices_and_edges')
2077
+
2078
+ def add(perm: list, i):
2079
+ r"""
2080
+ Internal recursive method to compute the max linear extension.
2081
+ """
2082
+ for j in sorted(initial_forest.neighbors_in(i), reverse=True):
2083
+ add(perm, j)
2084
+ perm.append(i)
2085
+ perm: list[int] = []
2086
+ for i in sorted(initial_forest.sinks(), reverse=True):
2087
+ add(perm, i)
2088
+ return Permutation(perm) # type:ignore
2089
+
2090
+ def linear_extensions(self) -> Iterator[Permutation]:
2091
+ r"""
2092
+ Return an iterator on the permutations which are linear
2093
+ extensions of ``self``.
2094
+
2095
+ They form an interval of the right weak order (also called the
2096
+ right permutohedron order -- see
2097
+ :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal`
2098
+ for a definition).
2099
+
2100
+ EXAMPLES::
2101
+
2102
+ sage: ip = TamariIntervalPoset(3, [(1,2),(3,2)])
2103
+ sage: list(ip.linear_extensions()) # needs sage.modules sage.rings.finite_rings
2104
+ [[3, 1, 2], [1, 3, 2]]
2105
+ sage: ip = TamariIntervalPoset(4, [(1,2),(2,3),(4,3)])
2106
+ sage: list(ip.linear_extensions()) # needs sage.modules sage.rings.finite_rings
2107
+ [[4, 1, 2, 3], [1, 2, 4, 3], [1, 4, 2, 3]]
2108
+ """
2109
+ for ext in self._poset.linear_extensions():
2110
+ yield Permutation(ext) # type:ignore
2111
+
2112
+ def lower_contained_intervals(self) -> Iterator[TIP]:
2113
+ r"""
2114
+ If ``self`` represents the interval `[t_1, t_2]` of the Tamari
2115
+ lattice, return an iterator on all intervals `[t_1,t]` with
2116
+ `t \leq t_2` for the Tamari lattice.
2117
+
2118
+ In terms of interval-posets, it corresponds to adding all possible
2119
+ relations of the form `n` precedes `m` with `n<m`.
2120
+
2121
+ EXAMPLES::
2122
+
2123
+ sage: ip = TamariIntervalPoset(4, [(2,4),(3,4),(2,1),(3,1)])
2124
+ sage: list(ip.lower_contained_intervals())
2125
+ [The Tamari interval of size 4 induced by relations
2126
+ [(2, 4), (3, 4), (3, 1), (2, 1)],
2127
+ The Tamari interval of size 4 induced by relations
2128
+ [(1, 4), (2, 4), (3, 4), (3, 1), (2, 1)],
2129
+ The Tamari interval of size 4 induced by relations
2130
+ [(2, 3), (3, 4), (3, 1), (2, 1)],
2131
+ The Tamari interval of size 4 induced by relations
2132
+ [(1, 4), (2, 3), (3, 4), (3, 1), (2, 1)]]
2133
+ sage: ip = TamariIntervalPoset(4,[])
2134
+ sage: len(list(ip.lower_contained_intervals()))
2135
+ 14
2136
+ """
2137
+ size = self._size
2138
+ yield self
2139
+ r"""
2140
+ we try to add links recursively in this order:
2141
+ 1 -> 2
2142
+ 2 -> 3
2143
+ 1 -> 3
2144
+ 3 -> 4
2145
+ 2 -> 4
2146
+ 1 -> 4
2147
+ ...
2148
+ ("Link" means "relation of the poset".)
2149
+
2150
+ One useful feature of interval-posets is that if you add a single
2151
+ new relation -- say, `x` precedes `y` -- to an existing
2152
+ interval-poset and take the transitive closure, and if the axioms
2153
+ of an interval-poset are still satisfied for `(a,c) = (x,y)` and
2154
+ for `(a,c) = (y,x)`, then the transitive closure is an
2155
+ interval-poset (i.e., roughly speaking, the other new relations
2156
+ forced by `x` preceding `y` under transitive closure cannot
2157
+ invalidate the axioms). This is helpful when extending
2158
+ interval-posets, and is the reason why this and other iterators
2159
+ don't yield invalid interval-posets.
2160
+ """
2161
+ def add_relations(poset, n, m):
2162
+ r"""
2163
+ Internal recursive method to generate all possible intervals.
2164
+ At every step during the iteration, we have n < m and every
2165
+ i satisfying n < i < m satisfies that i precedes m in the
2166
+ poset ``poset`` (except when m > size).
2167
+ """
2168
+ if n <= 0:
2169
+ # if n<=0, then we go to the next m
2170
+ n = m
2171
+ m += 1
2172
+ if m > size:
2173
+ # if m>size, it's finished
2174
+ return
2175
+
2176
+ if poset.le(n, m):
2177
+ # there is already a link n->m, so we go to the next n
2178
+ yield from add_relations(poset, n - 1, m)
2179
+ elif poset.le(m, n):
2180
+ # there is an inverse link m->n, we know we won't be able
2181
+ # to create a link i->m with i<=n, so we go to the next m
2182
+ yield from add_relations(poset, m, m + 1)
2183
+ else:
2184
+ # there is no link n->m
2185
+ # first option : we don't create the link and go to the next m
2186
+ # (since the lack of a link n->m forbids any links i->m
2187
+ # with i<n)
2188
+ yield from add_relations(poset, m, m + 1)
2189
+ # second option : we create the link
2190
+ # (this is allowed because links i->m already exist for all
2191
+ # n<i<m, or else we wouldn't be here)
2192
+ poset = TamariIntervalPoset(poset.size(), poset._cover_relations + ((n, m),))
2193
+ yield poset
2194
+ # and then, we go to the next n
2195
+ yield from add_relations(poset, n - 1, m)
2196
+
2197
+ yield from add_relations(self, 1, 2)
2198
+
2199
+ def interval_cardinality(self) -> Integer:
2200
+ r"""
2201
+ Return the cardinality of the interval, i.e., the number of elements
2202
+ (binary trees or Dyck words) in the interval represented by ``self``.
2203
+
2204
+ Not to be confused with :meth:`size` which is the number of
2205
+ vertices.
2206
+
2207
+ .. SEEALSO:: :meth:`binary_trees`
2208
+
2209
+ EXAMPLES::
2210
+
2211
+ sage: TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)]).interval_cardinality()
2212
+ 4
2213
+ sage: TamariIntervalPoset(4,[]).interval_cardinality()
2214
+ 14
2215
+ sage: TamariIntervalPoset(4,[(1,2),(2,3),(3,4)]).interval_cardinality()
2216
+ 1
2217
+ """
2218
+ return len(list(self.lower_contained_intervals()))
2219
+
2220
+ def binary_trees(self) -> Iterator:
2221
+ r"""
2222
+ Return an iterator on all the binary trees in the interval
2223
+ represented by ``self``.
2224
+
2225
+ .. SEEALSO:: :meth:`interval_cardinality`
2226
+
2227
+ EXAMPLES::
2228
+
2229
+ sage: list(TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)]).binary_trees())
2230
+ [[., [[., [., .]], .]],
2231
+ [[., [., [., .]]], .],
2232
+ [., [[[., .], .], .]],
2233
+ [[., [[., .], .]], .]]
2234
+ sage: set(TamariIntervalPoset(4,[]).binary_trees()) == set(BinaryTrees(4))
2235
+ True
2236
+ """
2237
+ for ip in self.lower_contained_intervals():
2238
+ yield ip.upper_binary_tree()
2239
+
2240
+ def dyck_words(self) -> Iterator:
2241
+ r"""
2242
+ Return an iterator on all the Dyck words in the interval
2243
+ represented by ``self``.
2244
+
2245
+ EXAMPLES::
2246
+
2247
+ sage: list(TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)]).dyck_words()) # needs sage.combinat
2248
+ [[1, 1, 1, 0, 0, 1, 0, 0],
2249
+ [1, 1, 1, 0, 0, 0, 1, 0],
2250
+ [1, 1, 0, 1, 0, 1, 0, 0],
2251
+ [1, 1, 0, 1, 0, 0, 1, 0]]
2252
+ sage: set(TamariIntervalPoset(4,[]).dyck_words()) == set(DyckWords(4)) # needs sage.combinat
2253
+ True
2254
+ """
2255
+ for ip in self.lower_contained_intervals():
2256
+ yield ip.upper_dyck_word()
2257
+
2258
+ def maximal_chain_tamari_intervals(self) -> Iterator[TIP]:
2259
+ r"""
2260
+ Return an iterator on the upper contained intervals of one
2261
+ longest chain of the Tamari interval represented by ``self``.
2262
+
2263
+ If ``self`` represents the interval `[T_1,T_2]` of the Tamari
2264
+ lattice, this returns intervals `[T',T_2]` with `T'` following
2265
+ one longest chain between `T_1` and `T_2`.
2266
+
2267
+ To obtain a longest chain, we use the Tamari inversions of ``self``.
2268
+ The elements of the chain are obtained by adding one by one the
2269
+ relations `(b,a)` from each Tamari inversion `(a,b)` to ``self``,
2270
+ where the Tamari inversions are taken in lexicographic order.
2271
+
2272
+ EXAMPLES::
2273
+
2274
+ sage: ip = TamariIntervalPoset(4, [(2,4),(3,4),(2,1),(3,1)])
2275
+ sage: list(ip.maximal_chain_tamari_intervals())
2276
+ [The Tamari interval of size 4 induced by relations
2277
+ [(2, 4), (3, 4), (3, 1), (2, 1)],
2278
+ The Tamari interval of size 4 induced by relations
2279
+ [(2, 4), (3, 4), (4, 1), (3, 1), (2, 1)],
2280
+ The Tamari interval of size 4 induced by relations
2281
+ [(2, 4), (3, 4), (4, 1), (3, 2), (2, 1)]]
2282
+ sage: ip = TamariIntervalPoset(4, [])
2283
+ sage: list(ip.maximal_chain_tamari_intervals())
2284
+ [The Tamari interval of size 4 induced by relations [],
2285
+ The Tamari interval of size 4 induced by relations [(2, 1)],
2286
+ The Tamari interval of size 4 induced by relations [(3, 1), (2, 1)],
2287
+ The Tamari interval of size 4 induced by relations [(4, 1), (3, 1), (2, 1)],
2288
+ The Tamari interval of size 4 induced by relations [(4, 1), (3, 2), (2, 1)],
2289
+ The Tamari interval of size 4 induced by relations [(4, 2), (3, 2), (2, 1)],
2290
+ The Tamari interval of size 4 induced by relations [(4, 3), (3, 2), (2, 1)]]
2291
+ """
2292
+ yield self
2293
+ n = self.size()
2294
+ cover_relations = list(self._cover_relations)
2295
+ for inv in self.tamari_inversions_iter():
2296
+ cover_relations.append((inv[1], inv[0]))
2297
+ yield TamariIntervalPoset(n, cover_relations, check=False)
2298
+
2299
+ def maximal_chain_binary_trees(self) -> Iterator:
2300
+ r"""
2301
+ Return an iterator on the binary trees forming a longest chain of
2302
+ ``self`` (regarding ``self`` as an interval of the Tamari
2303
+ lattice).
2304
+
2305
+ EXAMPLES::
2306
+
2307
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
2308
+ sage: list(ip.maximal_chain_binary_trees())
2309
+ [[[., [[., .], .]], .], [., [[[., .], .], .]], [., [[., [., .]], .]]]
2310
+ sage: ip = TamariIntervalPoset(4,[])
2311
+ sage: list(ip.maximal_chain_binary_trees())
2312
+ [[[[[., .], .], .], .],
2313
+ [[[., [., .]], .], .],
2314
+ [[., [[., .], .]], .],
2315
+ [., [[[., .], .], .]],
2316
+ [., [[., [., .]], .]],
2317
+ [., [., [[., .], .]]],
2318
+ [., [., [., [., .]]]]]
2319
+ """
2320
+ for it in self.maximal_chain_tamari_intervals():
2321
+ yield it.lower_binary_tree()
2322
+
2323
+ def maximal_chain_dyck_words(self) -> Iterator:
2324
+ r"""
2325
+ Return an iterator on the Dyck words forming a longest chain of
2326
+ ``self`` (regarding ``self`` as an interval of the Tamari
2327
+ lattice).
2328
+
2329
+ EXAMPLES::
2330
+
2331
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
2332
+ sage: list(ip.maximal_chain_dyck_words()) # needs sage.combinat
2333
+ [[1, 1, 0, 1, 0, 0, 1, 0], [1, 1, 0, 1, 0, 1, 0, 0], [1, 1, 1, 0, 0, 1, 0, 0]]
2334
+ sage: ip = TamariIntervalPoset(4,[])
2335
+ sage: list(ip.maximal_chain_dyck_words()) # needs sage.combinat
2336
+ [[1, 0, 1, 0, 1, 0, 1, 0],
2337
+ [1, 1, 0, 0, 1, 0, 1, 0],
2338
+ [1, 1, 0, 1, 0, 0, 1, 0],
2339
+ [1, 1, 0, 1, 0, 1, 0, 0],
2340
+ [1, 1, 1, 0, 0, 1, 0, 0],
2341
+ [1, 1, 1, 0, 1, 0, 0, 0],
2342
+ [1, 1, 1, 1, 0, 0, 0, 0]]
2343
+ """
2344
+ for it in self.maximal_chain_tamari_intervals():
2345
+ yield it.lower_dyck_word()
2346
+
2347
+ def tamari_inversions(self) -> list[tuple[int, int]]:
2348
+ r"""
2349
+ Return the Tamari inversions of ``self``.
2350
+
2351
+ A Tamari inversion is
2352
+ a pair of vertices `(a,b)` with `a < b` such that:
2353
+
2354
+ - the decreasing parent of `b` is strictly smaller than `a` (or
2355
+ does not exist), and
2356
+ - the increasing parent of `a` is strictly bigger than `b` (or
2357
+ does not exist).
2358
+
2359
+ "Smaller" and "bigger" refer to the numerical values of the
2360
+ elements, not to the poset order.
2361
+
2362
+ This method returns the list of all Tamari inversions in
2363
+ lexicographic order.
2364
+
2365
+ The number of Tamari inversions is the length of the
2366
+ longest chain of the Tamari interval represented by ``self``.
2367
+
2368
+ Indeed, when an interval consists of just one binary tree, it has
2369
+ no inversion. One can also prove that if a Tamari interval
2370
+ `I' = [T_1', T_2']` is a proper subset of a Tamari interval
2371
+ `I = [T_1, T_2]`, then the inversion number of `I'` is strictly
2372
+ lower than the inversion number of `I`. And finally, by adding
2373
+ the relation `(b,a)` to the interval-poset where `(a,b)` is the
2374
+ first inversion of `I` in lexicographic order, one reduces the
2375
+ inversion number by exactly `1`.
2376
+
2377
+ .. SEEALSO::
2378
+
2379
+ :meth:`tamari_inversions_iter`, :meth:`number_of_tamari_inversions`
2380
+
2381
+ EXAMPLES::
2382
+
2383
+ sage: ip = TamariIntervalPoset(3,[])
2384
+ sage: ip.tamari_inversions()
2385
+ [(1, 2), (1, 3), (2, 3)]
2386
+ sage: ip = TamariIntervalPoset(3,[(2,1)])
2387
+ sage: ip.tamari_inversions()
2388
+ [(1, 3), (2, 3)]
2389
+ sage: ip = TamariIntervalPoset(3,[(1,2)])
2390
+ sage: ip.tamari_inversions()
2391
+ [(2, 3)]
2392
+ sage: ip = TamariIntervalPoset(3,[(1,2),(3,2)])
2393
+ sage: ip.tamari_inversions()
2394
+ []
2395
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
2396
+ sage: ip.tamari_inversions()
2397
+ [(1, 4), (2, 3)]
2398
+ sage: ip = TamariIntervalPoset(4,[])
2399
+ sage: ip.tamari_inversions()
2400
+ [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
2401
+ sage: all(not TamariIntervalPosets.from_binary_trees(bt,bt) # needs sage.combinat
2402
+ ....: .tamari_inversions()
2403
+ ....: for bt in BinaryTrees(3))
2404
+ True
2405
+ sage: all(not TamariIntervalPosets.from_binary_trees(bt,bt) # needs sage.combinat
2406
+ ....: .tamari_inversions()
2407
+ ....: for bt in BinaryTrees(4))
2408
+ True
2409
+ """
2410
+ return list(self.tamari_inversions_iter())
2411
+
2412
+ def tamari_inversions_iter(self) -> Iterator[tuple[int, int]]:
2413
+ r"""
2414
+ Iterate over the Tamari inversions of ``self``, in
2415
+ lexicographic order.
2416
+
2417
+ See :meth:`tamari_inversions` for the definition of the terms
2418
+ involved.
2419
+
2420
+ EXAMPLES::
2421
+
2422
+ sage: T = TamariIntervalPoset(5, [[1,2],[3,4],[3,2],[5,2],[4,2]])
2423
+ sage: list(T.tamari_inversions_iter())
2424
+ [(4, 5)]
2425
+
2426
+ sage: T = TamariIntervalPoset(8, [(2, 7), (3, 7), (4, 7), (5, 7), (6, 7),
2427
+ ....: (8, 7), (6, 4), (5, 4), (4, 3), (3, 2)])
2428
+ sage: list(T.tamari_inversions_iter())
2429
+ [(1, 2), (1, 7), (5, 6)]
2430
+
2431
+ sage: T = TamariIntervalPoset(1, [])
2432
+ sage: list(T.tamari_inversions_iter())
2433
+ []
2434
+
2435
+ sage: T = TamariIntervalPoset(0, [])
2436
+ sage: list(T.tamari_inversions_iter())
2437
+ []
2438
+ """
2439
+ final_forest = DiGraph([list(self),
2440
+ self.decreasing_cover_relations()],
2441
+ format='vertices_and_edges')
2442
+ initial_forest = DiGraph([list(self),
2443
+ self.increasing_cover_relations()],
2444
+ format='vertices_and_edges')
2445
+ n1 = self.size() + 1
2446
+ for a in range(1, self.size()): # a == n will never work
2447
+ try:
2448
+ ipa = next(initial_forest.neighbor_out_iterator(a))
2449
+ max_b_1 = ipa
2450
+ except StopIteration:
2451
+ max_b_1 = n1
2452
+ for b in range(a + 1, max_b_1):
2453
+ try:
2454
+ dpb = next(final_forest.neighbor_out_iterator(b))
2455
+ if dpb < a:
2456
+ yield (a, b)
2457
+ except StopIteration:
2458
+ yield (a, b)
2459
+
2460
+ def number_of_tamari_inversions(self) -> Integer:
2461
+ r"""
2462
+ Return the number of Tamari inversions of ``self``.
2463
+
2464
+ This is also the length the longest chain of the Tamari
2465
+ interval represented by ``self``.
2466
+
2467
+ EXAMPLES::
2468
+
2469
+ sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)])
2470
+ sage: ip.number_of_tamari_inversions()
2471
+ 2
2472
+ sage: ip = TamariIntervalPoset(4,[])
2473
+ sage: ip.number_of_tamari_inversions()
2474
+ 6
2475
+ sage: ip = TamariIntervalPoset(3,[])
2476
+ sage: ip.number_of_tamari_inversions()
2477
+ 3
2478
+ """
2479
+ return len(self.tamari_inversions())
2480
+
2481
+ def number_of_new_components(self) -> Integer:
2482
+ """
2483
+ Return the number of terms in the decomposition in new interval-posets.
2484
+
2485
+ Every interval-poset has a unique decomposition as a planar tree
2486
+ of new interval-posets, as explained in [Cha2008]_. This function
2487
+ just computes the number of terms, not the planar tree nor
2488
+ the terms themselves.
2489
+
2490
+ .. SEEALSO:: :meth:`is_new`, :meth:`new_decomposition`
2491
+
2492
+ EXAMPLES::
2493
+
2494
+ sage: TIP4 = TamariIntervalPosets(4)
2495
+ sage: nb = [u.number_of_new_components() for u in TIP4]
2496
+ sage: [nb.count(i) for i in range(1, 5)]
2497
+ [12, 21, 21, 14]
2498
+ """
2499
+ t_low = self.lower_binary_tree().to_tilting()
2500
+ t_up = self.upper_binary_tree().to_tilting()
2501
+ return sum(1 for p in t_low if p in t_up)
2502
+
2503
+ def new_decomposition(self) -> list[TIP]:
2504
+ """
2505
+ Return the decomposition of the interval-poset into
2506
+ new interval-posets.
2507
+
2508
+ Every interval-poset has a unique decomposition as a planar
2509
+ tree of new interval-posets, as explained in
2510
+ [Cha2008]_. This function computes the terms of this
2511
+ decomposition, but not the planar tree.
2512
+
2513
+ For the number of terms, you can use instead the method
2514
+ :meth:`number_of_new_components`.
2515
+
2516
+ OUTPUT: list of new interval-posets
2517
+
2518
+ .. SEEALSO::
2519
+
2520
+ :meth:`number_of_new_components`, :meth:`is_new`
2521
+
2522
+ EXAMPLES::
2523
+
2524
+ sage: ex = TamariIntervalPosets(4)[11]
2525
+ sage: ex.number_of_new_components()
2526
+ 3
2527
+ sage: ex.new_decomposition() # needs sage.combinat
2528
+ [The Tamari interval of size 1 induced by relations [],
2529
+ The Tamari interval of size 2 induced by relations [],
2530
+ The Tamari interval of size 1 induced by relations []]
2531
+
2532
+ TESTS::
2533
+
2534
+ sage: # needs sage.combinat
2535
+ sage: ex = TamariIntervalPosets(4).random_element()
2536
+ sage: dec = ex.new_decomposition()
2537
+ sage: len(dec) == ex.number_of_new_components()
2538
+ True
2539
+ sage: all(u.is_new() for u in dec)
2540
+ True
2541
+ """
2542
+ from sage.combinat.binary_tree import BinaryTree
2543
+ t_low = self.lower_binary_tree().to_tilting()
2544
+ t_up = self.upper_binary_tree().to_tilting()
2545
+ common = [p for p in t_low if p in t_up]
2546
+
2547
+ def extract_tree(x, y, tilt, common):
2548
+ """
2549
+ Extract a tree with root at position xy (recursive).
2550
+ """
2551
+ left_tree = None
2552
+ for k in range(y - 1, x, -1):
2553
+ if (x, k) in tilt:
2554
+ if (x, k) not in common:
2555
+ left_tree = extract_tree(x, k, tilt, common)
2556
+ break
2557
+ right_tree = None
2558
+ for k in range(x + 1, y):
2559
+ if (k, y) in tilt:
2560
+ if (k, y) not in common:
2561
+ right_tree = extract_tree(k, y, tilt, common)
2562
+ break
2563
+ return BinaryTree([left_tree, right_tree], check=False)
2564
+
2565
+ tip = self.parent()
2566
+ return [tip.from_binary_trees(extract_tree(cx, cy, t_low, common),
2567
+ extract_tree(cx, cy, t_up, common))
2568
+ for cx, cy in common]
2569
+
2570
+ def decomposition_to_triple(self) -> None | tuple[TIP, TIP, int]:
2571
+ """
2572
+ Decompose an interval-poset into a triple (``left``, ``right``, ``r``).
2573
+
2574
+ For the inverse method, see
2575
+ :meth:`TamariIntervalPosets.recomposition_from_triple`.
2576
+
2577
+ OUTPUT:
2578
+
2579
+ a triple (``left``, ``right``, ``r``) where ``left`` and
2580
+ ``right`` are interval-posets and ``r`` (an integer) is the
2581
+ parameter of the decomposition.
2582
+
2583
+ EXAMPLES::
2584
+
2585
+ sage: tip = TamariIntervalPoset(8, [(1,2), (2,4), (3,4), (6,7),
2586
+ ....: (3,2), (5,4), (6,4), (8,7)])
2587
+ sage: tip.decomposition_to_triple()
2588
+ (The Tamari interval of size 3 induced by relations [(1, 2), (3, 2)],
2589
+ The Tamari interval of size 4 induced by relations [(2, 3), (4, 3)],
2590
+ 2)
2591
+ sage: tip == TamariIntervalPosets.recomposition_from_triple(
2592
+ ....: *tip.decomposition_to_triple())
2593
+ True
2594
+
2595
+ TESTS::
2596
+
2597
+ sage: tip = TamariIntervalPoset(0, [])
2598
+ sage: tip.decomposition_to_triple()
2599
+
2600
+ REFERENCES:
2601
+
2602
+ - [CP2015]_
2603
+ """
2604
+ n = self.size()
2605
+ if n == 0:
2606
+ return None
2607
+ root = self.increasing_roots()[-1]
2608
+ r = len(self.decreasing_children(root))
2609
+ left = self.subposet(1, root)
2610
+ right = self.subposet(root + 1, n + 1)
2611
+ return left, right, r
2612
+
2613
+ def grafting_tree(self) -> LabelledBinaryTree:
2614
+ """
2615
+ Return the grafting tree of the interval-poset.
2616
+
2617
+ For the inverse method,
2618
+ see :meth:`TamariIntervalPosets.from_grafting_tree`.
2619
+
2620
+ EXAMPLES::
2621
+
2622
+ sage: tip = TamariIntervalPoset(8, [(1,2), (2,4), (3,4), (6,7),
2623
+ ....: (3,2), (5,4), (6,4), (8,7)])
2624
+ sage: tip.grafting_tree()
2625
+ 2[1[0[., .], 0[., .]], 0[., 1[0[., .], 0[., .]]]]
2626
+ sage: tip == TamariIntervalPosets.from_grafting_tree(tip.grafting_tree())
2627
+ True
2628
+
2629
+ REFERENCES:
2630
+
2631
+ - [Pons2018]_
2632
+ """
2633
+ n = self.size()
2634
+ if n == 0:
2635
+ return LabelledBinaryTree(None) # type:ignore
2636
+ triplet = self.decomposition_to_triple()
2637
+ assert triplet is not None
2638
+ left, right, r = triplet
2639
+ return LabelledBinaryTree([left.grafting_tree(),
2640
+ right.grafting_tree()], label=r)
2641
+
2642
+ def is_new(self) -> bool:
2643
+ """
2644
+ Return whether ``self`` is a new Tamari interval.
2645
+
2646
+ Here 'new' means that the interval is not contained in any
2647
+ facet of the associahedron.
2648
+ This condition is invariant under complementation.
2649
+
2650
+ They have been considered in section 9 of [Cha2008]_.
2651
+
2652
+ .. SEEALSO:: :meth:`is_modern`
2653
+
2654
+ EXAMPLES::
2655
+
2656
+ sage: TIP4 = TamariIntervalPosets(4)
2657
+ sage: len([u for u in TIP4 if u.is_new()])
2658
+ 12
2659
+
2660
+ sage: TIP3 = TamariIntervalPosets(3)
2661
+ sage: len([u for u in TIP3 if u.is_new()])
2662
+ 3
2663
+ """
2664
+ c_up = self.upper_binary_tree().single_edge_cut_shapes()
2665
+ c_down = self.lower_binary_tree().single_edge_cut_shapes()
2666
+ return not any(x in c_up for x in c_down)
2667
+
2668
+ def is_simple(self) -> bool:
2669
+ """
2670
+ Return whether ``self`` is a simple Tamari interval.
2671
+
2672
+ Here 'simple' means that the interval contains a unique binary tree.
2673
+
2674
+ These intervals define the simple modules over the
2675
+ incidence algebras of the Tamari lattices.
2676
+
2677
+ .. SEEALSO:: :meth:`is_final_interval`, :meth:`is_initial_interval`
2678
+
2679
+ EXAMPLES::
2680
+
2681
+ sage: TIP4 = TamariIntervalPosets(4)
2682
+ sage: len([u for u in TIP4 if u.is_simple()])
2683
+ 14
2684
+
2685
+ sage: TIP3 = TamariIntervalPosets(3)
2686
+ sage: len([u for u in TIP3 if u.is_simple()])
2687
+ 5
2688
+ """
2689
+ return self.upper_binary_tree() == self.lower_binary_tree()
2690
+
2691
+ def is_synchronized(self) -> bool:
2692
+ """
2693
+ Return whether ``self`` is a synchronized Tamari interval.
2694
+
2695
+ This means that the upper and lower binary trees have the same canopee.
2696
+ This condition is invariant under complementation.
2697
+
2698
+ This has been considered in [FPR2015]_. The numbers of
2699
+ synchronized intervals are given by the sequence :oeis:`A000139`.
2700
+
2701
+ EXAMPLES::
2702
+
2703
+ sage: len([T for T in TamariIntervalPosets(3)
2704
+ ....: if T.is_synchronized()])
2705
+ 6
2706
+ """
2707
+ up = self.upper_binary_tree()
2708
+ down = self.lower_binary_tree()
2709
+ return down.canopee() == up.canopee()
2710
+
2711
+ def is_modern(self) -> bool:
2712
+ r"""
2713
+ Return whether ``self`` is a modern Tamari interval.
2714
+
2715
+ This is defined by exclusion of a simple pattern in the Hasse diagram,
2716
+ namely there is no configuration `y \rightarrow x \leftarrow z`
2717
+ with `1 \leq y < x < z \leq n`.
2718
+
2719
+ This condition is invariant under complementation.
2720
+
2721
+ .. SEEALSO:: :meth:`is_new`, :meth:`is_infinitely_modern`
2722
+
2723
+ EXAMPLES::
2724
+
2725
+ sage: len([T for T in TamariIntervalPosets(3) if T.is_modern()])
2726
+ 12
2727
+
2728
+ REFERENCES:
2729
+
2730
+ - [Rog2018]_
2731
+ """
2732
+ G = self.poset().hasse_diagram()
2733
+ for x in G:
2734
+ nx = G.neighbors_in(x)
2735
+ nx.append(x)
2736
+ if min(nx) < x < max(nx):
2737
+ return False
2738
+ return True
2739
+
2740
+ def is_infinitely_modern(self) -> bool:
2741
+ r"""
2742
+ Return whether ``self`` is an infinitely-modern Tamari interval.
2743
+
2744
+ This is defined by the exclusion of the configuration
2745
+ `i \rightarrow i + 1` and `j + 1 \rightarrow j` with `i < j`.
2746
+
2747
+ This condition is invariant under complementation.
2748
+
2749
+ .. SEEALSO:: :meth:`is_new`, :meth:`is_modern`
2750
+
2751
+ EXAMPLES::
2752
+
2753
+ sage: len([T for T in TamariIntervalPosets(3)
2754
+ ....: if T.is_infinitely_modern()])
2755
+ 12
2756
+
2757
+ REFERENCES:
2758
+
2759
+ - [Rog2018]_
2760
+ """
2761
+ n = self.size()
2762
+ found = False
2763
+ for i in range(1, n):
2764
+ if self.le(i, i + 1):
2765
+ found = True
2766
+ continue
2767
+ if self.le(i + 1, i) and found:
2768
+ return False
2769
+ return True
2770
+
2771
+ def is_exceptional(self) -> bool:
2772
+ r"""
2773
+ Return whether ``self`` is an exceptional Tamari interval.
2774
+
2775
+ This is defined by exclusion of a simple pattern in the Hasse diagram,
2776
+ namely there is no configuration ``y <-- x --> z``
2777
+ with `1 \leq y < x < z \leq n`.
2778
+
2779
+ This condition is invariant under complementation.
2780
+
2781
+ EXAMPLES::
2782
+
2783
+ sage: len([T for T in TamariIntervalPosets(3)
2784
+ ....: if T.is_exceptional()])
2785
+ 12
2786
+ """
2787
+ G = self.poset().hasse_diagram()
2788
+ for x in G:
2789
+ nx = G.neighbors_out(x)
2790
+ nx.append(x)
2791
+ if min(nx) < x < max(nx):
2792
+ return False
2793
+ return True
2794
+
2795
+ def is_dexter(self) -> bool:
2796
+ r"""
2797
+ Return whether ``self`` is a dexter Tamari interval.
2798
+
2799
+ This is defined by an exclusion pattern in the Hasse diagram.
2800
+ See the code for the exact description.
2801
+
2802
+ This condition is not invariant under complementation.
2803
+
2804
+ EXAMPLES::
2805
+
2806
+ sage: len([T for T in TamariIntervalPosets(3) if T.is_dexter()])
2807
+ 12
2808
+ """
2809
+ G = self.poset().hasse_diagram()
2810
+ n = self.size()
2811
+ for x in range(2, n):
2812
+ nx = G.neighbors_out(x)
2813
+ nx.append(x)
2814
+ y = min(nx)
2815
+ if y < x and any(z > x for z in G.neighbor_out_iterator(y)):
2816
+ return False
2817
+ return True
2818
+
2819
+ def is_indecomposable(self) -> bool:
2820
+ """
2821
+ Return whether ``self`` is an indecomposable Tamari interval.
2822
+
2823
+ This is the terminology of [Cha2008]_.
2824
+
2825
+ This means that the upper binary tree has an empty left subtree.
2826
+
2827
+ This condition is not invariant under complementation.
2828
+
2829
+ .. SEEALSO:: :meth:`is_connected`
2830
+
2831
+ EXAMPLES::
2832
+
2833
+ sage: len([T for T in TamariIntervalPosets(3)
2834
+ ....: if T.is_indecomposable()])
2835
+ 8
2836
+ """
2837
+ return not self.upper_binary_tree()[0]
2838
+
2839
+ def is_connected(self) -> bool:
2840
+ """
2841
+ Return whether ``self`` is a connected Tamari interval.
2842
+
2843
+ This means that the Hasse diagram is connected.
2844
+
2845
+ This condition is invariant under complementation.
2846
+
2847
+ .. SEEALSO:: :meth:`is_indecomposable`, :meth:`factor`
2848
+
2849
+ EXAMPLES::
2850
+
2851
+ sage: len([T for T in TamariIntervalPosets(3) if T.is_connected()])
2852
+ 8
2853
+ """
2854
+ return self.poset().is_connected()
2855
+
2856
+
2857
+ # Abstract class to serve as a Factory ; no instances are created.
2858
+ class TamariIntervalPosets(UniqueRepresentation, Parent):
2859
+ r"""
2860
+ Factory for interval-posets.
2861
+
2862
+ INPUT:
2863
+
2864
+ - ``size`` -- integer (optional)
2865
+
2866
+ OUTPUT:
2867
+
2868
+ - the set of all interval-posets (of the given ``size`` if specified)
2869
+
2870
+ EXAMPLES::
2871
+
2872
+ sage: TamariIntervalPosets()
2873
+ Interval-posets
2874
+
2875
+ sage: TamariIntervalPosets(2)
2876
+ Interval-posets of size 2
2877
+
2878
+ .. NOTE::
2879
+
2880
+ This is a factory class whose constructor returns instances of
2881
+ subclasses.
2882
+ """
2883
+ @staticmethod
2884
+ def __classcall_private__(cls, n=None):
2885
+ r"""
2886
+ TESTS::
2887
+
2888
+ sage: from sage.combinat.interval_posets import TamariIntervalPosets_all, TamariIntervalPosets_size
2889
+ sage: isinstance(TamariIntervalPosets(2), TamariIntervalPosets_size)
2890
+ True
2891
+ sage: isinstance(TamariIntervalPosets(), TamariIntervalPosets_all)
2892
+ True
2893
+ sage: TamariIntervalPosets(2) is TamariIntervalPosets_size(2)
2894
+ True
2895
+ sage: TamariIntervalPosets() is TamariIntervalPosets_all()
2896
+ True
2897
+
2898
+ sage: TamariIntervalPosets(-2)
2899
+ Traceback (most recent call last):
2900
+ ...
2901
+ ValueError: n must be a nonnegative integer
2902
+ """
2903
+ if n is None:
2904
+ return TamariIntervalPosets_all()
2905
+
2906
+ if n not in NN:
2907
+ raise ValueError("n must be a nonnegative integer")
2908
+ return TamariIntervalPosets_size(Integer(n))
2909
+
2910
+ # add options to class
2911
+ class options(GlobalOptions):
2912
+ r"""
2913
+ Set and display the options for Tamari interval-posets.
2914
+
2915
+ If no parameters are set, then the function returns a copy of
2916
+ the options dictionary.
2917
+
2918
+ The ``options`` to Tamari interval-posets can be accessed as the method
2919
+ :meth:`TamariIntervalPosets.options` of :class:`TamariIntervalPosets`
2920
+ and related parent classes.
2921
+
2922
+ @OPTIONS@
2923
+
2924
+ EXAMPLES::
2925
+
2926
+ sage: TIP = TamariIntervalPosets
2927
+ sage: TIP.options.latex_color_decreasing
2928
+ red
2929
+ sage: TIP.options.latex_color_decreasing = 'green'
2930
+ sage: TIP.options.latex_color_decreasing
2931
+ green
2932
+ sage: TIP.options._reset()
2933
+ sage: TIP.options.latex_color_decreasing
2934
+ red
2935
+ """
2936
+ NAME = 'TamariIntervalPosets'
2937
+ module = 'sage.combinat.interval_posets'
2938
+ latex_tikz_scale = dict(default=1,
2939
+ description='the default value for the tikz scale when latexed',
2940
+ checker=lambda x: True) # More trouble than it's worth to check
2941
+ latex_line_width_scalar = dict(default=0.5,
2942
+ description='the default value for the line width as a'
2943
+ 'multiple of the tikz scale when latexed',
2944
+ checker=lambda x: True) # More trouble than it's worth to check
2945
+ latex_color_decreasing = dict(default='red',
2946
+ description='the default color of decreasing relations when latexed',
2947
+ checker=lambda x: True) # More trouble than it's worth to check
2948
+ latex_color_increasing = dict(default='blue',
2949
+ description='the default color of increasing relations when latexed',
2950
+ checker=lambda x: True) # More trouble than it's worth to check
2951
+ latex_hspace = dict(default=1,
2952
+ description='the default difference between horizontal'
2953
+ ' coordinates of vertices when latexed',
2954
+ checker=lambda x: True) # More trouble than it's worth to check
2955
+ latex_vspace = dict(default=1,
2956
+ description='the default difference between vertical'
2957
+ ' coordinates of vertices when latexed',
2958
+ checker=lambda x: True) # More trouble than it's worth to check
2959
+
2960
+ @staticmethod
2961
+ def check_poset(poset) -> bool:
2962
+ r"""
2963
+ Check if the given poset ``poset`` is a interval-poset, that is,
2964
+ if it satisfies the following properties:
2965
+
2966
+ - Its labels are exactly `1, \ldots, n` where `n` is its size.
2967
+ - If `a < c` (as numbers) and `a` precedes `c`, then `b` precedes
2968
+ `c` for all `b` such that `a < b < c`.
2969
+ - If `a < c` (as numbers) and `c` precedes `a`, then `b` precedes
2970
+ `a` for all `b` such that `a < b < c`.
2971
+
2972
+ INPUT:
2973
+
2974
+ - ``poset`` -- a finite labeled poset
2975
+
2976
+ EXAMPLES::
2977
+
2978
+ sage: p = Poset(([1,2,3],[(1,2),(3,2)]))
2979
+ sage: TamariIntervalPosets.check_poset(p)
2980
+ True
2981
+ sage: p = Poset(([2,3],[(3,2)]))
2982
+ sage: TamariIntervalPosets.check_poset(p)
2983
+ False
2984
+ sage: p = Poset(([1,2,3],[(3,1)]))
2985
+ sage: TamariIntervalPosets.check_poset(p)
2986
+ False
2987
+ sage: p = Poset(([1,2,3],[(1,3)]))
2988
+ sage: TamariIntervalPosets.check_poset(p)
2989
+ False
2990
+ """
2991
+ if not set(poset._elements) == set(range(1, poset.cardinality() + 1)):
2992
+ return False
2993
+
2994
+ for i in range(1, poset.cardinality() + 1):
2995
+ stop = False
2996
+ for j in range(i - 1, 0, -1):
2997
+ if not poset.le(j, i):
2998
+ stop = True # j does not precede i so no j'<j should
2999
+ elif stop:
3000
+ return False
3001
+ stop = False
3002
+ for j in range(i + 1, poset.cardinality() + 1):
3003
+ if not poset.le(j, i):
3004
+ stop = True # j does not precede i so no j'>j should
3005
+ elif stop:
3006
+ return False
3007
+ return True
3008
+
3009
+ @staticmethod
3010
+ def final_forest(element) -> TIP:
3011
+ r"""
3012
+ Return the final forest of a binary tree, an interval-poset or a
3013
+ Dyck word.
3014
+
3015
+ A final forest is an interval-poset corresponding to a final
3016
+ interval of the Tamari lattice, i.e., containing only decreasing
3017
+ relations.
3018
+
3019
+ It can be constructed from a binary tree by its binary
3020
+ search tree labeling with the rule: `b` precedes
3021
+ `a` in the final forest iff `b` is in the right subtree of `a`
3022
+ in the binary search tree.
3023
+
3024
+ INPUT:
3025
+
3026
+ - ``element`` -- a binary tree, a Dyck word or an interval-poset
3027
+
3028
+ EXAMPLES::
3029
+
3030
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
3031
+ sage: TamariIntervalPosets.final_forest(ip)
3032
+ The Tamari interval of size 4 induced by relations [(4, 3)]
3033
+
3034
+ From binary trees::
3035
+
3036
+ sage: bt = BinaryTree(); bt
3037
+ .
3038
+ sage: TamariIntervalPosets.final_forest(bt)
3039
+ The Tamari interval of size 0 induced by relations []
3040
+ sage: bt = BinaryTree([]); bt
3041
+ [., .]
3042
+ sage: TamariIntervalPosets.final_forest(bt)
3043
+ The Tamari interval of size 1 induced by relations []
3044
+ sage: bt = BinaryTree([[],None]); bt
3045
+ [[., .], .]
3046
+ sage: TamariIntervalPosets.final_forest(bt)
3047
+ The Tamari interval of size 2 induced by relations []
3048
+ sage: bt = BinaryTree([None,[]]); bt
3049
+ [., [., .]]
3050
+ sage: TamariIntervalPosets.final_forest(bt)
3051
+ The Tamari interval of size 2 induced by relations [(2, 1)]
3052
+ sage: bt = BinaryTree([[],[]]); bt
3053
+ [[., .], [., .]]
3054
+ sage: TamariIntervalPosets.final_forest(bt)
3055
+ The Tamari interval of size 3 induced by relations [(3, 2)]
3056
+ sage: bt = BinaryTree([[None,[[],None]],[]]); bt
3057
+ [[., [[., .], .]], [., .]]
3058
+ sage: TamariIntervalPosets.final_forest(bt)
3059
+ The Tamari interval of size 5 induced by relations [(5, 4), (3, 1), (2, 1)]
3060
+
3061
+ From Dyck words::
3062
+
3063
+ sage: # needs sage.combinat
3064
+ sage: dw = DyckWord([1,0])
3065
+ sage: TamariIntervalPosets.final_forest(dw)
3066
+ The Tamari interval of size 1 induced by relations []
3067
+ sage: dw = DyckWord([1,1,0,1,0,0,1,1,0,0])
3068
+ sage: TamariIntervalPosets.final_forest(dw)
3069
+ The Tamari interval of size 5 induced by relations [(5, 4), (3, 1), (2, 1)]
3070
+
3071
+ TESTS::
3072
+
3073
+ sage: TamariIntervalPosets.final_forest('mont')
3074
+ Traceback (most recent call last):
3075
+ ...
3076
+ ValueError: do not know how to construct the final forest of mont
3077
+ """
3078
+ if isinstance(element, TamariIntervalPoset):
3079
+ return element.final_forest()
3080
+
3081
+ try:
3082
+ DW = DyckWords()
3083
+ except ImportError:
3084
+ DW = ()
3085
+
3086
+ if element in DW:
3087
+ binary_tree = element.to_binary_tree_tamari()
3088
+ elif element in BinaryTrees() or element in LabelledBinaryTrees():
3089
+ binary_tree = element
3090
+ else:
3091
+ raise ValueError(f"do not know how to construct the final forest of {element}")
3092
+
3093
+ def get_relations(bt, start=1):
3094
+ r"""
3095
+ Recursive method to get the binary tree final forest relations
3096
+ with only one recursive reading of the tree.
3097
+
3098
+ The vertices are being labelled with integers starting with
3099
+ ``start``.
3100
+
3101
+ OUTPUT:
3102
+
3103
+ - the indices of the nodes on the left border of the tree
3104
+ (these become the roots of the forest)
3105
+ - the relations of the final forest (as a list of tuples)
3106
+ - the next available index for a node (size of tree +
3107
+ ``start``)
3108
+ """
3109
+ if not bt:
3110
+ return [], [], start # leaf
3111
+ roots, relations, index = get_relations(bt[0], start=start)
3112
+ rroots, rrelations, rindex = get_relations(bt[1], start=index + 1)
3113
+ roots.append(index)
3114
+ relations.extend(rrelations)
3115
+ relations.extend((j, index) for j in rroots)
3116
+ return roots, relations, rindex
3117
+
3118
+ _, relations, index = get_relations(binary_tree)
3119
+ P = FinitePoset(DiGraph([list(range(1, index)), relations],
3120
+ format='vertices_and_edges')) # type:ignore
3121
+ return TamariIntervalPoset(P, check=False) # type:ignore
3122
+
3123
+ @staticmethod
3124
+ def initial_forest(element) -> TIP:
3125
+ r"""
3126
+ Return the initial forest of a binary tree, an interval-poset or
3127
+ a Dyck word.
3128
+
3129
+ An initial forest is an interval-poset corresponding to an initial
3130
+ interval of the Tamari lattice, i.e., containing only increasing
3131
+ relations.
3132
+
3133
+ It can be constructed from a binary tree by its binary
3134
+ search tree labeling with the rule: `a` precedes `b` in the
3135
+ initial forest iff `a` is in the left subtree of `b` in the
3136
+ binary search tree.
3137
+
3138
+ INPUT:
3139
+
3140
+ - ``element`` -- a binary tree, a Dyck word or an interval-poset
3141
+
3142
+ EXAMPLES::
3143
+
3144
+ sage: ip = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
3145
+ sage: TamariIntervalPosets.initial_forest(ip)
3146
+ The Tamari interval of size 4 induced by relations [(1, 2), (2, 3)]
3147
+
3148
+ with binary trees::
3149
+
3150
+ sage: bt = BinaryTree(); bt
3151
+ .
3152
+ sage: TamariIntervalPosets.initial_forest(bt)
3153
+ The Tamari interval of size 0 induced by relations []
3154
+ sage: bt = BinaryTree([]); bt
3155
+ [., .]
3156
+ sage: TamariIntervalPosets.initial_forest(bt)
3157
+ The Tamari interval of size 1 induced by relations []
3158
+ sage: bt = BinaryTree([[],None]); bt
3159
+ [[., .], .]
3160
+ sage: TamariIntervalPosets.initial_forest(bt)
3161
+ The Tamari interval of size 2 induced by relations [(1, 2)]
3162
+ sage: bt = BinaryTree([None,[]]); bt
3163
+ [., [., .]]
3164
+ sage: TamariIntervalPosets.initial_forest(bt)
3165
+ The Tamari interval of size 2 induced by relations []
3166
+ sage: bt = BinaryTree([[],[]]); bt
3167
+ [[., .], [., .]]
3168
+ sage: TamariIntervalPosets.initial_forest(bt)
3169
+ The Tamari interval of size 3 induced by relations [(1, 2)]
3170
+ sage: bt = BinaryTree([[None,[[],None]],[]]); bt
3171
+ [[., [[., .], .]], [., .]]
3172
+ sage: TamariIntervalPosets.initial_forest(bt)
3173
+ The Tamari interval of size 5 induced by relations [(1, 4), (2, 3), (3, 4)]
3174
+
3175
+ from Dyck words::
3176
+
3177
+ sage: # needs sage.combinat
3178
+ sage: dw = DyckWord([1,0])
3179
+ sage: TamariIntervalPosets.initial_forest(dw)
3180
+ The Tamari interval of size 1 induced by relations []
3181
+ sage: dw = DyckWord([1,1,0,1,0,0,1,1,0,0])
3182
+ sage: TamariIntervalPosets.initial_forest(dw)
3183
+ The Tamari interval of size 5 induced by relations [(1, 4), (2, 3), (3, 4)]
3184
+
3185
+ TESTS::
3186
+
3187
+ sage: TamariIntervalPosets.initial_forest('mont')
3188
+ Traceback (most recent call last):
3189
+ ...
3190
+ ValueError: do not know how to construct the initial forest of mont
3191
+ """
3192
+ if isinstance(element, TamariIntervalPoset):
3193
+ return element.initial_forest()
3194
+
3195
+ try:
3196
+ DW = DyckWords()
3197
+ except ImportError:
3198
+ DW = ()
3199
+
3200
+ if element in DW:
3201
+ binary_tree = element.to_binary_tree_tamari()
3202
+ elif element in BinaryTrees() or element in LabelledBinaryTrees():
3203
+ binary_tree = element
3204
+ else:
3205
+ raise ValueError(f"do not know how to construct the initial forest of {element}")
3206
+
3207
+ def get_relations(bt, start=1):
3208
+ r"""
3209
+ Recursive method to get the binary tree initial forest
3210
+ relations with only one recursive reading of the tree.
3211
+
3212
+ The vertices are being labelled with integers starting with
3213
+ ``start``.
3214
+
3215
+ OUTPUT:
3216
+
3217
+ - the indices of the nodes on the right border of the tree
3218
+ (these become the roots of the forest)
3219
+ - the relations of the initial forest (as a list of tuples)
3220
+ - the next available index for a node (size of tree +
3221
+ ``start``)
3222
+ """
3223
+ if not bt:
3224
+ return [], [], start # leaf
3225
+ lroots, lrelations, index = get_relations(bt[0], start=start)
3226
+ roots, relations, rindex = get_relations(bt[1], start=index + 1)
3227
+ roots.append(index)
3228
+ relations.extend(lrelations)
3229
+ relations.extend((j, index) for j in lroots)
3230
+ return roots, relations, rindex
3231
+
3232
+ _, relations, index = get_relations(binary_tree)
3233
+ P = FinitePoset(DiGraph([list(range(1, index)), relations],
3234
+ format='vertices_and_edges')) # type:ignore
3235
+ return TamariIntervalPoset(P, check=False) # type:ignore
3236
+
3237
+ @staticmethod
3238
+ def from_binary_trees(tree1, tree2) -> TIP:
3239
+ r"""
3240
+ Return the interval-poset corresponding to the interval
3241
+ [``tree1``, ``tree2``] of the Tamari lattice.
3242
+
3243
+ Raise an exception if
3244
+ ``tree1`` is not `\leq` ``tree2`` in the Tamari lattice.
3245
+
3246
+ INPUT:
3247
+
3248
+ - ``tree1`` -- a binary tree
3249
+ - ``tree2`` -- a binary tree greater or equal than ``tree1`` for
3250
+ the Tamari lattice
3251
+
3252
+ EXAMPLES::
3253
+
3254
+ sage: tree1 = BinaryTree([[],None])
3255
+ sage: tree2 = BinaryTree([None,[]])
3256
+ sage: TamariIntervalPosets.from_binary_trees(tree1,tree2)
3257
+ The Tamari interval of size 2 induced by relations []
3258
+ sage: TamariIntervalPosets.from_binary_trees(tree1,tree1)
3259
+ The Tamari interval of size 2 induced by relations [(1, 2)]
3260
+ sage: TamariIntervalPosets.from_binary_trees(tree2,tree2)
3261
+ The Tamari interval of size 2 induced by relations [(2, 1)]
3262
+
3263
+ sage: tree1 = BinaryTree([[],[[None,[]],[]]])
3264
+ sage: tree2 = BinaryTree([None,[None,[None,[[],[]]]]])
3265
+ sage: TamariIntervalPosets.from_binary_trees(tree1,tree2)
3266
+ The Tamari interval of size 6 induced by relations
3267
+ [(4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
3268
+
3269
+ sage: tree3 = BinaryTree([None,[None,[[],[None,[]]]]])
3270
+ sage: TamariIntervalPosets.from_binary_trees(tree1,tree3)
3271
+ Traceback (most recent call last):
3272
+ ...
3273
+ ValueError: the two binary trees are not comparable on the Tamari lattice
3274
+ sage: TamariIntervalPosets.from_binary_trees(tree1,BinaryTree())
3275
+ Traceback (most recent call last):
3276
+ ...
3277
+ ValueError: the two binary trees are not comparable on the Tamari lattice
3278
+ """
3279
+ initial_forest = TamariIntervalPosets.initial_forest(tree2)
3280
+ final_forest = TamariIntervalPosets.final_forest(tree1)
3281
+ try:
3282
+ return initial_forest.intersection(final_forest)
3283
+ except ValueError:
3284
+ raise ValueError("the two binary trees are not comparable on the Tamari lattice")
3285
+
3286
+ @staticmethod
3287
+ def from_dyck_words(dw1, dw2) -> TIP:
3288
+ r"""
3289
+ Return the interval-poset corresponding to the interval
3290
+ [``dw1``, ``dw2``] of the Tamari lattice.
3291
+
3292
+ Raise an exception if the
3293
+ two Dyck words ``dw1`` and ``dw2`` do not satisfy
3294
+ ``dw1`` `\leq` ``dw2`` in the Tamari lattice.
3295
+
3296
+ INPUT:
3297
+
3298
+ - ``dw1`` -- a Dyck word
3299
+ - ``dw2`` -- a Dyck word greater or equal than ``dw1`` for
3300
+ the Tamari lattice
3301
+
3302
+ EXAMPLES::
3303
+
3304
+ sage: # needs sage.combinat
3305
+ sage: dw1 = DyckWord([1,0,1,0])
3306
+ sage: dw2 = DyckWord([1,1,0,0])
3307
+ sage: TamariIntervalPosets.from_dyck_words(dw1, dw2)
3308
+ The Tamari interval of size 2 induced by relations []
3309
+ sage: TamariIntervalPosets.from_dyck_words(dw1,dw1)
3310
+ The Tamari interval of size 2 induced by relations [(1, 2)]
3311
+ sage: TamariIntervalPosets.from_dyck_words(dw2,dw2)
3312
+ The Tamari interval of size 2 induced by relations [(2, 1)]
3313
+
3314
+ sage: # needs sage.combinat
3315
+ sage: dw1 = DyckWord([1,0,1,1,1,0,0,1,1,0,0,0])
3316
+ sage: dw2 = DyckWord([1,1,1,1,0,1,1,0,0,0,0,0])
3317
+ sage: TamariIntervalPosets.from_dyck_words(dw1,dw2)
3318
+ The Tamari interval of size 6 induced by relations
3319
+ [(4, 5), (6, 5), (5, 2), (4, 3), (3, 2)]
3320
+ sage: dw3 = DyckWord([1,1,1,0,1,1,1,0,0,0,0,0])
3321
+ sage: TamariIntervalPosets.from_dyck_words(dw1,dw3)
3322
+ Traceback (most recent call last):
3323
+ ...
3324
+ ValueError: the two Dyck words are not comparable on the Tamari lattice
3325
+ sage: TamariIntervalPosets.from_dyck_words(dw1,DyckWord([1,0]))
3326
+ Traceback (most recent call last):
3327
+ ...
3328
+ ValueError: the two Dyck words are not comparable on the Tamari lattice
3329
+ """
3330
+ tree1 = dw1.to_binary_tree_tamari()
3331
+ tree2 = dw2.to_binary_tree_tamari()
3332
+ try:
3333
+ return TamariIntervalPosets.from_binary_trees(tree1, tree2)
3334
+ except ValueError:
3335
+ raise ValueError("the two Dyck words are not comparable on the Tamari lattice")
3336
+
3337
+ @staticmethod
3338
+ def recomposition_from_triple(left: TIP, right: TIP, r) -> TIP:
3339
+ """
3340
+ Recompose an interval-poset from a triple (``left``, ``right``, ``r``).
3341
+
3342
+ For the inverse method,
3343
+ see :meth:`TamariIntervalPoset.decomposition_to_triple`.
3344
+
3345
+ INPUT:
3346
+
3347
+ - ``left`` -- an interval-poset
3348
+ - ``right`` -- an interval-poset
3349
+ - ``r`` -- the parameter of the decomposition, an integer
3350
+
3351
+ OUTPUT: an interval-poset
3352
+
3353
+ EXAMPLES::
3354
+
3355
+ sage: T1 = TamariIntervalPoset(3, [(1, 2), (3, 2)])
3356
+ sage: T2 = TamariIntervalPoset(4, [(2, 3), (4, 3)])
3357
+ sage: TamariIntervalPosets.recomposition_from_triple(T1, T2, 2)
3358
+ The Tamari interval of size 8 induced by relations [(1, 2), (2, 4),
3359
+ (3, 4), (6, 7), (8, 7), (6, 4), (5, 4), (3, 2)]
3360
+
3361
+ REFERENCES:
3362
+
3363
+ - [Pons2018]_
3364
+ """
3365
+ root = left.size() + 1
3366
+ rel = left.poset().cover_relations()
3367
+ rel.extend((i, root) for i in left)
3368
+ rel.extend((root + a, root + b)
3369
+ for a, b in right.poset().cover_relations())
3370
+ decroot = right.decreasing_roots()[:r]
3371
+ rel.extend((root + i, root) for i in decroot)
3372
+ # does this describe only cover relations ?
3373
+ # if yes, create the poset here and pass it as argument
3374
+ return TamariIntervalPoset(left.size() + right.size() + 1, rel)
3375
+
3376
+ @staticmethod
3377
+ def from_grafting_tree(tree) -> TIP:
3378
+ """
3379
+ Return an interval-poset from a grafting tree.
3380
+
3381
+ For the inverse method,
3382
+ see :meth:`TamariIntervalPoset.grafting_tree`.
3383
+
3384
+ EXAMPLES::
3385
+
3386
+ sage: tip = TamariIntervalPoset(8, [(1,2), (2,4), (3,4), (6,7), (3,2), (5,4), (6,4), (8,7)])
3387
+ sage: t = tip.grafting_tree()
3388
+ sage: TamariIntervalPosets.from_grafting_tree(t) == tip
3389
+ True
3390
+
3391
+ REFERENCES:
3392
+
3393
+ - [Pons2018]_
3394
+ """
3395
+ if tree.is_empty():
3396
+ return TamariIntervalPoset(0, [])
3397
+ r = tree.label()
3398
+ left = TamariIntervalPosets.from_grafting_tree(tree[0])
3399
+ right = TamariIntervalPosets.from_grafting_tree(tree[1])
3400
+ return TamariIntervalPosets.recomposition_from_triple(left, right, r)
3401
+
3402
+ @staticmethod
3403
+ def from_minimal_schnyder_wood(graph) -> TIP:
3404
+ """
3405
+ Return a Tamari interval built from a minimal Schnyder wood.
3406
+
3407
+ This is an implementation of Bernardi and Bonichon's bijection
3408
+ [BeBo2009]_.
3409
+
3410
+ INPUT:
3411
+
3412
+ - ``graph`` -- a minimal Schnyder wood, given as a graph with colored
3413
+ and oriented edges, without the three exterior unoriented edges
3414
+
3415
+ The three boundary vertices must be -1, -2 and -3.
3416
+
3417
+ One assumes moreover that the embedding around -1 is the
3418
+ list of neighbors of -1 and not just a cyclic permutation of that.
3419
+
3420
+ Beware that the embedding convention used here is the opposite of
3421
+ the one used by the plot method.
3422
+
3423
+ OUTPUT: a Tamari interval-poset
3424
+
3425
+ EXAMPLES:
3426
+
3427
+ A small example::
3428
+
3429
+ sage: TIP = TamariIntervalPosets
3430
+ sage: G = DiGraph([(0,-1,0),(0,-2,1),(0,-3,2)], format='list_of_edges')
3431
+ sage: G.set_embedding({-1:[0],-2:[0],-3:[0],0:[-1,-2,-3]})
3432
+ sage: TIP.from_minimal_schnyder_wood(G) # needs sage.combinat
3433
+ The Tamari interval of size 1 induced by relations []
3434
+
3435
+ An example from page 14 of [BeBo2009]_::
3436
+
3437
+ sage: c0 = [(0,-1),(1,0),(2,0),(4,3),(3,-1),(5,3)]
3438
+ sage: c1 = [(5,-2),(3,-2),(4,5),(1,3),(2,3),(0,3)]
3439
+ sage: c2 = [(0,-3),(1,-3),(3,-3),(4,-3),(5,-3),(2,1)]
3440
+ sage: ed = [(u,v,0) for u,v in c0]
3441
+ sage: ed += [(u,v,1) for u,v in c1]
3442
+ sage: ed += [(u,v,2) for u,v in c2]
3443
+ sage: G = DiGraph(ed, format='list_of_edges')
3444
+ sage: embed = {-1:[3,0],-2:[5,3],-3:[0,1,3,4,5]}
3445
+ sage: data_emb = [[3,2,1,-3,-1],[2,3,-3,0],[3,1,0]]
3446
+ sage: data_emb += [[-2,5,4,-3,1,2,0,-1],[5,-3,3],[-2,-3,4,3]]
3447
+ sage: for k in range(6):
3448
+ ....: embed[k] = data_emb[k]
3449
+ sage: G.set_embedding(embed)
3450
+ sage: TIP.from_minimal_schnyder_wood(G) # needs sage.combinat
3451
+ The Tamari interval of size 6 induced by relations
3452
+ [(1, 4), (2, 4), (3, 4), (5, 6), (6, 4), (5, 4), (3, 1), (2, 1)]
3453
+
3454
+ An example from page 18 of [BeBo2009]_::
3455
+
3456
+ sage: c0 = [(0,-1),(1,0),(2,-1),(3,2),(4,2),(5,-1)]
3457
+ sage: c1 = [(5,-2),(2,-2),(4,-2),(3,4),(1,2),(0,2)]
3458
+ sage: c2 = [(0,-3),(1,-3),(3,-3),(4,-3),(2,-3),(5,2)]
3459
+ sage: ed = [(u,v,0) for u,v in c0]
3460
+ sage: ed += [(u,v,1) for u,v in c1]
3461
+ sage: ed += [(u,v,2) for u,v in c2]
3462
+ sage: G = DiGraph(ed, format='list_of_edges')
3463
+ sage: embed = {-1:[5,2,0],-2:[4,2,5],-3:[0,1,2,3,4]}
3464
+ sage: data_emb = [[2,1,-3,-1],[2,-3,0],[3,-3,1,0,-1,5,-2,4]]
3465
+ sage: data_emb += [[4,-3,2],[-2,-3,3,2],[-2,2,-1]]
3466
+ sage: for k in range(6):
3467
+ ....: embed[k] = data_emb[k]
3468
+ sage: G.set_embedding(embed)
3469
+ sage: TIP.from_minimal_schnyder_wood(G) # needs sage.combinat
3470
+ The Tamari interval of size 6 induced by relations
3471
+ [(1, 3), (2, 3), (4, 5), (5, 3), (4, 3), (2, 1)]
3472
+
3473
+ Another small example::
3474
+
3475
+ sage: c0 = [(0,-1),(2,-1),(1,0)]
3476
+ sage: c1 = [(2,-2),(1,-2),(0,2)]
3477
+ sage: c2 = [(0,-3),(1,-3),(2,1)]
3478
+ sage: ed = [(u,v,0) for u,v in c0]
3479
+ sage: ed += [(u,v,1) for u,v in c1]
3480
+ sage: ed += [(u,v,2) for u,v in c2]
3481
+ sage: G = DiGraph(ed, format='list_of_edges')
3482
+ sage: embed = {-1:[2,0],-2:[1,2],-3:[0,1]}
3483
+ sage: data_emb = [[2,1,-3,-1],[-3,0,2,-2],[-2,1,0,-1]]
3484
+ sage: for k in range(3):
3485
+ ....: embed[k] = data_emb[k]
3486
+ sage: G.set_embedding(embed)
3487
+ sage: TIP.from_minimal_schnyder_wood(G) # needs sage.combinat
3488
+ The Tamari interval of size 3 induced by relations [(2, 3), (2, 1)]
3489
+ """
3490
+ from sage.combinat.dyck_word import DyckWord
3491
+ color_a = graph.incoming_edges(-1)[0][2]
3492
+ color_b = graph.incoming_edges(-2)[0][2]
3493
+
3494
+ embedding = graph.get_embedding()
3495
+ graph0 = DiGraph([e for e in graph.edges(sort=False)
3496
+ if e[2] == color_a],
3497
+ format='list_of_edges')
3498
+ restricted_embedding = {u: [v for v in embedding[u]
3499
+ if v in graph0.neighbors_in(u) or
3500
+ v in graph0.neighbors_out(u)]
3501
+ for u in graph0}
3502
+
3503
+ voisins_in = {}
3504
+ for u in graph0:
3505
+ if u != -1:
3506
+ bad_emb = restricted_embedding[u]
3507
+ sortie = graph0.neighbors_out(u)[0]
3508
+ idx = bad_emb.index(sortie)
3509
+ restricted_embedding[u] = bad_emb[idx:] + bad_emb[:idx]
3510
+ voisins_in[u] = restricted_embedding[u][1:]
3511
+ else:
3512
+ voisins_in[u] = list(restricted_embedding[u])
3513
+ voisins_in[u].reverse() # To have them in the right order
3514
+
3515
+ graph0.set_embedding(restricted_embedding)
3516
+
3517
+ def clockwise_labelling(gr, vertex):
3518
+ if len(gr) == 1:
3519
+ return [vertex]
3520
+ lbl = [vertex]
3521
+ for w in voisins_in[vertex]:
3522
+ lbl += clockwise_labelling(gr, w)
3523
+ return lbl
3524
+
3525
+ def profil(gr, vertex):
3526
+ if len(gr) == 1:
3527
+ return []
3528
+ lbl = []
3529
+ for w in voisins_in[vertex]:
3530
+ lbl += [1] + profil(gr, w) + [0]
3531
+ return lbl
3532
+
3533
+ dyckword_bottom = profil(graph0, -1)
3534
+ # this is the profile of the planar graph graph0
3535
+
3536
+ liste = clockwise_labelling(graph0, -1)[1:]
3537
+ relabelling = {l: i for i, l in enumerate(liste)}
3538
+ relabelling.update((i, i) for i in [-1, -2, -3])
3539
+ new_graph = graph.relabel(relabelling, inplace=False)
3540
+
3541
+ dyckword_top = []
3542
+ for i in range(1, len(graph) - 3):
3543
+ indegree1 = len([u for u in new_graph.incoming_edges(i)
3544
+ if u[2] == color_b])
3545
+ dyckword_top += [1] + [0] * indegree1
3546
+ indegree1 = len([u for u in new_graph.incoming_edges(-2)
3547
+ if u[2] == color_b])
3548
+ dyckword_top += [1] + [0] * indegree1
3549
+
3550
+ dyckword_bottom = DyckWord(dyckword_bottom) # type:ignore
3551
+ dyckword_top = DyckWord(dyckword_top) # type:ignore
3552
+ tip = TamariIntervalPosets(len(dyckword_bottom) // 2)
3553
+ return tip.from_dyck_words(dyckword_bottom, dyckword_top)
3554
+
3555
+ def __call__(self, *args, **keywords):
3556
+ r"""
3557
+ Allow for a poset to be directly transformed into an interval-poset.
3558
+
3559
+ It is some kind of coercion but cannot be made through the coercion
3560
+ system because posets do not have parents.
3561
+
3562
+ EXAMPLES::
3563
+
3564
+ sage: TIP = TamariIntervalPosets()
3565
+ sage: p = Poset( ([1,2,3], [(1,2)]))
3566
+ sage: TIP(p)
3567
+ The Tamari interval of size 3 induced by relations [(1, 2)]
3568
+ sage: TIP(TIP(p))
3569
+ The Tamari interval of size 3 induced by relations [(1, 2)]
3570
+ sage: TIP(3,[(1,2)])
3571
+ The Tamari interval of size 3 induced by relations [(1, 2)]
3572
+ sage: p = Poset(([1,2,3],[(1,3)]))
3573
+ sage: TIP(p)
3574
+ Traceback (most recent call last):
3575
+ ...
3576
+ ValueError: this does not satisfy the Tamari interval-poset condition
3577
+ """
3578
+ if isinstance(args[0], TamariIntervalPoset):
3579
+ return args[0]
3580
+ if len(args) == 1 and isinstance(args[0], FinitePoset):
3581
+ return self.element_class(self, args[0])
3582
+
3583
+ return super().__call__(*args, **keywords)
3584
+
3585
+ def le(self, el1, el2) -> bool:
3586
+ r"""
3587
+ Poset structure on the set of interval-posets.
3588
+
3589
+ The comparison is first by size, then using the
3590
+ cubical coordinates.
3591
+
3592
+ .. SEEALSO:: :meth:`~TamariIntervalPoset.cubical_coordinates`
3593
+
3594
+ INPUT:
3595
+
3596
+ - ``el1`` -- an interval-poset
3597
+ - ``el2`` -- an interval-poset
3598
+
3599
+ EXAMPLES::
3600
+
3601
+ sage: ip1 = TamariIntervalPoset(4,[(1,2),(2,3),(4,3)])
3602
+ sage: ip2 = TamariIntervalPoset(4,[(1,2),(2,3)])
3603
+ sage: TamariIntervalPosets().le(ip1,ip2)
3604
+ False
3605
+ sage: TamariIntervalPosets().le(ip2,ip1)
3606
+ True
3607
+ """
3608
+ cc1 = el1.cubical_coordinates()
3609
+ cc2 = el2.cubical_coordinates()
3610
+ return all(x1 <= x2 for x1, x2 in zip(cc1, cc2))
3611
+
3612
+ #################################################################
3613
+ # Enumerated set of all Tamari Interval-posets
3614
+ #################################################################
3615
+
3616
+
3617
+ class TamariIntervalPosets_all(DisjointUnionEnumeratedSets, TamariIntervalPosets):
3618
+ r"""
3619
+ The enumerated set of all Tamari interval-posets.
3620
+ """
3621
+
3622
+ def __init__(self):
3623
+ r"""
3624
+ TESTS::
3625
+
3626
+ sage: from sage.combinat.interval_posets import TamariIntervalPosets_all
3627
+ sage: S = TamariIntervalPosets_all()
3628
+ sage: S.cardinality()
3629
+ +Infinity
3630
+
3631
+ sage: it = iter(S)
3632
+ sage: [next(it) for i in range(5)]
3633
+ [The Tamari interval of size 0 induced by relations [],
3634
+ The Tamari interval of size 1 induced by relations [],
3635
+ The Tamari interval of size 2 induced by relations [],
3636
+ The Tamari interval of size 2 induced by relations [(2, 1)],
3637
+ The Tamari interval of size 2 induced by relations [(1, 2)]]
3638
+ sage: next(it).parent()
3639
+ Interval-posets
3640
+ sage: S(0,[])
3641
+ The Tamari interval of size 0 induced by relations []
3642
+
3643
+ sage: S is TamariIntervalPosets_all()
3644
+ True
3645
+ sage: TestSuite(S).run() # long time (7s)
3646
+ """
3647
+ DisjointUnionEnumeratedSets.__init__(
3648
+ self, Family(NonNegativeIntegers(), TamariIntervalPosets_size),
3649
+ facade=True, keepkey=False,
3650
+ category=(Posets(), EnumeratedSets(), Monoids()))
3651
+
3652
+ def _repr_(self) -> str:
3653
+ r"""
3654
+ TESTS::
3655
+
3656
+ sage: TamariIntervalPosets()
3657
+ Interval-posets
3658
+ """
3659
+ return "Interval-posets"
3660
+
3661
+ def one(self) -> TIP:
3662
+ """
3663
+ Return the unit of the monoid.
3664
+
3665
+ This is the empty interval poset, of size 0.
3666
+
3667
+ EXAMPLES::
3668
+
3669
+ sage: TamariIntervalPosets().one()
3670
+ The Tamari interval of size 0 induced by relations []
3671
+ """
3672
+ return TamariIntervalPoset(0, [])
3673
+
3674
+ def _element_constructor_(self, size, relations) -> TIP:
3675
+ r"""
3676
+ EXAMPLES::
3677
+
3678
+ sage: TIP = TamariIntervalPosets()
3679
+ sage: TIP(3,[(1,2)])
3680
+ The Tamari interval of size 3 induced by relations [(1, 2)]
3681
+ """
3682
+ return self.element_class(self, size, relations)
3683
+
3684
+ def __contains__(self, x) -> bool:
3685
+ r"""
3686
+ TESTS::
3687
+
3688
+ sage: S = TamariIntervalPosets()
3689
+ sage: 1 in S
3690
+ False
3691
+ sage: S(0,[]) in S
3692
+ True
3693
+ """
3694
+ return isinstance(x, self.element_class)
3695
+
3696
+ Element = TamariIntervalPoset
3697
+
3698
+
3699
+ TIP = TamariIntervalPoset
3700
+
3701
+
3702
+ #################################################################
3703
+ # Enumerated set of Tamari interval-posets of a given size
3704
+ #################################################################
3705
+ class TamariIntervalPosets_size(TamariIntervalPosets):
3706
+ r"""
3707
+ The enumerated set of interval-posets of a given size.
3708
+ """
3709
+
3710
+ def __init__(self, size):
3711
+ r"""
3712
+ TESTS::
3713
+
3714
+ sage: S = TamariIntervalPosets(3)
3715
+ sage: assert S is TamariIntervalPosets(3)
3716
+ sage: for i in range(5): TestSuite(TamariIntervalPosets(i)).run()
3717
+ """
3718
+ # there is a natural order on interval-posets through inclusions
3719
+ # that is why we use the FinitePosets category
3720
+ super().__init__(category=(FinitePosets(), FiniteEnumeratedSets()))
3721
+
3722
+ self._size = size
3723
+
3724
+ def _repr_(self) -> str:
3725
+ r"""
3726
+ TESTS::
3727
+
3728
+ sage: TamariIntervalPosets(3)
3729
+ Interval-posets of size 3
3730
+ """
3731
+ return f"Interval-posets of size {self._size}"
3732
+
3733
+ def __contains__(self, x) -> bool:
3734
+ r"""
3735
+ TESTS::
3736
+
3737
+ sage: S = TamariIntervalPosets(3)
3738
+ sage: 1 in S
3739
+ False
3740
+ sage: S([]) in S
3741
+ True
3742
+ """
3743
+ return isinstance(x, self.element_class) and x.size() == self._size
3744
+
3745
+ def cardinality(self) -> Integer:
3746
+ r"""
3747
+ The cardinality of ``self``. That is, the number of
3748
+ interval-posets of size `n`.
3749
+
3750
+ The formula was given in [Cha2008]_:
3751
+
3752
+ .. MATH::
3753
+
3754
+ \frac{2(4n+1)!}{(n+1)!(3n+2)!}
3755
+ = \frac{2}{n(n+1)} \binom{4n+1}{n-1}.
3756
+
3757
+ EXAMPLES::
3758
+
3759
+ sage: [TamariIntervalPosets(i).cardinality() for i in range(6)]
3760
+ [1, 1, 3, 13, 68, 399]
3761
+ """
3762
+ n = self._size
3763
+ if n == 0:
3764
+ return ZZ.one()
3765
+ return (2 * Integer(4 * n + 1).binomial(n - 1)) // (n * (n + 1))
3766
+ # return Integer(2 * factorial(4*n+1)/(factorial(n+1)*factorial(3*n+2)))
3767
+
3768
+ def __iter__(self) -> Iterator[TIP]:
3769
+ r"""
3770
+ Recursive generation: we iterate through all interval-posets of
3771
+ size ``size - 1`` and add all possible relations to the last
3772
+ vertex.
3773
+
3774
+ This works thanks to the fact that the restriction of an
3775
+ interval-poset of size `n` to the subset `\{1, 2, \ldots, k\}` for
3776
+ a fixed `k \leq n` is an interval-poset.
3777
+
3778
+ TESTS::
3779
+
3780
+ sage: TIP1 = TamariIntervalPosets(1)
3781
+ sage: list(TIP1)
3782
+ [The Tamari interval of size 1 induced by relations []]
3783
+ sage: TIP2 = TamariIntervalPosets(2)
3784
+ sage: list(TIP2)
3785
+ [The Tamari interval of size 2 induced by relations [],
3786
+ The Tamari interval of size 2 induced by relations [(2, 1)],
3787
+ The Tamari interval of size 2 induced by relations [(1, 2)]]
3788
+ sage: TIP3 = TamariIntervalPosets(3)
3789
+ sage: list(TIP3)
3790
+ [The Tamari interval of size 3 induced by relations [],
3791
+ The Tamari interval of size 3 induced by relations [(3, 2)],
3792
+ The Tamari interval of size 3 induced by relations [(2, 3)],
3793
+ The Tamari interval of size 3 induced by relations [(1, 3), (2, 3)],
3794
+ The Tamari interval of size 3 induced by relations [(2, 1)],
3795
+ The Tamari interval of size 3 induced by relations [(3, 2), (2, 1)],
3796
+ The Tamari interval of size 3 induced by relations [(3, 1), (2, 1)],
3797
+ The Tamari interval of size 3 induced by relations [(2, 3), (2, 1)],
3798
+ The Tamari interval of size 3 induced by relations [(2, 3), (3, 1), (2, 1)],
3799
+ The Tamari interval of size 3 induced by relations [(1, 3), (2, 3), (2, 1)],
3800
+ The Tamari interval of size 3 induced by relations [(1, 2)],
3801
+ The Tamari interval of size 3 induced by relations [(1, 2), (3, 2)],
3802
+ The Tamari interval of size 3 induced by relations [(1, 2), (2, 3)]]
3803
+ sage: all(len(list(TamariIntervalPosets(i)))==TamariIntervalPosets(i).cardinality() for i in range(6))
3804
+ True
3805
+ """
3806
+ n = self._size
3807
+ if n <= 1:
3808
+ yield TamariIntervalPoset(n, [], check=False)
3809
+ return
3810
+
3811
+ for tip in TamariIntervalPosets(n - 1):
3812
+ new_tip = TamariIntervalPoset(n, tip._cover_relations, check=False)
3813
+ yield new_tip # we have added an extra vertex but no relations
3814
+
3815
+ # adding a decreasing relation n>>m2 with m2<n and no
3816
+ # increasing relations
3817
+ for m2 in range(n - 1, 0, -1):
3818
+ if new_tip.le(n - 1, m2):
3819
+ yield TamariIntervalPoset(n, new_tip._cover_relations + ((n, m2),), check=False)
3820
+
3821
+ for m in range(n - 1, 0, -1):
3822
+ # adding an increasing relation m>>n
3823
+ if not new_tip.le(m, n):
3824
+ new_tip = TamariIntervalPoset(n, new_tip._cover_relations + ((m, n),), check=False)
3825
+ yield new_tip
3826
+ else:
3827
+ continue
3828
+
3829
+ # further adding a decreasing relation n>>m2 with m2<m
3830
+ for m2 in range(m - 1, 0, -1):
3831
+ if new_tip.le(n - 1, m2):
3832
+ yield TamariIntervalPoset(n, new_tip._cover_relations + ((n, m2),), check=False)
3833
+
3834
+ def random_element(self) -> TIP:
3835
+ """
3836
+ Return a random Tamari interval of fixed size.
3837
+
3838
+ This is obtained by first creating a random rooted
3839
+ planar triangulation, then computing its unique
3840
+ minimal Schnyder wood, then applying a bijection
3841
+ of Bernardi and Bonichon [BeBo2009]_.
3842
+
3843
+ Because the random rooted planar triangulation is
3844
+ chosen uniformly at random, the Tamari interval is
3845
+ also chosen according to the uniform distribution.
3846
+
3847
+ EXAMPLES::
3848
+
3849
+ sage: # needs sage.combinat
3850
+ sage: T = TamariIntervalPosets(4).random_element()
3851
+ sage: T.parent()
3852
+ Interval-posets
3853
+ sage: u = T.lower_dyck_word(); u # random
3854
+ [1, 1, 0, 1, 0, 0, 1, 0]
3855
+ sage: v = T.lower_dyck_word(); v # random
3856
+ [1, 1, 0, 1, 0, 0, 1, 0]
3857
+ sage: len(u)
3858
+ 8
3859
+ """
3860
+ from sage.graphs.schnyder import minimal_schnyder_wood
3861
+ from sage.graphs.generators.random import RandomTriangulation
3862
+ n = self._size
3863
+ tri = RandomTriangulation(n + 3)
3864
+ tip = TamariIntervalPosets
3865
+ schnyder = minimal_schnyder_wood(tri, root_edge=(-1, -2),
3866
+ check=False)
3867
+ return tip.from_minimal_schnyder_wood(schnyder)
3868
+
3869
+ @lazy_attribute
3870
+ def _parent_for(self):
3871
+ r"""
3872
+ The parent of the element generated by ``self``.
3873
+
3874
+ TESTS::
3875
+
3876
+ sage: TIP3 = TamariIntervalPosets(3)
3877
+ sage: TIP3._parent_for
3878
+ Interval-posets
3879
+ """
3880
+ return TamariIntervalPosets_all()
3881
+
3882
+ # This is needed because this is a facade parent via DisjointUnionEnumeratedSets
3883
+ @lazy_attribute
3884
+ def element_class(self):
3885
+ r"""
3886
+ TESTS::
3887
+
3888
+ sage: S = TamariIntervalPosets(3)
3889
+ sage: S.element_class
3890
+ <class 'sage.combinat.interval_posets.TamariIntervalPosets_all_with_category.element_class'>
3891
+ sage: S.first().__class__ == TamariIntervalPosets().first().__class__
3892
+ True
3893
+ """
3894
+ return self._parent_for.element_class
3895
+
3896
+ def _element_constructor_(self, relations) -> TIP:
3897
+ r"""
3898
+ EXAMPLES::
3899
+
3900
+ sage: TIP3 = TamariIntervalPosets(3)
3901
+ sage: TIP3([(1,2)])
3902
+ The Tamari interval of size 3 induced by relations [(1, 2)]
3903
+ sage: TIP3([(3,4)])
3904
+ Traceback (most recent call last):
3905
+ ...
3906
+ ValueError: the relations do not correspond to the size of the poset
3907
+ """
3908
+ return self.element_class(self, self._size, relations)