Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/backtrack.py
4048 views
1
r"""
2
Backtracking
3
4
This library contains generic tools for constructing large sets whose
5
elements can be enumerated by exploring a search space with a (lazy)
6
tree or graph structure.
7
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
13
graph described by a ``neighbours`` relation.
14
- :class:`TransitiveIdealGraded`: Breath first search
15
through a graph described by a ``neighbours`` relation.
16
17
TODO:
18
19
#. Find a good and consistent naming scheme! Do we want to emphasize the
20
underlying graph/tree structure? The branch & bound aspect? The transitive
21
closure of a relation point of view?
22
23
#. Do we want ``TransitiveIdeal(relation, generators)`` or
24
``TransitiveIdeal(generators, relation)``? The code needs to be standardized once
25
the choice is made.
26
27
"""
28
#*****************************************************************************
29
# Copyright (C) 2008 Mike Hansen <[email protected]>,
30
# 2009 Nicolas M. Thiery <nthiery at users.sf.net>
31
# 2010 Nicolas Borie <nicolas.borie at math.u-psud.fr>
32
#
33
# Distributed under the terms of the GNU General Public License (GPL)
34
#
35
# This code is distributed in the hope that it will be useful,
36
# but WITHOUT ANY WARRANTY; without even the implied warranty of
37
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38
# General Public License for more details.
39
#
40
# The full text of the GPL is available at:
41
#
42
# http://www.gnu.org/licenses/
43
#*****************************************************************************
44
from sage.categories.enumerated_sets import EnumeratedSets
45
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
46
from sage.categories.monoids import Monoids
47
from sage.structure.parent import Parent
48
from sage.misc.prandom import randint
49
from sage.misc.abstract_method import abstract_method
50
from sage.categories.commutative_additive_semigroups import (
51
CommutativeAdditiveSemigroups)
52
from sage.structure.unique_representation import UniqueRepresentation
53
from sage.rings.integer_ring import ZZ
54
from sage.misc.sage_itertools import imap_and_filter_none
55
56
class GenericBacktracker(object):
57
r"""
58
A generic backtrack tool for exploring a search space organized as a tree,
59
with branch pruning, etc.
60
61
See also :class:`SearchForest` and :class:`TransitiveIdeal` for
62
handling simple special cases.
63
"""
64
65
def __init__(self, initial_data, initial_state):
66
r"""
67
EXAMPLES::
68
69
sage: from sage.combinat.backtrack import GenericBacktracker
70
sage: p = GenericBacktracker([], 1)
71
sage: loads(dumps(p))
72
<sage.combinat.backtrack.GenericBacktracker object at 0x...>
73
"""
74
self._initial_data = initial_data
75
self._initial_state = initial_state
76
77
def __iter__(self):
78
r"""
79
EXAMPLES::
80
81
sage: from sage.combinat.permutation import PatternAvoider
82
sage: p = PatternAvoider(4, [[1,3,2]])
83
sage: len(list(p))
84
14
85
"""
86
#Initialize the stack of generators with the initial data.
87
#The generator in stack[i] is a generator for the i^th level
88
#of the search tree.
89
stack = []
90
stack.append(self._rec(self._initial_data, self._initial_state))
91
92
done = False
93
while not done:
94
#Try to get the next object in this level
95
try:
96
obj, state, yld = stack[-1].next()
97
except StopIteration:
98
#If there are no more, go back up the tree
99
#We also need to check if we've exhausted all
100
#possibilities
101
stack.pop()
102
done = len(stack) == 0
103
continue
104
105
#If the return state is None, then obj is a leaf
106
#of the search tree. If yld is True, then obj
107
#should be yielded.
108
if yld is True:
109
yield obj
110
if state is not None:
111
stack.append( self._rec(obj, state) )
112
113
def search_forest_iterator(roots, children, algorithm='depth'):
114
r"""
115
Returns an iterator on the nodes of the forest having the given
116
roots, and where ``children(x)`` returns the children of the node ``x``
117
of the forest. Note that every node of the tree is returned,
118
not simply the leaves.
119
120
INPUT:
121
122
- ``roots``: a list (or iterable)
123
- ``children``: a function returning a list (or iterable)
124
- ``algorithm``: ``depth`` or ``breath`` (default: ``depth``)
125
126
EXAMPLES:
127
128
We construct the prefix tree of binary sequences of length at most
129
three, and enumerate its nodes::
130
131
sage: from sage.combinat.backtrack import search_forest_iterator
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]]
169
"""
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``)
186
stack = [iter(roots)]
187
while len(stack) > 0:
188
try:
189
node = stack[position].next()
190
except StopIteration:
191
# If there are no more, go back up the tree
192
# We also need to check if we've exhausted all
193
# possibilities
194
stack.pop(position)
195
continue
196
197
yield node
198
stack.append( iter(children(node)) )
199
200
class SearchForest(Parent):
201
r"""
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.
205
206
See also :class:`GenericBacktracker`, :class:`TransitiveIdeal`,
207
and :class:`TransitiveIdealGraded`.
208
209
INPUT:
210
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
222
EXAMPLES:
223
224
We construct the set of all binary sequences of length at most
225
three, and list them::
226
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]]
234
235
``SearchForest`` needs to be explicitly told that the set is
236
finite for the following to work::
237
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())
251
sage: tb[0]
252
[]
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
377
"""
378
def __init__(self, roots = None, children = None, post_process = None,
379
algorithm = 'depth', facade = None, category=None):
380
r"""
381
TESTS::
382
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
386
"""
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, facade = facade, category = EnumeratedSets().or_subcategory(category))
395
396
def _repr_(self):
397
r"""
398
TESTS::
399
400
sage: SearchForest( [1], lambda x: [x+1])
401
An enumerated set with a forest structure
402
"""
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
"""
442
443
def __iter__(self):
444
r"""
445
Returns an iterator over the elements of ``self``.
446
447
EXAMPLES::
448
449
sage: def children(l):
450
... return [l+[0], l+[1]]
451
...
452
sage: C = SearchForest(([],), children)
453
sage: f = C.__iter__()
454
sage: f.next()
455
[]
456
sage: f.next()
457
[0]
458
sage: f.next()
459
[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
467
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]]
478
"""
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
650
class 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 and Category of facade sets
665
sage: PP.cardinality()
666
+Infinity
667
sage: PP.one()
668
1
669
sage: PP.an_element()
670
1
671
sage: some_elements = list(PP.some_elements()); 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: TestSuite(PP).run(elements=some_elements)
679
sage: TestSuite(PP).run() # 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, facade = ZZ, 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()
730
731
class TransitiveIdeal():
732
r"""
733
Generic tool for constructing ideals of a relation.
734
735
INPUT:
736
737
- ``relation``: a function (or callable) returning a list (or iterable)
738
- ``generators``: a list (or iterable)
739
740
Returns the set `S` of elements that can be obtained by repeated
741
application of ``relation`` on the elements of ``generators``.
742
743
Consider ``relation`` as modeling a directed graph (possibly with
744
loops, cycles, or circuits). Then `S` is the ideal generated by
745
``generators`` under this relation.
746
747
Enumerating the elements of `S` is achieved by depth first search
748
through the graph. The time complexity is `O(n+m)` where `n` is
749
the size of the ideal, and `m` the number of edges in the
750
relation. The memory complexity is the depth, that is the maximal
751
distance between a generator and an element of `S`.
752
753
See also :class:`SearchForest` and :class:`TransitiveIdealGraded`.
754
755
EXAMPLES::
756
757
sage: [i for i in TransitiveIdeal(lambda i: [i+1] if i<10 else [], [0])]
758
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
759
760
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+1,3)], [0])]
761
[0, 1, 2]
762
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+2,3)], [0])]
763
[0, 2, 1]
764
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+2,10)], [0])]
765
[0, 2, 4, 6, 8]
766
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+3,10),mod(i+5,10)], [0])]
767
[0, 3, 8, 1, 4, 5, 6, 7, 9, 2]
768
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+4,10),mod(i+6,10)], [0])]
769
[0, 4, 8, 2, 6]
770
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+3,9)], [0,1])]
771
[0, 1, 3, 4, 6, 7]
772
773
sage: [p for p in TransitiveIdeal(lambda x:[x],[Permutation([3,1,2,4]), Permutation([2,1,3,4])])]
774
[[2, 1, 3, 4], [3, 1, 2, 4]]
775
776
We now illustrate that the enumeration is done lazily, by depth first
777
search::
778
779
sage: C = TransitiveIdeal(lambda x: [x-1, x+1], (-10, 0, 10))
780
sage: f = C.__iter__()
781
sage: [ f.next() for i in range(6) ]
782
[0, 1, 2, 3, 4, 5]
783
784
We compute all the permutations of 3::
785
786
sage: [p for p in TransitiveIdeal(attrcall("permutohedron_succ"), [Permutation([1,2,3])])]
787
[[1, 2, 3], [2, 1, 3], [1, 3, 2], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
788
789
We compute all the permutations which are larger than [3,1,2,4],
790
[2,1,3,4] in the right permutohedron::
791
792
sage: [p for p in TransitiveIdeal(attrcall("permutohedron_succ"), [Permutation([3,1,2,4]), Permutation([2,1,3,4])])]
793
[[2, 1, 3, 4], [2, 1, 4, 3], [2, 4, 1, 3], [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 2, 1], [3, 1, 2, 4], [2, 4, 3, 1], [3, 2, 1, 4], [2, 3, 1, 4], [2, 3, 4, 1], [3, 2, 4, 1], [3, 1, 4, 2], [3, 4, 2, 1], [3, 4, 1, 2], [4, 3, 1, 2]]
794
795
"""
796
def __init__(self, succ, generators):
797
r"""
798
TESTS::
799
800
sage: C = TransitiveIdeal(factor, (1, 2, 3))
801
sage: C._succ
802
<function factor at ...>
803
sage: C._generators
804
(1, 2, 3)
805
sage: loads(dumps(C)) # should test for equality with C, but equality is not implemented
806
"""
807
self._succ = succ
808
self._generators = generators
809
810
def __iter__(self):
811
r"""
812
Returns an iterator on the elements of self.
813
814
TESTS::
815
816
sage: C = TransitiveIdeal(lambda x: [1,2], ())
817
sage: list(C) # indirect doctest
818
[]
819
820
sage: C = TransitiveIdeal(lambda x: [1,2], (1,))
821
sage: list(C) # indirect doctest
822
[1, 2]
823
824
sage: C = TransitiveIdeal(lambda x: [], (1,2))
825
sage: list(C) # indirect doctest
826
[1, 2]
827
828
"""
829
known = set(self._generators)
830
todo = known.copy()
831
while len(todo) > 0:
832
x = todo.pop()
833
yield x
834
for y in self._succ(x):
835
if y == None or y in known:
836
continue
837
todo.add(y)
838
known.add(y)
839
return
840
841
842
class TransitiveIdealGraded(TransitiveIdeal):
843
r"""
844
Generic tool for constructing ideals of a relation.
845
846
INPUT:
847
848
- ``relation``: a function (or callable) returning a list (or iterable)
849
- ``generators``: a list (or iterable)
850
851
Returns the set `S` of elements that can be obtained by repeated
852
application of ``relation`` on the elements of ``generators``.
853
854
Consider ``relation`` as modeling a directed graph (possibly with
855
loops, cycles, or circuits). Then `S` is the ideal generated by
856
``generators`` under this relation.
857
858
Enumerating the elements of `S` is achieved by breath first search
859
through the graph; hence elements are enumerated by increasing
860
distance from the generators. The time complexity is `O(n+m)`
861
where `n` is the size of the ideal, and `m` the number of edges in
862
the relation. The memory complexity is the depth, that is the
863
maximal distance between a generator and an element of `S`.
864
865
See also :class:`SearchForest` and :class:`TransitiveIdeal`.
866
867
EXAMPLES::
868
869
sage: [i for i in TransitiveIdealGraded(lambda i: [i+1] if i<10 else [], [0])]
870
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
871
872
We now illustrates that the enumeration is done lazily, by breath first search::
873
874
sage: C = TransitiveIdealGraded(lambda x: [x-1, x+1], (-10, 0, 10))
875
sage: f = C.__iter__()
876
877
The elements at distance 0 from the generators::
878
879
sage: sorted([ f.next() for i in range(3) ])
880
[-10, 0, 10]
881
882
The elements at distance 1 from the generators::
883
884
sage: sorted([ f.next() for i in range(6) ])
885
[-11, -9, -1, 1, 9, 11]
886
887
The elements at distance 2 from the generators::
888
889
sage: sorted([ f.next() for i in range(6) ])
890
[-12, -8, -2, 2, 8, 12]
891
892
The enumeration order between elements at the same distance is not specified.
893
894
We compute all the permutations which are larger than [3,1,2,4] or
895
[2,1,3,4] in the permutohedron::
896
897
sage: [p for p in TransitiveIdealGraded(attrcall("permutohedron_succ"), [Permutation([3,1,2,4]), Permutation([2,1,3,4])])]
898
[[3, 1, 2, 4], [2, 1, 3, 4], [2, 1, 4, 3], [3, 2, 1, 4], [2, 3, 1, 4], [3, 1, 4, 2], [2, 3, 4, 1], [3, 4, 1, 2], [3, 2, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1], [4, 3, 1, 2], [4, 2, 1, 3], [3, 4, 2, 1], [4, 2, 3, 1], [4, 3, 2, 1]]
899
900
"""
901
def __init__(self, succ, generators):
902
r"""
903
TESTS::
904
905
sage: C = TransitiveIdealGraded(factor, (1, 2, 3))
906
sage: C._succ
907
<function factor at ...>
908
sage: C._generators
909
(1, 2, 3)
910
sage: loads(dumps(C)) # should test for equality with C, but equality is not implemented
911
"""
912
self._succ = succ
913
self._generators = generators
914
915
def __iter__(self):
916
r"""
917
Returns an iterator on the elements of self.
918
919
TESTS::
920
921
sage: C = TransitiveIdeal(lambda x: [1,2], ())
922
sage: list(C) # indirect doctest
923
[]
924
925
sage: C = TransitiveIdeal(lambda x: [1,2], (1,))
926
sage: list(C) # indirect doctest
927
[1, 2]
928
929
sage: C = TransitiveIdeal(lambda x: [], (1,2))
930
sage: list(C) # indirect doctest
931
[1, 2]
932
"""
933
current_level = self._generators
934
known = set(current_level)
935
while len(current_level) > 0:
936
next_level = set()
937
for x in current_level:
938
yield x
939
for y in self._succ(x):
940
if y == None or y in known:
941
continue
942
next_level.add(y)
943
known.add(y)
944
current_level = next_level
945
return
946
947