| 521 | |
| 522 | def parabolic_bruhat_graph(self, index_set = None, side="right"): |
| 523 | """ |
| 524 | Returns the Hasse graph of the poset ``self.bruhat_poset(index_set,side)`` with edges labeled by the cover relation. |
| 525 | """ |
| 526 | elements = minimal_representatives(index_set, side) |
| 527 | covers =[(x,y) for y in elements for x in y.bruhat_lower_covers() if x in elements] |
| 528 | res = DiGraph() |
| 529 | for u,v in covers: |
| 530 | res.add_edge(u,v,v.inverse()*u) |
| 531 | return res |
| 532 | |
| 533 | |
| 534 | def minimal_representatives(self, index_set = None, side="right"): |
| 535 | """ |
| 536 | Returns the set of minimal coset representatives of ``self`` by a parabolic subgroup. |
| 537 | |
| 538 | INPUT: |
| 539 | |
| 540 | - ``index_set`` - a subset (or iterable) of the nodes of the dynkin diagram, empty by default |
| 541 | - ``side`` - 'left' or 'right' (default) |
| 542 | |
| 543 | See documentation of ``self.bruhat_poset`` for more details. |
| 544 | |
| 545 | The output is equivalent to ``set(w.coset_representative(index_set,side))`` |
| 546 | |
| 547 | but this routine is much faster. For explanation of the algorithm see e.g. Cap, Slovak: |
| 548 | Parabolic geometries, p. 332 |
| 549 | |
| 550 | EXAMPLES:: |
| 551 | |
| 552 | sage: G = WeylGroup(CartanType("A4"),prefix="s") |
| 553 | sage: index_set = [1,3,4] |
| 554 | sage: side = "left" |
| 555 | sage: a = set(w for w in G.minimal_representatives(index_set,side)) |
| 556 | sage: b = set(w.coset_representative(index_set,side) for w in G) |
| 557 | sage: print a.difference(b) |
| 558 | set([]) |
| 559 | """ |
| 560 | from sage.combinat.root_system.root_system import RootSystem |
| 561 | from copy import copy |
| 562 | |
| 563 | if side != 'right' and side != 'left': |
| 564 | raise ValueError, "%s is neither 'right' nor 'left'"%(side) |
| 565 | |
| 566 | weight_space = RootSystem(self.cartan_type()).weight_space() |
| 567 | if index_set == None: |
| 568 | crossed_nodes = set(self.index_set()) |
| 569 | else: |
| 570 | crossed_nodes = set(self.index_set()).difference(index_set) |
| 571 | # the characteristic vector |
| 572 | rhop = sum([weight_space.fundamental_weight(i) for i in crossed_nodes]) |
| 573 | ''' |
| 574 | |
| 575 | The varibale "todo" serves for traversing the orbit of rhop, while the directory "known" serves as a memory to keep the visited elements. Its keys |
| 576 | are elements in the orbit of rhop while known[vec] are paths of simple reflections from rhop to vec. |
| 577 | |
| 578 | ''' |
| 579 | todo = [rhop] |
| 580 | known = dict() |
| 581 | known[rhop] = [] |
| 582 | if rhop == 0: |
| 583 | return set( [self.one()] ) |
| 584 | else: |
| 585 | while len(todo) > 0: |
| 586 | vec = todo.pop() |
| 587 | nonzero_coeffs = [i for i in self.index_set() if vec.coefficient(i) > 0] |
| 588 | for i in nonzero_coeffs: |
| 589 | new_vec = vec.simple_reflection(i) |
| 590 | new_reflections = copy(known[vec]) |
| 591 | new_reflections.append(i) |
| 592 | todo.append(new_vec) |
| 593 | known[new_vec] = new_reflections |
| 594 | if side =='left': |
| 595 | return set(self.from_reduced_word(w) for w in known.values()) |
| 596 | else: |
| 597 | #here we could just take the inverses of w but reversing the list of simple reflections is slightly more efficient |
| 598 | return set(self.from_reduced_word(w[::-1]) for w in known.values()) |
| 599 | |
| 600 | def bruhat_poset(self, index_set = None, side="right", facade = False): |
| 601 | """ |
| 602 | Returns the Bruhat poset of minimal coset representatives of ``self`` by a parabolic |
| 603 | subgroup. |
| 604 | |
| 605 | INPUT: |
| 606 | |
| 607 | - ``index_set`` - a subset (or iterable) of the nodes of the dynkin diagram, empty by default |
| 608 | - ``side`` - 'left' or 'right' (default) |
| 609 | |
| 610 | Let W_S be the subgroup of W (``self`` ) that is generated by the simple reflections |
| 611 | corresponding to the set S (``index_set``). Then the cosets W / W_S have representatives of |
| 612 | minimal length which are ordered by the Bruhat order of W. |
| 613 | |
| 614 | The ``index_set`` is the set of simple roots that generates the subgroup W_S of self. |
| 615 | If the ``index_set`` is empty (which is the default), then we get the whole Weyl group. |
| 616 | |
| 617 | The parameter ``side`` (which defaults to "right") determines whether the result is the |
| 618 | set of right coset representatives of W_S \ W or the set of left coset |
| 619 | representatives W / W_S. |
| 620 | |
| 621 | |
| 622 | EXAMPLES:: |
| 623 | |
| 624 | sage: W = WeylGroup(["A", 2]) |
| 625 | sage: P = W.bruhat_poset() |
| 626 | sage: P |
| 627 | Finite poset containing 6 elements |
| 628 | sage: P.show() |
| 629 | |
| 630 | Here are some typical operations on this poset:: |
| 631 | |
| 632 | sage: W = WeylGroup(["A", 3]) |
| 633 | sage: P = W.bruhat_poset() |
| 634 | sage: u = W.from_reduced_word([3,1]) |
| 635 | sage: v = W.from_reduced_word([3,2,1,2,3]) |
| 636 | sage: P(u) <= P(v) |
| 637 | True |
| 638 | sage: len(P.interval(P(u), P(v))) |
| 639 | 10 |
| 640 | sage: P.is_join_semilattice() |
| 641 | False |
| 642 | |
| 643 | By default, the elements of `P` are aware that they belong |
| 644 | to `P`:: |
| 645 | |
| 646 | sage: P.an_element().parent() |
| 647 | Finite poset containing 24 elements |
| 648 | |
| 649 | If instead one wants the elements to be plain elements of |
| 650 | the Coxeter group, one can use the ``facade`` option:: |
| 651 | |
| 652 | sage: P = W.bruhat_poset(facade = True) |
| 653 | sage: P.an_element().parent() |
| 654 | Weyl Group of type ['A', 3] (as a matrix group acting on the ambient space) |
| 655 | |
| 656 | .. see also:: :func:`Poset` for more on posets and facade posets. |
| 657 | |
| 658 | TESTS:: |
| 659 | |
| 660 | sage: [len(WeylGroup(["A", n]).bruhat_poset().cover_relations()) for n in [1,2,3]] |
| 661 | [1, 8, 58] |
| 662 | |
| 663 | .. todo:: |
| 664 | |
| 665 | - Use the symmetric group in the examples (for nicer |
| 666 | output), and print the edges for a stronger test. |
| 667 | - The constructed poset should be lazy, in order to |
| 668 | handle large / infinite Coxeter groups. |
| 669 | """ |
| 670 | from sage.combinat.posets.posets import Poset |
| 671 | elements = self.minimal_representatives(index_set, side) |
| 672 | # Since our Weyl elements should be already reduced (?), we could |
| 673 | # optimize this step by constructing the cover relations directly (see Cap, Slovak: Parabolic geometries, p. 332), |
| 674 | # thus reducing quadratic complexity of the next step to linear. On the other hand, we would have to introduce saturated subsets. |
| 675 | covers = tuple([x,y] for y in elements for x in y.bruhat_lower_covers() |
| 676 | if x in elements) |
| 677 | return Poset((self, covers), cover_relations = True, facade=facade) |