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,1070 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Rooted (unordered) trees
|
4
|
+
|
5
|
+
AUTHORS:
|
6
|
+
|
7
|
+
- Florent Hivert (2011): initial version
|
8
|
+
"""
|
9
|
+
|
10
|
+
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
|
11
|
+
from sage.categories.sets_cat import Sets
|
12
|
+
from sage.combinat.abstract_tree import (AbstractClonableTree,
|
13
|
+
AbstractLabelledClonableTree)
|
14
|
+
from sage.misc.cachefunc import cached_function, cached_method
|
15
|
+
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
|
16
|
+
from sage.misc.lazy_attribute import lazy_attribute, lazy_class_attribute
|
17
|
+
from sage.rings.integer import Integer
|
18
|
+
from sage.rings.integer_ring import ZZ
|
19
|
+
from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets
|
20
|
+
from sage.sets.family import Family
|
21
|
+
from sage.sets.non_negative_integers import NonNegativeIntegers
|
22
|
+
from sage.structure.list_clone import NormalizedClonableList
|
23
|
+
from sage.structure.parent import Parent
|
24
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
25
|
+
|
26
|
+
|
27
|
+
@cached_function
|
28
|
+
def number_of_rooted_trees(n):
|
29
|
+
r"""
|
30
|
+
Return the number of rooted trees with `n` nodes.
|
31
|
+
|
32
|
+
Compute the number `a(n)` of rooted trees with `n` nodes using the
|
33
|
+
recursive formula ([SL000081]_):
|
34
|
+
|
35
|
+
.. MATH::
|
36
|
+
|
37
|
+
a(n+1) = \frac{1}{n} \sum_{k=1}^{n} \left( \sum_{d|k} d a(d) \right) a(n-k+1)
|
38
|
+
|
39
|
+
EXAMPLES::
|
40
|
+
|
41
|
+
sage: from sage.combinat.rooted_tree import number_of_rooted_trees
|
42
|
+
sage: [number_of_rooted_trees(i) for i in range(10)]
|
43
|
+
[0, 1, 1, 2, 4, 9, 20, 48, 115, 286]
|
44
|
+
|
45
|
+
REFERENCES:
|
46
|
+
|
47
|
+
.. [SL000081] Sloane's :oeis:`A000081`
|
48
|
+
"""
|
49
|
+
if n == 0:
|
50
|
+
return Integer(0)
|
51
|
+
if n == 1:
|
52
|
+
return Integer(1)
|
53
|
+
n = Integer(n)
|
54
|
+
return sum(sum(d * number_of_rooted_trees(d) for d in k.divisors()) *
|
55
|
+
number_of_rooted_trees(n - k)
|
56
|
+
for k in ZZ.range(1, n)) // (n - 1)
|
57
|
+
|
58
|
+
|
59
|
+
class RootedTree(AbstractClonableTree, NormalizedClonableList,
|
60
|
+
metaclass=InheritComparisonClasscallMetaclass):
|
61
|
+
r"""
|
62
|
+
The class for unordered rooted trees.
|
63
|
+
|
64
|
+
The *unordered rooted trees* are an inductive datatype defined
|
65
|
+
as follows: An unordered rooted tree is a multiset of
|
66
|
+
unordered rooted trees. The trees that belong to this
|
67
|
+
multiset are said to be the *children* of the tree. The tree
|
68
|
+
that has no children is called a *leaf*.
|
69
|
+
|
70
|
+
The *labelled rooted trees* (:class:`LabelledRootedTree`)
|
71
|
+
form a subclass of this class; they carry additional data.
|
72
|
+
|
73
|
+
One can create a tree from any list (or more generally iterable)
|
74
|
+
of trees or objects convertible to a tree.
|
75
|
+
|
76
|
+
EXAMPLES::
|
77
|
+
|
78
|
+
sage: RootedTree([])
|
79
|
+
[]
|
80
|
+
sage: RootedTree([[], [[]]])
|
81
|
+
[[], [[]]]
|
82
|
+
sage: RootedTree([[[]], []])
|
83
|
+
[[], [[]]]
|
84
|
+
sage: O = OrderedTree([[[]], []]); O
|
85
|
+
[[[]], []]
|
86
|
+
sage: RootedTree(O) # this is O with the ordering forgotten
|
87
|
+
[[], [[]]]
|
88
|
+
|
89
|
+
One can also enter any small rooted tree ("small" meaning that
|
90
|
+
no vertex has more than `15` children) by using a simple
|
91
|
+
numerical encoding of rooted trees, namely, the
|
92
|
+
:func:`~sage.combinat.abstract_tree.from_hexacode` function.
|
93
|
+
(This function actually parametrizes ordered trees, and here
|
94
|
+
we make it parametrize unordered trees by forgetting the
|
95
|
+
ordering.) ::
|
96
|
+
|
97
|
+
sage: from sage.combinat.abstract_tree import from_hexacode
|
98
|
+
sage: RT = RootedTrees()
|
99
|
+
sage: from_hexacode('32001010', RT)
|
100
|
+
[[[]], [[]], [[], []]]
|
101
|
+
|
102
|
+
.. NOTE::
|
103
|
+
|
104
|
+
Unlike an ordered tree, an (unordered) rooted tree is a
|
105
|
+
multiset (rather than a list) of children. That is, two
|
106
|
+
ordered trees which differ from each other by switching
|
107
|
+
the order of children are equal to each other as (unordered)
|
108
|
+
rooted trees. Internally, rooted trees are encoded as
|
109
|
+
:class:`sage.structure.list_clone.NormalizedClonableList`
|
110
|
+
instances, and instead of storing their children as an
|
111
|
+
actual multiset, they store their children as a list which
|
112
|
+
is sorted according to their :meth:`sort_key` value. This
|
113
|
+
is as good as storing them as multisets, since the
|
114
|
+
:meth:`sort_key` values are sortable and distinguish
|
115
|
+
different (unordered) trees. However, if you wish to define
|
116
|
+
a subclass of :class:`RootedTree` which implements rooted
|
117
|
+
trees with extra structure (say, a class of edge-colored
|
118
|
+
rooted trees, or a class of rooted trees with a cyclic
|
119
|
+
order on the list of children), then the inherited
|
120
|
+
:meth:`sort_key` method will no longer distinguish different
|
121
|
+
trees (and, as a consequence, equal trees will be regarded
|
122
|
+
as distinct). Thus, you will have to override the method by
|
123
|
+
one that does distinguish different trees.
|
124
|
+
"""
|
125
|
+
# Standard auto-parent trick
|
126
|
+
@staticmethod
|
127
|
+
def __classcall_private__(cls, *args, **opts):
|
128
|
+
"""
|
129
|
+
Ensure that rooted trees created by the enumerated sets and directly
|
130
|
+
are the same and that they are instances of :class:`RootedTree`.
|
131
|
+
|
132
|
+
TESTS::
|
133
|
+
|
134
|
+
sage: from sage.combinat.rooted_tree import (RootedTrees_all,
|
135
|
+
....: RootedTrees_size)
|
136
|
+
sage: issubclass(RootedTrees_all().element_class, RootedTree)
|
137
|
+
True
|
138
|
+
sage: issubclass(RootedTrees_size(3).element_class, RootedTree)
|
139
|
+
True
|
140
|
+
sage: t0 = RootedTree([[],[[]]])
|
141
|
+
sage: t0.parent()
|
142
|
+
Rooted trees
|
143
|
+
sage: type(t0)
|
144
|
+
<class 'sage.combinat.rooted_tree.RootedTrees_all_with_category.element_class'>
|
145
|
+
|
146
|
+
sage: t1 = RootedTrees()([[],[[]]])
|
147
|
+
sage: t1.parent() is t0.parent()
|
148
|
+
True
|
149
|
+
sage: type(t1) is type(t0)
|
150
|
+
True
|
151
|
+
|
152
|
+
sage: t1 = RootedTrees(4)([[],[[]]])
|
153
|
+
sage: t1.parent() is t0.parent()
|
154
|
+
True
|
155
|
+
sage: type(t1) is type(t0)
|
156
|
+
True
|
157
|
+
"""
|
158
|
+
return cls._auto_parent.element_class(cls._auto_parent, *args, **opts)
|
159
|
+
|
160
|
+
@lazy_class_attribute
|
161
|
+
def _auto_parent(cls):
|
162
|
+
"""
|
163
|
+
The automatic parent of the elements of this class.
|
164
|
+
|
165
|
+
When calling the constructor of an element of this class, one needs a
|
166
|
+
parent. This class attribute specifies which parent is used.
|
167
|
+
|
168
|
+
EXAMPLES::
|
169
|
+
|
170
|
+
sage: RootedTree._auto_parent
|
171
|
+
Rooted trees
|
172
|
+
sage: RootedTree([]).parent()
|
173
|
+
Rooted trees
|
174
|
+
"""
|
175
|
+
return RootedTrees_all()
|
176
|
+
|
177
|
+
def __init__(self, parent=None, children=[], check=True):
|
178
|
+
"""
|
179
|
+
TESTS::
|
180
|
+
|
181
|
+
sage: RT4 = RootedTrees(4)
|
182
|
+
sage: t1 = RT4([[],[[]]])
|
183
|
+
sage: TestSuite(t1).run()
|
184
|
+
|
185
|
+
Some bad inputs are refused::
|
186
|
+
|
187
|
+
sage: RT4(69)
|
188
|
+
Traceback (most recent call last):
|
189
|
+
...
|
190
|
+
TypeError: input (69) is not a valid tree
|
191
|
+
"""
|
192
|
+
try:
|
193
|
+
children = list(children)
|
194
|
+
except TypeError:
|
195
|
+
raise TypeError(f"input ({children}) is not a valid tree")
|
196
|
+
# if not (children.__class__ is self.__class__
|
197
|
+
# and children.parent() == parent):
|
198
|
+
children = [self.__class__(parent, x) for x in children]
|
199
|
+
NormalizedClonableList.__init__(self, parent, children, check=check)
|
200
|
+
|
201
|
+
def sort_key(self):
|
202
|
+
"""
|
203
|
+
Return a tuple of nonnegative integers encoding the rooted
|
204
|
+
tree ``self``.
|
205
|
+
|
206
|
+
The first entry of the tuple is the number of children of the
|
207
|
+
root. Then the rest of the tuple is obtained as follows: List
|
208
|
+
the tuples corresponding to all children (we are regarding the
|
209
|
+
children themselves as trees). Order this list (not the
|
210
|
+
tuples!) in lexicographically increasing order, and flatten
|
211
|
+
it into a single tuple.
|
212
|
+
|
213
|
+
This tuple characterizes the rooted tree uniquely, and can be
|
214
|
+
used to sort the rooted trees.
|
215
|
+
|
216
|
+
.. NOTE::
|
217
|
+
|
218
|
+
The tree ``self`` must be normalized before calling this
|
219
|
+
method (see :meth:`normalize`). This does not matter
|
220
|
+
unless you are inside the :meth:`clone` context manager,
|
221
|
+
because outside of it every rooted tree is already
|
222
|
+
normalized.
|
223
|
+
|
224
|
+
.. NOTE::
|
225
|
+
|
226
|
+
By default, this method does not encode any extra
|
227
|
+
structure that ``self`` might have. If you have a subclass
|
228
|
+
inheriting from :class:`RootedTree` which allows for some
|
229
|
+
extra structure, you need to override :meth:`sort_key` in
|
230
|
+
order to preserve this structure (for example, the
|
231
|
+
:class:`LabelledRootedTree` class does this in
|
232
|
+
:meth:`LabelledRootedTree.sort_key`). See the note in the
|
233
|
+
docstring of
|
234
|
+
:meth:`sage.combinat.ordered_tree.OrderedTree.sort_key`
|
235
|
+
for a pitfall.
|
236
|
+
|
237
|
+
EXAMPLES::
|
238
|
+
|
239
|
+
sage: RT = RootedTree
|
240
|
+
sage: RT([[],[[]]]).sort_key()
|
241
|
+
(2, 0, 1, 0)
|
242
|
+
sage: RT([[[]],[]]).sort_key()
|
243
|
+
(2, 0, 1, 0)
|
244
|
+
"""
|
245
|
+
l = len(self)
|
246
|
+
if l == 0:
|
247
|
+
return (0,)
|
248
|
+
resu = [l] + [u for t in self for u in t.sort_key()]
|
249
|
+
return tuple(resu)
|
250
|
+
|
251
|
+
def __hash__(self):
|
252
|
+
"""
|
253
|
+
Return a hash for ``self``.
|
254
|
+
|
255
|
+
This is based on :meth:`sort_key`.
|
256
|
+
|
257
|
+
EXAMPLES::
|
258
|
+
|
259
|
+
sage: RT = RootedTree
|
260
|
+
sage: hash(RT([[],[[]]])) == hash((2, 0, 1, 0)) # indirect doctest
|
261
|
+
True
|
262
|
+
"""
|
263
|
+
return hash(self.sort_key())
|
264
|
+
|
265
|
+
def normalize(self):
|
266
|
+
r"""
|
267
|
+
Normalize ``self``.
|
268
|
+
|
269
|
+
This function is at the core of the implementation of rooted
|
270
|
+
(unordered) trees. The underlying structure is provided by
|
271
|
+
ordered rooted trees. Every rooted tree is represented by a
|
272
|
+
normalized element in the set of its planar embeddings.
|
273
|
+
|
274
|
+
There should be no need to call ``normalize`` directly as it
|
275
|
+
is called automatically upon creation and cloning or
|
276
|
+
modification (by ``NormalizedClonableList``).
|
277
|
+
|
278
|
+
The normalization has a recursive definition. It means first
|
279
|
+
that every sub-tree is itself normalized, and also that
|
280
|
+
sub-trees are sorted. Here the sort is performed according to
|
281
|
+
the values of the :meth:`sort_key` method.
|
282
|
+
|
283
|
+
EXAMPLES::
|
284
|
+
|
285
|
+
sage: RT = RootedTree
|
286
|
+
sage: RT([[],[[]]]) == RT([[[]],[]]) # indirect doctest
|
287
|
+
True
|
288
|
+
sage: rt1 = RT([[],[[]]])
|
289
|
+
sage: rt2 = RT([[[]],[]])
|
290
|
+
sage: rt1 is rt2
|
291
|
+
False
|
292
|
+
sage: rt1 == rt2
|
293
|
+
True
|
294
|
+
sage: rt1._get_list() == rt2._get_list()
|
295
|
+
True
|
296
|
+
"""
|
297
|
+
self._require_mutable()
|
298
|
+
for st in self:
|
299
|
+
assert st.is_immutable(), "Subtree {} is not normalized".format(st)
|
300
|
+
self._get_list().sort(key=lambda t: t.sort_key())
|
301
|
+
# ensure unique representation
|
302
|
+
self.set_immutable()
|
303
|
+
|
304
|
+
def is_empty(self):
|
305
|
+
r"""
|
306
|
+
Return if ``self`` is the empty tree.
|
307
|
+
|
308
|
+
For rooted trees, this always returns ``False``.
|
309
|
+
|
310
|
+
.. NOTE::
|
311
|
+
|
312
|
+
This is not the same as ``bool(t)``, which returns whether
|
313
|
+
``t`` has some child or not.
|
314
|
+
|
315
|
+
EXAMPLES::
|
316
|
+
|
317
|
+
sage: t = RootedTrees(4)([[],[[]]])
|
318
|
+
sage: t.is_empty()
|
319
|
+
False
|
320
|
+
sage: bool(t)
|
321
|
+
True
|
322
|
+
sage: t = RootedTrees(1)([])
|
323
|
+
sage: t.is_empty()
|
324
|
+
False
|
325
|
+
sage: bool(t)
|
326
|
+
False
|
327
|
+
"""
|
328
|
+
return False
|
329
|
+
|
330
|
+
def graft_list(self, other):
|
331
|
+
"""
|
332
|
+
Return the list of trees obtained by grafting ``other`` on ``self``.
|
333
|
+
|
334
|
+
Here grafting means that one takes the disjoint union of
|
335
|
+
``self`` and ``other``, chooses a node of ``self``,
|
336
|
+
and adds the root of ``other`` to the list of children of
|
337
|
+
this node. The root of the resulting tree is the root of
|
338
|
+
``self``. (This can be done for each node of ``self``;
|
339
|
+
this method returns the list of all results.)
|
340
|
+
|
341
|
+
This is useful for free pre-Lie algebras.
|
342
|
+
|
343
|
+
EXAMPLES::
|
344
|
+
|
345
|
+
sage: RT = RootedTree
|
346
|
+
sage: x = RT([])
|
347
|
+
sage: y = RT([x, x])
|
348
|
+
sage: x.graft_list(x)
|
349
|
+
[[[]]]
|
350
|
+
sage: l = y.graft_list(x); l
|
351
|
+
[[[], [], []], [[], [[]]], [[], [[]]]]
|
352
|
+
sage: [parent(i) for i in l]
|
353
|
+
[Rooted trees, Rooted trees, Rooted trees]
|
354
|
+
|
355
|
+
TESTS::
|
356
|
+
|
357
|
+
sage: x = RootedTrees(1)([])
|
358
|
+
sage: y = RootedTrees(3)([x, x])
|
359
|
+
sage: l = y.graft_list(x); l
|
360
|
+
[[[], [], []], [[], [[]]], [[], [[]]]]
|
361
|
+
sage: [parent(i) for i in l]
|
362
|
+
[Rooted trees, Rooted trees, Rooted trees]
|
363
|
+
|
364
|
+
sage: x = RootedTree([[[], []], []])
|
365
|
+
sage: y = RootedTree([[], []])
|
366
|
+
sage: len(set(x.graft_list(y)))
|
367
|
+
4
|
368
|
+
"""
|
369
|
+
resu = []
|
370
|
+
# Grafting ``other`` on the root:
|
371
|
+
with self.clone() as t:
|
372
|
+
t.append(other)
|
373
|
+
resu += [t]
|
374
|
+
for i, sub in enumerate(self):
|
375
|
+
# Grafting ``other`` on a descendant of the
|
376
|
+
# ``i``-th child:
|
377
|
+
for new_sub in sub.graft_list(other):
|
378
|
+
with self.clone() as t:
|
379
|
+
t[i] = new_sub
|
380
|
+
resu += [t]
|
381
|
+
return resu
|
382
|
+
|
383
|
+
def graft_on_root(self, other):
|
384
|
+
"""
|
385
|
+
Return the tree obtained by grafting ``other`` on the root of ``self``.
|
386
|
+
|
387
|
+
Here grafting means that one takes the disjoint union of
|
388
|
+
``self`` and ``other``, and adds the root of ``other`` to
|
389
|
+
the list of children of ``self``. The root of the resulting
|
390
|
+
tree is the root of ``self``.
|
391
|
+
|
392
|
+
This is useful for free Nap algebras.
|
393
|
+
|
394
|
+
EXAMPLES::
|
395
|
+
|
396
|
+
sage: RT = RootedTree
|
397
|
+
sage: x = RT([])
|
398
|
+
sage: y = RT([x, x])
|
399
|
+
sage: x.graft_on_root(x)
|
400
|
+
[[]]
|
401
|
+
sage: y.graft_on_root(x)
|
402
|
+
[[], [], []]
|
403
|
+
sage: x.graft_on_root(y)
|
404
|
+
[[[], []]]
|
405
|
+
"""
|
406
|
+
with self.clone() as t:
|
407
|
+
t.append(other)
|
408
|
+
return t
|
409
|
+
|
410
|
+
def single_graft(self, x, grafting_function, path_prefix=()):
|
411
|
+
r"""
|
412
|
+
Graft subtrees of `x` on ``self`` using the given function.
|
413
|
+
|
414
|
+
Let `x_1, x_2, \ldots, x_p` be the children of the root of
|
415
|
+
`x`. For each `i`, the subtree of `x` comprising all
|
416
|
+
descendants of `x_i` is joined by a new edge to
|
417
|
+
the vertex of ``self`` specified by the `i`-th path in the
|
418
|
+
grafting function (i.e., by the path
|
419
|
+
``grafting_function[i]``).
|
420
|
+
|
421
|
+
The number of vertices of the result is the sum of the numbers
|
422
|
+
of vertices of ``self`` and `x` minus one, because the root of
|
423
|
+
`x` is not used.
|
424
|
+
|
425
|
+
This is used to define the product of the Grossman-Larson algebras.
|
426
|
+
|
427
|
+
INPUT:
|
428
|
+
|
429
|
+
- ``x`` -- a rooted tree
|
430
|
+
|
431
|
+
- ``grafting_function`` -- list of paths in ``self``
|
432
|
+
|
433
|
+
- ``path_prefix`` -- tuple (default: ``()``)
|
434
|
+
|
435
|
+
The ``path_prefix`` argument is only used for internal recursion.
|
436
|
+
|
437
|
+
EXAMPLES::
|
438
|
+
|
439
|
+
sage: LT = LabelledRootedTrees()
|
440
|
+
sage: y = LT([LT([],label='b')], label='a')
|
441
|
+
sage: x = LT([LT([],label='d')], label='c')
|
442
|
+
sage: y.single_graft(x,[(0,)])
|
443
|
+
a[b[d[]]]
|
444
|
+
sage: t = LT([LT([],label='b'),LT([],label='c')], label='a')
|
445
|
+
sage: s = LT([LT([],label='d'),LT([],label='e')], label='f')
|
446
|
+
sage: t.single_graft(s,[(0,),(1,)])
|
447
|
+
a[b[d[]], c[e[]]]
|
448
|
+
"""
|
449
|
+
P = self.parent()
|
450
|
+
child_grafts = [suby.single_graft(x, grafting_function,
|
451
|
+
path_prefix + (i,))
|
452
|
+
for i, suby in enumerate(self)]
|
453
|
+
try:
|
454
|
+
y1 = P(child_grafts, label=self.label())
|
455
|
+
except AttributeError:
|
456
|
+
y1 = P(child_grafts)
|
457
|
+
|
458
|
+
with y1.clone() as y2:
|
459
|
+
for k in range(len(x)):
|
460
|
+
if grafting_function[k] == path_prefix:
|
461
|
+
y2.append(x[k])
|
462
|
+
return y2
|
463
|
+
|
464
|
+
|
465
|
+
class RootedTrees(UniqueRepresentation, Parent):
|
466
|
+
"""
|
467
|
+
Factory class for rooted trees.
|
468
|
+
|
469
|
+
INPUT:
|
470
|
+
|
471
|
+
- ``size`` -- integer (optional)
|
472
|
+
|
473
|
+
OUTPUT:
|
474
|
+
|
475
|
+
the set of all rooted trees (of the given size ``size`` if
|
476
|
+
specified)
|
477
|
+
|
478
|
+
EXAMPLES::
|
479
|
+
|
480
|
+
sage: RootedTrees()
|
481
|
+
Rooted trees
|
482
|
+
|
483
|
+
sage: RootedTrees(2)
|
484
|
+
Rooted trees with 2 nodes
|
485
|
+
"""
|
486
|
+
@staticmethod
|
487
|
+
def __classcall_private__(cls, n=None):
|
488
|
+
"""
|
489
|
+
TESTS::
|
490
|
+
|
491
|
+
sage: from sage.combinat.rooted_tree import (RootedTrees_all,
|
492
|
+
....: RootedTrees_size)
|
493
|
+
sage: RootedTrees(2) is RootedTrees_size(2)
|
494
|
+
True
|
495
|
+
sage: RootedTrees(5).cardinality()
|
496
|
+
9
|
497
|
+
sage: RootedTrees() is RootedTrees_all()
|
498
|
+
True
|
499
|
+
|
500
|
+
TESTS::
|
501
|
+
|
502
|
+
sage: RootedTrees(0)
|
503
|
+
Traceback (most recent call last):
|
504
|
+
...
|
505
|
+
ValueError: n must be a positive integer
|
506
|
+
"""
|
507
|
+
if n is None:
|
508
|
+
return RootedTrees_all()
|
509
|
+
|
510
|
+
if n not in ZZ or n < 1:
|
511
|
+
raise ValueError("n must be a positive integer")
|
512
|
+
return RootedTrees_size(Integer(n))
|
513
|
+
|
514
|
+
|
515
|
+
class RootedTrees_all(DisjointUnionEnumeratedSets, RootedTrees):
|
516
|
+
r"""
|
517
|
+
Class of all (unordered, unlabelled) rooted trees.
|
518
|
+
|
519
|
+
See :class:`RootedTree` for a definition.
|
520
|
+
"""
|
521
|
+
|
522
|
+
def __init__(self):
|
523
|
+
"""
|
524
|
+
TESTS::
|
525
|
+
|
526
|
+
sage: sum(x**len(t) # needs sage.symbolic
|
527
|
+
....: for t in set(RootedTree(t) for t in OrderedTrees(6)))
|
528
|
+
x^5 + x^4 + 3*x^3 + 6*x^2 + 9*x
|
529
|
+
sage: sum(x**len(t) for t in RootedTrees(6)) # needs sage.symbolic
|
530
|
+
x^5 + x^4 + 3*x^3 + 6*x^2 + 9*x
|
531
|
+
|
532
|
+
sage: TestSuite(RootedTrees()).run() # long time
|
533
|
+
"""
|
534
|
+
DisjointUnionEnumeratedSets.__init__(
|
535
|
+
self, Family(NonNegativeIntegers(), RootedTrees_size),
|
536
|
+
facade=True, keepkey=False)
|
537
|
+
|
538
|
+
def _repr_(self):
|
539
|
+
r"""
|
540
|
+
TESTS::
|
541
|
+
|
542
|
+
sage: RootedTrees()
|
543
|
+
Rooted trees
|
544
|
+
"""
|
545
|
+
return "Rooted trees"
|
546
|
+
|
547
|
+
def __contains__(self, x):
|
548
|
+
"""
|
549
|
+
TESTS::
|
550
|
+
|
551
|
+
sage: S = RootedTrees()
|
552
|
+
sage: 1 in S
|
553
|
+
False
|
554
|
+
sage: S([]) in S
|
555
|
+
True
|
556
|
+
"""
|
557
|
+
return isinstance(x, self.element_class)
|
558
|
+
|
559
|
+
def unlabelled_trees(self):
|
560
|
+
"""
|
561
|
+
Return the set of unlabelled trees associated to ``self``.
|
562
|
+
|
563
|
+
EXAMPLES::
|
564
|
+
|
565
|
+
sage: RootedTrees().unlabelled_trees()
|
566
|
+
Rooted trees
|
567
|
+
"""
|
568
|
+
return self
|
569
|
+
|
570
|
+
def labelled_trees(self):
|
571
|
+
"""
|
572
|
+
Return the set of labelled trees associated to ``self``.
|
573
|
+
|
574
|
+
EXAMPLES::
|
575
|
+
|
576
|
+
sage: RootedTrees().labelled_trees()
|
577
|
+
Labelled rooted trees
|
578
|
+
|
579
|
+
As a consequence::
|
580
|
+
|
581
|
+
sage: lb = RootedTrees()([[],[[], []]]).canonical_labelling()
|
582
|
+
sage: lb
|
583
|
+
1[2[], 3[4[], 5[]]]
|
584
|
+
sage: lb.__class__
|
585
|
+
<class 'sage.combinat.rooted_tree.LabelledRootedTrees_all_with_category.element_class'>
|
586
|
+
sage: lb.parent()
|
587
|
+
Labelled rooted trees
|
588
|
+
"""
|
589
|
+
return LabelledRootedTrees()
|
590
|
+
|
591
|
+
def _element_constructor_(self, *args, **keywords):
|
592
|
+
"""
|
593
|
+
EXAMPLES::
|
594
|
+
|
595
|
+
sage: B = RootedTrees()
|
596
|
+
sage: B._element_constructor_([])
|
597
|
+
[]
|
598
|
+
sage: B([[],[]]) # indirect doctest
|
599
|
+
[[], []]
|
600
|
+
"""
|
601
|
+
return self.element_class(self, *args, **keywords)
|
602
|
+
|
603
|
+
@cached_method
|
604
|
+
def leaf(self):
|
605
|
+
"""
|
606
|
+
Return a leaf tree with ``self`` as parent.
|
607
|
+
|
608
|
+
EXAMPLES::
|
609
|
+
|
610
|
+
sage: RootedTrees().leaf()
|
611
|
+
[]
|
612
|
+
"""
|
613
|
+
return self([])
|
614
|
+
|
615
|
+
Element = RootedTree
|
616
|
+
|
617
|
+
|
618
|
+
class RootedTrees_size(RootedTrees):
|
619
|
+
"""
|
620
|
+
The enumerated set of rooted trees with a given number of nodes.
|
621
|
+
|
622
|
+
The number of nodes of a rooted tree is defined recursively:
|
623
|
+
The number of nodes of a rooted tree with `a` children is `a`
|
624
|
+
plus the sum of the number of nodes of each of these children.
|
625
|
+
|
626
|
+
TESTS::
|
627
|
+
|
628
|
+
sage: from sage.combinat.rooted_tree import RootedTrees_size
|
629
|
+
sage: for i in range(1, 6): TestSuite(RootedTrees_size(i)).run() # needs sage.combinat
|
630
|
+
"""
|
631
|
+
|
632
|
+
def __init__(self, n):
|
633
|
+
"""
|
634
|
+
TESTS::
|
635
|
+
|
636
|
+
sage: for i in range(1, 6): # needs sage.combinat
|
637
|
+
....: TestSuite(RootedTrees(i)).run()
|
638
|
+
"""
|
639
|
+
super().__init__(category=FiniteEnumeratedSets())
|
640
|
+
self._n = n
|
641
|
+
|
642
|
+
def _repr_(self):
|
643
|
+
r"""
|
644
|
+
TESTS::
|
645
|
+
|
646
|
+
sage: RootedTrees(4) # indirect doctest
|
647
|
+
Rooted trees with 4 nodes
|
648
|
+
"""
|
649
|
+
return "Rooted trees with {} nodes".format(self._n)
|
650
|
+
|
651
|
+
def __contains__(self, x):
|
652
|
+
"""
|
653
|
+
TESTS::
|
654
|
+
|
655
|
+
sage: S = RootedTrees(3)
|
656
|
+
sage: 1 in S
|
657
|
+
False
|
658
|
+
sage: S([[],[]]) in S
|
659
|
+
True
|
660
|
+
"""
|
661
|
+
return isinstance(x, self.element_class) and x.node_number() == self._n
|
662
|
+
|
663
|
+
def _an_element_(self):
|
664
|
+
"""
|
665
|
+
TESTS::
|
666
|
+
|
667
|
+
sage: RootedTrees(4).an_element() # indirect doctest # needs sage.combinat
|
668
|
+
[[[[]]]]
|
669
|
+
"""
|
670
|
+
return self.first()
|
671
|
+
|
672
|
+
def __iter__(self):
|
673
|
+
"""
|
674
|
+
An iterator for ``self``.
|
675
|
+
|
676
|
+
This generates the rooted trees of given size. The algorithm
|
677
|
+
first picks a partition for the sizes of subtrees, then picks
|
678
|
+
appropriate tuples of smaller trees.
|
679
|
+
|
680
|
+
EXAMPLES::
|
681
|
+
|
682
|
+
sage: from sage.combinat.rooted_tree import *
|
683
|
+
sage: RootedTrees(1).list()
|
684
|
+
[[]]
|
685
|
+
sage: RootedTrees(2).list() # needs sage.combinat
|
686
|
+
[[[]]]
|
687
|
+
sage: RootedTrees(3).list() # needs sage.combinat
|
688
|
+
[[[[]]], [[], []]]
|
689
|
+
sage: RootedTrees(4).list() # needs sage.combinat
|
690
|
+
[[[[[]]]], [[[], []]], [[], [[]]], [[], [], []]]
|
691
|
+
"""
|
692
|
+
if self._n == 1:
|
693
|
+
yield self._element_constructor_([])
|
694
|
+
return
|
695
|
+
|
696
|
+
from sage.combinat.partition import Partitions
|
697
|
+
from itertools import combinations_with_replacement, product
|
698
|
+
for part in Partitions(self._n - 1):
|
699
|
+
mults = part.to_exp_dict()
|
700
|
+
choices = []
|
701
|
+
for p, mp in mults.items():
|
702
|
+
lp = self.__class__(p).list()
|
703
|
+
new_choice = [list(z) for z in combinations_with_replacement(lp, mp)]
|
704
|
+
choices.append(new_choice)
|
705
|
+
for c in product(*choices):
|
706
|
+
yield self.element_class(self._parent_for, sum(c, []))
|
707
|
+
|
708
|
+
def check_element(self, el, check=True):
|
709
|
+
r"""
|
710
|
+
Check that a given tree actually belongs to ``self``.
|
711
|
+
|
712
|
+
This just checks the number of vertices.
|
713
|
+
|
714
|
+
EXAMPLES::
|
715
|
+
|
716
|
+
sage: RT3 = RootedTrees(3)
|
717
|
+
sage: RT3([[],[]]) # indirect doctest
|
718
|
+
[[], []]
|
719
|
+
sage: RT3([[],[],[]]) # indirect doctest
|
720
|
+
Traceback (most recent call last):
|
721
|
+
...
|
722
|
+
ValueError: wrong number of nodes
|
723
|
+
"""
|
724
|
+
if el.node_number() != self._n:
|
725
|
+
raise ValueError("wrong number of nodes")
|
726
|
+
|
727
|
+
def cardinality(self):
|
728
|
+
r"""
|
729
|
+
Return the cardinality of ``self``.
|
730
|
+
|
731
|
+
EXAMPLES::
|
732
|
+
|
733
|
+
sage: RootedTrees(1).cardinality()
|
734
|
+
1
|
735
|
+
sage: RootedTrees(3).cardinality()
|
736
|
+
2
|
737
|
+
"""
|
738
|
+
return number_of_rooted_trees(self._n)
|
739
|
+
|
740
|
+
@lazy_attribute
|
741
|
+
def _parent_for(self):
|
742
|
+
"""
|
743
|
+
The parent of the elements generated by ``self``.
|
744
|
+
|
745
|
+
TESTS::
|
746
|
+
|
747
|
+
sage: S = RootedTrees(3)
|
748
|
+
sage: S._parent_for
|
749
|
+
Rooted trees
|
750
|
+
"""
|
751
|
+
return RootedTrees_all()
|
752
|
+
|
753
|
+
@lazy_attribute
|
754
|
+
def element_class(self):
|
755
|
+
"""
|
756
|
+
TESTS::
|
757
|
+
|
758
|
+
sage: S = RootedTrees(3)
|
759
|
+
sage: S.element_class
|
760
|
+
<class 'sage.combinat.rooted_tree.RootedTrees_all_with_category.element_class'>
|
761
|
+
sage: S.first().__class__ == RootedTrees().first().__class__ # needs sage.combinat
|
762
|
+
True
|
763
|
+
"""
|
764
|
+
return self._parent_for.element_class
|
765
|
+
|
766
|
+
def _element_constructor_(self, *args, **keywords):
|
767
|
+
"""
|
768
|
+
EXAMPLES::
|
769
|
+
|
770
|
+
sage: S = RootedTrees(2)
|
771
|
+
sage: S([]) # indirect doctest
|
772
|
+
Traceback (most recent call last):
|
773
|
+
...
|
774
|
+
ValueError: wrong number of nodes
|
775
|
+
sage: S([[]]) # indirect doctest
|
776
|
+
[[]]
|
777
|
+
|
778
|
+
sage: S = RootedTrees(1) # indirect doctest
|
779
|
+
sage: S([])
|
780
|
+
[]
|
781
|
+
"""
|
782
|
+
res = self.element_class(self._parent_for, *args, **keywords)
|
783
|
+
if res.node_number() != self._n:
|
784
|
+
raise ValueError("wrong number of nodes")
|
785
|
+
return res
|
786
|
+
|
787
|
+
|
788
|
+
class LabelledRootedTree(AbstractLabelledClonableTree, RootedTree):
|
789
|
+
"""
|
790
|
+
Labelled rooted trees.
|
791
|
+
|
792
|
+
A labelled rooted tree is a rooted tree with a label
|
793
|
+
attached at each node.
|
794
|
+
|
795
|
+
More formally:
|
796
|
+
The *labelled rooted trees* are an inductive datatype defined
|
797
|
+
as follows: A labelled rooted tree is a multiset of labelled
|
798
|
+
rooted trees, endowed with a label (which can be any object,
|
799
|
+
including ``None``). The trees that belong to this multiset
|
800
|
+
are said to be the *children* of the tree. (Notice that the
|
801
|
+
labels of these children may and may not be of the same type
|
802
|
+
as the label of the tree). A labelled rooted tree which has
|
803
|
+
no children (so the only information it carries is its label)
|
804
|
+
is said to be a *leaf*.
|
805
|
+
|
806
|
+
Every labelled rooted tree gives rise to an unlabelled rooted
|
807
|
+
tree (:class:`RootedTree`) by forgetting the labels. (This is
|
808
|
+
implemented as a conversion.)
|
809
|
+
|
810
|
+
INPUT:
|
811
|
+
|
812
|
+
- ``children`` -- list or tuple or more generally any iterable
|
813
|
+
of trees or objects convertible to trees
|
814
|
+
|
815
|
+
- ``label`` -- any hashable Sage object (default: ``None``)
|
816
|
+
|
817
|
+
.. NOTE::
|
818
|
+
|
819
|
+
It is required that all labels are comparable.
|
820
|
+
|
821
|
+
EXAMPLES::
|
822
|
+
|
823
|
+
sage: x = LabelledRootedTree([], label = 3); x
|
824
|
+
3[]
|
825
|
+
sage: LabelledRootedTree([x, x, x], label = 2)
|
826
|
+
2[3[], 3[], 3[]]
|
827
|
+
sage: LabelledRootedTree((x, x, x), label = 2)
|
828
|
+
2[3[], 3[], 3[]]
|
829
|
+
sage: LabelledRootedTree([[],[[], []]], label = 3)
|
830
|
+
3[None[], None[None[], None[]]]
|
831
|
+
|
832
|
+
Children are reordered using the value of the :meth:`sort_key` method::
|
833
|
+
|
834
|
+
sage: y = LabelledRootedTree([], label = 5); y
|
835
|
+
5[]
|
836
|
+
sage: xyy2 = LabelledRootedTree((x, y, y), label = 2); xyy2
|
837
|
+
2[3[], 5[], 5[]]
|
838
|
+
sage: yxy2 = LabelledRootedTree((y, x, y), label = 2); yxy2
|
839
|
+
2[3[], 5[], 5[]]
|
840
|
+
sage: xyy2 == yxy2
|
841
|
+
True
|
842
|
+
|
843
|
+
Converting labelled into unlabelled rooted trees by
|
844
|
+
forgetting the labels, and back (the labels are
|
845
|
+
initialized as ``None``)::
|
846
|
+
|
847
|
+
sage: yxy2crude = RootedTree(yxy2); yxy2crude
|
848
|
+
[[], [], []]
|
849
|
+
sage: LabelledRootedTree(yxy2crude)
|
850
|
+
None[None[], None[], None[]]
|
851
|
+
|
852
|
+
TESTS::
|
853
|
+
|
854
|
+
sage: xyy2._get_list() == yxy2._get_list()
|
855
|
+
True
|
856
|
+
"""
|
857
|
+
@staticmethod
|
858
|
+
def __classcall_private__(cls, *args, **opts):
|
859
|
+
"""
|
860
|
+
Ensure that trees created by the sets and directly are the same and
|
861
|
+
that they are instances of :class:`LabelledRootedTree`.
|
862
|
+
|
863
|
+
TESTS::
|
864
|
+
|
865
|
+
sage: issubclass(LabelledRootedTrees().element_class, LabelledRootedTree)
|
866
|
+
True
|
867
|
+
sage: t0 = LabelledRootedTree([[],[[], []]], label = 3)
|
868
|
+
sage: t0.parent()
|
869
|
+
Labelled rooted trees
|
870
|
+
sage: type(t0)
|
871
|
+
<class 'sage.combinat.rooted_tree.LabelledRootedTrees_all_with_category.element_class'>
|
872
|
+
"""
|
873
|
+
return cls._auto_parent.element_class(cls._auto_parent, *args, **opts)
|
874
|
+
|
875
|
+
@lazy_class_attribute
|
876
|
+
def _auto_parent(cls):
|
877
|
+
"""
|
878
|
+
The automatic parent of the element of this class.
|
879
|
+
|
880
|
+
When calling the constructor of an element of this class, one needs a
|
881
|
+
parent. This class attribute specifies which parent is used.
|
882
|
+
|
883
|
+
EXAMPLES::
|
884
|
+
|
885
|
+
sage: LabelledRootedTree._auto_parent
|
886
|
+
Labelled rooted trees
|
887
|
+
sage: LabelledRootedTree([], label = 3).parent()
|
888
|
+
Labelled rooted trees
|
889
|
+
"""
|
890
|
+
return LabelledRootedTrees()
|
891
|
+
|
892
|
+
def sort_key(self):
|
893
|
+
"""
|
894
|
+
Return a tuple of nonnegative integers encoding the labelled
|
895
|
+
rooted tree ``self``.
|
896
|
+
|
897
|
+
The first entry of the tuple is a pair consisting of the
|
898
|
+
number of children of the root and the label of the root. Then
|
899
|
+
the rest of the tuple is obtained as follows: List
|
900
|
+
the tuples corresponding to all children (we are regarding the
|
901
|
+
children themselves as trees). Order this list (not the
|
902
|
+
tuples!) in lexicographically increasing order, and flatten
|
903
|
+
it into a single tuple.
|
904
|
+
|
905
|
+
This tuple characterizes the labelled rooted tree uniquely, and
|
906
|
+
can be used to sort the labelled rooted trees provided that the
|
907
|
+
labels belong to a type which is totally ordered.
|
908
|
+
|
909
|
+
.. NOTE::
|
910
|
+
|
911
|
+
The tree ``self`` must be normalized before calling this
|
912
|
+
method (see :meth:`normalize`). This does not matter
|
913
|
+
unless you are inside the :meth:`clone` context manager,
|
914
|
+
because outside of it every rooted tree is already
|
915
|
+
normalized.
|
916
|
+
|
917
|
+
.. NOTE::
|
918
|
+
|
919
|
+
This method overrides :meth:`RootedTree.sort_key`
|
920
|
+
and returns a result different from what the latter
|
921
|
+
would return, as it wants to encode the whole labelled
|
922
|
+
tree including its labelling rather than just the
|
923
|
+
unlabelled tree. Therefore, be careful with using this
|
924
|
+
method on subclasses of :class:`RootedOrderedTree`;
|
925
|
+
under some circumstances they could inherit it from
|
926
|
+
another superclass instead of from :class:`RootedTree`,
|
927
|
+
which would cause the method to forget the labelling.
|
928
|
+
See the docstrings of :meth:`RootedTree.sort_key` and
|
929
|
+
:meth:`sage.combinat.ordered_tree.OrderedTree.sort_key`.
|
930
|
+
|
931
|
+
EXAMPLES::
|
932
|
+
|
933
|
+
sage: LRT = LabelledRootedTrees(); LRT
|
934
|
+
Labelled rooted trees
|
935
|
+
sage: x = LRT([], label = 3); x
|
936
|
+
3[]
|
937
|
+
sage: x.sort_key()
|
938
|
+
((0, 3),)
|
939
|
+
sage: y = LRT([x, x, x], label = 2); y
|
940
|
+
2[3[], 3[], 3[]]
|
941
|
+
sage: y.sort_key()
|
942
|
+
((3, 2), (0, 3), (0, 3), (0, 3))
|
943
|
+
sage: LRT.an_element().sort_key()
|
944
|
+
((3, 'alpha'), (0, 3), (1, 5), (0, None), (2, 42), (0, 3), (0, 3))
|
945
|
+
sage: lb = RootedTrees()([[],[[], []]]).canonical_labelling()
|
946
|
+
sage: lb.sort_key()
|
947
|
+
((2, 1), (0, 2), (2, 3), (0, 4), (0, 5))
|
948
|
+
"""
|
949
|
+
l = len(self)
|
950
|
+
if l == 0:
|
951
|
+
return ((0, self.label()),)
|
952
|
+
resu = [(l, self.label())] + [u for t in self for u in t.sort_key()]
|
953
|
+
return tuple(resu)
|
954
|
+
|
955
|
+
def __hash__(self):
|
956
|
+
"""
|
957
|
+
Return a hash for ``self``.
|
958
|
+
|
959
|
+
EXAMPLES::
|
960
|
+
|
961
|
+
sage: lb = RootedTrees()([[],[[], []]]).canonical_labelling()
|
962
|
+
sage: hash(lb) == hash(((2, 1), (0, 2), (2, 3), (0, 4), (0, 5))) # indirect doctest
|
963
|
+
True
|
964
|
+
"""
|
965
|
+
return hash(self.sort_key())
|
966
|
+
|
967
|
+
_UnLabelled = RootedTree
|
968
|
+
|
969
|
+
|
970
|
+
class LabelledRootedTrees(UniqueRepresentation, Parent):
|
971
|
+
"""
|
972
|
+
This is a parent stub to serve as a factory class for labelled
|
973
|
+
rooted trees.
|
974
|
+
|
975
|
+
EXAMPLES::
|
976
|
+
|
977
|
+
sage: LRT = LabelledRootedTrees(); LRT
|
978
|
+
Labelled rooted trees
|
979
|
+
sage: x = LRT([], label = 3); x
|
980
|
+
3[]
|
981
|
+
sage: x.parent() is LRT
|
982
|
+
True
|
983
|
+
sage: y = LRT([x, x, x], label = 2); y
|
984
|
+
2[3[], 3[], 3[]]
|
985
|
+
sage: y.parent() is LRT
|
986
|
+
True
|
987
|
+
|
988
|
+
.. TODO::
|
989
|
+
|
990
|
+
Add the possibility to restrict the labels to a fixed set.
|
991
|
+
"""
|
992
|
+
@staticmethod
|
993
|
+
def __classcall_private__(cls, n=None):
|
994
|
+
"""
|
995
|
+
TESTS::
|
996
|
+
|
997
|
+
sage: from sage.combinat.rooted_tree import LabelledRootedTrees_all
|
998
|
+
sage: LabelledRootedTrees_all() == LabelledRootedTrees()
|
999
|
+
True
|
1000
|
+
"""
|
1001
|
+
return LabelledRootedTrees_all()
|
1002
|
+
|
1003
|
+
|
1004
|
+
class LabelledRootedTrees_all(LabelledRootedTrees):
|
1005
|
+
r"""
|
1006
|
+
Class of all (unordered) labelled rooted trees.
|
1007
|
+
|
1008
|
+
See :class:`LabelledRootedTree` for a definition.
|
1009
|
+
"""
|
1010
|
+
|
1011
|
+
def __init__(self, category=None):
|
1012
|
+
"""
|
1013
|
+
TESTS::
|
1014
|
+
|
1015
|
+
sage: TestSuite(LabelledRootedTrees()).run()
|
1016
|
+
"""
|
1017
|
+
if category is None:
|
1018
|
+
category = Sets()
|
1019
|
+
category = category.Infinite()
|
1020
|
+
Parent.__init__(self, category=category)
|
1021
|
+
|
1022
|
+
def _repr_(self):
|
1023
|
+
"""
|
1024
|
+
Return the string representation of ``self``.
|
1025
|
+
|
1026
|
+
TESTS::
|
1027
|
+
|
1028
|
+
sage: LabelledRootedTrees()
|
1029
|
+
Labelled rooted trees
|
1030
|
+
"""
|
1031
|
+
return "Labelled rooted trees"
|
1032
|
+
|
1033
|
+
def _an_element_(self):
|
1034
|
+
"""
|
1035
|
+
Return a labelled tree.
|
1036
|
+
|
1037
|
+
EXAMPLES::
|
1038
|
+
|
1039
|
+
sage: LabelledRootedTrees().an_element() # indirect doctest
|
1040
|
+
alpha[3[], 5[None[]], 42[3[], 3[]]]
|
1041
|
+
"""
|
1042
|
+
LT = self._element_constructor_
|
1043
|
+
t = LT([], label=3)
|
1044
|
+
t1 = LT([t, t], label=42)
|
1045
|
+
t2 = LT([[]], label=5)
|
1046
|
+
return LT([t, t1, t2], label='alpha')
|
1047
|
+
|
1048
|
+
def unlabelled_trees(self):
|
1049
|
+
"""
|
1050
|
+
Return the set of unlabelled trees associated to ``self``.
|
1051
|
+
|
1052
|
+
EXAMPLES::
|
1053
|
+
|
1054
|
+
sage: LabelledRootedTrees().unlabelled_trees()
|
1055
|
+
Rooted trees
|
1056
|
+
"""
|
1057
|
+
return RootedTrees_all()
|
1058
|
+
|
1059
|
+
def labelled_trees(self):
|
1060
|
+
"""
|
1061
|
+
Return the set of labelled trees associated to ``self``.
|
1062
|
+
|
1063
|
+
EXAMPLES::
|
1064
|
+
|
1065
|
+
sage: LabelledRootedTrees().labelled_trees()
|
1066
|
+
Labelled rooted trees
|
1067
|
+
"""
|
1068
|
+
return self
|
1069
|
+
|
1070
|
+
Element = LabelledRootedTree
|