Ticket #8288: trac_8288reviewer.patch
File trac_8288reviewer.patch, 16.2 KB (added by , 11 years ago) 


sage/combinat/backtrack.py
# HG changeset patch # User Minh Van Nguyen <nguyenminh2@gmail.com> # Date 1271645265 25200 # Node ID 5fdfa5c79f8fefa4860b28751af6e1f6bc48642f # Parent 54bf196dd08eb8b87dad86a1a2ad17975507a388 #8288: Depth/Breadth improvement for SearchForest: reviewer patch diff git a/sage/combinat/backtrack.py b/sage/combinat/backtrack.py
a b 5 5 elements can be enumerated by exploring a search space with a (lazy) 6 6 tree or graph structure. 7 7 8  :class:`~sage.combinat.backtrack.SearchForest`: Depth and Breath first8  :class:`~sage.combinat.backtrack.SearchForest`: Depth and breadth first 9 9 search through a tree described by a ``children`` function. 10 10  :class:`~sage.combinat.backtrack.GenericBacktracker`: Depth first search 11 11 through a tree described by a ``children`` function, with branch pruning, … … 101 101 stack.append( self._rec(obj, state) ) 102 102 103 103 104 def search_forest_iterator(roots, children, method="depth"):104 def search_forest_iterator(roots, children, algorithm="depth"): 105 105 r""" 106 106 Returns an iterator on the nodes of the forest having the given 107 107 roots, and where ``children(x)`` returns the children of the node ``x`` … … 112 112 113 113  ``roots``: a list (or iterable) 114 114  ``children``: a function returning a list (or iterable) 115  ``method``: ``"depth"`` or ``"breadth"`` 115  ``algorithm``: the search strategy (default: ``"depth"``). If 116 ``algorithm="depth"``, use depthfirst search. If 117 ``algorithm="breadth"``, use breadthfirst search. 116 118 117 119 EXAMPLES: 118 120 … … 128 130 sage: list(search_forest_iterator([[]], lambda l: [l + [i] for i in range(4) if i not in l] if len(l) < 2 else [])) 129 131 [[], [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]] 130 132 131 We show how to set the enumeration method::133 We show how to set the enumeration algorithm:: 132 134 133 135 sage: from sage.combinat.backtrack import search_forest_iterator 134 sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else [], method="breadth"))136 sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else [], algorithm="breadth")) 135 137 [[], [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]] 136 138 """ 137 #Set the position according the method depth/breadth 138 if method == "depth": 139 # Set the element removal position according to depthfirst or 140 # breadthfirst search. 141 if algorithm == "depth": 139 142 position = 1 143 elif algorithm == "breadth": 144 position = 0 140 145 else: 141 position = 0146 raise ValueError("Unsupported search algorithm (= %s)" % algorithm) 142 147 143 148 #Invariant: stack[i] contains an iterator for the siblings of the ith node of the current branch 144 149 stack = [iter(roots)] … … 159 164 # We define a pickable function for the TestSuite of SearchForest 160 165 def pick_children(x): 161 166 r""" 162 pick_children is example of pickable function of children.163 A SearchForestinstance is pickable if and only if it is167 The function ``pick_children`` is an example of a pickable function of 168 children. A ``SearchForest`` instance is pickable if and only if it is 164 169 defined with pickable functions. 165 170 166 171 EXAMPLES:: … … 176 181 sage: for i in range(20): 177 182 ... assert pick_children(i) == unpickable_children(i) 178 183 """ 179 return [x +1]184 return [x + 1] 180 185 181 186 182 187 from sage.combinat.combinat import CombinatorialClass … … 315 320 r""" 316 321 Returns an iterable over the children of the element ``x``. 317 322 323 INPUT: 324 325  ``x``: an element. 326 318 327 EXAMPLES:: 319 328 320 329 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)]) … … 333 342 334 343 def father(self, x): 335 344 r""" 336 Returns the father of the element ``x`` if a f onction ``father``345 Returns the father of the element ``x`` if a function ``father`` 337 346 was given in argument. If not raise an ``AssertionError``. 338 347 348 INPUT: 349 350  ``x``: an element. 351 339 352 EXAMPLES:: 340 353 341 354 sage: father = lambda t: (t[0]1,0) if t[1] == 0 else (t[0],t[1]1) … … 351 364 sage: I.father(_) 352 365 (0, 0) 353 366 """ 354 assert self._father !=None, "No 'father' function given"367 assert self._father is not None, "No 'father' function given" 355 368 return self._father(x) 356 369 357 370 def next_brother(self, x, father=None): 358 371 r""" 359 360 Returns the next brother of the element ``x`` if it exist, returns 372 Returns the next brother of the element ``x`` if it exist. Returns 361 373 ``None`` otherwise. 362 374 375 INPUT: 376 377  ``x``: an element. 378  ``father``: (default: ``None``) a father function. 379 363 380 If a function ``_next_brother`` was given at the creation of ``self``, 364 the method s ``next_brother`` simply returns its results, otherwise it381 the method ``next_brother`` simply returns its results. Otherwise it 365 382 returns the next children of the father of ``x``. 366 383 367 384 EXAMPLES:: … … 383 400 (2, 1) 384 401 sage: I.next_brother((2,1)) 385 402 """ 386 # If the function next_brother was given in argument of ``self.__init__``,387 # we use it firstly.388 if self._next_brother !=None:403 # If the function next_brother was given in argument of 404 # self.__init__, we use it firstly. 405 if self._next_brother is not None: 389 406 return self._next_brother(x) 390 407 else: 391 408 # If we have no next_brother method, we search for the father and 392 409 # mainly the father given in argument, otherwise the father method. 393 if father ==None:410 if father is None: 394 411 father = self.father(x) 395 412 children = self.children(father) 396 413 brother = children.next() … … 424 441 425 442 def depth_first_search_iterator(self): 426 443 r""" 427 Returns an iterator on the elements of ``self`` exploring the tree by depth. 444 Returns an iterator on the elements of ``self`` exploring the tree 445 by depthfirst search. 428 446 429 447 EXAMPLES:: 430 448 431 sage: f = SearchForest([[]], 432 ... lambda l: [l+[0], l+[1]] if len(l) < 3 else []) 449 sage: f = SearchForest( 450 ... [[]], 451 ... lambda l: [l+[0], l+[1]] if len(l) < 3 else []) 433 452 sage: list(f.depth_first_search_iterator()) 434 [[], [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]] 453 [[], [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]] 435 454 """ 436 return search_forest_iterator(self._roots, self._children, method="depth") 455 return search_forest_iterator(self._roots, 456 self._children, 457 algorithm="depth") 437 458 438 459 def breadth_first_search_iterator(self): 439 460 r""" 440 461 Returns an iterator on the elements of ``self`` exploring the 441 tree by breadth 462 tree by breadthfirst search. 442 463 443 464 EXAMPLES:: 444 465 445 sage: f = SearchForest([[]], 446 ... lambda l: [l+[0], l+[1]] if len(l) < 3 else []) 466 sage: f = SearchForest( 467 ... [[]], 468 ... lambda l: [l+[0], l+[1]] if len(l) < 3 else []) 447 469 sage: list(f.breadth_first_search_iterator()) 448 470 [[], [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]] 449 471 """ 450 return search_forest_iterator(self._roots, self._children, method="breadth") 472 return search_forest_iterator(self._roots, 473 self._children, 474 algorithm="breadth") 451 475 452 476 def next_generation_iterator(self, fathers): 453 477 r""" 454 From an iterator ``it`` over some elements of ``self``, return an iterator 455 over all children of elements generated by ``it`. 478 Iterate over the children of ``fathers``. 479 480  ``fathers``: an iterable of father objects. 456 481 457 482 EXAMPLES:: 458 483 459 sage: I = SearchForest( [(0,0)],460 ... lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0461 ... 484 sage: I = SearchForest( 485 ... [(0,0)], 486 ... lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)]) 462 487 sage: list(I.next_generation_iterator(I.roots())) 463 488 [(1, 0), (0, 1)] 464 sage: I = SearchForest([[]], 465 ... lambda l: [l+[0], l+[1]] if len(l) < 3 else []) 489 sage: I = SearchForest( 490 ... [[]], 491 ... lambda l: [l+[0], l+[1]] if len(l) < 3 else []) 466 492 sage: list(I.next_generation_iterator(I.roots())) 467 493 [[0], [1]] 468 494 """ … … 472 498 473 499 def _next_element_by_father(self, x): 474 500 r""" 475 Returns the next element of ``self`` by breadth enumeration using501 Returns the next element of ``self`` by breadthfirst enumeration using 476 502 ``father`` and ``next_brother`` method. 477 503 504  ``x``: an element. 505 478 506 EXAMPLES:: 479 507 480 508 sage: def children(t): … … 528 556 roots = list(self.roots()) 529 557 for i in range(len(roots)): 530 558 if x == roots[i]: 531 if i < len(roots) 1:532 return roots[i +1]559 if i < len(roots)  1: 560 return roots[i + 1] 533 561 else: 534 562 return self.children(roots[0]).next() 535 563 brother = self.next_brother(x) 536 if brother !=None:564 if brother is not None: 537 565 return brother 538 566 else: 539 567 next_father = self._next_element_by_father(self.father(x)) … … 553 581 Returns an iterator starting with the element ``start`` and stopping 554 582 just before the element ``end``. 555 583 584 INPUT: 585 586  ``start``: an element from which to begin the iteration. 587  ``end``: an element up to which we stop the iteration. If our 588 iteration stops at ``end``, then the last element in our iteration 589 is the element just before ``end``. Thus the range ``start`` to 590 ``end`` includes ``start``, but excludes ``end``. 591 556 592 EXAMPLES:: 557 593 558 594 sage: father = lambda t: (t[0]1,0) if t[1] == 0 else (t[0],t[1]1) … … 571 607 r""" 572 608 Returns the first element of given ``depth``. 573 609 610 INPUT: 611 612  ``depth``: nonnegative integer. 613 574 614 EXAMPLES:: 575 615 576 616 sage: father = lambda l : l[:1] … … 592 632 except StopIteration: 593 633 return None 594 634 else: 595 if father_with_depth ==None:635 if father_with_depth is None: 596 636 try: 597 next_father = self.roots().next() 598 return self.first_element_of_depth(depth, father_with_depth=(next_father,0)) 637 next_father = self.roots().next() 638 return self.first_element_of_depth( 639 depth, 640 father_with_depth=(next_father, 0)) 599 641 except StopIteration: 600 642 return None 601 643 else: … … 603 645 depth_father = father_with_depth[1] 604 646 try: 605 647 next_father = self.children(father).next() 606 if depth == depth_father +1:648 if depth == depth_father + 1: 607 649 return next_father 608 650 else: 609 return self.first_element_of_depth(depth, father_with_depth=(next_father,depth_father+1)) 651 return self.first_element_of_depth( 652 depth, 653 father_with_depth=(next_father, depth_father + 1)) 610 654 except StopIteration: 611 655 next_father = self._next_element_by_father(father) 612 if next_father ==None:656 if next_father is None: 613 657 return None 614 658 else: 615 return self.first_element_of_depth(depth, father_with_depth=(next_father,depth_father)) 659 return self.first_element_of_depth( 660 depth, 661 father_with_depth=(next_father, depth_father)) 616 662 617 def element_of_depth_iterator(self, depth=0, method="full_generation"):663 def element_of_depth_iterator(self, depth=0, full_generation=True): 618 664 r""" 619 665 Returns an iterator on the elements of ``self`` of given depth. 620 666 An element of depth `n` can be obtained applying `n` times the … … 622 668 623 669 INPUT: 624 670 625  ``depth``: a non negative integer626  ``method``: optional string.671  ``depth``: a nonnegative integer (default: ``0``). The search 672 depth. 627 673 628 629 By default ``method`` is ``"full_generation"``. This means the 630 algorithm start from the roots and generate all elements until the 631 given depth is reached. 632 633 If ``method`` is different of ``"full_generation"``, the algorithm 634 makes use of ``father`` and ``next_brother`` methods. This reduces the 635 number of calls to the ``children`` method. 674  ``full_generation``: boolean (default: ``True``). If 675 ``full_generation=True``, the search starts from the root and 676 generate all elements until the given depth is reached. If 677 ``full_generation=False``, the search algorithm makes use of the 678 ``father`` and ``next_brother`` methods. This reduces the number 679 of calls to the ``children`` method. 636 680 637 681 EXAMPLES:: 638 682 … … 652 696 [] 653 697 sage: father = lambda t: (t[0]1,0) if t[1] == 0 else (t[0],t[1]1) 654 698 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)], father=father) 655 sage: list(I.element_of_depth_iterator(10, method="father"))699 sage: list(I.element_of_depth_iterator(10, full_generation=False)) 656 700 [(10, 0), (9, 1), (8, 2), (7, 3), (6, 4), (5, 5), (4, 6), (3, 7), (2, 8), (1, 9), (0, 10)] 657 701 sage: father = lambda l : l[:1] 658 702 sage: I = SearchForest([[3]], lambda l: (l+[i] for i in range(l[1])), father = father) 659 sage: list(I.element_of_depth_iterator(0, method="father"))703 sage: list(I.element_of_depth_iterator(0, full_generation=False)) 660 704 [[3]] 661 sage: list(I.element_of_depth_iterator(1, method="father"))705 sage: list(I.element_of_depth_iterator(1, full_generation=False)) 662 706 [[3, 0], [3, 1], [3, 2]] 663 707 sage: for i in range(8): 664 ... assert list(I.element_of_depth_iterator(i)) == list(I.element_of_depth_iterator(i, method="father"))708 ... assert list(I.element_of_depth_iterator(i)) == list(I.element_of_depth_iterator(i, full_generation=False)) 665 709 """ 666 if method == "full_generation":710 if full_generation: 667 711 iter = self.roots() 668 712 for i in range(depth): 669 713 iter = self.next_generation_iterator(iter) … … 673 717 # use father and next_brother method... 674 718 node = self.first_element_of_depth(depth) 675 719 # node is now the starting point for the enumeration 676 if node ==None:720 if node is None: 677 721 return [].__iter__() 678 722 else: 679 end_node = self.first_element_of_depth(depth+1, father_with_depth=(node,depth)) 723 end_node = self.first_element_of_depth( 724 depth + 1, 725 father_with_depth=(node, depth)) 680 726 return self._iter_from_to(node, end_node) 681 727 682 728