Ticket #8288: trac_8288_search_forest_depth_and_breath_improvement-nb.patch

File trac_8288_search_forest_depth_and_breath_improvement-nb.patch, 35.6 KB (added by nborie, 10 years ago)
  • sage/categories/coxeter_groups.py

    # HG changeset patch
    # User Nicolas Borie <nicolas.borie at math.u-psud.fr>
    # Date 1300984746 -3600
    # Node ID 04eeaead51afcbedeff24bd034942c9013dd207b
    # Parent  0143a31bac8d7a30754929047cbed8d3aa9d7bda
    #8288: Depth/Breadth improvement for SearchForest
    
    diff --git a/sage/categories/coxeter_groups.py b/sage/categories/coxeter_groups.py
    a b from sage.misc.abstract_method import ab 
    1414from sage.misc.constant_function import ConstantFunction
    1515from sage.categories.category import Category
    1616from sage.categories.groups import Groups
     17from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
    1718from sage.combinat.backtrack import SearchForest
    1819from sage.combinat.finite_class import FiniteCombinatorialClass
    1920from sage.misc.flatten import flatten
    class CoxeterGroups(Category): 
    837838            factorizations `self = uv` such `p(u)` holds.
    838839
    839840            EXAMPLES:
     841
     842            We construct the set of all factorizations of the maximal
     843            element of the group::
     844
    840845                sage: W = WeylGroup(['A',3])
    841846                sage: s = W.simple_reflections()
    842847                sage: w0 = W.from_reduced_word([1,2,3,1,2,1])
    843848                sage: w0.binary_factorizations().cardinality()
    844849                24
     850
     851            The same number of factorizations, by bounded length::
     852
    845853                sage: [w0.binary_factorizations(lambda u: u.length() <= l).cardinality() for l in [-1,0,1,2,3,4,5,6]]
    846854                [0, 1, 4, 9, 15, 20, 23, 24]
     855
     856            The number of factorizations of the elements just below
     857            the maximal element::
     858
    847859                sage: [(s[i]*w0).binary_factorizations().cardinality() for i in [1,2,3]]
    848860                [12, 12, 12]
    849861                sage: w0.binary_factorizations(lambda u: False).cardinality()
    850862                0
    851863
     864            TESTS::
     865
     866                sage: w0.binary_factorizations().category()
     867                Category of finite enumerated sets
    852868            """
    853869            W = self.parent()
    854870            if not predicate(W.one()):
    class CoxeterGroups(Category): 
    859875                    u1 = u * s[i]
    860876                    if i == u1.first_descent() and predicate(u1):
    861877                        yield (u1, s[i]*v)
    862             return SearchForest(((W.one(), self),), succ)
     878            return SearchForest(((W.one(), self),), succ, category = FiniteEnumeratedSets())
    863879
    864880        # TODO: standardize / cleanup
    865881        def apply_simple_reflections(self, word, side = 'right'):
  • sage/combinat/backtrack.py

    diff --git a/sage/combinat/backtrack.py b/sage/combinat/backtrack.py
    a b This library contains generic tools for  
    55elements can be enumerated by exploring a search space with a (lazy)
    66tree or graph structure.
    77
    8 - :class:`~sage.combinat.backtrack.SearchForest`: Depth first search through a
    9   tree described by a ``children`` function.
    10 - :class:`~sage.combinat.backtrack.GenericBacktracker`: Depth first search
    11   through a tree described by a ``children`` function, with branch pruning,
    12   etc.
    13 - :class:`~sage.combinat.backtrack.TransitiveIdeal`: Depth first search through a
     8- :class:`SearchForest`: Depth and breath first
     9  search through a tree described by a ``children`` function.
     10- :class:`GenericBacktracker`: Depth first search through a tree
     11  described by a ``children`` function, with branch pruning, etc.
     12- :class:`TransitiveIdeal`: Depth first search through a
    1413  graph described by a ``neighbours`` relation.
    15 - :class:`~sage.combinat.backtrack.TransitiveIdealGraded`: Breath first search
     14- :class:`TransitiveIdealGraded`: Breath first search
    1615  through a graph described by a ``neighbours`` relation.
    1716
    18 TODO: 
     17TODO:
    1918
    2019#. Find a good and consistent naming scheme! Do we want to emphasize the
    2120   underlying graph/tree structure? The branch & bound aspect? The transitive
    TODO: 
    2928#*****************************************************************************
    3029#       Copyright (C) 2008 Mike Hansen <mhansen@gmail.com>,
    3130#                     2009 Nicolas M. Thiery <nthiery at users.sf.net>
     31#                     2010 Nicolas Borie <nicolas.borie at math.u-psud.fr>
    3232#
    3333#  Distributed under the terms of the GNU General Public License (GPL)
    3434#
    TODO: 
    4141#
    4242#                  http://www.gnu.org/licenses/
    4343#*****************************************************************************
     44from sage.categories.enumerated_sets import EnumeratedSets
     45from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
     46from sage.categories.monoids import Monoids
     47from sage.structure.parent import Parent
     48from sage.misc.prandom import randint
     49from sage.misc.abstract_method import abstract_method
     50from sage.categories.commutative_additive_semigroups import (
     51CommutativeAdditiveSemigroups)
     52from sage.structure.unique_representation import UniqueRepresentation
     53from sage.rings.integer_ring import ZZ
     54from sage.misc.sage_itertools import imap_and_filter_none
     55
    4456class GenericBacktracker(object):
    4557    r"""
    4658    A generic backtrack tool for exploring a search space organized as a tree,
    4759    with branch pruning, etc.
    4860
    49     See also :class:`~sage.combinat.backtrack.SearchForest` and
    50     :class:`~sage.combinat.backtrack.TransitiveIdeal` for handling simple
    51     special cases.
     61    See also :class:`SearchForest` and :class:`TransitiveIdeal` for
     62    handling simple special cases.
    5263    """
    5364
    5465    def __init__(self, initial_data, initial_state):
    class GenericBacktracker(object): 
    93104
    94105            #If the return state is None, then obj is a leaf
    95106            #of the search tree.  If yld is True, then obj
    96             #should be yielded. 
     107            #should be yielded.
    97108            if yld is True:
    98109                yield obj
    99110            if state is not None:
    100111                stack.append( self._rec(obj, state) )
    101112
    102 
    103 def search_forest_iterator(roots, children):
     113def search_forest_iterator(roots, children, algorithm='depth'):
    104114    r"""
    105115    Returns an iterator on the nodes of the forest having the given
    106116    roots, and where ``children(x)`` returns the children of the node ``x``
    def search_forest_iterator(roots, childr 
    109119
    110120    INPUT:
    111121
    112     - ``roots``: a list (or iterable)
    113     - ``children``: a function returning a list (or iterable)
    114        
     122     - ``roots``: a list (or iterable)
     123     - ``children``: a function returning a list (or iterable)
     124     - ``algorithm``: ``depth`` or ``breath`` (default: ``depth``)
     125
    115126    EXAMPLES:
    116    
    117     Search tree where leaves are binary sequences of length 3::
     127
     128    We construct the prefix tree of binary sequences of length at most
     129    three, and enumerate its nodes::
    118130
    119131        sage: from sage.combinat.backtrack import search_forest_iterator
    120         sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else []))
    121         [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
    122        
    123     Search tree where leaves are ordered sequences of length 2 from a 4-set::
    124    
    125         sage: from sage.combinat.backtrack import search_forest_iterator
    126         sage: list(search_forest_iterator([[]], lambda l: [l + [i] for i in range(4) if i not in l] if len(l) < 2 else []))
    127         [[], [0], [0, 1], [0, 2], [0, 3], [1], [1, 0], [1, 2], [1, 3], [2], [2, 0], [2, 1], [2, 3], [3], [3, 0], [3, 1], [3, 2]]
     132        sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]]
     133        ...                                     if len(l) < 3 else []))
     134        [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0],
     135         [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
     136
     137    By default, the nodes are iterated through by depth first search.
     138    We can instead use a breadth first search (increasing depth)::
     139
     140        sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]]
     141        ...                                     if len(l) < 3 else [],
     142        ...                               algorithm='breadth'))
     143        [[],
     144         [0], [1],
     145         [0, 0], [0, 1], [1, 0], [1, 1],
     146         [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1],
     147         [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
     148
     149    This allows for iterating trough trees of infinite depth::
     150
     151        sage: it = search_forest_iterator([[]], lambda l: [l+[0], l+[1]], algorithm='breadth')
     152        sage: [ it.next() for i in range(16) ]
     153        [[],
     154         [0], [1], [0, 0], [0, 1], [1, 0], [1, 1],
     155         [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1],
     156         [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1],
     157         [0, 0, 0, 0]]
     158
     159    Here is an interator through the prefix tree of sequences of
     160    letters in `0,1,2` without repetitions, sorted by length; the
     161    leaves are therefore permutations::
     162
     163        sage: list(search_forest_iterator([[]], lambda l: [l + [i] for i in range(3) if i not in l],
     164        ...                               algorithm='breadth'))
     165        [[],
     166         [0], [1], [2],
     167         [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1],
     168         [0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]
    128169    """
    129    
    130     #Invariant: stack[i] contains an iterator for the siblings of the i-th node of the current branch
     170    # Little trick: the same implementation handles both depth and
     171    # breath first search. Setting position to -1 makes a depth search
     172    # (you ask the children for the last node you met). Setting
     173    # position on 0 makes a breadth search (enumarate all the
     174    # descendants of a node before going on to the next father)
     175    if algorithm == 'depth':
     176        position = -1
     177    else:
     178        position = 0
     179
     180    # Invariant:
     181    #  - for breadth first search: stack[i] contains an iterator over the nodes
     182    #    of depth ``i`` in the tree
     183    #  - for depth first search: stack[i] contains an iterator over the children
     184    #    of the node at depth ``i-1`` in the current branch (assuming a virtual
     185    #    father of all roots at depth ``-1``)
    131186    stack = [iter(roots)]
    132187    while len(stack) > 0:
    133         # Try to get the next node at this depth
    134188        try:
    135             node = stack[-1].next()
     189            node = stack[position].next()
    136190        except StopIteration:
    137             #If there are no more, go back up the tree
     191            # If there are no more, go back up the tree
    138192            # We also need to check if we've exhausted all
    139193            # possibilities
    140             stack.pop()
     194            stack.pop(position)
    141195            continue
    142196
    143197        yield node
    144198        stack.append( iter(children(node)) )
    145199
    146 from sage.combinat.combinat import CombinatorialClass
    147 class SearchForest(CombinatorialClass):
     200class SearchForest(Parent):
    148201    r"""
    149     Returns the set of nodes of the forest having the given roots, and where
    150     ``children(x)`` returns the children of the node ``x`` of the forest.
     202    The enumerated set of the nodes of the forest having the given
     203    ``roots``, and where ``children(x)`` returns the children of the
     204    node ``x`` of the forest.
    151205
    152     See also :class:`~sage.combinat.backtrack.GenericBacktracker`,
    153     :class:`~sage.combinat.backtrack.TransitiveIdeal`, and
    154     :class:`~sage.combinat.backtrack.TransitiveIdealGraded`.
     206    See also :class:`GenericBacktracker`, :class:`TransitiveIdeal`,
     207    and :class:`TransitiveIdealGraded`.
    155208
    156209    INPUT:
    157210
    158     - ``roots``: a list (or iterable)
    159     - ``children``: a function returning a list (or iterable)
    160    
     211     - ``roots`` : a list (or iterable)
     212     - ``children`` : a function returning a list (or iterable, or iterator)
     213     - ``post_process`` : a function defined over the nodes of the
     214       forest (default: no post processing)
     215     - ``algorithm``: ``depth`` or ``breath`` (default: ``depth``)
     216     - ``category`` : a category (default: :class:`EnumeratedSets`)
     217
     218    The option ``post_process`` allows for customizing the nodes that
     219    are actually produced. Furthermore, if ``f(x)`` returns ``None``,
     220    then ``x`` won't be output at all.
     221
    161222    EXAMPLES:
    162223
    163     A generator object for binary sequences of length 3, listed::
     224    We construct the set of all binary sequences of length at most
     225    three, and list them::
    164226
    165         sage: list(SearchForest([[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else []))
    166         [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
     227        sage: S = SearchForest( [[]],
     228        ...       lambda l: [l+[0], l+[1]] if len(l) < 3 else [],
     229        ...       category=FiniteEnumeratedSets())
     230        sage: S.list()
     231        [[],
     232         [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1],
     233         [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
    167234
    168     A generator object for ordered sequences of length 2 from a 4-set, sampled::
     235    ``SearchForest`` needs to be explicitly told that the set is
     236    finite for the following to work::
    169237
    170         sage: tb = SearchForest([[]], lambda l: [l + [i] for i in range(4) if i not in l] if len(l) < 2 else [])
     238        sage: S.category()
     239        Category of finite enumerated sets
     240        sage: S.cardinality()
     241        15
     242
     243    We proceed with the set of all lists of letters in ``0,1,2``
     244    without repetitions, ordered by increasing length (i.e. using a
     245    breath first search through the tree)::
     246
     247        sage: tb = SearchForest( [[]],
     248        ...         lambda l: [l + [i] for i in range(3) if i not in l],
     249        ...         algorithm = 'breath',
     250        ...         category=FiniteEnumeratedSets())
    171251        sage: tb[0]
    172252        []
    173         sage: tb[16]
    174         [3, 2]
     253        sage: tb.cardinality()
     254        16
     255        sage: list(tb)
     256        [[],
     257         [0], [1], [2],
     258         [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1],
     259         [0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]
     260
     261    For infinite sets, this option should be set carefully to ensure
     262    that all elements are actually generated. The following example
     263    builds the set of all ordered pairs `(i,j)` of non negative
     264    integers such that `j\leq 1`::
     265
     266        sage: I = SearchForest([(0,0)],
     267        ...                    lambda l: [(l[0]+1, l[1]), (l[0], 1)]
     268        ...                              if l[1] == 0 else [(l[0], l[1]+1)])
     269
     270    With a depth first search, only the elements of the form `(i,0)`
     271    are generated::
     272
     273        sage: depth_search = I.depth_first_search_iterator()
     274        sage: [depth_search.next() for i in range(7)]
     275        [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)]
     276
     277    Using instead breath first search gives the usual anti-diagonal
     278    iterator::
     279
     280        sage: breadth_search = I.breadth_first_search_iterator()
     281        sage: [breadth_search.next() for i in range(15)]
     282        [(0, 0),
     283         (1, 0), (0, 1),
     284         (2, 0), (1, 1), (0, 2),
     285         (3, 0), (2, 1), (1, 2), (0, 3),
     286         (4, 0), (3, 1), (2, 2), (1, 3), (0, 4)]
     287
     288    .. rubric:: Deriving subclasses
     289
     290    The class of a parent `A` may derive from :class:`SearchForest` so
     291    that `A` can benefit from enumeration tools. As a running example,
     292    we consider the problem of enumerating integers whose binary
     293    expansion have at most three non zero digits. For example, `3 =
     294    2^1 + 2^0` has two non zero digits. `15 = 2^3 + 2^2 + 2^1 + 2^0`
     295    has four non zero digits. In fact, `15` is the smallest integer
     296    which is not in the enumerated set.
     297
     298    To achieve this, we use ``SearchForest`` to enumerate binary tuples
     299    with at most three non zero digits, apply a post processing to
     300    recover the corresponding integers, and discard tuples finishing
     301    by zero.
     302
     303    A first approach is to pass the ``roots`` and ``children``
     304    functions as arguments to :meth:`SearchForest.__init__`::
     305
     306        sage: class A(UniqueRepresentation, SearchForest):
     307        ...       def __init__(self):
     308        ...           SearchForest.__init__(self, [()],
     309        ...               lambda x : [x+(0,), x+(1,)] if sum(x) < 3 else [],
     310        ...               lambda x : sum(x[i]*2^i for i in range(len(x))) if sum(x) != 0 and x[-1] != 0 else None,
     311        ...               algorithm = 'breath',
     312        ...               category=InfiniteEnumeratedSets())
     313        sage: MyForest = A(); MyForest
     314        An enumerated set with a forest structure
     315        sage: MyForest.category()
     316        Category of infinite enumerated sets
     317        sage: p = iter(MyForest)
     318        sage: [p.next() for i in range(30)]
     319        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
     320
     321    An alternative approach is to implement ``roots`` and ``children``
     322    as methods of the subclass (in fact they could also be attributes
     323    of `A`). Namely, ``A.roots()`` must return an iterable containing
     324    the enumeration generators, and ``A.children(x)`` must return an
     325    iterable over the children of `x`. Optionally, `A` can have a
     326    method or attribute such that ``A.post_process(x)`` returns the
     327    desired output for the node ``x`` of the tree::
     328
     329        sage: class A(UniqueRepresentation, SearchForest):
     330        ...       def __init__(self):
     331        ...           SearchForest.__init__(self, algorithm = 'breath',
     332        ...                                 category=InfiniteEnumeratedSets())
     333        ...
     334        ...       def roots(self):
     335        ...           return [()]
     336        ...
     337        ...       def children(self, x):
     338        ...           if sum(x) < 3:
     339        ...               return [x+(0,), x+(1,)]
     340        ...           else:
     341        ...               return []
     342        ...
     343        ...       def post_process(self, x):
     344        ...           if sum(x) == 0 or x[-1] == 0:
     345        ...               return None
     346        ...           else:
     347        ...               return sum(x[i]*2^i for i in range(len(x)))
     348        sage: MyForest = A(); MyForest
     349        An enumerated set with a forest structure
     350        sage: MyForest.category()
     351        Category of infinite enumerated sets
     352        sage: p = iter(MyForest)
     353        sage: [p.next() for i in range(30)]
     354        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
     355
     356    .. warning::
     357
     358        A :class:`SearchForest` instance is pickable if and only if
     359        the input functions are themselves pickable. This excludes
     360        anonymous or interactively defined functions::
     361
     362            sage: def children(x):
     363            ...       return [x+1]
     364            sage: S = SearchForest( [1], children, category=InfiniteEnumeratedSets())
     365            sage: dumps(S)
     366            Traceback (most recent call last):
     367            ...
     368            PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
     369
     370        Let us now fake ``children`` being defined in a Python module::
     371
     372            sage: import __main__
     373            sage: __main__.children = children
     374            sage: S = SearchForest( [1], children, category=InfiniteEnumeratedSets())
     375            sage: loads(dumps(S))
     376            An enumerated set with a forest structure
    175377    """
    176     def __init__(self, roots, children):
     378    def __init__(self, roots = None, children = None, post_process = None,
     379                 algorithm = 'depth', category=None):
    177380        r"""
    178381        TESTS::
    179382
    180             sage: C = SearchForest((1,), lambda x: [x+1])
    181             sage: C._roots
    182             (1,)
    183             sage: C._children
    184             <function <lambda> at ...>
    185 
     383            sage: S = SearchForest(NN, lambda x : [], lambda x: x^2 if x.is_prime() else None)
     384            sage: S.category()
     385            Category of enumerated sets
    186386        """
    187         self._roots = roots
    188         self._children = children
     387        if roots is not None:
     388            self._roots = roots
     389        if children is not None:
     390            self.children = children
     391        if post_process is not None:
     392            self.post_process = post_process
     393        self._algorithm = algorithm
     394        Parent.__init__(self, category = EnumeratedSets().or_subcategory(category))
    189395
    190396    def _repr_(self):
    191397        r"""
    192398        TESTS::
    193399
    194             sage: SearchForest((1,), lambda x: [x+1])   # Todo: improve!
    195             An enumerated set
     400            sage: SearchForest( [1], lambda x: [x+1])
     401            An enumerated set with a forest structure
    196402        """
    197         return "An enumerated set"
     403        return "An enumerated set with a forest structure"
     404
     405    def roots(self):
     406        r"""
     407        Returns an iterable over the roots of ``self``.
     408
     409        EXAMPLES::
     410
     411            sage: I = SearchForest([(0,0)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)])
     412            sage: [i for i in I.roots()]
     413            [(0, 0)]
     414            sage: I = SearchForest([(0,0),(1,1)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)])
     415            sage: [i for i in I.roots()]
     416            [(0, 0), (1, 1)]
     417        """
     418        return self._roots
     419
     420    @abstract_method
     421    def children(self, x):
     422        r"""
     423        Returns the children of the element ``x``
     424
     425        The result can be a list, an iterable, an iterator, or even a
     426        generator.
     427
     428        EXAMPLES::
     429
     430            sage: I = SearchForest([(0,0)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)])
     431            sage: [i for i in I.children((0,0))]
     432            [(1, 0), (0, 1)]
     433            sage: [i for i in I.children((1,0))]
     434            [(2, 0), (1, 1)]
     435            sage: [i for i in I.children((1,1))]
     436            [(1, 2)]
     437            sage: [i for i in I.children((4,1))]
     438            [(4, 2)]
     439            sage: [i for i in I.children((4,0))]
     440            [(5, 0), (4, 1)]
     441        """
    198442
    199443    def __iter__(self):
    200444        r"""
    201         Returns an iterator on the elements of self.
     445        Returns an iterator over the elements of ``self``.
    202446
    203447        EXAMPLES::
    204448
    205             sage: def succ(l):
     449            sage: def children(l):
    206450            ...        return [l+[0], l+[1]]
    207451            ...
    208             sage: C = SearchForest(([],), succ)
     452            sage: C = SearchForest(([],), children)
    209453            sage: f = C.__iter__()
    210454            sage: f.next()
    211455            []
    class SearchForest(CombinatorialClass): 
    213457            [0]
    214458            sage: f.next()
    215459            [0, 0]
     460        """
     461        iter = search_forest_iterator(self.roots(),
     462                                      self.children,
     463                                      algorithm = self._algorithm)
     464        if hasattr(self, "post_process"):
     465            iter = imap_and_filter_none(self.post_process, iter)
     466        return iter
    216467
    217             sage: import __main__
    218             sage: __main__.succ = succ # just because succ has been defined interactively
    219             sage: loads(dumps(C))
    220             An enumerated set
     468    def depth_first_search_iterator(self):
     469        r"""
     470        Returns a depth first search iterator over the elements of ``self``
     471
     472        EXAMPLES::
     473
     474            sage: f = SearchForest([[]],
     475            ...                    lambda l: [l+[0], l+[1]] if len(l) < 3 else [])
     476            sage: list(f.depth_first_search_iterator())
     477            [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
    221478        """
    222         return search_forest_iterator(self._roots, self._children)
     479        return self.__iter__()
     480
     481    def breadth_first_search_iterator(self):
     482        r"""
     483        Returns a breadth first search iterator over the elements of ``self``
     484
     485        EXAMPLES::
     486
     487            sage: f = SearchForest([[]],
     488            ...                    lambda l: [l+[0], l+[1]] if len(l) < 3 else [])
     489            sage: list(f.breadth_first_search_iterator())
     490            [[], [0], [1], [0, 0], [0, 1], [1, 0], [1, 1], [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
     491            sage: S = SearchForest([(0,0)],
     492            ...   lambda x : [(x[0], x[1]+1)] if x[1] != 0 else [(x[0]+1,0), (x[0],1)],
     493            ...   post_process = lambda x: x if ((is_prime(x[0]) and is_prime(x[1])) and ((x[0] - x[1]) == 2)) else None)
     494            sage: p = S.breadth_first_search_iterator()
     495            sage: [p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next()]
     496            [(5, 3), (7, 5), (13, 11), (19, 17), (31, 29), (43, 41), (61, 59)]
     497        """
     498        iter = search_forest_iterator(self.roots(), self.children, algorithm='breadth')
     499        if hasattr(self, "post_process"):
     500            iter = imap_and_filter_none(self.post_process, iter)
     501        return iter
     502
     503    def _elements_of_depth_iterator_rec(self, depth=0):
     504        r"""
     505        Returns an iterator over the elements of ``self`` of given depth.
     506        An element of depth `n` can be obtained applying `n` times the
     507        children function from a root. This function is not affected
     508        by post processing.
     509
     510        EXAMPLES::
     511
     512            sage: I = SearchForest([(0,0)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)])
     513            sage: list(I._elements_of_depth_iterator_rec(8))
     514            [(8, 0), (7, 1), (6, 2), (5, 3), (4, 4), (3, 5), (2, 6), (1, 7), (0, 8)]
     515            sage: I = SearchForest([[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else [])
     516            sage: list(I._elements_of_depth_iterator_rec(0))
     517            [[]]
     518            sage: list(I._elements_of_depth_iterator_rec(1))
     519            [[0], [1]]
     520            sage: list(I._elements_of_depth_iterator_rec(2))
     521            [[0, 0], [0, 1], [1, 0], [1, 1]]
     522            sage: list(I._elements_of_depth_iterator_rec(3))
     523            [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
     524            sage: list(I._elements_of_depth_iterator_rec(4))
     525            []
     526        """
     527        if depth == 0:
     528            for node in self.roots():
     529                yield node
     530        else:
     531            for father in self._elements_of_depth_iterator_rec(depth - 1):
     532                for node in self.children(father):
     533                    yield node
     534
     535    def elements_of_depth_iterator(self, depth=0):
     536        r"""
     537        Returns an iterator over the elements of ``self`` of given depth.
     538        An element of depth `n` can be obtained applying `n` times the
     539        children function from a root.
     540
     541        EXAMPLES::
     542
     543            sage: S = SearchForest([(0,0)] ,
     544            ...          lambda x : [(x[0], x[1]+1)] if x[1] != 0 else [(x[0]+1,0), (x[0],1)],
     545            ...          post_process = lambda x: x if ((is_prime(x[0]) and is_prime(x[1]))
     546            ...                                          and ((x[0] - x[1]) == 2)) else None)
     547            sage: p = S.elements_of_depth_iterator(8)
     548            sage: p.next()
     549            (5, 3)
     550            sage: S = SearchForest(NN, lambda x : [],
     551            ...                        lambda x: x^2 if x.is_prime() else None)
     552            sage: p = S.elements_of_depth_iterator(0)
     553            sage: [p.next(), p.next(), p.next(), p.next(), p.next()]
     554            [4, 9, 25, 49, 121]
     555        """
     556        iter = self._elements_of_depth_iterator_rec(depth)
     557        if hasattr(self, "post_process"):
     558            iter = imap_and_filter_none(self.post_process, iter)
     559        return iter
     560
     561    def __contains__(self, elt):
     562        r"""
     563        Returns ``True`` if ``elt`` is in ``self``.
     564
     565        .. warning::
     566
     567           This is achieved by iterating through the elements until
     568           ``elt`` is found. In particular, this method will never
     569           stop when ``elt`` is not in ``self`` and ``self`` is
     570           infinite.
     571
     572        EXAMPLES::
     573
     574            sage: S = SearchForest( [[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else [], category=FiniteEnumeratedSets())
     575            sage: [4] in S
     576            False
     577            sage: [1] in S
     578            True
     579            sage: [1,1,1,1] in S
     580            False
     581            sage: all(S.__contains__(i) for i in iter(S))
     582            True
     583            sage: S = SearchForest([1], lambda x: [x+1], category=InfiniteEnumeratedSets())
     584            sage: 1 in S
     585            True
     586            sage: 732 in S
     587            True
     588            sage: -1 in S # not tested : Will never stop
     589
     590        The algorithm uses a random enumeration of the nodes of the
     591        forest. This choice was motivated by examples in which both
     592        depth first search and breadth first search failed. The
     593        following example enumerates all ordered pairs of non negative
     594        integers, starting from an infinite set of roots, where each
     595        roots has an infinite number of children::
     596
     597            sage: S = SearchForest(Family(NN, lambda x : (x, 0)),
     598            ...   lambda x : Family(PositiveIntegers(), lambda y : (x[0], y)) if x[1] == 0 else [])
     599            sage: p = S.depth_first_search_iterator()
     600            sage: [p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next()]
     601            [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
     602            sage: p = S.breadth_first_search_iterator()
     603            sage: [p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next()]
     604            [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)]
     605            sage: (0,0) in S
     606            True
     607            sage: (1,1) in S
     608            True
     609            sage: (10,10) in S
     610            True
     611            sage: (42,18) in S
     612            True
     613
     614        We now consider the same set of all ordered pairs of non
     615        negative integers but constructed in a different way. There
     616        still are infinitely many roots, but each node has a single
     617        child. From each root starts an infinite branch of breath
     618        `1`::
     619
     620            sage: S = SearchForest(Family(NN, lambda x : (x, 0)) , lambda x : [(x[0], x[1]+1)])
     621            sage: p = S.depth_first_search_iterator()
     622            sage: [p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next()]
     623            [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
     624            sage: p = S.breadth_first_search_iterator()
     625            sage: [p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next()]
     626            [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)]
     627            sage: (0,0) in S
     628            True
     629            sage: (1,1) in S
     630            True
     631            sage: (10,10) in S
     632            True
     633            sage: (37,11) in S
     634            True
     635        """
     636        stack = [iter(self.roots())]
     637        while len(stack) > 0:
     638            position = randint(0,len(stack)-1)
     639            try:
     640                node = stack[position].next()
     641            except StopIteration:
     642                stack.pop(position)
     643                continue
     644
     645            if node == elt:
     646                return True
     647            stack.append( iter(self.children(node)) )
     648        return False
     649
     650class PositiveIntegerSemigroup(UniqueRepresentation, SearchForest):
     651    r"""
     652    The commutative additive semigroup of positive integers.
     653
     654    This class provides an example of algebraic structure which
     655    inherits from :class:`SearchForest`. It builds the positive
     656    integers a la Peano, and endows it with its natural commutative
     657    additive semigroup structure.
     658
     659    EXAMPLES::
     660
     661        sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
     662        sage: PP = PositiveIntegerSemigroup()
     663        sage: PP.category()
     664        Join of Category of infinite enumerated sets and Category of commutative additive semigroups and Category of monoids
     665        sage: PP.cardinality()
     666        +Infinity
     667        sage: PP.one()
     668        1
     669        sage: PP.an_element()
     670        1
     671        sage: list(PP.some_elements())
     672        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
     673
     674    TESTS::
     675
     676        sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
     677        sage: PP = PositiveIntegerSemigroup()
     678        sage: # TODO: remove the skip; see #9065
     679        sage: TestSuite(PP).run(skip='_test_one')  # long time
     680    """
     681    def __init__(self):
     682        r"""
     683        TESTS::
     684
     685            sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
     686            sage: PP = PositiveIntegerSemigroup()
     687        """
     688        SearchForest.__init__(self, category=(InfiniteEnumeratedSets(), CommutativeAdditiveSemigroups(), Monoids()))
     689
     690    def roots(self):
     691        r"""
     692        Returns the single root of ``self``.
     693
     694        EXAMPLES::
     695
     696            sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
     697            sage: PP = PositiveIntegerSemigroup()
     698            sage: list(PP.roots())
     699            [1]
     700        """
     701        return [ZZ(1)]
     702
     703    def children(self, x):
     704        r"""
     705        Returns the single child ``x+1`` of the integer ``x``
     706
     707        EXAMPLES::
     708
     709            sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
     710            sage: PP = PositiveIntegerSemigroup()
     711            sage: list(PP.children(1))
     712            [2]
     713            sage: list(PP.children(42))
     714            [43]
     715        """
     716        return [ZZ(x+1)]
     717
     718    def one(self):
     719        r"""
     720        Return the unit of ``self``.
     721
     722        EXAMPLES::
     723
     724            sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
     725            sage: PP = PositiveIntegerSemigroup()
     726            sage: PP.one()
     727            1
     728        """
     729        return self.first()
    223730
    224731class TransitiveIdeal():
    225732    r"""
    class TransitiveIdeal(): 
    243750    relation. The memory complexity is the depth, that is the maximal
    244751    distance between a generator and an element of `S`.
    245752
    246     See also :class:`~sage.combinat.backtrack.SearchForest` and
    247     :class:`~sage.combinat.backtrack.TransitiveIdealGraded`.
     753    See also :class:`SearchForest` and :class:`TransitiveIdealGraded`.
    248754
    249755    EXAMPLES::
    250756
    class TransitiveIdealGraded(TransitiveId 
    356862    the relation. The memory complexity is the depth, that is the
    357863    maximal distance between a generator and an element of `S`.
    358864
    359     See also :class:`~sage.combinat.backtrack.SearchForest` and
    360     :class:`~sage.combinat.backtrack.TransitiveIdeal`.
     865    See also :class:`SearchForest` and :class:`TransitiveIdeal`.
    361866
    362867    EXAMPLES::
    363868     
  • sage/misc/sage_itertools.py

    diff --git a/sage/misc/sage_itertools.py b/sage/misc/sage_itertools.py
    a b  
    11"""
    22Miscellaneous functions which should eventually be moved upstream into
    33Python's standard itertools module.
     4
     5TODO: Cython this file as soon as Cython supports ``yield``.
    46"""
    57#*****************************************************************************
    68#  Copyright (C) 2010     Nicolas M. Thiery <nthiery at users.sf.net>
    def max_cmp(L, cmp=None): 
    137139        if cmp(item, m) > 0:
    138140            m = item
    139141    return m
     142
     143def imap_and_filter_none(function, iterable):
     144    r"""
     145    Returns an iterator over the elements ``function(x)``, where ``x``
     146    iterates through ``iterable``, such that ``function(x)`` is not ``None``.
     147
     148    EXAMPLES::
     149
     150        sage: from sage.misc.sage_itertools import imap_and_filter_none
     151        sage: p = imap_and_filter_none(lambda x: x if is_prime(x) else None, range(15))
     152        sage: [p.next(), p.next(), p.next(), p.next(), p.next(), p.next()]
     153        [2, 3, 5, 7, 11, 13]
     154        sage: p = imap_and_filter_none(lambda x: x+x, ['a','b','c','d','e'])
     155        sage: [p.next(), p.next(), p.next(), p.next(), p.next()]
     156        ['aa', 'bb', 'cc', 'dd', 'ee']
     157    """
     158    for x in iterable:
     159        x = function(x)
     160        if x is not None:
     161            yield x