Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/backtrack.py
8817 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 breadth 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`: Breadth 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(Permutations(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
Return 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 ``breadth`` (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
# breadth 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 ``breadth`` (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
breadth 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 = 'breadth',
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 nonnegative
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 breadth 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 nonzero digits. For example, `3 =
294
2^1 + 2^0` has two nonzero digits. `15 = 2^3 + 2^2 + 2^1 + 2^0`
295
has four nonzero 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 nonzero 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 = 'breadth',
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 = 'breadth',
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 picklable if and only if
359
the input functions are themselves picklable. 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
Return 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
Return 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
Return 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
Return 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
Return 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
Return 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
Return 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
Return ``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 nonnegative
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
615
nonnegative 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 breadth
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 monoids and Category of commutative additive semigroups and Category of infinite enumerated sets 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
679
We factor out the long test from the ``TestSuite``::
680
681
sage: TestSuite(PP).run(skip='_test_enumerated_set_contains')
682
sage: PP._test_enumerated_set_contains() # long time
683
"""
684
def __init__(self):
685
r"""
686
TESTS::
687
688
sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
689
sage: PP = PositiveIntegerSemigroup()
690
"""
691
SearchForest.__init__(self, facade = ZZ, category=(InfiniteEnumeratedSets(), CommutativeAdditiveSemigroups(), Monoids()))
692
693
def roots(self):
694
r"""
695
Return the single root of ``self``.
696
697
EXAMPLES::
698
699
sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
700
sage: PP = PositiveIntegerSemigroup()
701
sage: list(PP.roots())
702
[1]
703
"""
704
return [ZZ(1)]
705
706
def children(self, x):
707
r"""
708
Return the single child ``x+1`` of the integer ``x``
709
710
EXAMPLES::
711
712
sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
713
sage: PP = PositiveIntegerSemigroup()
714
sage: list(PP.children(1))
715
[2]
716
sage: list(PP.children(42))
717
[43]
718
"""
719
return [ZZ(x+1)]
720
721
def one(self):
722
r"""
723
Return the unit of ``self``.
724
725
EXAMPLES::
726
727
sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
728
sage: PP = PositiveIntegerSemigroup()
729
sage: PP.one()
730
1
731
"""
732
return self.first()
733
734
class TransitiveIdeal():
735
r"""
736
Generic tool for constructing ideals of a relation.
737
738
INPUT:
739
740
- ``relation`` -- a function (or callable) returning a list (or iterable)
741
- ``generators`` -- a list (or iterable)
742
743
Returns the set `S` of elements that can be obtained by repeated
744
application of ``relation`` on the elements of ``generators``.
745
746
Consider ``relation`` as modeling a directed graph (possibly with
747
loops, cycles, or circuits). Then `S` is the ideal generated by
748
``generators`` under this relation.
749
750
Enumerating the elements of `S` is achieved by depth first search
751
through the graph. The time complexity is `O(n+m)` where `n` is
752
the size of the ideal, and `m` the number of edges in the
753
relation. The memory complexity is the depth, that is the maximal
754
distance between a generator and an element of `S`.
755
756
See also :class:`SearchForest` and :class:`TransitiveIdealGraded`.
757
758
EXAMPLES::
759
760
sage: [i for i in TransitiveIdeal(lambda i: [i+1] if i<10 else [], [0])]
761
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
762
763
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+1,3)], [0])]
764
[0, 1, 2]
765
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+2,3)], [0])]
766
[0, 2, 1]
767
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+2,10)], [0])]
768
[0, 2, 4, 6, 8]
769
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+3,10),mod(i+5,10)], [0])]
770
[0, 3, 8, 1, 4, 5, 6, 7, 9, 2]
771
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+4,10),mod(i+6,10)], [0])]
772
[0, 4, 8, 2, 6]
773
sage: [i for i in TransitiveIdeal(lambda i: [mod(i+3,9)], [0,1])]
774
[0, 1, 3, 4, 6, 7]
775
776
sage: [p for p in TransitiveIdeal(lambda x:[x],[Permutation([3,1,2,4]), Permutation([2,1,3,4])])]
777
[[2, 1, 3, 4], [3, 1, 2, 4]]
778
779
We now illustrate that the enumeration is done lazily, by depth first
780
search::
781
782
sage: C = TransitiveIdeal(lambda x: [x-1, x+1], (-10, 0, 10))
783
sage: f = C.__iter__()
784
sage: [ f.next() for i in range(6) ]
785
[0, 1, 2, 3, 4, 5]
786
787
We compute all the permutations of 3::
788
789
sage: [p for p in TransitiveIdeal(attrcall("permutohedron_succ"), [Permutation([1,2,3])])]
790
[[1, 2, 3], [2, 1, 3], [1, 3, 2], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
791
792
We compute all the permutations which are larger than [3,1,2,4],
793
[2,1,3,4] in the right permutohedron::
794
795
sage: [p for p in TransitiveIdeal(attrcall("permutohedron_succ"), [Permutation([3,1,2,4]), Permutation([2,1,3,4])])]
796
[[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]]
797
798
"""
799
def __init__(self, succ, generators):
800
r"""
801
TESTS::
802
803
sage: C = TransitiveIdeal(factor, (1, 2, 3))
804
sage: C._succ
805
<function factor at ...>
806
sage: C._generators
807
(1, 2, 3)
808
sage: loads(dumps(C)) # should test for equality with C, but equality is not implemented
809
"""
810
self._succ = succ
811
self._generators = generators
812
813
def __iter__(self):
814
r"""
815
Return an iterator on the elements of ``self``.
816
817
TESTS::
818
819
sage: C = TransitiveIdeal(lambda x: [1,2], ())
820
sage: list(C) # indirect doctest
821
[]
822
823
sage: C = TransitiveIdeal(lambda x: [1,2], (1,))
824
sage: list(C) # indirect doctest
825
[1, 2]
826
827
sage: C = TransitiveIdeal(lambda x: [], (1,2))
828
sage: list(C) # indirect doctest
829
[1, 2]
830
831
"""
832
known = set(self._generators)
833
todo = known.copy()
834
while len(todo) > 0:
835
x = todo.pop()
836
yield x
837
for y in self._succ(x):
838
if y == None or y in known:
839
continue
840
todo.add(y)
841
known.add(y)
842
return
843
844
845
class TransitiveIdealGraded(TransitiveIdeal):
846
r"""
847
Generic tool for constructing ideals of a relation.
848
849
INPUT:
850
851
- ``relation`` -- a function (or callable) returning a list (or iterable)
852
853
- ``generators`` -- a list (or iterable)
854
855
- ``max_depth`` -- (Default: infinity) Specifies the maximal depth to
856
which elements are computed
857
858
Return the set `S` of elements that can be obtained by repeated
859
application of ``relation`` on the elements of ``generators``.
860
861
Consider ``relation`` as modeling a directed graph (possibly with
862
loops, cycles, or circuits). Then `S` is the ideal generated by
863
``generators`` under this relation.
864
865
Enumerating the elements of `S` is achieved by breadth first search
866
through the graph; hence elements are enumerated by increasing
867
distance from the generators. The time complexity is `O(n+m)`
868
where `n` is the size of the ideal, and `m` the number of edges in
869
the relation. The memory complexity is the depth, that is the
870
maximal distance between a generator and an element of `S`.
871
872
See also :class:`SearchForest` and :class:`TransitiveIdeal`.
873
874
EXAMPLES::
875
876
sage: [i for i in TransitiveIdealGraded(lambda i: [i+1] if i<10 else [], [0])]
877
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
878
879
We now illustrate that the enumeration is done lazily, by breadth first search::
880
881
sage: C = TransitiveIdealGraded(lambda x: [x-1, x+1], (-10, 0, 10))
882
sage: f = C.__iter__()
883
884
The elements at distance 0 from the generators::
885
886
sage: sorted([ f.next() for i in range(3) ])
887
[-10, 0, 10]
888
889
The elements at distance 1 from the generators::
890
891
sage: sorted([ f.next() for i in range(6) ])
892
[-11, -9, -1, 1, 9, 11]
893
894
The elements at distance 2 from the generators::
895
896
sage: sorted([ f.next() for i in range(6) ])
897
[-12, -8, -2, 2, 8, 12]
898
899
The enumeration order between elements at the same distance is not specified.
900
901
We compute all the permutations which are larger than [3,1,2,4] or
902
[2,1,3,4] in the permutohedron::
903
904
sage: [p for p in TransitiveIdealGraded(attrcall("permutohedron_succ"), [Permutation([3,1,2,4]), Permutation([2,1,3,4])])]
905
[[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]]
906
907
"""
908
def __init__(self, succ, generators, max_depth=float("inf")):
909
r"""
910
TESTS::
911
912
sage: C = TransitiveIdealGraded(factor, (1, 2, 3))
913
sage: C._succ
914
<function factor at ...>
915
sage: C._generators
916
(1, 2, 3)
917
sage: loads(dumps(C)) # should test for equality with C, but equality is not implemented
918
"""
919
self._succ = succ
920
self._generators = generators
921
self._max_depth = max_depth
922
923
def __iter__(self):
924
r"""
925
Return an iterator on the elements of ``self``.
926
927
TESTS::
928
929
sage: C = TransitiveIdeal(lambda x: [1,2], ())
930
sage: list(C) # indirect doctest
931
[]
932
933
sage: C = TransitiveIdeal(lambda x: [1,2], (1,))
934
sage: list(C) # indirect doctest
935
[1, 2]
936
937
sage: C = TransitiveIdeal(lambda x: [], (1,2))
938
sage: list(C) # indirect doctest
939
[1, 2]
940
941
sage: [i for i in TransitiveIdealGraded(lambda i: [i+1] if i<10 else [], [0], max_depth=1).__iter__()]
942
[0, 1]
943
"""
944
current_level = self._generators
945
known = set(current_level)
946
depth = 0
947
while len(current_level) > 0 and depth <= self._max_depth:
948
next_level = set()
949
for x in current_level:
950
yield x
951
for y in self._succ(x):
952
if y == None or y in known:
953
continue
954
next_level.add(y)
955
known.add(y)
956
current_level = next_level
957
depth += 1
958
return
959
960
961