Ticket #4327: trac_4327-root_system_plot_refactor-nt.patch

File trac_4327-root_system_plot_refactor-nt.patch, 127.4 KB (added by nthiery, 6 years ago)
  • doc/en/reference/combinat/root_systems.rst

    # HG changeset patch
    # User Nicolas M. Thiery <nthiery@users.sf.net>
    # Date 1368475206 14400
    # Node ID a209da9b9684cf783f9d99a04f0bf03b07056898
    # Parent  75e26170e32bcbb5f78e1784a1df985ecb71e1db
    #4327: Refactor and extend root systems plots
    
    diff --git a/doc/en/reference/combinat/root_systems.rst b/doc/en/reference/combinat/root_systems.rst
    a b Root Systems 
    1010   ../sage/combinat/root_system/coxeter_matrix
    1111
    1212   ../sage/combinat/root_system/root_system
     13   ../sage/combinat/root_system/plot
    1314   ../sage/combinat/root_system/root_lattice_realizations
    1415   ../sage/combinat/root_system/weight_lattice_realizations
    1516   ../sage/combinat/root_system/root_space
  • doc/en/thematic_tutorials/index.rst

    diff --git a/doc/en/thematic_tutorials/index.rst b/doc/en/thematic_tutorials/index.rst
    a b Algebraic Combinatorics 
    7070
    7171* :class:`Tutorial: Symmetric Functions <SymmetricFunctions>`
    7272* :ref:`lie`
     73* :ref:`sage.combinat.root_system.plot`
    7374* :ref:`abelian_sandpile_model`
    7475
    7576.. Words
  • doc/en/thematic_tutorials/lie.rst

    diff --git a/doc/en/thematic_tutorials/lie.rst b/doc/en/thematic_tutorials/lie.rst
    a b DMS-1001079, DMS--0652641, DMS--0652652, 
    3030   lie/iwahori_hecke_algebra
    3131   lie/kazhdan_lusztig_polynomials
    3232   lie/bibliography
     33
  • doc/en/thematic_tutorials/lie/lie_basics.rst

    diff --git a/doc/en/thematic_tutorials/lie/lie_basics.rst b/doc/en/thematic_tutorials/lie/lie_basics.rst
    a b does support relabeled Cartan types. See 
    316316Standard realizations of the ambient spaces
    317317-------------------------------------------
    318318
    319 These realizations follow the Appendix in Bourbaki, *Lie Groups
    320 and Lie Algebras, Chapters 4-6*.
    321 
     319These realizations follow the Appendix in Bourbaki, *Lie Groups and
     320Lie Algebras, Chapters 4-6*. See the :ref:`Root system plot tutorial
     321<sage.combinat.root_system.plot>` for how to visualize them.
    322322
    323323Type A
    324324^^^^^^
  • sage/combinat/root_system/cartan_type.py

    diff --git a/sage/combinat/root_system/cartan_type.py b/sage/combinat/root_system/cartan_type.py
    a b class CartanTypeFactory(SageObject): 
    538538            if "x" in t:
    539539                import type_reducible
    540540                return type_reducible.CartanType([CartanType(u) for u in t.split("x")])
     541            elif t[-1] == "*":
     542                return CartanType(t[:-1]).dual()
     543            elif t[-1] == "~":
     544                return CartanType(t[:-1]).affine()
    541545            else:
    542546                return CartanType([t[0], eval(t[1:])])
    543547
    class CartanTypeFactory(SageObject): 
    772776            sage: CartanType.color(3)
    773777            'green'
    774778
    775         The default color is black. Well, some sort of black, because
    776         plots don't handle well plain black::
     779        The default color is black::
    777780
    778781            sage: CartanType.color(0)
    779             (0.1, 0.1, 0.1)
     782            'black'
    780783
    781784        Negative indices get the same color as their positive counterparts::
    782785
    class CartanTypeFactory(SageObject): 
    787790            sage: CartanType.color(-3)
    788791            'green'
    789792        """
    790         return cls._colors.get(i, (0.1, 0.1, 0.1))
     793        return cls._colors.get(i, 'black')
    791794
    792795CartanType = CartanTypeFactory()
    793796
  • new file sage/combinat/root_system/plot.py

    diff --git a/sage/combinat/root_system/plot.py b/sage/combinat/root_system/plot.py
    new file mode 100644
    - +  
     1r"""
     2Tutorial: visualizing root systems
     3
     4Root systems encode the positions of collections of hyperplanes in
     5space, and form the fundamental combinatorial data underlying Coxeter
     6and Weyl groups, Lie algebras and groups, etc. The theory can be a bit
     7intimidating at first because of the many technical gadgets (roots,
     8coroots, weights, ...). Vizualizing them goes a long way toward
     9building a geometric intuition.
     10
     11This tutorial starts from simple plots and guides you all the way to
     12advanced plots with your own combinatorial data drawn on top of it.
     13
     14.. SEEALSO::
     15
     16    - :ref:`sage.combinat.root_system.root_system`
     17      -- An overview of root systems in Sage
     18
     19    - :meth:`RootLatticeRealizations.ParentMethods.plot()
     20      <sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot>`
     21      -- the main plotting function, with pointers to all the subroutines
     22
     23
     24First plots
     25-----------
     26
     27In this first plot, we draw the root system for type `A_2` in the
     28ambient space. It is generated from two hyperplanes at a 120 degree
     29angle::
     30
     31    sage: L = RootSystem(["A",2]).ambient_space()
     32    sage: L.plot()
     33
     34Each of those hyperplane `H_{\alpha^\vee_i}` is described by a linear
     35form `\alpha_i^\vee` called simple coroot. To each such hyperplane
     36corresponds a reflection along a vector called root. In this picture,
     37the reflections are orthogonal and the two simple roots `\alpha_1` and
     38`\alpha_2` are vectors which are normal to the reflection hyperplanes.
     39The same color code is used uniformly: blue for 1, red for 2, green
     40for 3, ... (see :meth:`CartanType.color()
     41<sage.combinat.root_system.cartan_type.CartanTypeFactory.color>`). The
     42fundamental weights, `\Lambda_1` and `\Lambda_2` form the dual basis of
     43the coroots.
     44
     45The two reflections generate a group of order six which is nothing but
     46the usual symmetric group `S_3`, in its natural action by permutations
     47of the coordinates of the ambient space. Wait, but the ambient space
     48should be of dimension `3` then? That's perfectly right. Here is the
     49full picture in 3D::
     50
     51    sage: L = RootSystem(["A",2]).ambient_space()
     52    sage: L.plot(projection=False)
     53
     54However in this space, the line `(1,1,1)` is fixed by the action of
     55the group. Therefore, the so called barycentric projection orthogonal
     56to `(1,1,1)` gives a convenient 2D picture which contains all the
     57essential information. The same projection is used by default in type
     58`G_2`::
     59
     60    sage: L = RootSystem(["G",2]).ambient_space()
     61    sage: L.plot(reflection_hyperplanes="all")
     62
     63The group is now the dihedral group of order 12, generated by the two
     64reflections `s_1` and `s_2`. The picture displays the hyperplanes for
     65all 12 reflections of the group. Those reflections delimit 12 chambers
     66which are in one to one correspondance with the elements of the
     67group. The fundamental chamber, which is grayed out, is associated
     68with the identity of the group.
     69
     70.. WARNING::
     71
     72    The fundamental chamber is currently plotted as the cone generated
     73    by the fundamental weights. As can be seen on the previous 3D
     74    picture this is not quite correct if the fundamental weights do
     75    not span the space.
     76
     77    Another caveat is that some plotting features may require
     78    manipulating elements with rational coordinates which will fail if
     79    one is working in, say, the weight lattice. It is therefore
     80    recommended to use the root, weight, or ambient spaces for
     81    plotting purposes rather than their lattice counterparts.
     82
     83Coming back to the symmetric group, here is the picture in the weight
     84space, with all roots and all reflection hyperplanes; remark that,
     85unlike in the ambient space, a root is not necessarily orthogonal to
     86its corresponding reflection hyperplane::
     87
     88    sage: L = RootSystem(["A",2]).weight_space()
     89    sage: L.plot(roots = "all", reflection_hyperplanes="all").show(figsize=15)
     90
     91.. NOTE::
     92
     93    Setting a larger figure size as above can help reduce
     94    the overlap between the text labels when the figure
     95    gets crowded.
     96
     97One can further customize which roots to display, as in
     98the following example showing the positive roots in the
     99weight space for type ['G',2], labelled by their
     100coordinates in the root lattice::
     101
     102    sage: Q = RootSystem(["G",2]).root_space()
     103    sage: L = RootSystem(["G",2]).ambient_space()
     104    sage: L.plot(roots=list(Q.positive_roots()), fundamental_weights=False)
     105
     106One can also customize the projection by specifying a function. Here,
     107we display all the roots for type `E_8` using the projection from its
     108eight dimensional ambient space onto 3D described on
     109:wikipedia:`Wikipedia's E8 3D picture <File:E8_3D.png>`::
     110
     111    sage: M = matrix([[0., -0.556793440452, 0.19694925177, -0.19694925177, 0.0805477263944, -0.385290876171, 0., 0.385290876171],
     112    ...               [0.180913155536, 0., 0.160212955043, 0.160212955043, 0., 0.0990170516545, 0.766360424875, 0.0990170516545],
     113    ...               [0.338261212718, 0, 0, -0.338261212718, 0.672816364803, 0.171502564281, 0, -0.171502564281]])
     114    sage: L = RootSystem(["E",8]).ambient_space()
     115    sage: L.dimension()
     116    8
     117    sage: L.plot(roots="all", reflection_hyperplanes=False, projection=lambda v: M*vector(v), labels=False) # long time
     118
     119The projection function should be linear or affine, and return a
     120vector with rational coordinates. The rationale for the later
     121constraint is to allow for using the PPL exact library for
     122manipulating polytopes. Indeed exact calculations give cleaner
     123pictures (adjacent objects, intersection with the bounding box, ...).
     124Besides the interface to PPL is indeed currently faster than that for
     125CDD, and it is likely to become even more so.
     126
     127.. TOPIC:: Exercise
     128
     129    Draw all finite root systems in 2D, using the canonical projection
     130    onto their Coxeter plane. See
     131    `Stembridge's page <http://www.math.lsa.umich.edu/~jrs/coxplane.html>`_.
     132
     133
     134Alcoves and chambers
     135--------------------
     136
     137We now draw the root system for type `G_2`, with its alcoves (in
     138finite type, those really are the chambers) and the corresponding
     139elements of the Weyl group. We enlarge a bit the bounding box to make
     140sure everything fits in the picture::
     141
     142    sage: RootSystem(["G",2]).ambient_space().plot(alcoves=True, alcove_labels=True, bounding_box=5)
     143
     144The same picture in 3D, for type `B_3`::
     145
     146    sage: RootSystem(["B",3]).ambient_space().plot(alcoves=True, alcove_labels=True)
     147
     148.. TOPIC:: Exercise
     149
     150    Can you spot the fundamental chamber? The fundamental weights? The
     151    simple roots? The longest element of the Weyl group?
     152
     153Alcove pictures for affine types
     154--------------------------------
     155
     156We now draw the usual alcove picture for affine type `A_2^{(1)}`::
     157
     158    sage: L = RootSystem(["A",2,1]).ambient_space()
     159    sage: L.plot()                                     # long time
     160
     161This picture is convenient because it is low dimensional and contains
     162most of the relevant information. Beside, by choosing the ambient
     163space, the elements of the Weyl group act as orthogonal affine
     164maps. In particular, reflections are usual (affine) orthogonal
     165reflections. However this is in fact only a slice of the real picture:
     166the Weyl group actually acts by linear maps on the full ambient
     167space. Those maps stabilize the so-called level `l` hyperplanes, and
     168we are visualizing here what's happening at level `1`. Here is the
     169full picture in 3D::
     170
     171    sage: L.plot(bounding_box=[[-3,3],[-3,3],[-1,1]], affine=False) # long time
     172
     173In fact, in type `A`, this really is a picture in 4D, but as usual the
     174barycentric projection kills the boring extra dimension for us.
     175
     176It's usually more readable to only draw the intersection of the
     177reflection hyperplanes with the level `1` hyperplane::
     178
     179    sage: L.plot(affine=False, level=1)                # long time
     180
     181Such 3D pictures are useful to better understand technicalities, like
     182the fact that the fundamental weights do not necessarily all live at
     183level 1::
     184
     185    sage: L = RootSystem(["G",2,1]).ambient_space()
     186    sage: L.plot(affine=False, level=1)
     187
     188.. NOTE::
     189
     190    Such pictures may tend to be a bit flat, and it may be helpful to
     191    play with the aspect_ratio and more generaly with the various
     192    options of the :meth:`~sage.plot.plot3d.base.Graphics3d.show`
     193    method::
     194
     195        sage: p = L.plot(affine=False, level=1)
     196        sage: p.show(aspect_ratio=[1,1,2], frame=False)
     197
     198.. TOPIC:: Exercise
     199
     200    Draw the alcove picture at level 1, and compare the position of
     201    the fundamental weights and the vertices of the fundamental
     202    alcove.
     203
     204As for finite root systems, the alcoves are indexed by the elements of
     205the Weyl group `W`. Two alcoves indexed by `u` and `v` respectively
     206share a wall if `u` and `v` are neighbors in the right Cayley graph:
     207`u = vs_i`; the color of that wall is given by `i`::
     208
     209    sage: L = RootSystem(["C",2,1]).ambient_space()
     210    sage: L.plot(coroots="simple", alcove_labels=True) # long time
     211
     212Even 2D pictures of the rank `1 + 1` cases can give some food for
     213thought. Here, we draw the root lattice, with the positive roots of
     214small height in the root poset::
     215
     216    sage: L = RootSystem(["A",1,1]).root_lattice()
     217    sage: positive_roots = TransitiveIdealGraded(attrcall("pred"), L.simple_roots())
     218    sage: it = iter(positive_roots)
     219    sage: first_positive_roots = [it.next() for i in range(10)]
     220    sage: L.plot(roots=first_positive_roots, affine=False, alcoves=False)
     221
     222.. TOPIC:: Exercises
     223
     224    #. Use the same trick to draw the reflection hyperplanes in the
     225       weight lattice for the coroots of small height. Add the
     226       indexing of the alcoves by elements of the Weyl group.
     227       See below for a solution.
     228
     229    #. Draw the positive roots in the weight lattice and in the
     230       extended weight lattice.
     231
     232    #. Draw the reflection hyperplanes in the root lattice
     233
     234    #. Recreate John Stembridge's
     235       `"Sandwich" arrangement pictures <http://www.math.lsa.umich.edu/~jrs/archive.html>`_
     236       by choosing appropriate coroots for the reflection hyperplanes.
     237
     238Here is a polished solution for the first exercise::
     239
     240    sage: L = RootSystem(["A",1,1]).weight_space()
     241    sage: positive_coroots = TransitiveIdealGraded(attrcall("pred"), L.simple_coroots())
     242    sage: it = iter(positive_coroots)
     243    sage: first_positive_coroots = [it.next() for i in range(20)]
     244    sage: p = L.plot(fundamental_chamber=True, reflection_hyperplanes=first_positive_coroots,
     245    ...              affine=False, alcove_labels=1,
     246    ...              bounding_box=[[-9,9],[-1,2]],
     247    ...              projection=lambda x: matrix([[1,-1],[1,1]])*vector(x))
     248    sage: p.show(figsize=20)                           # long time
     249
     250
     251Higher dimension affine pictures
     252--------------------------------
     253
     254We now do some plots for rank 4 affine types, at level 1. The space is
     255tiled by the alcoves, each of which is a 3D simplex::
     256
     257    sage: L = RootSystem(["A",3,1]).ambient_space()
     258    sage: L.plot(reflection_hyperplanes=False, bounding_box=85/100) # long time
     259
     260It is recommended to use a small bounding box here, for otherwise the
     261number of simplices grows quicker than what Sage can handle
     262smoothly. It can help to specify explicitly which alcoves to
     263visualize. Here is the fundamental alcove, specified by an element of
     264the Weyl group::
     265
     266    sage: W = L.weyl_group()
     267    sage: L.plot(reflection_hyperplanes=False, alcoves=[W.one()], bounding_box=2)
     268
     269and the fundamental polygon, specified by the coordinates of its
     270center in the root lattice::
     271
     272    sage: W = L.weyl_group()
     273    sage: L.plot(reflection_hyperplanes=False, alcoves=[[0,0]], bounding_box=2)
     274
     275Finally, we draw the alcoves in the classical fundamental chambers,
     276using that those are indexed by the elements of the Weyl group having
     277no other left descent than `0`. In order to see the inner structure,
     278we only draw the wireframe of the facets of the alcoves. Specifying
     279the ``wireframe`` option requires a more flexible syntax for plots
     280which will be explained later on in this tutorial::
     281
     282    sage: L = RootSystem(["B",3,1]).ambient_space()
     283    sage: W = L.weyl_group()
     284    sage: alcoves = [~w for d in range(12) for w in W.affine_grassmannian_elements_of_given_length(d)]
     285    sage: p = L.plot_fundamental_chamber("classical")
     286    sage: p += L.plot_alcoves(alcoves=alcoves, wireframe=True)
     287    sage: p += L.plot_fundamental_weights()
     288    sage: p.show(frame=False)
     289
     290.. TOPIC:: Exercises
     291
     292    #. Draw the fundamental alcove in the ambient space, just by
     293       itself (no reflection hyperplane, root, ...).  The automorphism
     294       group of the Dynkin diagram for `A_3^{(1)}` (a cycle of length 4)
     295       is the dihedral group. Visualize the corresponding symmetries
     296       of the fundamental alcove.
     297
     298    #. Draw the fundamental alcoves for the other rank 4 affine types,
     299       and recover the automorphism groups of their Dynkin diagram
     300       from the pictures.
     301
     302Drawing on top of a root system plot
     303------------------------------------
     304
     305The root system plots have been designed to be used as wallpaper on
     306top of which to draw more information. In the following example, we
     307draw an alcove walk, specified by a word of indices of simple
     308reflections, on top of the weight lattice in affine type `A_{2,1}`::
     309
     310    sage: L = RootSystem(["A",2,1]).ambient_space()
     311    sage: w1 = [0,2,1,2,0,2,1,0,2,1,2,1,2,0,2,0,1,2,0]
     312    sage: L.plot(alcove_walk=w1, bounding_box=6)       # long time
     313
     314Now, what about drawing several alcove walks, and specifying some
     315colors? A single do-it-all plot method would be cumbersome; so
     316instead, it is actually built on top of many methods (see the list
     317below) that can be called independently and combined at will::
     318
     319    sage: L.plot_roots() + L.plot_reflection_hyperplanes()
     320
     321.. NOTE::
     322
     323    By default the axes are disabled in root system plots since they
     324    tend to polute the picture. Annoyingly they come back when
     325    combining them. Here is a workaround::
     326
     327        sage: p = L.plot_roots() + L.plot_reflection_hyperplanes()
     328        sage: p.axes(False)
     329        sage: p
     330
     331In order to specify common information for all the pieces of a root
     332system plot (choice of projection, bounding box, color code for the
     333index set, ...), the easiest is to create an option object using
     334:meth:`~sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot_parse_options`,
     335and pass it down to each piece. We use this to plot our two walks::
     336
     337    sage: plot_options = L.plot_parse_options(bounding_box=[[-2,5],[-2,6]])
     338    sage: w2 = [2,1,2,0,2,0,2,1,2,0,1,2,1,2,1,0,1,2,0,2,0,1,2,0,2]
     339    sage: p = L.plot_alcoves(plot_options=plot_options) # long time
     340    sage: p += L.plot_alcove_walk(w1, color="green", plot_options=plot_options)
     341    sage: p += L.plot_alcove_walk(w2, color="orange", plot_options=plot_options)
     342    sage: p
     343
     344And another with some foldings::
     345
     346    sage: p += L.plot_alcove_walk([0,1,2,0,2,0,1,2,0,1],
     347    ...                           foldings= [False, False, True, False, False, False, True, False, True, False],
     348    ...                           color="purple")
     349    sage: p.axes(False)
     350    sage: p.show(figsize=20)
     351
     352Here we show a weight at level `0` and the reduced word implementing
     353the translation by this weight::
     354
     355    sage: L = RootSystem(["A",2,1]).ambient_space()
     356    sage: P = RootSystem(["A",2,1]).weight_space(extended=True)
     357    sage: Lambda = P.fundamental_weights()
     358    sage: t = 6*Lambda[1] - 2*Lambda[2] - 4*Lambda[0]
     359    sage: walk = L.reduced_word_of_translation(L(t))
     360    sage: plot_options = L.plot_parse_options(bounding_box=[[-2,5],[-2,5]])
     361    sage: p = L.plot(plot_options=plot_options)        # long time
     362    sage: p += L.plot_alcove_walk(walk, color="green", plot_options=plot_options)
     363    sage: p += plot_options.family_of_vectors({t: L(t)})
     364    sage: plot_options.finalize(p)
     365    sage: p
     366
     367Note that the coloring of the translated alcove does not match with
     368that of the fundamental alcove: the translation actually lives in the
     369extended Weyl group and is the composition of the simple reflections
     370indexed by the alcove walk together with a rotation implementing an
     371automorphism of the Dynkin diagram.
     372
     373We conclude with a rank `3 + 1` alcove walk::
     374
     375    sage: L = RootSystem(["B",3,1]).ambient_space()
     376    sage: w3 = [0,2,1,3,2,0,2,1,0,2,3,1,2,1,3,2,0,2,0,1,2,0]
     377    sage: L.plot_fundamental_weights() + L.plot_reflection_hyperplanes(bounding_box=2) + L.plot_alcove_walk(w3)
     378
     379.. TOPIC:: Exercise
     380
     381    #. Draw the tiling of 3D space by the fundamental polygons for
     382       types A,B,C,D. Hints: use the ``wireframe`` option of
     383       :meth:`RootLatticeRealizations.ParentMethods.plot_alcoves` and
     384       the ``color`` option of :meth:`plot` to only draw the alcove
     385       facets indexed by `0`.
     386
     387.. TOPIC:: Solution
     388
     389    ::
     390
     391        sage: L = RootSystem(["A",3,1]).ambient_space()
     392        sage: alcoves = CartesianProduct([0,1],[0,1],[0,1])
     393        sage: color = lambda i: "black" if i==0 else None
     394        sage: L.plot_alcoves(alcoves=alcoves, color=color, bounding_box=10,wireframe=True).show(frame=False) # long time
     395
     396Hand drawing on top of a root system plot (aka Coxeter graph paper)
     397-------------------------------------------------------------------
     398
     399Taken from John Stembridge's excellent
     400`data archive <http://www.math.lsa.umich.edu/~jrs/archive.html>`_:
     401
     402"If you've ever worked with affine reflection groups, you've probably
     403wasted lots of time drawing the reflecting hyperplanes of the rank 2
     404groups on scraps of paper. You may also have wished you had pads of
     405graph paper with these lines drawn in for you. If so, you've come to
     406the right place. Behold! Coxeter graph paper!".
     407
     408Now you can create your own customized color Coxeter graph paper::
     409
     410    sage: L = RootSystem(["C",2,1]).ambient_space()
     411    sage: p = L.plot(bounding_box=[[-8,9],[-5,7]], coroots="simple") # long time (10 s)
     412    sage: p
     413
     414By default Sage's plot are bitmap pictures which would come out ugly
     415if printed on paper. Instead, we recommend saving the picture in
     416postscript or svg before printing it::
     417
     418    sage: p.save("C21paper.eps")        # not tested
     419
     420.. NOTE::
     421
     422    Drawing pictures with a large number of alcoves is currently
     423    somewhat ridiculously slow. This is due to the use of generic code
     424    that works uniformly in all dimension rather than taylor-made code
     425    for 2D. Things should improve with the upcoming fast interface to
     426    the PPL library (see e.g. :trac:`12553`).
     427
     428Drawing custom objects on top of a root system plot
     429---------------------------------------------------
     430
     431So far so good. Now, what if one wants to draw, on top of a root
     432system plot, some object for which there is no preexisting plot
     433method? Again, the ``plot_options`` object come in handy, as it can be
     434used to compute appropriate coordinates. Here we draw the
     435permutohedron, that is the Cayley graph of the symmetric group `W`, by
     436positioning each element `w` at `w(\rho)`, where `\rho` is in the
     437fundamental alcove::
     438
     439    sage: L = RootSystem(["A",2]).ambient_space()
     440    sage: rho = L.rho()
     441    sage: plot_options = L.plot_parse_options()
     442    sage: W = L.weyl_group()
     443    sage: g = W.cayley_graph(side="right")
     444    sage: positions = {w: plot_options.projection(w.action(rho)) for w in W}
     445    sage: p = L.plot_alcoves()
     446    sage: p += g.plot(pos = positions, vertex_size=0,
     447    ...               color_by_label=plot_options.color)
     448    sage: p.axes(False)
     449    sage: p
     450
     451.. TODO:: Could we have nice `\LaTeX` labels in this graph?
     452
     453The same picture for `A_3` gives a nice 3D permutohedron::
     454
     455    sage: L = RootSystem(["A",3]).ambient_space()
     456    sage: rho = L.rho()
     457    sage: plot_options = L.plot_parse_options()
     458    sage: W = L.weyl_group()
     459    sage: g = W.cayley_graph(side="right")
     460    sage: positions = {w: plot_options.projection(w.action(rho)) for w in W}
     461    sage: p = L.plot_roots()
     462    sage: p += g.plot3d(pos3d = positions, color_by_label=plot_options.color)
     463    sage: p
     464
     465.. TOPIC:: Exercises
     466
     467    #. Locate the identity element of `W` in the previous picture
     468
     469    #. Rotate the picture appropriately to highlight the
     470       various symmetries of the permutohedron.
     471
     472    #. Make a function out of the previous example, and
     473       explore the Cayley graphs of all rank 2 and 3 Weyl groups.
     474
     475    #. Draw the root poset for type `B_2` and `B_3`
     476
     477    #. Draw the root poset for type `E_8` to recover the picture from
     478       :wikipedia:`File:E8_3D.png`
     479
     480Similarly, we display a crystal graph by positioning each element
     481according to its weight::
     482
     483    sage: C = CrystalOfTableaux(["A",2], shape=[4,2])
     484    sage: L = C.weight_lattice_realization()
     485    sage: plot_options = L.plot_parse_options()
     486
     487    sage: g = C.digraph()
     488    sage: positions = {x: plot_options.projection(x.weight()) for x in C}
     489    sage: p = L.plot()
     490    sage: p += g.plot(pos = positions,
     491    ...               color_by_label=plot_options.color, vertex_size=0)
     492    sage: p.axes(False)
     493    sage: p.show(figsize=15)
     494
     495.. NOTE::
     496
     497    In the above picture, many pairs of tableaux have the
     498    same weight and are thus superposed (look for example
     499    near the center). Some more layout logic would be
     500    needed to separate those nodes properly, but the
     501    foundations are laid firmly and uniformly accross all
     502    types of root systems for writing such extensions.
     503
     504Here is an analogue picture in 3D::
     505
     506    sage: C = CrystalOfTableaux(["A",3], shape=[3,2,1])
     507    sage: L = C.weight_lattice_realization()
     508    sage: plot_options = L.plot_parse_options()
     509    sage: g = C.digraph()
     510    sage: positions = {x:plot_options.projection(x.weight()) for x in C}
     511    sage: p = L.plot(reflection_hyperplanes=False, fundamental_weights=False)
     512    sage: p += g.plot3d(pos3d = positions, vertex_labels=True,
     513    ...                 color_by_label=plot_options.color, edge_labels=True)
     514    sage: p
     515
     516
     517.. TOPIC:: Exercise
     518
     519    Explore the previous picture and notice how the edges
     520    of the crystal graph are parallel to the simple roots.
     521
     522
     523Enjoy and please post your best pictures on the
     524`Sage-Combinat wiki <http://wiki.sagemath.org/combinat/CoolPictures>`_.
     525"""
     526#*****************************************************************************
     527#       Copyright (C) 2013 Nicolas M. Thiery <nthiery at users.sf.net>
     528#
     529#  Distributed under the terms of the GNU General Public License (GPL)
     530#                  http://www.gnu.org/licenses/
     531#*****************************************************************************
     532
     533from sage.misc.cachefunc import cached_method, cached_function
     534from sage.misc.latex import latex
     535from sage.misc.lazy_import import lazy_import
     536from sage.modules.free_module_element import vector
     537from sage.rings.all import ZZ, QQ
     538from sage.combinat.root_system.cartan_type import CartanType
     539lazy_import("sage.combinat.root_system.root_lattice_realizations", "RootLatticeRealizations")
     540
     541class PlotOptions:
     542    r"""
     543    A class for plotting options for root lattice realizations.
     544
     545    .. SEEALSO::
     546
     547        - :meth:`RootLatticeRealizations.ParentMethods.plot()
     548          <sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot>`
     549          for a description of the plotting options
     550        - :ref:`sage.combinat.root_system.plot` for a tutorial on root
     551          system plotting
     552    """
     553    def __init__(self, space,
     554                 projection=True,
     555                 bounding_box=3,
     556                 color=CartanType.color,
     557                 labels=True,
     558                 level=None,
     559                 affine=None,
     560                 ):
     561        r"""
     562        TESTS::
     563
     564            sage: L = RootSystem(['B',2,1]).weight_space()
     565            sage: options = L.plot_parse_options()
     566            sage: options.dimension
     567            2
     568            sage: options._projections
     569            [Weight space over the Rational Field of the Root system of type ['B', 2],
     570             <bound method WeightSpace_with_category._plot_projection of Weight space over the Rational Field of the Root system of type ['B', 2]>]
     571
     572            sage: L = RootSystem(['B',2,1]).ambient_space()
     573            sage: options = L.plot_parse_options()
     574            sage: options.dimension
     575            2
     576            sage: options._projections
     577            [Ambient space of the Root system of type ['B', 2],
     578             <bound method AmbientSpace_with_category._plot_projection of Ambient space of the Root system of type ['B', 2]>]
     579
     580            sage: options = L.plot_parse_options(affine=True)
     581            sage: options.dimension
     582            2
     583            sage: options._projections
     584            [Ambient space of the Root system of type ['B', 2],
     585             <bound method AmbientSpace_with_category._plot_projection of Ambient space of the Root system of type ['B', 2]>]
     586
     587            sage: options = L.plot_parse_options(affine=False)
     588            sage: options._projections
     589            [<bound method AmbientSpace_with_category._plot_projection of Ambient space of the Root system of type ['B', 2, 1]>]
     590            sage: options.dimension
     591            3
     592
     593            sage: options = L.plot_parse_options(affine=False, projection='barycentric')
     594            sage: options._projections
     595            [<bound method AmbientSpace_with_category._plot_projection_barycentric of Ambient space of the Root system of type ['B', 2, 1]>]
     596            sage: options.dimension
     597            3
     598        """
     599        self.space = space
     600        self._color=color
     601        self.labels=labels
     602
     603        # self.level = l != None: whether to intersect the alcove picture at level l
     604        # self.affine: whether to project at level l and then onto the classical space
     605
     606        if affine is None:
     607            affine = space.cartan_type().is_affine()
     608        if affine:
     609            if level is None:
     610                level = 1
     611            if not space.cartan_type().is_affine():
     612                raise ValueError("affine option only valid for affine types")
     613            projections=[space.classical()]
     614            projection_space = space.classical()
     615        else:
     616            projections=[]
     617            projection_space = space
     618
     619        self.affine = affine
     620        self.level = level
     621
     622        if projection is True:
     623            projections.append(projection_space._plot_projection)
     624        elif projection == "barycentric":
     625            projections.append(projection_space._plot_projection_barycentric)
     626        elif projection is not False:
     627            # assert projection is a callable
     628            projections.append(projection)
     629
     630        self._projections = projections
     631
     632        self.origin_projected = self.projection(space.zero())
     633
     634        self.dimension = len(self.origin_projected)
     635
     636        # Bounding box
     637        from sage.rings.real_mpfr import RR
     638        from sage.geometry.polyhedron.all import Polyhedron
     639        from sage.combinat.cartesian_product import CartesianProduct
     640        if bounding_box in RR:
     641            bounding_box = [[-bounding_box,bounding_box]] * self.dimension
     642        else:
     643            if not len(bounding_box) == self.dimension:
     644                raise TypeError("bounding_box argument doesn't match with the plot dimension")
     645            elif not all(len(b)==2 for b in bounding_box):
     646                raise TypeError("Invalid bounding box %s"%bounding_box)
     647        self.bounding_box = Polyhedron(vertices=CartesianProduct(*bounding_box))
     648
     649    @cached_method
     650    def in_bounding_box(self, x):
     651        r"""
     652        Return whether ``x`` is in the bounding box.
     653
     654        INPUT:
     655
     656        - ``x`` -- an element of the root lattice realization
     657
     658        This method is currently one of the bottlenecks, and therefore
     659        cached.
     660
     661        EXAMPLES::
     662
     663            sage: L = RootSystem(["A",2,1]).ambient_space()
     664            sage: options = L.plot_parse_options()
     665            sage: alpha = L.simple_roots()
     666            sage: options.in_bounding_box(alpha[1])
     667            True
     668            sage: options.in_bounding_box(3*alpha[1])
     669            False
     670        """
     671        return self.bounding_box.contains(self.projection(x))
     672
     673    def text(self, label, position):
     674        r"""
     675        Return text widget with label ``label`` at position ``position``
     676
     677        INPUT:
     678
     679        - ``label`` -- a string, or a Sage object upon which latex will
     680          be called
     681
     682        - ``position`` -- a position
     683
     684        EXAMPLES::
     685
     686            sage: L = RootSystem(["A",2]).root_lattice()
     687            sage: options = L.plot_parse_options()
     688            sage: list(options.text("coucou", [0,1]))
     689            [Text 'coucou' at the point (0.0,1.0)]
     690            sage: list(options.text(L.simple_root(1), [0,1]))
     691            [Text '$\alpha_{1}$' at the point (0.0,1.0)]
     692
     693            sage: options = RootSystem(["A",2]).root_lattice().plot_parse_options(labels=False)
     694            sage: options.text("coucou", [0,1])
     695            0
     696
     697            sage: options = RootSystem(["B",3]).root_lattice().plot_parse_options()
     698            sage: print options.text("coucou", [0,1,2]).x3d_str()
     699            <Transform translation='0 1 2'>
     700            <Shape><Text string='coucou' solid='true'/><Appearance><Material diffuseColor='0.0 0.0 0.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance></Shape>
     701            <BLANKLINE>
     702            </Transform>
     703        """
     704        if self.labels:
     705            if self.dimension <= 2:
     706                if not isinstance(label, basestring):
     707                    label = "$"+str(latex(label))+"$"
     708                from sage.plot.text import text
     709                return text(label, position, fontsize=15)
     710            elif self.dimension == 3:
     711                # LaTeX labels not yet supported in 3D
     712                if isinstance(label, basestring):
     713                    label = label.replace("{","").replace("}","").replace("$","").replace("_","")
     714                else:
     715                    label = str(label)
     716                from sage.plot.plot3d.shapes2 import text3d
     717                return text3d(label, position)
     718            else:
     719                raise NotImplementedError("Plots in dimension > 3")
     720        else:
     721            return self.empty()
     722
     723    def color(self, i):
     724        r"""
     725        Return the color to be used for `i`.
     726
     727        INPUT:
     728
     729        - ``i`` -- an element of the index, or an element of a root lattice
     730          realization
     731
     732        If ``i`` is a monomial like `\alpha_j` then ``j`` is used as index.
     733
     734        EXAMPLES::
     735
     736            sage: L = RootSystem(["A",2]).root_lattice()
     737            sage: options = L.plot_parse_options(labels=False)
     738            sage: alpha = L.simple_roots()
     739            sage: options.color(1)
     740            'blue'
     741            sage: options.color(2)
     742            'red'
     743            sage: for alpha in L.roots():
     744            ...       print alpha, options.color(alpha)
     745            alpha[1]             blue
     746            alpha[2]             red
     747            alpha[1] + alpha[2]  black
     748            -alpha[1]            black
     749            -alpha[2]            black
     750            -alpha[1] - alpha[2] black
     751        """
     752        if i in ZZ:
     753            return self._color(i)
     754        else:
     755            assert i.parent() in RootLatticeRealizations
     756            if len(i) == 1 and i.leading_coefficient().is_one():
     757                return self._color(i.leading_support())
     758            else:
     759                return self._color("other")
     760
     761    def projection(self, v):
     762        r"""
     763        Return the projection of ``v``.
     764
     765        INPUT:
     766
     767        - ``x`` -- an element of the root lattice realization
     768
     769        OUTPUT:
     770
     771        An immutable vector with integer or rational coefficients.
     772
     773        EXAMPLES::
     774
     775            sage: L = RootSystem(["A",2,1]).ambient_space()
     776            sage: options = L.plot_parse_options()
     777            sage: options.projection(L.rho())
     778            (0, 989/571)
     779
     780            sage: options = L.plot_parse_options(projection=False)
     781            sage: options.projection(L.rho())
     782            (2, 1, 0)
     783        """
     784        for projection in self._projections:
     785            v = projection(v)
     786        v = vector(v)
     787        v.set_immutable()
     788        return v
     789
     790    def intersection_at_level_1(self, x):
     791        r"""
     792        Return ``x`` scaled at the appropriate level, if level is set;
     793        otherwise return ``x``.
     794
     795        INPUT:
     796
     797        - ``x`` -- an element of the root lattice realization
     798
     799        EXAMPLES::
     800
     801            sage: L = RootSystem(["A",2,1]).weight_space()
     802            sage: options = L.plot_parse_options()
     803            sage: options.intersection_at_level_1(L.rho())
     804            1/3*Lambda[0] + 1/3*Lambda[1] + 1/3*Lambda[2]
     805
     806            sage: options = L.plot_parse_options(affine=False, level=2)
     807            sage: options.intersection_at_level_1(L.rho())
     808            2/3*Lambda[0] + 2/3*Lambda[1] + 2/3*Lambda[2]
     809
     810        When ``level`` is not set, ``x`` is returned::
     811
     812            sage: options = L.plot_parse_options(affine=False)
     813            sage: options.intersection_at_level_1(L.rho())
     814            Lambda[0] + Lambda[1] + Lambda[2]
     815
     816        """
     817        if self.level is not None:
     818            return x * self.level / x.level()
     819        else:
     820            return x
     821
     822    def empty(self, *args):
     823        r"""
     824        Return an empty plot.
     825
     826        EXAMPLES::
     827
     828            sage: L = RootSystem(["A",2]).root_lattice()
     829            sage: options = L.plot_parse_options(labels=True)
     830
     831        This currently returns ``int(0)``::
     832
     833            sage: options.empty()
     834            0
     835
     836        This is not a plot, so may cause some corner cases. On the
     837        other hand, `0` behaves as a fast neutral element, which is
     838        important given the typical idioms used in the plotting code::
     839
     840            sage: p = point([0,0])
     841            sage: p + options.empty() is p
     842            True
     843        """
     844        return 0
     845        # if self.dimension == 2:
     846        #     from sage.plot.graphics import Graphics
     847        #     G = Graphics()
     848        # elif self.dimension == 3:
     849        #     from sage.plot.plot3d.base import Graphics3dGroup
     850        #     G = Graphics3dGroup()
     851        # else:
     852        #     assert False, "Dimension too high (or too low!)"
     853        # self.finalize(G)
     854        # return G
     855
     856    def finalize(self, G):
     857        r"""
     858        Finalize a root system plot.
     859
     860        INPUT:
     861
     862        - ``G`` -- a root system plot or ``0``
     863
     864        This sets the aspect ratio to 1 and remove the axes. This
     865        should be called by all the user-level plotting methods of
     866        root systems. This will become mostly obsolete when
     867        customization options won't be lost anymore upon addition of
     868        graphics objects and there will be a proper empty object for
     869        2D and 3D plots.
     870
     871        EXAMPLES::
     872
     873            sage: L = RootSystem(["B",2,1]).ambient_space()
     874            sage: options = L.plot_parse_options()
     875            sage: p = L.plot_roots(plot_options=options)
     876            sage: p += L.plot_coroots(plot_options=options)
     877            sage: p.axes()
     878            True
     879            sage: p = options.finalize(p)
     880            sage: p.axes()
     881            False
     882            sage: p.aspect_ratio()
     883            1.0
     884
     885            sage: options = L.plot_parse_options(affine=False)
     886            sage: p = L.plot_roots(plot_options=options)
     887            sage: p += point([[1,1,0]])
     888            sage: p = options.finalize(p)
     889            sage: p.aspect_ratio()
     890            [1.0, 1.0, 1.0]
     891
     892        If the input is ``0``, this returns an empty graphics object::
     893
     894            sage: type(options.finalize(0))
     895            <class 'sage.plot.plot3d.base.Graphics3dGroup'>
     896
     897            sage: options = L.plot_parse_options()
     898            sage: type(options.finalize(0))
     899            <class 'sage.plot.graphics.Graphics'>
     900            sage: list(options.finalize(0))
     901            []
     902        """
     903        from sage.plot.graphics import Graphics
     904        if self.dimension == 2:
     905            if G == 0:
     906                G = Graphics()
     907            G.set_aspect_ratio(1)
     908            # TODO: make this customizable
     909            G.axes(False)
     910        elif self.dimension == 3:
     911            if G == 0:
     912                from sage.plot.plot3d.base import Graphics3dGroup
     913                G = Graphics3dGroup()
     914            G.aspect_ratio(1)
     915            # TODO: Configuration axes
     916        return G
     917
     918    def family_of_vectors(self, vectors):
     919        r"""
     920        Return a plot of a family of vectors.
     921
     922        INPUT:
     923
     924        - ``vectors`` -- family or vectors in ``self``
     925
     926        The vectors are labelled by their index.
     927
     928        EXAMPLES::
     929
     930            sage: L = RootSystem(["A",2]).root_lattice()
     931            sage: options = L.plot_parse_options()
     932            sage: alpha = L.simple_roots()
     933            sage: p = options.family_of_vectors(alpha); p
     934            sage: list(p)
     935            [Arrow from (0.0,0.0) to (1.0,0.0),
     936             Text '$1$' at the point (1.05,0.0),
     937             Arrow from (0.0,0.0) to (0.0,1.0),
     938             Text '$2$' at the point (0.0,1.05)]
     939
     940        Handling of colors and labels::
     941
     942            sage: color=lambda i: "purple" if i==1 else None
     943            sage: options = L.plot_parse_options(labels=False, color=color)
     944            sage: p = options.family_of_vectors(alpha)
     945            sage: list(p)
     946            [Arrow from (0.0,0.0) to (1.0,0.0)]
     947            sage: p[0].options()['rgbcolor']
     948            'purple'
     949
     950        Matplotlib emits a warning for arrows of length 0 and draws
     951        nothing anyway. So we do not draw them at all::
     952
     953            sage: L = RootSystem(["A",2,1]).ambient_space()
     954            sage: options = L.plot_parse_options()
     955            sage: Lambda = L.fundamental_weights()
     956            sage: p = options.family_of_vectors(Lambda); p
     957            sage: list(p)
     958            [Text '$0$' at the point (0.0,0.0),
     959             Arrow from (0.0,0.0) to (0.5,0.866024518389),
     960             Text '$1$' at the point (0.525,0.909325744308),
     961             Arrow from (0.0,0.0) to (-0.5,0.866024518389),
     962             Text '$2$' at the point (-0.525,0.909325744308)]
     963        """
     964        from sage.plot.arrow import arrow
     965        tail = self.origin_projected
     966        G = self.empty()
     967        for i in vectors.keys():
     968            if self.color(i) is None:
     969                continue
     970            head = self.projection(vectors[i])
     971            if head != tail:
     972                G += arrow(tail, head, rgbcolor=self.color(i))
     973            G += self.text(i, 1.05*head)
     974        return self.finalize(G)
     975
     976    def cone(self, rays=[], lines=[], color="black", alpha=1, wireframe=False,
     977             label=None, draw_degenerate=True, as_polyhedron=False):
     978        r"""
     979        Return the cone generated by the given rays and lines.
     980
     981        INPUT:
     982
     983        - ``rays``, ``lines`` -- lists of elements of the root lattice
     984          realization (default: ``[]``)
     985
     986        - ``color`` -- a color (default: ``"black"``)
     987
     988        - ``alpha`` -- a number in the interval `[0, 1]` (default: `1`)
     989          the desired transparency
     990
     991        - ``label`` -- an object to be used as for this cone.
     992          The label itself will be constructed by calling
     993          :func:`~sage.misc.latex.latex` or :func:`repr` on the
     994          object depending on the graphics backend.
     995
     996        - ``draw_degenerate`` -- a boolean (default: ``True``)
     997          whether to draw cones with a degenerate intersection with
     998          the bounding box
     999
     1000        - ``as_polyhedron`` -- a boolean (default: ``False``)
     1001          whether to return the result as a polyhedron, without
     1002          clipping it to the bounding box, and without making a plot
     1003          out of it (for testing purposes)
     1004
     1005        OUTPUT:
     1006
     1007        A graphic object, a polyhedron, or ``0``.
     1008
     1009        EXAMPLES::
     1010
     1011            sage: L = RootSystem(["A",2]).root_lattice()
     1012            sage: options = L.plot_parse_options()
     1013            sage: alpha = L.simple_roots()
     1014            sage: p = options.cone(rays=[alpha[1]], lines=[alpha[2]], color='green', label=2)
     1015            sage: p
     1016            sage: list(p)
     1017            [Polygon defined by 4 points,
     1018             Text '$2$' at the point (3.15,3.15)]
     1019            sage: options.cone(rays=[alpha[1]], lines=[alpha[2]], color='green', label=2, as_polyhedron=True)
     1020            A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line
     1021
     1022        An empty result, being outside of the bounding box::
     1023
     1024            sage: options = L.plot_parse_options(labels=True, bounding_box=[[-10,-9]]*2)
     1025            sage: options.cone(rays=[alpha[1]], lines=[alpha[2]], color='green', label=2)
     1026            0
     1027
     1028        This method is tested indirectly but extensively by the
     1029        various plot methods of root lattice realizations.
     1030        """
     1031        if color is None:
     1032            return self.empty()
     1033        from sage.geometry.polyhedron.all import Polyhedron
     1034        # TODO: we currently convert lines into rays, which simplify a
     1035        # bit the calculation of the intersection. But it would be
     1036        # nice to benefit from the new ``lines`` option of Polyhedrons
     1037        rays = list(rays)+[ray for ray in lines]+[-ray for ray in lines]
     1038
     1039        # Compute the intersection at level 1, if needed
     1040        if self.level:
     1041            old_rays = rays
     1042            vertices = [self.intersection_at_level_1(ray) for ray in old_rays if ray.level() > 0]
     1043            rays     = [ray for ray in old_rays if ray.level() == 0]
     1044            rays    += [vertex - self.intersection_at_level_1(ray) for ray in old_rays if ray.level() < 0 for vertex in vertices]
     1045        else:
     1046            vertices = []
     1047
     1048        # Apply the projection (which is supposed to be affine)
     1049        vertices = [ self.projection(vertex) for vertex in vertices ]
     1050        rays = [ self.projection(ray)-self.projection(self.space.zero()) for ray in rays ]
     1051        rays = [ ray for ray in rays if ray ] # Polyhedron does not accept yet zero rays
     1052
     1053        # Build the polyhedron
     1054        p = Polyhedron(vertices=vertices, rays = rays)
     1055        if as_polyhedron:
     1056            return p
     1057
     1058        # Compute the intersection with the bounding box
     1059        q = p & self.bounding_box
     1060        if q.dim() >= 0 and p.dim() >= 0 and (draw_degenerate or p.dim()==q.dim()):
     1061            if wireframe:
     1062                options = dict(point=False, line=dict(width=10), polygon=False)
     1063                center = q.center()
     1064                q = q.translation(-center).dilation(ZZ(95)/ZZ(100)).translation(center)
     1065            else:
     1066                options = dict(wireframe=False)
     1067            result = q.plot(color = color, alpha=alpha, **options)
     1068            if label is not None:
     1069                # Put the label on the vertex having largest z, then y, then x coordinate.
     1070                vertices = sorted([vector(v) for v in q.vertices()],
     1071                                  key=lambda x: list(reversed(x)))
     1072                result += self.text(label, 1.05*vector(vertices[-1]))
     1073            return result
     1074        else:
     1075            return self.empty()
     1076
     1077    def reflection_hyperplane(self, coroot, as_polyhedron=False):
     1078        r"""
     1079        Return a plot of the reflection hyperplane indexed by this coroot.
     1080
     1081        - ``coroot`` -- a coroot
     1082
     1083        EXAMPLES::
     1084
     1085            sage: L = RootSystem(["B",2]).weight_space()
     1086            sage: alphacheck = L.simple_coroots()
     1087            sage: options = L.plot_parse_options()
     1088            sage: H = options.reflection_hyperplane(alphacheck[1]); H
     1089
     1090        TESTS::
     1091
     1092            sage: from sage.combinat.root_system.plot import plot_expose
     1093            sage: plot_expose(H)
     1094            Line defined by 2 points: [(0.0, 3.0), (0.0, -3.0)]
     1095            Text '$H_{\alpha^\vee_{1}}$' at the point (0.0,3.15)
     1096
     1097        ::
     1098
     1099            sage: L = RootSystem(["A",3,1]).ambient_space()
     1100            sage: alphacheck = L.simple_coroots()
     1101            sage: options = L.plot_parse_options()
     1102            sage: H = options.reflection_hyperplane(alphacheck[1], as_polyhedron=True); H
     1103            A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 2 lines
     1104            sage: H.lines()
     1105            (A line in the direction (0, 0, 1), A line in the direction (0, 1, 0))
     1106            sage: H.vertices()
     1107            (A vertex at (0, 0, 0),)
     1108
     1109        ::
     1110
     1111            sage: all(options.reflection_hyperplane(c, as_polyhedron=True).dim() == 2
     1112            ...       for c in alphacheck)
     1113            True
     1114
     1115
     1116        .. TODO::
     1117
     1118            Display the periodic orientation by adding a `+` and
     1119            a `-` sign close to the label. Typically by using
     1120            the associated root to shift a bit from the vertex
     1121            upon which the hyperplane label is attached.
     1122        """
     1123        from sage.matrix.constructor import matrix
     1124        L = self.space
     1125        label = coroot
     1126        # scalar currently only handles scalar product with
     1127        # elements of self.coroot_lattice(). Furthermore, the
     1128        # latter is misnamed: for ambient spaces, this does
     1129        # not necessarily coincide with the coroot lattice of
     1130        # the rootsystem. So we need to do a coercion.
     1131        coroot = self.space.coroot_lattice()(coroot)
     1132        # Compute the kernel of the linear form associated to the coroot
     1133        vectors = matrix([b.scalar(coroot) for b in L.basis()]).right_kernel().basis()
     1134        basis = [L.from_vector(v) for v in vectors]
     1135        if self.dimension == 3: # LaTeX labels not yet supported in 3D
     1136            text_label = "H_%s$"%(str(label))
     1137        else:
     1138            text_label = "$H_{%s}$"%(latex(label))
     1139        return self.cone(lines = basis, color = self.color(label), label=text_label,
     1140                         as_polyhedron=as_polyhedron)
     1141
     1142def plot_expose(graphic_object):
     1143    r"""
     1144    Print some data on a 2D graphic objects for testing purposes.
     1145
     1146    EXAMPLES::
     1147
     1148        sage: from sage.combinat.root_system.plot import plot_expose
     1149        sage: plot_expose(polytopes.n_cube(2).plot())
     1150        Point set defined by 4 point(s): [(-1.0, -1.0), (-1.0, 1.0), (1.0, -1.0), (1.0, 1.0)]
     1151        Line defined by 2 points: [(-1.0, -1.0), (-1.0, 1.0)]
     1152        Line defined by 2 points: [(-1.0, -1.0), (1.0, -1.0)]
     1153        Line defined by 2 points: [(-1.0, 1.0), (1.0, 1.0)]
     1154        Line defined by 2 points: [(1.0, -1.0), (1.0, 1.0)]
     1155        Polygon defined by 4 points: [(1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), (1.0, -1.0)]
     1156    """
     1157    for g in graphic_object:
     1158        if hasattr(g, "xdata"):
     1159            print "%s:\t%s"%(g, zip(g.xdata, g.ydata))
     1160        else:
     1161            print g
     1162
     1163
     1164@cached_function
     1165def barycentric_projection_matrix(n, angle=0):
     1166    r"""
     1167    Returns a family of `n+1` vectors evenly spaced in a real vector space of dimension `n`
     1168
     1169    Those vectors are of norm `1`, the scalar product between any two
     1170    vector is `1/n`, thus the distance between two tips is constant.
     1171
     1172    The family is built recursively and uniquely determined by the
     1173    following property: the last vector is `(0,\dots,0,-1)`, and the
     1174    projection of the first `n` vectors in dimension `n-1`, after
     1175    appropriate rescaling to norm `1`, retrieves the family for `n-1`.
     1176
     1177    OUTPUT:
     1178
     1179    A matrix with `n+1` columns of height `n` with rational or
     1180    symbolic coefficients.
     1181
     1182    EXAMPLES:
     1183
     1184    One vector in dimension `0`::
     1185
     1186        sage: from sage.combinat.root_system.root_lattice_realizations import barycentric_projection_matrix
     1187        sage: m = barycentric_projection_matrix(0); m
     1188        []
     1189        sage: matrix(QQ,0,1).nrows()
     1190        0
     1191        sage: matrix(QQ,0,1).ncols()
     1192        1
     1193
     1194    Two vectors in dimension 1::
     1195
     1196        sage: barycentric_projection_matrix(1)
     1197        [ 1 -1]
     1198
     1199    Three vectors in dimension 2::
     1200
     1201        sage: barycentric_projection_matrix(2)
     1202        [ 1/2*sqrt(3) -1/2*sqrt(3)            0]
     1203        [         1/2          1/2           -1]
     1204
     1205    Four vectors in dimension 3::
     1206
     1207        sage: m = barycentric_projection_matrix(3); m
     1208        [ 1/3*sqrt(2)*sqrt(3) -1/3*sqrt(2)*sqrt(3)             0   0]
     1209        [         1/3*sqrt(2)          1/3*sqrt(2)  -2/3*sqrt(2)   0]
     1210        [                 1/3                  1/3           1/3  -1]
     1211
     1212    The columns give four vectors that sum up to zero::
     1213
     1214        sage: sum(m.columns())
     1215        (0, 0, 0)
     1216
     1217    and have regular mutual angles::
     1218
     1219        sage: m.transpose()*m
     1220        [   1 -1/3 -1/3 -1/3]
     1221        [-1/3    1 -1/3 -1/3]
     1222        [-1/3 -1/3    1 -1/3]
     1223        [-1/3 -1/3 -1/3    1]
     1224
     1225    Here is a plot of them::
     1226
     1227        sage: sum(arrow((0,0,0),x) for x in m.columns())
     1228
     1229    For 2D drawings of root systems, it is desirable to rotate the
     1230    result to match with the usual conventions::
     1231
     1232        sage: barycentric_projection_matrix(2, angle=2*pi/3)
     1233        [         1/2           -1          1/2]
     1234        [ 1/2*sqrt(3)            0 -1/2*sqrt(3)]
     1235
     1236    TESTS::
     1237
     1238        sage: for n in range(1, 7):
     1239        ...       m = barycentric_projection_matrix(n)
     1240        ...       assert sum(m.columns()).is_zero()
     1241        ...       assert matrix(QQ, n+1,n+1, lambda i,j: 1 if i==j else -1/n) == m.transpose()*m
     1242
     1243    """
     1244    from sage.matrix.constructor import matrix
     1245    from sage.functions.other import sqrt
     1246    n = ZZ(n)
     1247    if n == 0:
     1248        return matrix(QQ, 0, 1)
     1249    a = 1/n
     1250    b = sqrt(1-a**2)
     1251    result = b * barycentric_projection_matrix(n-1)
     1252    result = result.augment(vector([0]*(n-1)))
     1253    result = result.stack(matrix([[a]*n+[-1]]))
     1254    assert sum(result.columns()).is_zero()
     1255    if angle and n == 2:
     1256        from sage.functions.trig import sin
     1257        from sage.functions.trig import cos
     1258        rotation = matrix([[sin(angle), cos(angle)],[-cos(angle), sin(angle)]])
     1259        result = rotation * result
     1260    result.set_immutable()
     1261    return result
  • sage/combinat/root_system/root_lattice_realizations.py

    diff --git a/sage/combinat/root_system/root_lattice_realizations.py b/sage/combinat/root_system/root_lattice_realizations.py
    a b  
     1# -*- coding: utf-8 -*-
    12"""
    23Root lattice realizations
    34"""
    45#*****************************************************************************
    5 #       Copyright (C) 2007-2012 Nicolas M. Thiery <nthiery at users.sf.net>
     6#       Copyright (C) 2007-2013 Nicolas M. Thiery <nthiery at users.sf.net>
     7#                          2012 Nicolas Borie  <nicolas.borie at univ-mlv.fr>
    68#
    79#       (with contributions of many others)
    810#
    911#  Distributed under the terms of the GNU General Public License (GPL)
    10 #
    11 #    This code is distributed in the hope that it will be useful,
    12 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
    13 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    14 #    General Public License for more details.
    15 #
    16 #  The full text of the GPL is available at:
    17 #
    1812#                  http://www.gnu.org/licenses/
    1913#*****************************************************************************
    2014
    2115from sage.misc.abstract_method import abstract_method, AbstractMethod
    22 from sage.misc.all import attrcall
     16from sage.misc.misc import attrcall
    2317from sage.misc.cachefunc import cached_method, cached_in_parent_method
    2418from sage.misc.lazy_attribute import lazy_attribute
     19from sage.misc.superseded import deprecated_function_alias
    2520from sage.categories.coxeter_groups import CoxeterGroups
    2621from sage.categories.category_types import Category_over_base_ring
    2722from sage.categories.modules_with_basis import ModulesWithBasis
     23from sage.structure.element import Element
    2824from sage.sets.family import Family
    2925from sage.rings.all import ZZ, QQ
     26from sage.modules.free_module_element import vector
    3027from sage.combinat.backtrack import TransitiveIdeal, TransitiveIdealGraded
    31 from sage.misc.superseded import deprecated_function_alias
    32 from sage.structure.element import Element
     28from sage.combinat.root_system.plot import PlotOptions, barycentric_projection_matrix
    3329
    3430class RootLatticeRealizations(Category_over_base_ring):
    3531    r"""
    class RootLatticeRealizations(Category_o 
    4642
    4743    To describe the embedding, a root lattice realization must
    4844    implement a method
    49     :meth:`~RootLatticeRealizations.ParentMethods.simple_root` (i)
     45    :meth:`~RootLatticeRealizations.ParentMethods.simple_root`
    5046    returning for each `i` in the index set the image of the simple root
    5147    `\alpha_i` under the embedding.
    5248
    class RootLatticeRealizations(Category_o 
    5753    Using those, this category provides tools for reflections, roots,
    5854    the Weyl group and its action, ...
    5955
    60     .. seealso::
     56    .. SEEALSO::
    6157
    6258        - :class:`~sage.combinat.root_system.root_system.RootSystem`
    6359        - :class:`~sage.combinat.root_system.weight_lattice_realizations.WeightLatticeRealizations`
    class RootLatticeRealizations(Category_o 
    157153    class ParentMethods:
    158154
    159155        def __init_extra__(self):
    160             """
    161             Registers the embedding of the root lattice into ``self``
     156            r"""
     157            Register the embedding of the root lattice into ``self``.
    162158
    163159            Also registers the embedding of the root space over the same
    164160            base field `K` into ``self`` if `K` is not `\ZZ`.
    class RootLatticeRealizations(Category_o 
    13401336            under the action of the dihedral group generated by the
    13411337            operators `\tau_+` and `\tau_-`.
    13421338
    1343             .. seealso:: :meth:`almost_positive_roots`, :meth:`tau_plus_minus`
     1339            .. SEEALSO::
     1340
     1341                - :meth:`almost_positive_roots`
     1342                - :meth:`tau_plus_minus`
    13441343
    13451344            EXAMPLES::
    13461345
    class RootLatticeRealizations(Category_o 
    13581357
    13591358            REFERENCES:
    13601359
    1361                 .. [CFZ2] Chapoton, Fomin, Zelevinsky - Polytopal realizations of generalized associahedra
     1360            .. [CFZ2] Chapoton, Fomin, Zelevinsky - Polytopal realizations of
     1361               generalized associahedra
    13621362            """
    13631363            # TODO: this should use a generic function for computing
    13641364            # orbits under the action of a group:
    class RootLatticeRealizations(Category_o 
    14561456            """
    14571457            Return the projection of `\alpha_0` in the classical space.
    14581458
     1459            This is used e.g. to construct the projections onto the
     1460            classical space.
     1461
    14591462            EXAMPLES:
    14601463
    14611464            This is the opposite of the highest root in the untwisted case::
    14621465
    14631466                sage: L = RootSystem(["B",3,1]).root_space()
     1467                sage: L._classical_alpha_0()
     1468                -alpha[1] - 2*alpha[2] - 2*alpha[3]
    14641469                sage: L._to_classical_on_basis(0)
    14651470                -alpha[1] - 2*alpha[2] - 2*alpha[3]
    14661471                sage: L.classical().highest_root()
    class RootLatticeRealizations(Category_o 
    14821487                                  for i in self.index_set() if i != special_node) \
    14831488                                  / a[special_node]
    14841489
     1490        ######################################################################
     1491        # Root system plots
     1492
     1493        def plot(self,
     1494                 roots="simple",
     1495                 coroots=False,
     1496                 reflection_hyperplanes="simple",
     1497                 fundamental_weights=None,
     1498                 fundamental_chamber=None,
     1499                 alcoves=None,
     1500                 alcove_labels=False,
     1501                 alcove_walk=None,
     1502                 **options):
     1503            r"""
     1504            Return a picture of this root lattice realization.
     1505
     1506            INPUT:
     1507
     1508            - ``roots`` -- which roots to display, if any.
     1509              Can be one of the following:
     1510
     1511              * ``"simple"`` -- The simple roots (the default)
     1512              * ``"classical"`` -- Not yet implemented
     1513              * ``"all"`` -- Only works in the finite case
     1514              * A list or tuple of roots
     1515              * ``False``
     1516
     1517            - ``coroots`` -- which coroots to display, if any.
     1518              Can be one of the following:
     1519
     1520              * ``"simple"`` -- The simple coroots (the default)
     1521              * ``"classical"`` -- Not yet implemented
     1522              * ``"all"`` -- Only works in the finite case
     1523              * A list or tuple of coroots
     1524              * ``False``
     1525
     1526            - ``fundamental_weights`` -- a boolean or ``None`` (default: ``None``)
     1527              whether to display the fundamental weights.
     1528              If ``None``, the fundamental weights are drawn if available.
     1529
     1530            - ``reflection_hyperplanes`` -- which reflection
     1531              hyperplanes to display, if any. Can be one of the
     1532              following:
     1533
     1534              * ``"simple"`` -- The simple roots
     1535              * ``"classical"`` -- Not yet implemented
     1536              * ``"all"`` -- Only works in the finite case
     1537              * A list or tuple of roots
     1538              * ``False`` (the default)
     1539
     1540            - ``fundamental_chamber`` -- whether and how to draw the
     1541              fundamental chamber. Can be one of the following:
     1542
     1543              * A boolean -- Set to ``True`` to draw the fundamental
     1544                chamber
     1545              * ``"classical"`` -- Draw the classical fundamental chamber
     1546              * ``None`` -- (the default) The fundamental chamber is
     1547                drawn except in the root lattice where this is not yet
     1548                implemented. For affine types the classical
     1549                fundamental chamber is drawn instead.
     1550
     1551            - ``alcoves`` -- one of the following (default: ``True``):
     1552
     1553              * A boolean -- Whether to display the alcoves
     1554              * A list of alcoves -- The alcoves to be drawn. Each alcove is
     1555                specified by the coordinates of its center in the root lattice
     1556                (affine type only). Otherwise the alcoves that intersect the
     1557                bounding box are drawn.
     1558
     1559            - ``alcove_labels`` -- one of the following (default: ``False``):
     1560
     1561              * A boolean -- Whether to display the elements of the Weyl group
     1562                indexing the alcoves. This currently requires to also
     1563                set the ``alcoves`` option.
     1564              * A number `l` -- The label is drawn at level `l` (affine type
     1565                only), which only makes sense if ``affine`` is ``False``.
     1566
     1567            - ``bounding_box`` -- a rational number or a list of pairs
     1568              thereof (default: 3)
     1569
     1570              Specifies a bounding box, in the coordinate system for
     1571              this plot, in which to plot alcoves and other infinite
     1572              objects. If the bounding box is a number `a`, then the
     1573              bounding box is of the form `[-a,a]` in all directions.
     1574              Beware that there can be some border effects and the
     1575              returned graphic is not necessarily strictly contained
     1576              in the bounding box.
     1577
     1578            - ``alcove_walk`` -- an alcove walk or ``None`` (default: ``None``)
     1579
     1580              The alcove walk is described by a list (or iterable) of
     1581              vertices of the Dynkin diagram which specifies which
     1582              wall is crossed at each step, starting from the
     1583              fundamental alcove.
     1584
     1585            - ``projection`` -- one of the following (default: ``True``):
     1586
     1587              * ``True`` -- The default projection for the root
     1588                lattice realization is used.
     1589              * ``False`` -- No projection is used.
     1590              * ``barycentric`` -- A barycentric projection is used.
     1591              * A function -- If a function is specified, it should implement a
     1592                linear (or affine) map taking as input an element of
     1593                this root lattice realization and returning its
     1594                desired coordinates in the plot, as a vector with
     1595                rational coordinates.
     1596
     1597            - ``color`` -- a function mapping vertices of the Dynkin
     1598              diagram to colors (default: ``"black"`` for 0,
     1599              ``"blue"`` for 1, ``"red"`` for 2, ``"green"`` for 3)
     1600
     1601              This is used to set the color for the simple roots,
     1602              fundamental weights, reflection hyperplanes, alcove
     1603              facets, etc. If the color is ``None``, the object is not
     1604              drawn.
     1605
     1606            - ``labels`` -- a boolean (default: ``True``)
     1607              whether to display labels on the simple roots,
     1608              fundamental weights, etc.
     1609
     1610            EXAMPLES::
     1611
     1612                sage: L = RootSystem(["A",2,1]).ambient_space().plot()
     1613
     1614            .. SEEALSO::
     1615
     1616                - :meth:`plot_parse_options`
     1617                - :meth:`plot_roots`, :meth:`plot_coroots`
     1618                - :meth:`plot_fundamental_weights`
     1619                - :meth:`plot_fundamental_chamber`
     1620                - :meth:`plot_reflection_hyperplanes`
     1621                - :meth:`plot_alcoves`
     1622                - :meth:`plot_alcove_walk`
     1623            """
     1624            plot_options = self.plot_parse_options(**options)
     1625            G = plot_options.empty()
     1626
     1627            if roots:
     1628                G += self.plot_roots(roots, plot_options=plot_options)
     1629
     1630            # if coroots is None:
     1631            #    coroot_lattice = self.root_system.coroot_lattice()
     1632            #    if self.has_coerce_map_from(coroot_lattice):
     1633            #        coroots="simple"
     1634            #    else:
     1635            #        coroots=False
     1636            if coroots:
     1637                G += self.plot_coroots(coroots, plot_options=plot_options)
     1638
     1639            if fundamental_weights is None:
     1640                fundamental_weights = hasattr(self, "fundamental_weights")
     1641            if fundamental_weights:
     1642                G += self.plot_fundamental_weights(plot_options=plot_options)
     1643
     1644            if reflection_hyperplanes:
     1645                G += self.plot_reflection_hyperplanes(reflection_hyperplanes, plot_options=plot_options)
     1646
     1647            if alcoves is None:
     1648                alcoves = self.cartan_type().is_affine() and hasattr(self, "fundamental_weights")
     1649            if alcoves:
     1650                G += self.plot_alcoves(alcoves, alcove_labels=alcove_labels, plot_options=plot_options)
     1651
     1652            if fundamental_chamber is None:
     1653                if not hasattr(self, "fundamental_weights"):
     1654                    fundamental_chamber = False
     1655                elif self.cartan_type().is_affine():
     1656                    fundamental_chamber = "classical"
     1657                else:
     1658                    fundamental_chamber = True
     1659            if fundamental_chamber:
     1660                G += self.plot_fundamental_chamber(fundamental_chamber, plot_options=plot_options)
     1661
     1662            if alcove_walk is not None:
     1663                G += self.plot_alcove_walk(alcove_walk, plot_options=plot_options)
     1664
     1665            return plot_options.finalize(G)
     1666
     1667        def plot_parse_options(self, **args):
     1668            r"""
     1669            Return an option object to be used for root system plotting.
     1670
     1671            EXAMPLES::
     1672
     1673                sage: L = RootSystem(["A",2,1]).ambient_space()
     1674                sage: options = L.plot_parse_options()
     1675                sage: options
     1676                <sage.combinat.root_system.plot.PlotOptions instance at ...>
     1677
     1678            .. SEEALSO::
     1679
     1680                - :meth:`plot` for a description of the plotting options
     1681                - :ref:`sage.combinat.root_system.plot` for a tutorial
     1682                  on root system plotting
     1683            """
     1684            if len(args) == 1 and "plot_options" in args:
     1685                return args["plot_options"]
     1686            else:
     1687                return PlotOptions(self, **args)
     1688
     1689        def _plot_projection(self, x):
     1690            r"""
     1691            Implement the default projection to be used for plots.
     1692
     1693            EXAMPLES:
     1694
     1695            By default, this is just the identity::
     1696
     1697                sage: L = RootSystem(["B",3]).root_lattice()
     1698                sage: l = L.an_element(); l
     1699                2*alpha[1] + 2*alpha[2] + 3*alpha[3]
     1700                sage: L._plot_projection(l)
     1701                2*alpha[1] + 2*alpha[2] + 3*alpha[3]
     1702
     1703            In the ambient space of type `A_2`, this is the
     1704            barycentric projection. In the ambient space of affine
     1705            type this goes through the classical ambient space.
     1706
     1707            .. SEEALSO::
     1708
     1709                - :meth:`sage.combinat.root_system.type_A.AmbientSpace._plot_projection`
     1710                - :meth:`sage.combinat.root_system.type_affine.AmbientSpace._plot_projection`
     1711                - :meth:`plot` for a description of the plotting options
     1712                - :ref:`sage.combinat.root_system.plot` for a tutorial
     1713                  on root system plotting
     1714            """
     1715            return x
     1716
     1717        @cached_method
     1718        def _plot_projection_barycentric_matrix(self):
     1719            """
     1720            A rational approximation of the matrix for the barycentric projection
     1721
     1722            OUTPUT: a matrix with rational coefficients whose column sum is zero
     1723
     1724            .. SEE_ALSO::
     1725
     1726                - :func:`sage.combinat.root_system.plot.barycentric_projection_matrix`
     1727                - :meth:`_plot_projection_barycentric`
     1728
     1729            EXAMPLES::
     1730
     1731                sage: RootSystem(["A",0]).ambient_space()._plot_projection_barycentric_matrix()
     1732                []
     1733                sage: m = RootSystem(["A",1]).ambient_space()._plot_projection_barycentric_matrix(); m
     1734                [ 1 -1]
     1735                sage: sum(m.columns())
     1736                (0)
     1737                sage: m = RootSystem(["A",2]).ambient_space()._plot_projection_barycentric_matrix(); m
     1738                [      1/2        -1       1/2]
     1739                [ 989/1142         0 -989/1142]
     1740                sage: sum(m.columns())
     1741                (0, 0)
     1742                sage: m = RootSystem(["A",3]).ambient_space()._plot_projection_barycentric_matrix(); m
     1743                [      1277/1564      -1277/1564               0               0]
     1744                [1009460/2141389        849/1801      -1121/1189               0]
     1745                [            1/3             1/3             1/3              -1]
     1746                sage: sum(m.columns())
     1747                (0, 0, 0)
     1748
     1749            """
     1750            from sage.matrix.constructor import matrix
     1751            from sage.symbolic.constants import pi
     1752            m = matrix(QQ, barycentric_projection_matrix(self.dimension()-1, angle=2*pi/3).n(20))
     1753            # We want to guarantee that the sum of the columns of the
     1754            # result is zero. This is close to be the case for the
     1755            # original matrix and for the current rational
     1756            # approximation. We tidy up the work by replacing the
     1757            # first colum by the opposite of the sum of the others.
     1758            if self.dimension()>1: # not needed in the trivial cases
     1759                m.set_column(0, -sum(m[:,1:].columns()))
     1760            m.set_immutable()
     1761            return m
     1762
     1763        def _plot_projection_barycentric(self, x):
     1764            r"""
     1765            Implement the barycentric projection to be used for plots.
     1766
     1767            It is in fact a rational approximation thereof, but the
     1768            sum of the basis vectors is guaranteed to be mapped to
     1769            zero.
     1770
     1771            EXAMPLES::
     1772
     1773                sage: L = RootSystem(["A",2]).ambient_space()
     1774                sage: e = L.basis()
     1775                sage: L._plot_projection_barycentric(e[0])
     1776                (1/2, 989/1142)
     1777                sage: L._plot_projection_barycentric(e[1])
     1778                (-1, 0)
     1779                sage: L._plot_projection_barycentric(e[2])
     1780                (1/2, -989/1142)
     1781
     1782            .. SEEALSO::
     1783
     1784                - :meth:`_plot_projection`, :meth:`plot`
     1785                - :ref:`sage.combinat.root_system.plot` for a tutorial
     1786                  on root system plotting
     1787            """
     1788            return self._plot_projection_barycentric_matrix()*vector(x)
     1789
     1790        def plot_roots(self, collection="simple", **options):
     1791            r"""
     1792            Plot the (simple/classical) roots of this root lattice.
     1793
     1794            INPUT:
     1795
     1796            - ``collection`` -- which roots to display
     1797              can be one of the following:
     1798
     1799              * ``"simple"`` (the default)
     1800              * ``"classical"``
     1801              * ``"all"``
     1802
     1803            - ``**options`` -- Plotting options
     1804
     1805            .. SEEALSO::
     1806
     1807                - :meth:`plot` for a description of the plotting options
     1808                - :ref:`sage.combinat.root_system.plot` for a tutorial
     1809                  on root system plotting
     1810
     1811            EXAMPLES::
     1812
     1813                sage: RootSystem(["B",3]).ambient_space().plot_roots()
     1814                sage: RootSystem(["B",3]).ambient_space().plot_roots("all")
     1815
     1816            TESTS::
     1817
     1818                sage: list(RootSystem(["A",2]).root_lattice().plot_roots())
     1819                [Arrow from (0.0,0.0) to (1.0,0.0),
     1820                 Text '$\alpha_{1}$' at the point (1.05,0.0),
     1821                 Arrow from (0.0,0.0) to (0.0,1.0),
     1822                 Text '$\alpha_{2}$' at the point (0.0,1.05)]
     1823
     1824                sage: list(RootSystem(["A",2]).weight_lattice().plot_roots(labels=False))
     1825                [Arrow from (0.0,0.0) to (2.0,-1.0),
     1826                 Arrow from (0.0,0.0) to (-1.0,2.0)]
     1827
     1828                 sage: list(RootSystem(["A",2]).ambient_lattice().plot_roots())
     1829                 [Arrow from (0.0,0.0) to (1.5,0.86...),
     1830                  Text '$\alpha_{1}$' at the point (1.575,0.90...),
     1831                  Arrow from (0.0,0.0) to (-1.5,0.86...),
     1832                  Text '$\alpha_{2}$' at the point (-1.575,0.90...)]
     1833
     1834                 sage: list(RootSystem(["B",2]).ambient_space().plot_roots())
     1835                 [Arrow from (0.0,0.0) to (1.0,-1.0),
     1836                  Text '$\alpha_{1}$' at the point (1.05,-1.05),
     1837                  Arrow from (0.0,0.0) to (0.0,1.0),
     1838                  Text '$\alpha_{2}$' at the point (0.0,1.05)]
     1839
     1840                sage: list(RootSystem(["A",2]).root_lattice().plot_roots("all"))
     1841                [Arrow from (0.0,0.0) to (1.0,0.0),
     1842                 Text '$\alpha_{1}$' at the point (1.05,0.0),
     1843                 Arrow from (0.0,0.0) to (0.0,1.0),
     1844                 Text '$\alpha_{2}$' at the point (0.0,1.05),
     1845                 Arrow from (0.0,0.0) to (1.0,1.0),
     1846                 Text '$\alpha_{1} + \alpha_{2}$' at the point (1.05,1.05),
     1847                 Arrow from (0.0,0.0) to (-1.0,0.0),
     1848                 Text '$\left(-1\right)\alpha_{1}$' at the point (-1.05,0.0),
     1849                 Arrow from (0.0,0.0) to (0.0,-1.0),
     1850                 Text '$\left(-1\right)\alpha_{2}$' at the point (0.0,-1.05),
     1851                 Arrow from (0.0,0.0) to (-1.0,-1.0),
     1852                 Text '$\left(-1\right)\alpha_{1} + \left(-1\right)\alpha_{2}$' at the point (-1.05,-1.05)]
     1853            """
     1854            plot_options = self.plot_parse_options(**options)
     1855            root_lattice = self.root_system.root_lattice()
     1856            if collection == "simple":
     1857                roots = root_lattice.simple_roots()
     1858            elif collection == "classical":
     1859                if not self.cartan_type().is_affine():
     1860                    raise ValueError("plotting classical roots only available in affine type")
     1861                raise NotImplementedError("classical roots")
     1862            elif collection == "all":
     1863                assert self.cartan_type().is_finite(), "plotting all roots only available in finite type"
     1864                roots = root_lattice.roots()
     1865            elif isinstance(collection, (list, tuple)):
     1866                roots = collection
     1867            else:
     1868                raise ValueError("Unknown value: %s"%collection)
     1869            roots = Family(roots, self)
     1870            return plot_options.family_of_vectors(roots)
     1871
     1872        def plot_coroots(self, collection="simple", **options):
     1873            r"""
     1874            Plot the (simple/classical) coroots of this root lattice.
     1875
     1876            INPUT:
     1877
     1878            - ``collection`` -- which coroots to display.
     1879              Can be one of the following:
     1880
     1881              * ``"simple"`` (the default)
     1882              * ``"classical"``
     1883              * ``"all"``
     1884
     1885            - ``**options`` -- Plotting options
     1886
     1887            .. SEEALSO::
     1888
     1889                - :meth:`plot` for a description of the plotting options
     1890                - :ref:`sage.combinat.root_system.plot` for a tutorial
     1891                  on root system plotting
     1892
     1893            EXAMPLES::
     1894
     1895                sage: RootSystem(["B",3]).ambient_space().plot_coroots()
     1896
     1897            TESTS::
     1898
     1899                 sage: list(RootSystem(["B",2]).ambient_space().plot_coroots())
     1900                 [Arrow from (0.0,0.0) to (1.0,-1.0),
     1901                  Text '$\alpha^\vee_{1}$' at the point (1.05,-1.05),
     1902                  Arrow from (0.0,0.0) to (0.0,2.0),
     1903                  Text '$\alpha^\vee_{2}$' at the point (0.0,2.1)]
     1904            """
     1905            # Functionally speaking, this is duplicated from plot_roots ...
     1906            # Can we avoid that, say by going to the dual space?
     1907            plot_options = self.plot_parse_options(**options)
     1908            coroot_lattice = self.root_system.coroot_lattice()
     1909            if not self.has_coerce_map_from(coroot_lattice):
     1910                raise ValueError("Can't plot the coroots: there is no embedding of the coroot lattice to this space")
     1911            if collection == "simple":
     1912                coroots = coroot_lattice.simple_roots()
     1913            elif collection == "classical":
     1914                if not self.cartan_type().is_affine():
     1915                    raise ValueError("plotting classical coroots only available in affine type")
     1916                raise NotImplementedError("classical coroots")
     1917            elif collection == "all":
     1918                assert self.cartan_type().is_finite(), "plotting all coroots only available in finite type"
     1919                coroots = coroot_lattice.roots()
     1920            elif isinstance(collection, (list, tuple)):
     1921                coroots = collection
     1922            else:
     1923                raise ValueError("Unknown value: %s"%collection)
     1924            coroots = Family(coroots, self)
     1925            return plot_options.family_of_vectors(coroots)
     1926
     1927        def plot_fundamental_weights(self, **options):
     1928            r"""
     1929            Plot the fundamental weights of this root lattice.
     1930
     1931            INPUT:
     1932
     1933            - ``**options`` -- Plotting options
     1934
     1935            .. SEEALSO::
     1936
     1937                - :meth:`plot` for a description of the plotting options
     1938                - :ref:`sage.combinat.root_system.plot` for a tutorial
     1939                  on root system plotting
     1940
     1941            EXAMPLES::
     1942
     1943                sage: RootSystem(["B",3]).ambient_space().plot_fundamental_weights()
     1944
     1945            TESTS::
     1946
     1947                sage: list(RootSystem(["A",2]).weight_lattice().plot_fundamental_weights())
     1948                [Arrow from (0.0,0.0) to (0.0,1.0),
     1949                 Text '$\Lambda_{2}$' at the point (0.0,1.05),
     1950                 Arrow from (0.0,0.0) to (1.0,0.0),
     1951                 Text '$\Lambda_{1}$' at the point (1.05,0.0)]
     1952
     1953                 sage: list(RootSystem(["A",2]).ambient_lattice().plot_fundamental_weights())
     1954                 [Arrow from (0.0,0.0) to (-0.5,0.86...),
     1955                  Text '$\Lambda_{2}$' at the point (-0.525,0.90...),
     1956                  Arrow from (0.0,0.0) to (0.5,0.86...),
     1957                  Text '$\Lambda_{1}$' at the point (0.525,0.90...)]
     1958            """
     1959            plot_options = self.plot_parse_options(**options)
     1960            # We build the family of fundamental weights in this space,
     1961            # indexed by the fundamental weights in the weight lattice.
     1962            #
     1963            # To this end, we don't use the embdding of the weight
     1964            # lattice into self as for the roots or coroots because
     1965            # the ambient space can define the fundamental weights
     1966            # slightly differently (the usual GL_n vs SL_n catch).
     1967            weight_lattice = self.root_system.weight_lattice()
     1968            fundamental_weights = Family(dict(zip(weight_lattice.fundamental_weights(),
     1969                                                  self.fundamental_weights())))
     1970            return plot_options.family_of_vectors(fundamental_weights)
     1971
     1972        def plot_reflection_hyperplanes(self, collection="simple", **options):
     1973            r"""
     1974            Plot the simple reflection hyperplanes.
     1975
     1976            INPUT:
     1977
     1978            - ``collection`` -- which reflection hyperplanes to display.
     1979              Can be one of the following:
     1980
     1981              * ``"simple"`` (the default)
     1982              * ``"classical"``
     1983              * ``"all"``
     1984
     1985            - ``**options`` -- Plotting options
     1986
     1987            .. SEEALSO::
     1988
     1989                - :meth:`plot` for a description of the plotting options
     1990                - :ref:`sage.combinat.root_system.plot` for a tutorial
     1991                  on root system plotting
     1992
     1993            EXAMPLES::
     1994
     1995                sage: RootSystem(["A",2,1]).ambient_space().plot_reflection_hyperplanes()
     1996                sage: RootSystem(["G",2,1]).ambient_space().plot_reflection_hyperplanes()
     1997                sage: RootSystem(["A",3]).weight_space().plot_reflection_hyperplanes()
     1998                sage: RootSystem(["B",3]).ambient_space().plot_reflection_hyperplanes()
     1999                sage: RootSystem(["A",3,1]).weight_space().plot_reflection_hyperplanes()
     2000                sage: RootSystem(["B",3,1]).ambient_space().plot_reflection_hyperplanes()
     2001                sage: RootSystem(["A",2,1]).weight_space().plot_reflection_hyperplanes(affine=False, level=1)
     2002                sage: RootSystem(["A",2]).root_lattice().plot_reflection_hyperplanes()
     2003
     2004            TESTS::
     2005
     2006                sage: from sage.combinat.root_system.plot import plot_expose
     2007                sage: L = RootSystem(["A",2]).ambient_space()
     2008                sage: plot_expose(L.plot_reflection_hyperplanes())
     2009                Line defined by 2 points: [(-1.73..., 3.0), (1.73..., -3.0)]
     2010                Text '$H_{\alpha^\vee_{1}}$' at the point (-1.81...,3.15)
     2011                Line defined by 2 points: [(1.73..., 3.0), (-1.73..., -3.0)]
     2012                Text '$H_{\alpha^\vee_{2}}$' at the point (1.81...,3.15)
     2013
     2014                sage: plot_expose(L.plot_reflection_hyperplanes("all"))
     2015                Line defined by 2 points: [(-1.73..., 3.0), (1.73..., -3.0)]
     2016                Text '$H_{\alpha^\vee_{1}}$' at the point (-1.81...,3.15)
     2017                Line defined by 2 points: [(1.73..., 3.0), (-1.73..., -3.0)]
     2018                Text '$H_{\alpha^\vee_{2}}$' at the point (1.81...,3.15)
     2019                Line defined by 2 points: [(3.0, 0.0), (-3.0, 0.0)]
     2020                Text '$H_{\alpha^\vee_{1} + \alpha^\vee_{2}}$' at the point (3.15,0.0)
     2021
     2022                sage: L = RootSystem(["A",2,1]).ambient_space()
     2023                sage: plot_expose(L.plot_reflection_hyperplanes())
     2024                Line defined by 2 points: [(3.0, 0.86...), (-3.0, 0.86...)]
     2025                Text '$H_{\alpha^\vee_{0}}$' at the point (3.15,0.90...)
     2026                Line defined by 2 points: [(-1.73..., 3.0), (1.73..., -3.0)]
     2027                Text '$H_{\alpha^\vee_{1}}$' at the point (-1.81...,3.15)
     2028                Line defined by 2 points: [(1.73..., 3.0), (-1.73..., -3.0)]
     2029                Text '$H_{\alpha^\vee_{2}}$' at the point (1.81...,3.15)
     2030
     2031            .. TODO:: Provide an option for transparency?
     2032            """
     2033            plot_options = self.plot_parse_options(**options)
     2034
     2035            coroot_lattice = self.root_system.coroot_lattice()
     2036            # Recall that the coroots are given by the roots of the coroot lattice
     2037            if collection == "simple":
     2038                coroots = coroot_lattice.simple_roots()
     2039            elif collection == "classical":
     2040                if not self.cartan_type().is_affine():
     2041                    raise ValueError("plotting classical reflection hyperplanes only available in affine type")
     2042                raise NotImplementedError("classical roots")
     2043            elif collection == "all":
     2044                assert self.cartan_type().is_finite(), "plotting all reflection hyperplanes only available in finite type"
     2045                coroots = coroot_lattice.positive_roots()
     2046            elif isinstance(collection, (list, tuple)):
     2047                coroots = collection
     2048            else:
     2049                raise ValueError("Unknown value: %s"%collection)
     2050
     2051            G = plot_options.empty()
     2052            for coroot in coroots:
     2053                G += plot_options.reflection_hyperplane(coroot)
     2054            return plot_options.finalize(G)
     2055
     2056
     2057        def plot_hedron(self, **options):
     2058            r"""
     2059            Plot the polyhedron whose vertices are given by the orbit
     2060            of `\rho`.
     2061
     2062            In type `A`, this is the usual permutohedron.
     2063
     2064            .. SEEALSO::
     2065
     2066                - :meth:`plot` for a description of the plotting options
     2067                - :ref:`sage.combinat.root_system.plot` for a tutorial
     2068                  on root system plotting
     2069
     2070            EXAMPLES::
     2071
     2072                sage: RootSystem(["A",2]).ambient_space().plot_hedron()
     2073                sage: RootSystem(["A",3]).ambient_space().plot_hedron()
     2074                sage: RootSystem(["B",3]).ambient_space().plot_hedron()
     2075                sage: RootSystem(["C",3]).ambient_space().plot_hedron()
     2076                sage: RootSystem(["D",3]).ambient_space().plot_hedron()
     2077
     2078            Surprise: polyhedrons of large dimension know how to
     2079            project themselves nicely::
     2080
     2081                sage: RootSystem(["F",4]).ambient_space().plot_hedron() # long time
     2082
     2083            TESTS::
     2084
     2085                sage: from sage.combinat.root_system.plot import plot_expose
     2086                sage: L = RootSystem(["B",2]).ambient_space()
     2087                sage: plot_expose(L.plot_hedron())
     2088                Point set defined by 8 point(s): [(-1.5, -0.5), (-1.5, 0.5), (-0.5, -1.5), (-0.5, 1.5), (0.5, -1.5), (0.5, 1.5), (1.5, -0.5), (1.5, 0.5)]
     2089                Line defined by 2 points:        [(-1.5, -0.5), (-1.5, 0.5)]
     2090                Line defined by 2 points:        [(-1.5, -0.5), (-0.5, -1.5)]
     2091                Line defined by 2 points:        [(-1.5, 0.5), (-0.5, 1.5)]
     2092                Line defined by 2 points:        [(-0.5, -1.5), (0.5, -1.5)]
     2093                Line defined by 2 points:        [(-0.5, 1.5), (0.5, 1.5)]
     2094                Line defined by 2 points:        [(0.5, -1.5), (1.5, -0.5)]
     2095                Line defined by 2 points:        [(0.5, 1.5), (1.5, 0.5)]
     2096                Line defined by 2 points:        [(1.5, -0.5), (1.5, 0.5)]
     2097                Polygon defined by 8 points:     [(1.5, 0.5), (0.5, 1.5), (-0.5, 1.5), (-1.5, 0.5), (-1.5, -0.5), (-0.5, -1.5), (0.5, -1.5), (1.5, -0.5)]
     2098            """
     2099            from sage.geometry.polyhedron.all import Polyhedron
     2100            plot_options = self.plot_parse_options(**options)
     2101            assert self.cartan_type().is_finite()
     2102            vertices = [plot_options.projection(vertex)
     2103                        for vertex in self.rho().orbit()]
     2104            return Polyhedron(vertices=vertices).plot()
     2105
     2106        def plot_fundamental_chamber(self, style="normal", **options):
     2107            r"""
     2108            Plot the (classical) fundamental chamber.
     2109
     2110            INPUT:
     2111
     2112            - ``style`` -- ``"normal"`` or ``"classical"`` (default: ``"normal"``)
     2113
     2114            - ``**options`` -- Plotting options
     2115
     2116            .. SEEALSO::
     2117
     2118                - :meth:`plot` for a description of the plotting options
     2119                - :ref:`sage.combinat.root_system.plot` for a tutorial
     2120                  on root system plotting
     2121
     2122            EXAMPLES:
     2123
     2124            2D plots::
     2125
     2126                sage: RootSystem(["B",2]).ambient_space().plot_fundamental_chamber()
     2127                sage: RootSystem(["B",2,1]).ambient_space().plot_fundamental_chamber()
     2128                sage: RootSystem(["B",2,1]).ambient_space().plot_fundamental_chamber("classical")
     2129
     2130            3D plots::
     2131
     2132                sage: RootSystem(["A",3,1]).weight_space() .plot_fundamental_chamber()
     2133                sage: RootSystem(["B",3,1]).ambient_space().plot_fundamental_chamber()
     2134
     2135            This feature is currently not available in the root lattice/space::
     2136
     2137                sage: list(RootSystem(["A",2]).root_lattice().plot_fundamental_chamber())
     2138                Traceback (most recent call last):
     2139                ...
     2140                TypeError: classical fundamental chamber not yet available in the root lattice
     2141
     2142            TESTS::
     2143
     2144                sage: from sage.combinat.root_system.plot import plot_expose
     2145                sage: L = RootSystem(["B",2,1]).ambient_space()
     2146                sage: plot_expose(L.plot_fundamental_chamber())
     2147                Polygon defined by 3 points:     [(0.5, 0.5), (1.0, 0.0), (0.0, 0.0)]
     2148
     2149                sage: plot_expose(L.plot_fundamental_chamber(style="classical"))
     2150                Polygon defined by 3 points:     [(0.0, 0.0), (3.0, 3.0), (3.0, 0.0)]
     2151            """
     2152            plot_options = self.plot_parse_options(**options)
     2153            if not hasattr(self, "fundamental_weights"):
     2154                raise TypeError("classical fundamental chamber not yet available in the root lattice")
     2155            Lambda = self.fundamental_weights()
     2156            cartan_type = self.cartan_type()
     2157            if style=="classical":
     2158                if not cartan_type.is_affine():
     2159                    raise TypeError("classical fundamental chamber only available in affine type")
     2160                I = cartan_type.classical().index_set()
     2161                lines = [Lambda[cartan_type.special_node()]]
     2162            else:
     2163                I = cartan_type.index_set()
     2164                lines = []
     2165            return plot_options.cone(rays = [Lambda[i] for i in I],
     2166                                     lines=lines,
     2167                                     color="lightgrey",
     2168                                     alpha=.3)
     2169
     2170        def plot_alcoves(self, alcoves=True, alcove_labels=False, wireframe=False, **options):
     2171            r"""
     2172            Plot the alcoves and optionaly their labels.
     2173
     2174            INPUT:
     2175
     2176            - ``alcoves`` -- a list of alcoves or ``True`` (default: ``True``)
     2177
     2178            - ``alcove_labels`` -- a boolean or a number specifying at
     2179              which level to put the label (default: ``False``)
     2180
     2181            - ``**options`` -- Plotting options
     2182
     2183            .. SEEALSO::
     2184
     2185                - :meth:`plot` for a description of the plotting options
     2186                - :ref:`sage.combinat.root_system.plot` for a
     2187                  tutorial on root system plotting, and in particular
     2188                  how the alcoves can be specified.
     2189
     2190            EXAMPLES:
     2191
     2192            2D plots::
     2193
     2194                sage: RootSystem(["B",2,1]).ambient_space().plot_alcoves()                      # long time (3s)
     2195
     2196            3D plots::
     2197
     2198                sage: RootSystem(["A",2,1]).weight_space() .plot_alcoves(affine=False)          # long time (3s)
     2199                sage: RootSystem(["G",2,1]).ambient_space().plot_alcoves(affine=False, level=1) # long time (3s)
     2200
     2201            Here we plot a single alcove::
     2202
     2203                sage: L = RootSystem(["A",3,1]).ambient_space()
     2204                sage: W = L.weyl_group()
     2205                sage: L.plot(alcoves=[W.one()], reflection_hyperplanes=False, bounding_box=2)
     2206
     2207            TESTS::
     2208
     2209                sage: from sage.combinat.root_system.plot import plot_expose
     2210                sage: L = RootSystem(["A",2,1]).weight_space()
     2211                sage: plot_expose(L.plot_alcoves(alcoves=[[0,0]]))
     2212                Line defined by 2 points: [(0.0, 1.0), (1.0, 0.0)]
     2213                Line defined by 2 points: [(0.0, 1.0), (0.0, 0.0)]
     2214                Line defined by 2 points: [(1.0, 0.0), (0.0, 0.0)]
     2215                Line defined by 2 points: [(0.0, 1.0), (-1.0, 1.0)]
     2216                Line defined by 2 points: [(-1.0, 1.0), (0.0, 0.0)]
     2217                Line defined by 2 points: [(-1.0, 1.0), (-1.0, 0.0)]
     2218                Line defined by 2 points: [(0.0, 0.0), (-1.0, 0.0)]
     2219                Line defined by 2 points: [(-1.0, 0.0), (0.0, -1.0)]
     2220                Line defined by 2 points: [(1.0, 0.0), (1.0, -1.0)]
     2221                Line defined by 2 points: [(0.0, 0.0), (1.0, -1.0)]
     2222                Line defined by 2 points: [(1.0, -1.0), (0.0, -1.0)]
     2223                Line defined by 2 points: [(0.0, 0.0), (0.0, -1.0)]
     2224            """
     2225            plot_options = self.plot_parse_options(**options)
     2226            if not hasattr(self, "fundamental_weights"):
     2227                raise TypeError("alcoves not yet available in the root lattice")
     2228            Lambda = self.fundamental_weights()
     2229            cartan_type = self.cartan_type()
     2230            I = cartan_type.index_set()
     2231            W = self.weyl_group()
     2232            if alcove_labels is not False:
     2233                rho = self.rho()
     2234                if alcove_labels is not True:
     2235                    # The input is the desired level
     2236                    rho = rho * alcove_labels / rho.level()
     2237                else:
     2238                    rho = plot_options.intersection_at_level_1(rho)
     2239            # The rays of the fundamental alcove
     2240            fundamental_alcove_rays = Lambda.map(plot_options.intersection_at_level_1)
     2241
     2242            def alcove_in_bounding_box(w):
     2243                return any(plot_options.in_bounding_box(w.action(fundamental_alcove_rays[i]))
     2244                           for i in I)
     2245            def alcove_facet(w, i):
     2246                # Alcove facets with degenerate intersection with the
     2247                # bounding box bring no information; we might as well
     2248                # not draw them. Besides this avoids ugly fat points
     2249                # in dimension 2.
     2250                return plot_options.cone(rays=[w.action(fundamental_alcove_rays[j]) for j in I if j != i],
     2251                                         color = plot_options.color(i),
     2252                                         wireframe=wireframe,
     2253                                         draw_degenerate=False)
     2254            def alcove_label(w):
     2255                label = "$1$" if w.is_one() else "$s_{"+"".join(str(j) for j in w.reduced_word())+"}$"
     2256                position = plot_options.projection(w.action(rho))
     2257                if position in plot_options.bounding_box:
     2258                    return plot_options.text(label, position)
     2259                else:
     2260                    return plot_options.empty()
     2261
     2262            G = plot_options.empty()
     2263            if alcoves is not True:
     2264                alcoves = list(alcoves)
     2265            if alcoves is True or (len(alcoves)>0 and W.is_parent_of(alcoves[0])):
     2266                if alcoves is True:
     2267                    alcoves = W.weak_order_ideal(alcove_in_bounding_box, side="right")
     2268                # We assume that the fundamental alcove lies within
     2269                # the bounding box, and explore the alcoves
     2270                # intersecting the bounding box by going up right
     2271                # order (i.e. going away from the fundamental alcove)
     2272                for w in alcoves:
     2273                    for i in w.descents(side="right", positive=True):
     2274                        G += alcove_facet(w, i)
     2275                    if alcove_labels is not False:
     2276                        G += alcove_label(w)
     2277            else:
     2278                if not cartan_type.is_affine():
     2279                    raise TypeError("alcoves=list only available in affine type")
     2280                translation_factors = cartan_type.translation_factors()
     2281                simple_roots = self.simple_roots()
     2282                translation_vectors = Family({i: translation_factors[i]*simple_roots[i]
     2283                                          for i in cartan_type.classical().index_set()})
     2284                # The elements of the classical weyl group, as elements of W
     2285                W0 = [W.from_reduced_word(w.reduced_word()) for w in self.weyl_group().classical()]
     2286                for alcove in alcoves:
     2287                    # The translation mapping the center of the
     2288                    # fundamental polygon to polygon indexed by alcove
     2289                    shift = sum(x*v for x,v in zip(alcove, translation_vectors))
     2290                    shift = W.from_morphism(shift.translation)
     2291                    for w in W0:
     2292                        for i in w.descents(side="right", positive=True):
     2293                            G += alcove_facet(shift * w, i)
     2294                        if alcove_labels:
     2295                            G += alcove_label(w)
     2296            return plot_options.finalize(G)
     2297
     2298            # In this alternative commented-out implementation, the
     2299            # alcove picture is constructed directly in the
     2300            # projection. It only works for rank 2+1 with, but it is
     2301            # faster; we keep for reference for now. With #12553
     2302            # (Cythoned PPL polytopes), the difference is likely to
     2303            # disappear. If this is confirmed, the code below should be discarded.
     2304            #
     2305            # from sage.plot.line import line
     2306            # translation_vectors = Family({i: translation_factors[i]*plot_options.projection(simple_roots[i])
     2307            #                               for i in cartan_type.classical().index_set()})
     2308            #
     2309            # # For each polygon P to be drawn, alcoves_shift contains the translation
     2310            # # from fundamental polygon to P in the plot coordinate system
     2311            # def immutable_vector(x):
     2312            #     # Takes care of possible numerical instabilities
     2313            #     x = x.numerical_approx(8)
     2314            #     x.set_immutable()
     2315            #     return x
     2316            #
     2317            # # Construct the fundamental polygon
     2318            # # The classical group acting on ``self``
     2319            # W0 = self.weyl_group().classical().list()
     2320            # # The coordinates of the vertices of the fundamental alcove
     2321            # fundamental_alcove_rays = Lambda.map(plot_options.intersection_at_level_1)
     2322            # # The coordinates of the vertices of the fundamental polygon
     2323            # fundamental_polygon_rays = {
     2324            #     (i, w): plot_options.projection(w.action(fundamental_alcove_rays[i]))
     2325            #     for w in W0
     2326            #     for i in I
     2327            #     }
     2328            #
     2329            # # Get the center of the polygons
     2330            # if alcoves is True:
     2331            #     def neighbors(x):
     2332            #         return filter(lambda y: plot_options.bounding_box.contains(plot_options.origin_projected+y),
     2333            #                       [immutable_vector(x+epsilon*t) for t in translation_vectors for epsilon in [-1,1]])
     2334            #     alcoves_shift = list(TransitiveIdeal(neighbors, [immutable_vector(plot_options.origin_projected)]))
     2335            # else:
     2336            #     alcoves_shift = [sum(x*v for x,v in zip(alcove, translation_vectors))
     2337            #                      for alcove in alcoves]
     2338            #
     2339            # G = plot_options.empty()
     2340            # for shift in alcoves_shift:
     2341            #     # for each center of polygon and each element of classical
     2342            #     # parabolic subgroup, we have to draw an alcove.
     2343            #     polygon_center = plot_options.origin_projected + shift
     2344            #
     2345            #     for w in W0:
     2346            #         for i in I:
     2347            #             facet_indices = [j for j in I if j != i]
     2348            #             assert len(facet_indices) == 2
     2349            #             facet = [fundamental_polygon_rays[j, w] + shift for j in facet_indices]
     2350            #             # This takes a bit of time; do we really want that feature?
     2351            #             #if not all(bounding_box_as_polytope.contains(v) for v in facet):
     2352            #             #    continue
     2353            #             G += line(facet,
     2354            #                       rgbcolor = plot_options.color(i),
     2355            #                       thickness = 2 if i == special_node else 1)
     2356
     2357
     2358        def plot_bounding_box(self, **options):
     2359            r"""
     2360            Plot the bounding box.
     2361
     2362            INPUT:
     2363
     2364            - ``**options`` -- Plotting options
     2365
     2366            This is mostly for testing purposes.
     2367
     2368            .. SEEALSO::
     2369
     2370                - :meth:`plot` for a description of the plotting options
     2371                - :ref:`sage.combinat.root_system.plot` for a tutorial
     2372                  on root system plotting
     2373
     2374            EXAMPLES::
     2375
     2376                sage: L = RootSystem(["A",2,1]).ambient_space()
     2377                sage: L.plot_bounding_box()
     2378
     2379            TESTS::
     2380
     2381                sage: list(L.plot_bounding_box())
     2382                [Polygon defined by 4 points]
     2383            """
     2384            plot_options = self.plot_parse_options(**options)
     2385            return plot_options.bounding_box.plot(color="gray", alpha=0.5, wireframe=False)
     2386
     2387        def plot_alcove_walk(self, word, start=None, foldings=None, color ="orange", **options):
     2388            r"""
     2389            Plot an alcove walk.
     2390
     2391            INPUT:
     2392
     2393            - ``word`` -- a list of elements of the index set
     2394            - ``foldings`` -- a list of booleans or ``None`` (default: ``None``)
     2395            - ``start`` -- an element of this space (default: ``None`` for `\rho`)
     2396            - ``**options`` -- plotting options
     2397
     2398            .. SEEALSO::
     2399
     2400                - :meth:`plot` for a description of the plotting options
     2401                - :ref:`sage.combinat.root_system.plot` for a tutorial
     2402                  on root system plotting
     2403
     2404            EXAMPLES:
     2405
     2406            An alcove walk of type `A_2^{(1)}`::
     2407
     2408                sage: L = RootSystem(["A",2,1]).ambient_space()
     2409                sage: w1 = [0,2,1,2,0,2,1,0,2,1,2,1,2,0,2,0,1,2,0]
     2410                sage: p = L.plot_alcoves(bounding_box=5)           # long time (5s)
     2411                sage: p += L.plot_alcove_walk(w1)                  # long time
     2412                sage: p                                            # long time
     2413
     2414            The same plot with another alcove walk::
     2415
     2416                sage: w2 = [2,1,2,0,2,0,2,1,2,0,1,2,1,2,1,0,1,2,0,2,0,1,2,0,2]
     2417                sage: p += L.plot_alcove_walk(w2, color="orange")  # long time
     2418
     2419            And another with some foldings::
     2420
     2421                sage: L.plot_alcoves(bounding_box=3) + \
     2422                ...   L.plot_alcove_walk([0,1,2,0,2,0,1,2,0,1],
     2423                ...                      foldings = [False, False, True, False, False, False, True, False, True, False],
     2424                ...                      color="green")            # long time (3s)
     2425
     2426            TESTS::
     2427
     2428                sage: from sage.combinat.root_system.plot import plot_expose
     2429                sage: L = RootSystem(["A",2,1]).weight_space()
     2430                sage: p = L.plot_alcove_walk([0,1,2,0,2,0,1,2,0,1],
     2431                ...                          foldings = [False, False, True, False, False, False, True, False, True, False],
     2432                ...                          color="green",
     2433                ...                          start=L.rho())
     2434                sage: plot_expose(p)
     2435                Arrow from (1.0,1.0) to (2.0,2.0)
     2436                Arrow from (2.0,2.0) to (1.0,4.0)
     2437                Line defined by 2 points: [(1.0, 4.0), (1.5, 4.5)]
     2438                Arrow from (1.5,4.5) to (1.0,4.0)
     2439                Arrow from (1.0,4.0) to (-1.0,5.0)
     2440                Arrow from (-1.0,5.0) to (-2.0,7.0)
     2441                Arrow from (-2.0,7.0) to (-1.0,8.0)
     2442                Line defined by 2 points: [(-1.0, 8.0), (-1.5, 9.0)]
     2443                Arrow from (-1.5,9.0) to (-1.0,8.0)
     2444                Arrow from (-1.0,8.0) to (1.0,7.0)
     2445                Line defined by 2 points: [(1.0, 7.0), (1.5, 6.0)]
     2446                Arrow from (1.5,6.0) to (1.0,7.0)
     2447                Arrow from (1.0,7.0) to (2.0,8.0)
     2448            """
     2449            from sage.plot.line import line
     2450            from sage.plot.arrow import arrow
     2451            plot_options = self.plot_parse_options(**options)
     2452            W = self.weyl_group()
     2453            s = W.simple_reflections()
     2454            if start is None:
     2455                start = plot_options.intersection_at_level_1(self.rho())
     2456            if foldings is None:
     2457                foldings = [False] * len(word)
     2458            w = W.one()
     2459            source  = plot_options.projection(start)
     2460            G = plot_options.empty()
     2461            for (i, folding) in zip(word, foldings):
     2462                w = w * s[i]
     2463                target = plot_options.projection(w.action(start))
     2464                if folding:
     2465                    middle = (source+target)/2
     2466                    G += line ([source, middle], rgbcolor=color)
     2467                    G += arrow(middle, source, rgbcolor=color)
     2468                    # reset w
     2469                    w = w * s[i]
     2470                else:
     2471                    G += arrow(source, target, rgbcolor=color)
     2472                    source=target
     2473            return G
     2474
     2475    ##########################################################################
     2476
    14852477    class ElementMethods:
    14862478
    14872479        @abstract_method
    class RootLatticeRealizations(Category_o 
    22523244                if i not in index_set:
    22533245                    return False
    22543246            return True
    2255 
  • sage/combinat/root_system/root_system.py

    diff --git a/sage/combinat/root_system/root_system.py b/sage/combinat/root_system/root_system.py
    a b Quickref 
    1414Documentation
    1515-------------
    1616
    17 - :mod:`sage.combinat.root_system.root_system`    -- This current overview
     17- :ref:`sage.combinat.root_system.root_system`    -- This current overview
    1818- :class:`CartanType`                             -- An introduction to Cartan types
    1919- :class:`RootSystem`                             -- An introduction to root systems
     20- :ref:`sage.combinat.root_system.plot`           -- A root system visualization tutorial
    2021- The ``Lie Methods and Related Combinatorics`` thematic tutorial
    2122
    2223See also
    2324--------
    2425
    2526- :class:`CoxeterGroups`, :class:`WeylGroups`, ...-- The categories of Coxeter and Weyl groups
    26 - :mod:`sage.combinat.crystals.crystals`          -- An introduction to crystals
     27- :ref:`sage.combinat.crystals.crystals`          -- An introduction to crystals
    2728- :mod:`.type_A`, :mod:`.type_B_affine`, ...      -- Type specific root system data
    2829
    2930"""
    class RootSystem(UniqueRepresentation, S 
    209210        sage: L = RootSystem(["A",2,1]).ambient_space(); L
    210211        Ambient space of the Root system of type ['A', 2, 1]
    211212
    212     Define the "identity" by an appropriate vector at level -3::
     213    Define the "identity" by an appropriate vector at level `-3`::
    213214
    214215        sage: e = L.basis(); Lambda = L.fundamental_weights()
    215216        sage: id = e[0] + 2*e[1] + 3*e[2]  - 3*Lambda[0]
    class RootSystem(UniqueRepresentation, S 
    236237        sage: [L.classical()(s[0].action(x)) for x in S3]
    237238        [(0, 2, 4), (0, 1, 5), (-1, 1, 6), (-2, 2, 6), (-1, 3, 4), (-2, 3, 5)]
    238239
     240    We can also plot various components of the ambient spaces::
     241
     242        sage: L = RootSystem(['A',2]).ambient_space()
     243        sage: L.plot()
     244
     245    For more on plotting, see :ref:`sage.combinat.root_system.plot`.
     246
    239247    .. RUBRIC:: Dual root systems
    240248
    241249    The root system is aware of its dual root system::
    class RootSystem(UniqueRepresentation, S 
    243251        sage: R.dual
    244252        Dual of root system of type ['B', 3]
    245253   
    246     R.dual is really the root system of type `C_3`::
     254    ``R.dual`` is really the root system of type `C_3`::
    247255   
    248256        sage: R.dual.cartan_type()
    249257        ['C', 3]
    class RootSystem(UniqueRepresentation, S 
    764772            return None
    765773        return AmbientSpace(self, base_ring)
    766774
     775    def coambient_space(self, base_ring=QQ):
     776        r"""
     777        Return the coambient space for this root system.
     778
     779        This is the ambient space of the dual root system.
     780
     781        .. SEEALSO::
     782
     783            - :meth:`ambient_space`
     784
     785        EXAMPLES::
     786
     787            sage: L = RootSystem(["B",2]).ambient_space(); L
     788            Ambient space of the Root system of type ['B', 2]
     789            sage: coL = RootSystem(["B",2]).coambient_space(); coL
     790            Coambient space of the Root system of type ['B', 2]
     791
     792        The roots and coroots are interchanged::
     793
     794            sage: coL.simple_roots()
     795            Finite family {1: (1, -1), 2: (0, 2)}
     796            sage: L.simple_coroots()
     797            Finite family {1: (1, -1), 2: (0, 2)}
     798
     799            sage: coL.simple_coroots()
     800            Finite family {1: (1, -1), 2: (0, 1)}
     801            sage: L.simple_roots()
     802            Finite family {1: (1, -1), 2: (0, 1)}
     803        """
     804        return self.dual.ambient_space(base_ring)
     805
    767806
    768807def WeylDim(ct, coeffs):
    769808    """
  • sage/combinat/root_system/type_A.py

    diff --git a/sage/combinat/root_system/type_A.py b/sage/combinat/root_system/type_A.py
    a b Root system data for type A 
    1010#                  http://www.gnu.org/licenses/
    1111#*****************************************************************************
    1212from sage.rings.all import ZZ
     13from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations
    1314import ambient_space
    1415
    1516class AmbientSpace(ambient_space.AmbientSpace):
    class AmbientSpace(ambient_space.Ambient 
    135136        given, returns (k, ... ,k), the k-th power of the
    136137        determinant.
    137138
    138         EXAMPLES:
     139        EXAMPLES::
     140
    139141            sage: e = RootSystem(['A',3]).ambient_space()
    140142            sage: e.det(1/2)
    141143            (1/2, 1/2, 1/2, 1/2)
    142144        """
    143145        return self.sum(self.monomial(j)*k for j in range(self.n))
    144146
     147    __doc__ += """
     148    By default, this ambient space uses the barycentric projection for plotting::
     149
     150        sage: L = RootSystem(["A",2]).ambient_space()
     151        sage: e = L.basis()
     152        sage: L._plot_projection(e[0])
     153        (1/2, 989/1142)
     154        sage: L._plot_projection(e[1])
     155        (-1, 0)
     156        sage: L._plot_projection(e[2])
     157        (1/2, -989/1142)
     158        sage: L = RootSystem(["A",3]).ambient_space()
     159        sage: l = L.an_element(); l
     160        (2, 2, 3, 0)
     161        sage: L._plot_projection(l)
     162        (0, -1121/1189, 7/3)
     163
     164    .. SEEALSO::
     165
     166        - :meth:`sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods._plot_projection`
     167    """
     168    _plot_projection = RootLatticeRealizations.ParentMethods.__dict__['_plot_projection_barycentric']
    145169
    146170from cartan_type import CartanType_standard_finite, CartanType_simply_laced, CartanType_simple
    147171class CartanType(CartanType_standard_finite, CartanType_simply_laced, CartanType_simple):
  • sage/combinat/root_system/type_G.py

    diff --git a/sage/combinat/root_system/type_G.py b/sage/combinat/root_system/type_G.py
    a b Root system data for type G 
    1111#*****************************************************************************
    1212import ambient_space
    1313from sage.sets.family import Family
    14 
     14from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations
    1515class AmbientSpace(ambient_space.AmbientSpace):
    1616    """
    1717    EXAMPLES::
    class AmbientSpace(ambient_space.Ambient 
    8282        return Family({ 1: self([1,0,-1]),
    8383                        2: self([2,-1,-1])})
    8484
     85    __doc__ += """
     86    By default, this ambient space uses the barycentric projection for plotting::
     87
     88        sage: L = RootSystem(["G",2]).ambient_space()
     89        sage: e = L.basis()
     90        sage: L._plot_projection(e[0])
     91        (1/2, 989/1142)
     92        sage: L._plot_projection(e[1])
     93        (-1, 0)
     94        sage: L._plot_projection(e[2])
     95        (1/2, -989/1142)
     96        sage: L = RootSystem(["A",3]).ambient_space()
     97        sage: l = L.an_element(); l
     98        (2, 2, 3, 0)
     99        sage: L._plot_projection(l)
     100        (0, -1121/1189, 7/3)
     101
     102    .. SEEALSO::
     103
     104        - :meth:`sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods._plot_projection`
     105    """
     106    _plot_projection = RootLatticeRealizations.ParentMethods.__dict__['_plot_projection_barycentric']
     107
     108
    85109from cartan_type import CartanType_standard_finite, CartanType_simple, CartanType_crystalographic
    86110class CartanType(CartanType_standard_finite, CartanType_simple, CartanType_crystalographic):
    87111    def __init__(self):
  • sage/combinat/root_system/type_affine.py

    diff --git a/sage/combinat/root_system/type_affine.py b/sage/combinat/root_system/type_affine.py
    a b class AmbientSpace(CombinatorialFreeModu 
    366366        """
    367367        return self.simple_root(i).associated_coroot()
    368368
     369    def coroot_lattice(self):
     370        """
     371        EXAMPLES::
     372
     373            sage: RootSystem(["A",3,1]).ambient_lattice().coroot_lattice()
     374            Ambient lattice of the Root system of type ['A', 3, 1]
     375
     376        .. TODO:: Factor out this code with the classical ambient space.
     377        """
     378        return self
     379
     380    def _plot_projection(self, x):
     381        """
     382        Implements the default projection to be used for plots
     383
     384        For affine ambient spaces, the default implementation is to
     385        project onto the classical coordinates according to the
     386        default projection for the classical ambient space, while
     387        keeping an extra coordinate for the coefficient of
     388        `\delta^\vee` to keep the level information.
     389
     390        .. SEEALSO:: :meth:`sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations._plot_projection`
     391
     392        EXAMPLES::
     393
     394            sage: L = RootSystem(["B",2,1]).ambient_space()
     395            sage: e = L.basis()
     396            sage: L._plot_projection(e[0])
     397            (1, 0, 0)
     398            sage: L._plot_projection(e[1])
     399            (0, 1, 0)
     400            sage: L._plot_projection(e["delta"])
     401            (0, 0, 0)
     402            sage: L._plot_projection(e["deltacheck"])
     403            (0, 0, 1)
     404
     405            sage: L = RootSystem(["A",2,1]).ambient_space()
     406            sage: e = L.basis()
     407            sage: L._plot_projection(e[0])
     408            (1/2, 989/1142, 0)
     409            sage: L._plot_projection(e[1])
     410            (-1, 0, 0)
     411            sage: L._plot_projection(e["delta"])
     412            (0, 0, 0)
     413            sage: L._plot_projection(e["deltacheck"])
     414            (0, 0, 1)
     415        """
     416        from sage.modules.free_module_element import vector
     417        classical = self.classical()
     418        # Any better way to concatenate two vectors?
     419        return vector(list(vector(classical._plot_projection(classical(x)))) +
     420                      [x["deltacheck"]])
     421
    369422    class Element(CombinatorialFreeModule.Element):
    370423
    371424        def inner_product(self, other):
  • sage/combinat/root_system/type_dual.py

    diff --git a/sage/combinat/root_system/type_dual.py b/sage/combinat/root_system/type_dual.py
    a b Root system data for dual Cartan types 
    1010#*****************************************************************************
    1111from sage.misc.misc import attrcall
    1212from sage.misc.cachefunc import cached_method
     13from sage.misc.lazy_attribute import lazy_attribute
    1314from sage.structure.unique_representation import UniqueRepresentation
    1415from sage.structure.sage_object import SageObject
    1516from sage.combinat.root_system.cartan_type import CartanType_crystalographic, CartanType_finite, CartanType_affine, CartanType_simple
     17from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations
    1618import sage
    1719import ambient_space
    1820
    class AmbientSpace(ambient_space.Ambient 
    303305        sage: TestSuite(L).run()
    304306    """
    305307
     308    @lazy_attribute
     309    def _dual_space(self):
     310        """
     311        The dual of this ambient space.
     312
     313        EXAMPLES::
     314
     315            sage: L = CartanType(["F",4]).dual().root_system().ambient_space(); L
     316            Ambient space of the Root system of type ['F', 4]^*
     317            sage: L._dual_space
     318            Ambient space of the Root system of type ['F', 4]
     319
     320        The basic data for this space will be fetched from the dual
     321        space::
     322
     323            sage: L._dual_space.simple_root(1)
     324            (0, 1, -1, 0)
     325            sage: L.simple_root(1)
     326            (0, 1, -1, 0)
     327        """
     328        K = self.base_ring()
     329        return self.cartan_type().dual().root_system().ambient_space(K)
     330        #return self.root_system.dual.ambient_space()
     331
    306332    def dimension(self):
    307333        """
    308334        Return the dimension of this ambient space.
    class AmbientSpace(ambient_space.Ambient 
    315341            sage: F4.dual().root_system().ambient_space().simple_root(1)
    316342            (0, 1, -1, 0)
    317343        """
     344        # Can't yet use _dual_space for the base ring is not yet initialized
    318345        return self.root_system.dual.ambient_space().dimension()
    319346
    320347    @cached_method
    class AmbientSpace(ambient_space.Ambient 
    337364            sage: F4.root_system().ambient_space().simple_coroots()
    338365            Finite family {1: (0, 1, -1, 0), 2: (0, 0, 1, -1), 3: (0, 0, 0, 2), 4: (1, -1, -1, -1)}
    339366        """
    340         K = self.base_ring()
    341         dual_coroot = self.cartan_type().dual().root_system().ambient_space(K).simple_coroot(i)
     367        dual_coroot = self._dual_space.simple_coroot(i)
    342368        return self.sum_of_terms(dual_coroot)
    343369
    344370    @cached_method
    class AmbientSpace(ambient_space.Ambient 
    364390        """
    365391        return self.fundamental_weights_from_simple_roots()
    366392
     393    @lazy_attribute
     394    def _plot_projection(self):
     395        """
     396        A hack so that if an ambient space uses barycentric projection, then so does its dual
     397
     398        EXAMPLES::
     399
     400            sage: L = CartanType(["G",2]).dual().root_system().ambient_space()
     401            sage: L._plot_projection == L._plot_projection_barycentric
     402            True
     403
     404            sage: L = RootSystem(["G",2]).coambient_space()
     405            sage: L._plot_projection == L._plot_projection_barycentric
     406            True
     407        """
     408        dual_space = self.cartan_type().dual().root_system().ambient_space(self.base_ring())
     409        if dual_space._plot_projection == dual_space._plot_projection_barycentric:
     410            return self._plot_projection_barycentric
     411        else:
     412            RootLatticeRealizations.ParentMethods.__dict__["_plot_projection"]
     413
     414
    367415class CartanType_finite(CartanType, sage.combinat.root_system.cartan_type.CartanType_finite):
    368416    AmbientSpace = AmbientSpace
    369417
  • sage/combinat/root_system/weight_lattice_realizations.py

    diff --git a/sage/combinat/root_system/weight_lattice_realizations.py b/sage/combinat/root_system/weight_lattice_realizations.py
    a b class WeightLatticeRealizations(Category 
    799799            d = prod([ rho.dot_product(x) for x in self.positive_roots()])
    800800            from sage.rings.integer import Integer
    801801            return Integer(n/d)
    802 
    803         def plot(self, size=[[0],[0]], projection='usual', simple_roots=True, fundamental_weights=True, alcovewalks=[]):
    804             r"""
    805             Return a graphics object built from a space of weight(space/lattice).
    806             There is a different technic to plot if the Cartan type is affine or not.
    807             The graphics returned is a Graphics object.
    808 
    809             This function is experimental, and is subject to short term evolutions.
    810 
    811             EXAMPLES::
    812 
    813               By default, the plot returned has no axes and the ratio between axes is 1.
    814                 sage: G = RootSystem(['C',2]).weight_lattice().plot()
    815                 sage: G.axes(True)
    816                 sage: G.set_aspect_ratio(2)
    817 
    818               For a non affine Cartan type, the plot method work for type with 2 generators,
    819               it will draw the hyperlane(line for this dimension) accrow the fundamentals weights.
    820                 sage: G = RootSystem(['A',2]).weight_lattice().plot()
    821                 sage: G = RootSystem(['B',2]).weight_lattice().plot()
    822                 sage: G = RootSystem(['G',2]).weight_lattice().plot()
    823 
    824               The plot returned has a size of one fundamental polygon by default. We can
    825               ask plot to give a bigger plot by using the argument size
    826                 sage: G = RootSystem(['G',2,1]).weight_space().plot(size = [[0..1],[-1..1]])
    827                 sage: G = RootSystem(['A',2,1]).weight_space().plot(size = [[-1..1],[-1..1]])
    828 
    829               A very important argument is the projection which will draw the plot. There are
    830               some usual projections is this method. If you want to draw in the plane a very
    831               special Cartan type, Sage will ask you to specify the projection. The projection
    832               is a matrix over a ring. In practice, calcul over float is a good way to draw.
    833                 sage: L = RootSystem(['A',2,1]).weight_space()
    834                 sage: G = L.plot(projection=matrix(RR, [[0,0.5,-0.5],[0,0.866,0.866]]))
    835                 sage: G = RootSystem(['C',2,1]).weight_space().plot()
    836 
    837               By default, the plot method draw the simple roots, this can be disabled by setting
    838               the argument simple_roots=False
    839                 sage: G = RootSystem(['A',2]).weight_space().plot(simple_roots=False)
    840 
    841               By default, the plot method draw the fundamental weights,this can be disabled by
    842               setting the argument fundamental_weights=False
    843                 sage: G = RootSystem(['A',2]).weight_space().plot(fundamental_weights=False, simple_roots=False)
    844 
    845               There is in a plot an argument to draw alcoves walks. The good way to do this is
    846               to use the crystals theory. the plot method contains only the drawing part...
    847                 sage: L = RootSystem(['A',2,1]).weight_space()
    848                 sage: G = L.plot(size=[[-1..1],[-1..1]],alcovewalks=[[0,2,0,1,2,1,2,0,2,1]])
    849             """
    850 
    851             from sage.plot.all import Graphics
    852             from sage.plot.line import line
    853             from cartan_type import CartanType
    854             from sage.matrix.constructor import matrix
    855             from sage.rings.all import QQ, RR
    856             from sage.plot.arrow import arrow
    857             from sage.plot.point import point
    858 
    859             # We begin with an empty plot G
    860             G = Graphics()
    861 
    862             ct = self.cartan_type()
    863             n = ct.n
    864 
    865             # Define a set of colors
    866             # TODO : Colors in option ?
    867             colors=[(0,1,0),(1,0,0),(0,0,1),(1,1,0),(0,1,1),(1,0,1)]
    868 
    869             # plot the affine types:
    870             if ct.is_affine():
    871 
    872                 # Check the projection
    873                 # TODO : try to have usual_projection for main plotable types
    874                 if projection == 'usual':
    875                     if ct == CartanType(['A',2,1]):
    876                         projection = matrix(RR, [[0,0.5,-0.5],[0,0.866,0.866]])
    877                     elif ct == CartanType(['C',2,1]):
    878                         projection = matrix(QQ, [[0,1,1],[0,0,1]])
    879                     elif ct == CartanType(['G',2,1]):
    880                         projection = matrix(RR, [[0,0.5,0],[0,0.866,1.732]])
    881                     else:
    882                         raise 'There is no usual projection for this Cartan type, you have to give one in argument'
    883 
    884                 assert(n + 1 == projection.ncols())
    885                 assert(2 == projection.nrows())
    886 
    887                 # Check the size is correct with the lattice
    888                 assert(len(size) == n)
    889 
    890                 # Select the center of the translated fundamental polygon to plot
    891                 translation_factors = ct.translation_factors()
    892                 simple_roots = self.simple_roots()
    893                 translation_vectors = [translation_factors[i]*simple_roots[i] for i in ct.classical().index_set()]
    894 
    895                 initial = [[]]
    896                 for i in range(n):
    897                     prod_list = []
    898                     for elem in size[i]:
    899                         for partial_list in initial:
    900                             prod_list.append( [elem]+partial_list );
    901                     initial = prod_list;
    902 
    903                 part_lattice = []
    904                 for combinaison in prod_list:
    905                     elem_lattice = self.zero()
    906                     for i in range(n):
    907                         elem_lattice = elem_lattice + combinaison[i]*translation_vectors[i]
    908                     part_lattice.append(elem_lattice)
    909 
    910                 # Get the vertices of the fundamental alcove
    911                 fundamental_weights = self.fundamental_weights()
    912                 vertices = map(lambda x: (1/x.level())*x, fundamental_weights.list())
    913 
    914                 # Recup the group which act on the fundamental polygon
    915                 classical = self.weyl_group().classical()
    916 
    917                 for center in part_lattice:
    918                     for w in classical:
    919                         # for each center of polygon and each element of classical
    920                         # parabolic subgroup, we have to draw an alcove.
    921 
    922                         #first, iterate over pairs of fundamental weights, drawing lines border of polygons:
    923                         for i in range(1,n+1):
    924                             for j in range(i+1,n+1):
    925                                 p1=projection*((w.action(vertices[i])).to_vector() + center.to_vector())
    926                                 p2=projection*((w.action(vertices[j])).to_vector() + center.to_vector())
    927                                 G+=line([p1,p2],rgbcolor=(0,0,0),thickness=2)
    928 
    929                         #next, get all lines from point to a fundamental weight, that separe different
    930                         #chanber in a same polygon (important: associate a color with a fundamental weight)
    931                         pcenter = projection*(center.to_vector())
    932                         for i in range(1,n+1):
    933                             p3=projection*((w.action(vertices[i])).to_vector() + center.to_vector())
    934                             G+=line([p3,pcenter], rgbcolor=colors[n-i+1])
    935 
    936                 #Draw alcovewalks
    937                 #FIXME : The good way to draw this is to use the alcoves walks works made in Cristals
    938                 #The code here just draw like example and import the good things.
    939                 rho = (1/self.rho().level())*self.rho()
    940                 W = self.weyl_group()
    941                 for walk in alcovewalks:
    942                     target = W.from_reduced_word(walk).action(rho)
    943                     for i in range(len(walk)):
    944                         walk.pop()
    945                         origin = W.from_reduced_word(walk).action(rho)
    946                         G+=arrow(projection*(origin.to_vector()),projection*(target.to_vector()), rgbcolor=(0.6,0,0.6), width=1, arrowsize=5)
    947                         target = origin
    948 
    949             else:
    950                 # non affine plot
    951 
    952                 # Check the projection
    953                 # TODO : try to have usual_projection for main plotable types
    954                 if projection == 'usual':
    955                     if ct == CartanType(['A',2]):
    956                         projection = matrix(RR, [[0.5,-0.5],[0.866,0.866]])
    957                     elif ct == CartanType(['B',2]):
    958                         projection = matrix(QQ, [[1,0],[1,1]])
    959                     elif ct == CartanType(['C',2]):
    960                         projection = matrix(QQ, [[1,1],[0,1]])
    961                     elif ct == CartanType(['G',2]):
    962                         projection = matrix(RR, [[0.5,0],[0.866,1.732]])
    963                     else:
    964                         raise 'There is no usual projection for this Cartan type, you have to give one in argument'
    965 
    966                 # Get the fundamental weights
    967                 fundamental_weights = self.fundamental_weights()
    968                 WeylGroup = self.weyl_group()
    969 
    970                 #Draw not the alcove but the cones delimited by the hyperplanes
    971                 #The size of the line depend of the fundamental weights.
    972                 pcenter = projection*(self.zero().to_vector())
    973                 for w in WeylGroup:
    974                     for i in range(1,n+1):
    975                         p3=3*projection*((w.action(fundamental_weights[i])).to_vector())
    976                         G+=line([p3,pcenter], rgbcolor=colors[n-i+1])
    977 
    978             #Draw the simple roots
    979             if simple_roots:
    980                 SimpleRoots = self.simple_roots()
    981                 if ct.is_affine():
    982                     G+=arrow((0,0), projection*(SimpleRoots[0].to_vector()), rgbcolor=(0,0,0))
    983                 for j in range(1,n+1):
    984                     G+=arrow((0,0),projection*(SimpleRoots[j].to_vector()), rgbcolor=colors[j])
    985 
    986             #Draw the fundamental weights
    987             if fundamental_weights:
    988                 FundWeight = self.fundamental_weights()
    989                 for j in range(1,n+1):
    990                     G+=point(projection*(FundWeight[j].to_vector()), rgbcolor=colors[j], pointsize=60)
    991 
    992             G.set_aspect_ratio(1)
    993             G.axes(False)
    994             return G
    995 
  • sage/doctest/sources.py

    diff --git a/sage/doctest/sources.py b/sage/doctest/sources.py
    a b class FileDocTestSource(DocTestSource): 
    675675            There are 18 tests in sage/combinat/partition.py that are not being run
    676676            There are 12 tests in sage/combinat/tableau.py that are not being run
    677677            There are 15 tests in sage/combinat/root_system/cartan_type.py that are not being run
     678            There are 8 tests in sage/combinat/root_system/type_A.py that are not being run
     679            There are 8 tests in sage/combinat/root_system/type_G.py that are not being run
    678680            There are 3 unexpected tests being run in sage/doctest/parsing.py
    679681            There are 1 unexpected tests being run in sage/doctest/reporting.py
    680682            There are 9 tests in sage/graphs/graph_plot.py that are not being run