From 3375d2139dba607c173aff11cfcaed035f5e0f68 Mon Sep 17 00:00:00 2001 From: Unknown User Date: Sun, 20 Apr 2014 09:15:31 +0000 Subject: [PATCH 1/5] #11529: data structure and enumerated sets for rooted trees. --- src/doc/en/reference/combinat/index.rst | 1 + src/sage/combinat/all.py | 2 + src/sage/combinat/ordered_tree.py | 35 ++ src/sage/combinat/rooted_tree.py | 662 ++++++++++++++++++++++++ 4 files changed, 700 insertions(+) create mode 100644 src/sage/combinat/rooted_tree.py diff --git a/src/doc/en/reference/combinat/index.rst b/src/doc/en/reference/combinat/index.rst index 2f2744634d9..be74dea7229 100644 --- a/src/doc/en/reference/combinat/index.rst +++ b/src/doc/en/reference/combinat/index.rst @@ -56,6 +56,7 @@ Combinatorics sage/combinat/abstract_tree sage/combinat/ordered_tree sage/combinat/binary_tree + sage/combinat/rooted_tree sage/combinat/similarity_class_type sage/combinat/skew_partition sage/combinat/subset diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 08ae21d522f..0b979213a78 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -114,6 +114,8 @@ LabelledOrderedTree, LabelledOrderedTrees) from binary_tree import (BinaryTree, BinaryTrees, LabelledBinaryTree, LabelledBinaryTrees) +from rooted_tree import (RootedTree, RootedTrees, + LabelledRootedTree, LabelledRootedTrees) from combination import Combinations from cartesian_product import CartesianProduct diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index a662238bc5e..75c7fc98a17 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -511,6 +511,41 @@ def left_right_symmetry(self): children.reverse() return OrderedTree(children) + import sage.combinat.ranker + from sage.misc.cachefunc import cached_method + _cayley_ranker = sage.combinat.ranker.on_fly() + @cached_method + def cayley_normalize(self): + """ + sage: (OrderedTree([[],[[]]]).cayley_normalize() == + ... OrderedTree([[[]],[]]).cayley_normalize()) + True + """ + rank, unrank = self._cayley_ranker + with self.clone() as res: + resl = res._get_list() + for i in range(len(resl)): + resl[i] = resl[i].cayley_normalize() + resl.sort(key = rank) + return unrank(rank(res)) + + # TODO !!! + def cayley_normalize_in_place(self): + """ + In place cayley normalization + + EXAMPLES:: + + sage: (OrderedTree([[],[[]]]).cayley_normalize() == + ... OrderedTree([[[]],[]]).cayley_normalize()) + True + """ + rank, unrank = self._cayley_ranker + resl = self._get_list() + for i in range(len(resl)): + resl[i] = resl[i].cayley_normalized() + resl.sort(key = rank) + from sage.categories.sets_cat import Sets, EmptySetError from sage.rings.integer import Integer from sage.sets.non_negative_integers import NonNegativeIntegers diff --git a/src/sage/combinat/rooted_tree.py b/src/sage/combinat/rooted_tree.py new file mode 100644 index 00000000000..375d575417b --- /dev/null +++ b/src/sage/combinat/rooted_tree.py @@ -0,0 +1,662 @@ +r""" +Rooted (Cayley, Unordered) Trees + +AUTHORS: + +- Florent Hivert (2011): initial revision +""" + +import sage +from sage.structure.list_clone import NormalizedClonableList +from sage.combinat.abstract_tree import ( + AbstractClonableTree, AbstractLabelledClonableTree ) +from sage.misc.cachefunc import ( + cached_method, cached_function, cached_in_parent_method ) +from sage.misc.classcall_metaclass import ClasscallMetaclass +from sage.misc.lazy_attribute import lazy_attribute, lazy_class_attribute +from sage.misc.misc import srange +from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ + +@cached_function +def number_of_rooted_trees(n): + """ + Number of rooted trees with `n` nodes + + Compute the number `a(n)` of rooted trees with `n` nodes using the + recsursive formula ([SL]_): + + .. math:: + + a(n+1) = (1/n) * sum_{k=1..n} ( sum_{d|k} d*a(d) ) * a(n-k+1) + + EXAMPLES:: + + sage: from sage.combinat.rooted_tree import number_of_rooted_trees + sage: [number_of_rooted_trees(i) for i in range(10)] + [0, 1, 1, 2, 4, 9, 20, 48, 115, 286] + + REFERENCES: + + .. [SL] Sloane's A000081 + """ + if n == 0: + return Integer(0) + if n == 1: + return Integer(1) + n = Integer(n) + return sum(sum(d*number_of_rooted_trees(d) for d in k.divisors()) * + number_of_rooted_trees(n-k) + for k in ZZ.range(1, n)) // (n-1) + +class RootedTree(AbstractClonableTree, NormalizedClonableList): + r""" + The class for unordered rooted trees + + One can create a tree from any list (or more generally iterable) of trees + or objects convertible to a tree. + + EXAMPLES:: + + sage: RootedTree([]) + [] + sage: RootedTree([[], [[]]]) + [[], [[]]] + sage: RootedTree([[[]], []]) + [[], [[]]] + """ + + # Standard auto-parent trick + __metaclass__ = ClasscallMetaclass + @staticmethod + def __classcall_private__(cls, *args, **opts): + """ + Ensure that rooted trees created by the enumerated sets and directly + are the same and that they are instance of :class:`RootedTree` + + TESTS:: + + sage: from sage.combinat.rooted_tree import RootedTrees_all, RootedTrees_size + sage: issubclass(RootedTrees_all().element_class, RootedTree) + True + sage: t0 = RootedTree([[],[[]]]) + sage: t0.parent() + Rooted trees + sage: type(t0) + + + sage: t1 = RootedTrees()([[],[[]]]) + sage: t1.parent() is t0.parent() + True + sage: type(t1) is type(t0) + True + + sage: t1 = RootedTrees(4)([[],[[]]]) + sage: t1.parent() is t0.parent() + True + sage: type(t1) is type(t0) + True + """ + return cls._auto_parent.element_class(cls._auto_parent, *args, **opts) + + @lazy_class_attribute + def _auto_parent(cls): + """ + The automatic parent of the element of this class + + When calling the constructor of an element of this class, one need a + parent. This class attribute specifies which parent is used. + + EXAMPLES:: + + sage: RootedTree._auto_parent + Rooted trees + sage: RootedTree([]).parent() + Rooted trees + """ + return RootedTrees_all() + + def __init__(self, parent=None, children=[], check=True): + """ + TESTS:: + + sage: t1 = RootedTrees(4)([[],[[]]]) + sage: TestSuite(t1).run() + """ + if (children.__class__ is self.__class__ and + children.parent() == parent): + children = list(children) + else: + children = [self.__class__(parent, x) for x in children] + NormalizedClonableList.__init__(self, parent, children, check=check) + + _cayley_ranker = sage.combinat.ranker.on_fly() + def normalize(self): + r""" + Normalise ``self`` + + There should be no need to call normalize directly as it is called + automatically upon creation and cloning/modification. + + EXAMPLES:: + + sage: RootedTree([[],[[]]]) == RootedTree([[[]],[]]) # indirect doctest + True + sage: rt1 = RootedTree([[],[[]]]) + sage: rt2 = RootedTree([[[]],[]]) + sage: rt1 is rt2 + False + sage: rt1._get_list() is rt2._get_list() + True + """ + # print "Normalizing %s"%self + rank, unrank = self._cayley_ranker + self._require_mutable() + for st in self: + assert st.is_immutable(), "Subtree %s is not normalized"%st + # print self._get_list() + self._get_list().sort(key=rank) + # print self._get_list() + + # ensure unique representation + self.set_immutable() + res = unrank(rank(self)) + if self is not res: + self._set_list(res._get_list()) + + def is_empty(self): + """ + Return if ``self`` is the empty tree + + For rooted trees, returns always ``False`` + + .. note:: this is not the same as ``bool(t)`` which returns whether + ``t`` has some child or not. + + EXAMPLES:: + + sage: t = RootedTrees(4)([[],[[]]]) + sage: t.is_empty() + False + sage: bool(t) + True + """ + return False + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +from sage.categories.sets_cat import Sets, EmptySetError +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.sets.family import Family +import sage.structure.set_factories as factories + +class RootedTreesFactory(factories.SetFactory): + """ + Factory class for rooted trees + """ + + def __call__(self, n=(), policy=None): + """ + TESTS:: + + sage: from sage.combinat.rooted_tree import RootedTrees_all, RootedTrees_size + sage: RootedTrees(2) is RootedTrees_size(2) + True + sage: RootedTrees(5).cardinality() + 9 + sage: RootedTrees() is RootedTrees_all() + True + """ + if policy is None: + policy = self._default_policy + + if n == (): + return RootedTrees_all(policy) + elif isinstance(n, (Integer, int)) and n >= 0: + return RootedTrees_size(n, policy) + raise NotImplementedError, "Don't know how to compute %s(%s)"%( + self, n) + + # try: + # from sage.combinat.partition import Partition + # lst = Partition(n) + # except ValueError: + # raise NotImplementedError, "Don't know how to compute %%(%s)"%( + # self, n) + # else: + # return RootedTrees_part(lst, policy) + + def add_constraints(self, cons, (n, st)): + """ + EXAMPLES:: + + sage: RootedTrees.add_constraints((), ((3,), {})) + (3,) + sage: RootedTrees.add_constraints((3,), ((), {})) + (3,) + """ + return cons + n + + @lazy_attribute + def _default_policy(self): + r""" + TESTS:: + + sage: from sage.combinat.rooted_tree import RootedTreesFactory + sage: RT = RootedTreesFactory() + sage: RT._default_policy + Set factory policy for with parent Rooted trees[=Rooted tree set factory(())] + """ + return factories.TopMostParentPolicy(self, (), RootedTree) + + def __repr__(self): + """ + TESTS:: + + sage: from sage.combinat.rooted_tree import RootedTreesFactory + sage: RootedTreesFactory() + Rooted tree set factory + """ + return "Rooted tree set factory" + +RootedTrees = RootedTreesFactory() + +from sage.sets.non_negative_integers import NonNegativeIntegers +class RootedTrees_all(factories.SetFactoryParent, + DisjointUnionEnumeratedSets): + """ + TESTS:: + + sage: sum(x^len(t) for t in + ... set(RootedTree(t) for t in OrderedTrees(6))) + x^5 + x^4 + 3*x^3 + 6*x^2 + 9*x + sage: sum(x^len(t) for t in RootedTrees(6)) + x^5 + x^4 + 3*x^3 + 6*x^2 + 9*x + """ + @staticmethod + def __classcall_private__(cls, policy=RootedTrees._default_policy): + """ + Input normalization + + TESTS:: + + sage: from sage.combinat.rooted_tree import RootedTrees_all + sage: RootedTrees() is RootedTrees_all() + True + """ + return super(RootedTrees_all, cls).__classcall__(cls, policy) + + def __init__(self, policy=RootedTrees._default_policy): + """ + TESTS:: + + sage: TestSuite(RootedTrees()).run() + """ + factories.SetFactoryParent.__init__(self, (), policy, + category = InfiniteEnumeratedSets()) + DisjointUnionEnumeratedSets.__init__( + self, Family(NonNegativeIntegers(), self._of_size), + facade=True, keepkey = False, + category = self.category()) + + def _repr_(self): + r""" + TESTS:: + + sage: RootedTrees() # indirect doctest + Rooted trees + """ + return "Rooted trees" + + def _of_size(self, size): + r""" + The sub-enumerated set of trees of a given size + + Passed to :class:DisjointUnionEnumeratedSets + + TESTS:: + + sage: RootedTrees()._of_size(4) + Rooted trees with 4 nodes + """ + return RootedTrees_size(size, policy=self.facade_policy()) + + def check_element(self, el, check): + r""" + Check that a given tree actually belongs to ``self`` + + See :class:`sage.structure.set_factories.SetFactoryParent` + + TESTS:: + + sage: RT = RootedTrees() + sage: RT([[],[]]) # indirect doctest + [[], []] + """ + pass + + def unlabelled_trees(self): + """ + Returns the set of unlabelled trees associated to ``self`` + + EXAMPLES:: + + sage: RootedTrees().unlabelled_trees() + Rooted trees + """ + return self + + def labelled_trees(self): + """ + Returns the set of unlabelled trees associated to ``self`` + + EXAMPLES:: + + sage: RootedTrees().labelled_trees() + Labelled rooted trees + + As a consequence:: + + sage: lb = RootedTrees()([[],[[], []]]).canonical_labelling() + sage: lb + 1[2[], 3[4[], 5[]]] + sage: lb.__class__ + + sage: lb.parent() + Labelled rooted trees + """ + return LabelledRootedTrees() + + +class RootedTrees_size(factories.SetFactoryParent, UniqueRepresentation): + """ + The enumerated set of rooted trees with a given number of node + + EXAMPLES:: + + """ + @staticmethod + def __classcall_private__(cls, n, policy=RootedTrees._default_policy): + """ + Input normalization + + TESTS:: + + sage: from sage.combinat.rooted_tree import RootedTrees_size + sage: RootedTrees(4) is RootedTrees_size(4) + True + sage: RootedTrees_size(4) is RootedTrees_size(int(4)) + True + """ + return super(RootedTrees_size, cls).__classcall__( + cls, Integer(n), policy) + + def __init__(self, n, policy): + """ + TESTS:: + + sage: for i in range(0, 6): + ... TestSuite(RootedTrees(i)).run() + """ + self._n = n + factories.SetFactoryParent.__init__(self, (n,), policy, + category = FiniteEnumeratedSets()) + + def _repr_(self): + r""" + TESTS:: + + sage: RootedTrees(4) # indirect doctest + Rooted trees with 4 nodes + """ + return "Rooted trees with %s nodes"%(self._n) + + def __iter__(self): + """ + An iterator for ``self`` + + EXAMPLES:: + + sage: from sage.combinat.rooted_tree import * + sage: RootedTrees(0).list() + [] + sage: RootedTrees(1).list() + [[]] + sage: RootedTrees(2).list() + [[[]]] + sage: RootedTrees(3).list() + [[[[]]], [[], []]] + """ + if self._n == 0: + pass + elif self._n == 1: + yield self._element_constructor_([]) + else: + from sage.combinat.partition import Partitions + from sage.combinat.multichoose_nk import MultichooseNK + from sage.combinat.cartesian_product import CartesianProduct + for part in Partitions(self._n - 1): + mults = part.to_exp_dict() + choices = [] + for p, mp in mults.items(): + lp = self.__class__(p, self.policy()).list() + new_choice = MultichooseNK(len(lp), mp).map( + lambda l: [lp[i] for i in l]).list() + choices.append(new_choice) + for c in CartesianProduct(*choices): + yield self._element_constructor_(sum(c, [])) + + def check_element(self, el, check=True): + r""" + Check that a given tree actually belongs to ``self`` + + See :class:`sage.structure.set_factories.SetFactoryParent` + + EXAMPLES:: + + sage: RT3 = RootedTrees(3) + sage: RT3([[],[]]) # indirect doctest + [[], []] + sage: RT3([[],[],[]]) # indirect doctest + Traceback (most recent call last): + ... + ValueError: Wrong number of nodes + """ + if el.node_number() != self._n: + raise ValueError, "Wrong number of nodes" + + def cardinality(self): + r""" + Return the cardinality of ``self`` + + EXAMPLES:: + + sage: RootedTrees(1).cardinality() + 1 + sage: RootedTrees(3).cardinality() + 2 + sage: RootedTrees(0).cardinality() + 0 + """ + return number_of_rooted_trees(self._n) + + +class LabelledRootedTree(AbstractLabelledClonableTree, RootedTree): + """ + Labelled rooted trees + + A labellel rooted tree is a rooted tree with a label attached at each node + + INPUT: + + - ``children`` -- a list or tuple or more generally any iterable + of trees or object convertible to trees + - ``label`` -- any Sage object default to ``None`` + + EXAMPLES:: + + sage: x = LabelledRootedTree([], label = 3); x + 3[] + sage: LabelledRootedTree([x, x, x], label = 2) + 2[3[], 3[], 3[]] + sage: LabelledRootedTree((x, x, x), label = 2) + 2[3[], 3[], 3[]] + sage: LabelledRootedTree([[],[[], []]], label = 3) + 3[None[], None[None[], None[]]] + + Children are reordered in a session dependant order: + + sage: y = LabelledRootedTree([], label = 5); x + 3[] + sage: xyy2 = LabelledRootedTree((x, y, y), label = 2); xyy2 #random + 2[3[], 5[], 5[]] + sage: yxy2 = LabelledRootedTree((y, x, y), label = 2); yxy2 #random + 2[3[], 5[], 5[]] + sage: xyy2 == yxy2 + True + + TESTS:: + + sage: xyy2._get_list() is yxy2._get_list() + True + """ + __metaclass__ = ClasscallMetaclass + + @staticmethod + def __classcall_private__(cls, *args, **opts): + """ + Ensure that trees created by the sets and directly are the same and + that they are instance of :class:`LabelledRootedTree` + + TESTS:: + + sage: issubclass(LabelledRootedTrees().element_class, LabelledRootedTree) + True + sage: t0 = LabelledRootedTree([[],[[], []]], label = 3) + sage: t0.parent() + Labelled rooted trees + sage: type(t0) + + """ + return cls._auto_parent.element_class(cls._auto_parent, *args, **opts) + + @lazy_class_attribute + def _auto_parent(cls): + """ + The automatic parent of the element of this class + + When calling the constructor of an element of this class, one need a + parent. This class attribute specifies which parent is used. + + EXAMPLES:: + + sage: LabelledRootedTree._auto_parent + Labelled rooted trees + sage: LabelledRootedTree([], label = 3).parent() + Labelled rooted trees + """ + return LabelledRootedTrees() + + _UnLabelled = RootedTree + + +from sage.rings.infinity import Infinity +class LabelledRootedTrees(UniqueRepresentation, Parent): + """ + This is a parent stub to serve as a factory class for trees with various + labels constraints + + EXAMPLES:: + + sage: LOT = LabelledRootedTrees(); LOT + Labelled rooted trees + sage: x = LOT([], label = 3); x + 3[] + sage: x.parent() is LOT + True + sage: y = LOT([x, x, x], label = 2); y + 2[3[], 3[], 3[]] + sage: y.parent() is LOT + True + """ + def __init__(self, category=None): + """ + TESTS:: + + sage: TestSuite(LabelledRootedTrees()).run() + """ + if category is None: + category = Sets() + Parent.__init__(self, category = category) + + def _repr_(self): + """ + TESTS:: + + sage: LabelledRootedTrees() # indirect doctest + Labelled rooted trees + """ + return "Labelled rooted trees" + + def cardinality(self): + """ + Returns the cardinality of `self` + + EXAMPLE:: + + sage: LabelledRootedTrees().cardinality() + +Infinity + """ + return Infinity + + def _an_element_(self): + """ + Returns a labelled tree + + EXAMPLE:: + + sage: LabelledRootedTrees().an_element() # indirect doctest + toto[3[], 42[3[], 3[]], 5[None[]]] + """ + LT = self._element_constructor_ + t = LT([], label = 3) + t1 = LT([t,t], label = 42) + t2 = LT([[]], label = 5) + return LT([t,t1,t2], label = "toto") + + def _element_constructor_(self, *args, **keywords): + """ + EXAMPLES:: + + sage: T = LabelledRootedTrees() + sage: T([], label=2) # indirect doctest + 2[] + """ + return self.element_class(self, *args, **keywords) + + def unlabelled_trees(self): + """ + Returns the set of unlabelled trees associated to ``self`` + + EXAMPLES:: + + sage: LabelledRootedTrees().unlabelled_trees() + Rooted trees + """ + return RootedTrees_all() + + def labelled_trees(self): + """ + Returns the set of labelled trees associated to ``self`` + + EXAMPLES:: + + sage: LabelledRootedTrees().labelled_trees() + Labelled rooted trees + """ + return self + + Element = LabelledRootedTree + From bad859055dcfc8e014125b31e25702359d6e462a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 20 Apr 2014 11:27:08 +0200 Subject: [PATCH 2/5] trac #11529 pyflakes details --- src/sage/combinat/rooted_tree.py | 53 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/rooted_tree.py b/src/sage/combinat/rooted_tree.py index 375d575417b..4534c48b2b7 100644 --- a/src/sage/combinat/rooted_tree.py +++ b/src/sage/combinat/rooted_tree.py @@ -6,17 +6,27 @@ - Florent Hivert (2011): initial revision """ -import sage from sage.structure.list_clone import NormalizedClonableList from sage.combinat.abstract_tree import ( AbstractClonableTree, AbstractLabelledClonableTree ) -from sage.misc.cachefunc import ( - cached_method, cached_function, cached_in_parent_method ) +from sage.misc.cachefunc import cached_function +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.categories.sets_cat import Sets from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.misc.lazy_attribute import lazy_attribute, lazy_class_attribute -from sage.misc.misc import srange from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.sets.family import Family +from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.infinity import Infinity +from sage.combinat.ranker import on_fly +import sage.structure.set_factories as factories + + @cached_function def number_of_rooted_trees(n): @@ -49,6 +59,7 @@ def number_of_rooted_trees(n): number_of_rooted_trees(n-k) for k in ZZ.range(1, n)) // (n-1) + class RootedTree(AbstractClonableTree, NormalizedClonableList): r""" The class for unordered rooted trees @@ -72,7 +83,7 @@ class RootedTree(AbstractClonableTree, NormalizedClonableList): def __classcall_private__(cls, *args, **opts): """ Ensure that rooted trees created by the enumerated sets and directly - are the same and that they are instance of :class:`RootedTree` + are the same and that they are instances of :class:`RootedTree` TESTS:: @@ -104,7 +115,7 @@ def _auto_parent(cls): """ The automatic parent of the element of this class - When calling the constructor of an element of this class, one need a + When calling the constructor of an element of this class, one needs a parent. This class attribute specifies which parent is used. EXAMPLES:: @@ -130,13 +141,14 @@ def __init__(self, parent=None, children=[], check=True): children = [self.__class__(parent, x) for x in children] NormalizedClonableList.__init__(self, parent, children, check=check) - _cayley_ranker = sage.combinat.ranker.on_fly() + _cayley_ranker = on_fly() def normalize(self): r""" Normalise ``self`` - There should be no need to call normalize directly as it is called - automatically upon creation and cloning/modification. + There should be no need to call ``normalize`` directly as it + is called automatically upon creation and + cloning/modification. EXAMPLES:: @@ -153,7 +165,7 @@ def normalize(self): rank, unrank = self._cayley_ranker self._require_mutable() for st in self: - assert st.is_immutable(), "Subtree %s is not normalized"%st + assert st.is_immutable(), "Subtree {} is not normalized".format(st) # print self._get_list() self._get_list().sort(key=rank) # print self._get_list() @@ -183,14 +195,6 @@ def is_empty(self): """ return False -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent -from sage.categories.sets_cat import Sets, EmptySetError -from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets -from sage.sets.family import Family -import sage.structure.set_factories as factories class RootedTreesFactory(factories.SetFactory): """ @@ -216,15 +220,14 @@ def __call__(self, n=(), policy=None): return RootedTrees_all(policy) elif isinstance(n, (Integer, int)) and n >= 0: return RootedTrees_size(n, policy) - raise NotImplementedError, "Don't know how to compute %s(%s)"%( - self, n) + raise NotImplementedError("Do not know how to compute {}({})".format(self, n)) # try: # from sage.combinat.partition import Partition # lst = Partition(n) # except ValueError: - # raise NotImplementedError, "Don't know how to compute %%(%s)"%( - # self, n) + # raise NotImplementedError("Don't know how to compute %%(%s)"%( + # self, n)) # else: # return RootedTrees_part(lst, policy) @@ -263,7 +266,8 @@ def __repr__(self): RootedTrees = RootedTreesFactory() -from sage.sets.non_negative_integers import NonNegativeIntegers + + class RootedTrees_all(factories.SetFactoryParent, DisjointUnionEnumeratedSets): """ @@ -465,7 +469,7 @@ def check_element(self, el, check=True): ValueError: Wrong number of nodes """ if el.node_number() != self._n: - raise ValueError, "Wrong number of nodes" + raise ValueError("Wrong number of nodes") def cardinality(self): r""" @@ -562,7 +566,6 @@ def _auto_parent(cls): _UnLabelled = RootedTree -from sage.rings.infinity import Infinity class LabelledRootedTrees(UniqueRepresentation, Parent): """ This is a parent stub to serve as a factory class for trees with various From f0bd1323350839e28c20e43d6e813e73b4880278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 21 Apr 2014 10:01:05 +0200 Subject: [PATCH 3/5] trac #11529 minor details --- src/sage/combinat/rooted_tree.py | 112 +++++++++++++++---------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/sage/combinat/rooted_tree.py b/src/sage/combinat/rooted_tree.py index 4534c48b2b7..6b4de34d4ca 100644 --- a/src/sage/combinat/rooted_tree.py +++ b/src/sage/combinat/rooted_tree.py @@ -6,35 +6,34 @@ - Florent Hivert (2011): initial revision """ -from sage.structure.list_clone import NormalizedClonableList -from sage.combinat.abstract_tree import ( - AbstractClonableTree, AbstractLabelledClonableTree ) -from sage.misc.cachefunc import cached_function from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_cat import Sets +from sage.combinat.ranker import on_fly +from sage.combinat.abstract_tree import ( + AbstractClonableTree, AbstractLabelledClonableTree) +from sage.misc.cachefunc import cached_function from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.misc.lazy_attribute import lazy_attribute, lazy_class_attribute +from sage.rings.infinity import Infinity from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.structure.list_clone import NormalizedClonableList from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.infinity import Infinity -from sage.combinat.ranker import on_fly import sage.structure.set_factories as factories - @cached_function def number_of_rooted_trees(n): """ Number of rooted trees with `n` nodes Compute the number `a(n)` of rooted trees with `n` nodes using the - recsursive formula ([SL]_): + recursive formula ([SL000081]_): .. math:: @@ -48,24 +47,24 @@ def number_of_rooted_trees(n): REFERENCES: - .. [SL] Sloane's A000081 + .. [SL000081] Sloane's :oeis:`A000081` """ if n == 0: return Integer(0) if n == 1: return Integer(1) n = Integer(n) - return sum(sum(d*number_of_rooted_trees(d) for d in k.divisors()) * - number_of_rooted_trees(n-k) - for k in ZZ.range(1, n)) // (n-1) + return sum(sum(d * number_of_rooted_trees(d) for d in k.divisors()) * + number_of_rooted_trees(n - k) + for k in ZZ.range(1, n)) // (n - 1) class RootedTree(AbstractClonableTree, NormalizedClonableList): r""" The class for unordered rooted trees - One can create a tree from any list (or more generally iterable) of trees - or objects convertible to a tree. + One can create a tree from any list (or more generally iterable) + of trees or objects convertible to a tree. EXAMPLES:: @@ -76,9 +75,9 @@ class RootedTree(AbstractClonableTree, NormalizedClonableList): sage: RootedTree([[[]], []]) [[], [[]]] """ - # Standard auto-parent trick __metaclass__ = ClasscallMetaclass + @staticmethod def __classcall_private__(cls, *args, **opts): """ @@ -87,7 +86,7 @@ def __classcall_private__(cls, *args, **opts): TESTS:: - sage: from sage.combinat.rooted_tree import RootedTrees_all, RootedTrees_size + sage: from sage.combinat.rooted_tree import RootedTrees_all sage: issubclass(RootedTrees_all().element_class, RootedTree) True sage: t0 = RootedTree([[],[[]]]) @@ -113,7 +112,7 @@ def __classcall_private__(cls, *args, **opts): @lazy_class_attribute def _auto_parent(cls): """ - The automatic parent of the element of this class + The automatic parent of the elements of this class When calling the constructor of an element of this class, one needs a parent. This class attribute specifies which parent is used. @@ -134,41 +133,41 @@ def __init__(self, parent=None, children=[], check=True): sage: t1 = RootedTrees(4)([[],[[]]]) sage: TestSuite(t1).run() """ - if (children.__class__ is self.__class__ and - children.parent() == parent): + tst = (children.__class__ is self.__class__ + and children.parent() == parent) + if tst: children = list(children) else: children = [self.__class__(parent, x) for x in children] NormalizedClonableList.__init__(self, parent, children, check=check) _cayley_ranker = on_fly() + def normalize(self): r""" - Normalise ``self`` + Normalize ``self`` There should be no need to call ``normalize`` directly as it - is called automatically upon creation and - cloning/modification. + is called automatically upon creation and cloning or + modification. EXAMPLES:: - sage: RootedTree([[],[[]]]) == RootedTree([[[]],[]]) # indirect doctest + sage: RT = RootedTree + sage: RT([[],[[]]]) == RT([[[]],[]]) # indirect doctest True - sage: rt1 = RootedTree([[],[[]]]) - sage: rt2 = RootedTree([[[]],[]]) + sage: rt1 = RT([[],[[]]]) + sage: rt2 = RT([[[]],[]]) sage: rt1 is rt2 False sage: rt1._get_list() is rt2._get_list() True """ - # print "Normalizing %s"%self rank, unrank = self._cayley_ranker self._require_mutable() for st in self: assert st.is_immutable(), "Subtree {} is not normalized".format(st) - # print self._get_list() self._get_list().sort(key=rank) - # print self._get_list() # ensure unique representation self.set_immutable() @@ -177,13 +176,15 @@ def normalize(self): self._set_list(res._get_list()) def is_empty(self): - """ + r""" Return if ``self`` is the empty tree For rooted trees, returns always ``False`` - .. note:: this is not the same as ``bool(t)`` which returns whether - ``t`` has some child or not. + .. NOTE:: + + This is not the same as ``bool(t)`` which returns whether + ``t`` has some child or not. EXAMPLES:: @@ -220,7 +221,8 @@ def __call__(self, n=(), policy=None): return RootedTrees_all(policy) elif isinstance(n, (Integer, int)) and n >= 0: return RootedTrees_size(n, policy) - raise NotImplementedError("Do not know how to compute {}({})".format(self, n)) + msg = "Do not know how to compute {}({})".format(self, n) + raise NotImplementedError(msg) # try: # from sage.combinat.partition import Partition @@ -240,7 +242,7 @@ def add_constraints(self, cons, (n, st)): sage: RootedTrees.add_constraints((3,), ((), {})) (3,) """ - return cons + n + return cons + n @lazy_attribute def _default_policy(self): @@ -267,7 +269,6 @@ def __repr__(self): RootedTrees = RootedTreesFactory() - class RootedTrees_all(factories.SetFactoryParent, DisjointUnionEnumeratedSets): """ @@ -299,11 +300,11 @@ def __init__(self, policy=RootedTrees._default_policy): sage: TestSuite(RootedTrees()).run() """ factories.SetFactoryParent.__init__(self, (), policy, - category = InfiniteEnumeratedSets()) + category=InfiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, Family(NonNegativeIntegers(), self._of_size), - facade=True, keepkey = False, - category = self.category()) + facade=True, keepkey=False, + category=self.category()) def _repr_(self): r""" @@ -354,7 +355,7 @@ def unlabelled_trees(self): def labelled_trees(self): """ - Returns the set of unlabelled trees associated to ``self`` + Returns the set of labelled trees associated to ``self`` EXAMPLES:: @@ -376,7 +377,7 @@ def labelled_trees(self): class RootedTrees_size(factories.SetFactoryParent, UniqueRepresentation): """ - The enumerated set of rooted trees with a given number of node + The enumerated set of rooted trees with a given number of nodes EXAMPLES:: @@ -402,11 +403,11 @@ def __init__(self, n, policy): TESTS:: sage: for i in range(0, 6): - ... TestSuite(RootedTrees(i)).run() + ....: TestSuite(RootedTrees(i)).run() """ self._n = n factories.SetFactoryParent.__init__(self, (n,), policy, - category = FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) def _repr_(self): r""" @@ -415,7 +416,7 @@ def _repr_(self): sage: RootedTrees(4) # indirect doctest Rooted trees with 4 nodes """ - return "Rooted trees with %s nodes"%(self._n) + return "Rooted trees with {} nodes".format(self._n) def __iter__(self): """ @@ -491,7 +492,7 @@ class LabelledRootedTree(AbstractLabelledClonableTree, RootedTree): """ Labelled rooted trees - A labellel rooted tree is a rooted tree with a label attached at each node + A labelled rooted tree is a rooted tree with a label attached at each node INPUT: @@ -532,7 +533,7 @@ class LabelledRootedTree(AbstractLabelledClonableTree, RootedTree): def __classcall_private__(cls, *args, **opts): """ Ensure that trees created by the sets and directly are the same and - that they are instance of :class:`LabelledRootedTree` + that they are instances of :class:`LabelledRootedTree` TESTS:: @@ -551,7 +552,7 @@ def _auto_parent(cls): """ The automatic parent of the element of this class - When calling the constructor of an element of this class, one need a + When calling the constructor of an element of this class, one needs a parent. This class attribute specifies which parent is used. EXAMPLES:: @@ -573,15 +574,15 @@ class LabelledRootedTrees(UniqueRepresentation, Parent): EXAMPLES:: - sage: LOT = LabelledRootedTrees(); LOT + sage: LRT = LabelledRootedTrees(); LRT Labelled rooted trees - sage: x = LOT([], label = 3); x + sage: x = LRT([], label = 3); x 3[] - sage: x.parent() is LOT + sage: x.parent() is LRT True - sage: y = LOT([x, x, x], label = 2); y + sage: y = LRT([x, x, x], label = 2); y 2[3[], 3[], 3[]] - sage: y.parent() is LOT + sage: y.parent() is LRT True """ def __init__(self, category=None): @@ -592,7 +593,7 @@ def __init__(self, category=None): """ if category is None: category = Sets() - Parent.__init__(self, category = category) + Parent.__init__(self, category=category) def _repr_(self): """ @@ -624,10 +625,10 @@ def _an_element_(self): toto[3[], 42[3[], 3[]], 5[None[]]] """ LT = self._element_constructor_ - t = LT([], label = 3) - t1 = LT([t,t], label = 42) - t2 = LT([[]], label = 5) - return LT([t,t1,t2], label = "toto") + t = LT([], label=3) + t1 = LT([t, t], label=42) + t2 = LT([[]], label=5) + return LT([t, t1, t2], label="toto") def _element_constructor_(self, *args, **keywords): """ @@ -662,4 +663,3 @@ def labelled_trees(self): return self Element = LabelledRootedTree - From 5a15e0796b249344ef66b5a6f9a1d7e1a17107f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 21 Apr 2014 10:14:52 +0200 Subject: [PATCH 4/5] trac #11529 trying to make it work --- src/sage/combinat/rooted_tree.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/rooted_tree.py b/src/sage/combinat/rooted_tree.py index 6b4de34d4ca..039bd4c7175 100644 --- a/src/sage/combinat/rooted_tree.py +++ b/src/sage/combinat/rooted_tree.py @@ -269,7 +269,7 @@ def __repr__(self): RootedTrees = RootedTreesFactory() -class RootedTrees_all(factories.SetFactoryParent, +class RootedTrees_all(factories.ParentWithSetFactory, DisjointUnionEnumeratedSets): """ TESTS:: @@ -299,7 +299,7 @@ def __init__(self, policy=RootedTrees._default_policy): sage: TestSuite(RootedTrees()).run() """ - factories.SetFactoryParent.__init__(self, (), policy, + factories.ParentWithSetFactory.__init__(self, (), policy, category=InfiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, Family(NonNegativeIntegers(), self._of_size), @@ -332,7 +332,7 @@ def check_element(self, el, check): r""" Check that a given tree actually belongs to ``self`` - See :class:`sage.structure.set_factories.SetFactoryParent` + See :class:`sage.structure.set_factories.ParentWithSetFactory` TESTS:: @@ -375,7 +375,7 @@ def labelled_trees(self): return LabelledRootedTrees() -class RootedTrees_size(factories.SetFactoryParent, UniqueRepresentation): +class RootedTrees_size(factories.ParentWithSetFactory, UniqueRepresentation): """ The enumerated set of rooted trees with a given number of nodes @@ -406,7 +406,7 @@ def __init__(self, n, policy): ....: TestSuite(RootedTrees(i)).run() """ self._n = n - factories.SetFactoryParent.__init__(self, (n,), policy, + factories.ParentWithSetFactory.__init__(self, (n,), policy, category=FiniteEnumeratedSets()) def _repr_(self): @@ -457,7 +457,7 @@ def check_element(self, el, check=True): r""" Check that a given tree actually belongs to ``self`` - See :class:`sage.structure.set_factories.SetFactoryParent` + See :class:`sage.structure.set_factories.ParentWithSetFactory` EXAMPLES:: From 5849b284ed4e139efebc38a8f1d7bbf8a09e301b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 21 Apr 2014 10:21:16 +0200 Subject: [PATCH 5/5] trac #11529 tests seem to pass --- src/sage/combinat/ordered_tree.py | 18 ++++++++++-------- src/sage/combinat/rooted_tree.py | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index 75c7fc98a17..6463c2c830c 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -22,6 +22,13 @@ from sage.combinat.abstract_tree import (AbstractClonableTree, AbstractLabelledClonableTree) from sage.combinat.combinatorial_map import combinatorial_map +from sage.misc.cachefunc import cached_method +from sage.categories.sets_cat import Sets, EmptySetError +from sage.rings.integer import Integer +from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.sets.family import Family +from sage.misc.cachefunc import cached_method class OrderedTree(AbstractClonableTree, ClonableList): @@ -512,8 +519,8 @@ def left_right_symmetry(self): return OrderedTree(children) import sage.combinat.ranker - from sage.misc.cachefunc import cached_method _cayley_ranker = sage.combinat.ranker.on_fly() + @cached_method def cayley_normalize(self): """ @@ -546,12 +553,6 @@ def cayley_normalize_in_place(self): resl[i] = resl[i].cayley_normalized() resl.sort(key = rank) -from sage.categories.sets_cat import Sets, EmptySetError -from sage.rings.integer import Integer -from sage.sets.non_negative_integers import NonNegativeIntegers -from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets -from sage.sets.family import Family -from sage.misc.cachefunc import cached_method # Abstract class to serve as a Factory no instance are created. class OrderedTrees(UniqueRepresentation, Parent): @@ -623,6 +624,7 @@ def leaf(self): """ return self([]) + class OrderedTrees_all(DisjointUnionEnumeratedSets, OrderedTrees): """ The set of all ordered trees. @@ -694,7 +696,7 @@ def unlabelled_trees(self): def labelled_trees(self): """ - Return the set of unlabelled trees associated to ``self`` + Return the set of labelled trees associated to ``self`` EXAMPLES:: diff --git a/src/sage/combinat/rooted_tree.py b/src/sage/combinat/rooted_tree.py index 039bd4c7175..2788c5c8989 100644 --- a/src/sage/combinat/rooted_tree.py +++ b/src/sage/combinat/rooted_tree.py @@ -300,7 +300,7 @@ def __init__(self, policy=RootedTrees._default_policy): sage: TestSuite(RootedTrees()).run() """ factories.ParentWithSetFactory.__init__(self, (), policy, - category=InfiniteEnumeratedSets()) + category=InfiniteEnumeratedSets()) DisjointUnionEnumeratedSets.__init__( self, Family(NonNegativeIntegers(), self._of_size), facade=True, keepkey=False, @@ -407,7 +407,7 @@ def __init__(self, n, policy): """ self._n = n factories.ParentWithSetFactory.__init__(self, (n,), policy, - category=FiniteEnumeratedSets()) + category=FiniteEnumeratedSets()) def _repr_(self): r"""