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.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- 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)
|