Ticket #8986: trac_8986_add_support_for_convex_rational_polyhedral_cones.patch

File trac_8986_add_support_for_convex_rational_polyhedral_cones.patch, 72.0 KB (added by novoselt, 12 years ago)

Reviewer's comments taken into account.

  • doc/en/reference/geometry.rst

    # HG changeset patch
    # User Andrey Novoseltsev <novoselt@gmail.com>
    # Date 1274173825 21600
    # Node ID 3bc5e812f2d41ac2a1282e42721c8e7b23439532
    # Parent  2bccc589431942796d82cbd410692f8db3d47370
    Trac 8986: Add support for convex rational polyhedral cones.
    
    diff -r 2bccc5894319 -r 3bc5e812f2d4 doc/en/reference/geometry.rst
    a b  
    11Combinatorial Geometry
    22======================
    33
    4 Sage includes support for computing with lattice and reflexive
    5 polytopes and Groebner fans.  Polytopes with rational or numerical
    6 coordinates are supported by the Polyhedron class.
     4Sage includes classes for convex rational polyhedral cones, Groebner fans,
     5lattice and reflexive polytopes (with integral coordinates), and generic
     6polytopes and polyhedra (with rational or numerical coordinates).
    77
    88.. toctree::
    99   :maxdepth: 2
    1010
     11   sage/geometry/cone
     12   sage/rings/polynomial/groebner_fan
    1113   sage/geometry/lattice_polytope
    12    sage/rings/polynomial/groebner_fan
    1314   sage/geometry/polyhedra
    1415   sage/geometry/toric_lattice
  • sage/geometry/all.py

    diff -r 2bccc5894319 -r 3bc5e812f2d4 sage/geometry/all.py
    a b  
     1from cone import Cone
     2
    13from polytope import polymake
    24
    35from polyhedra import Polyhedron, polytopes
  • new file sage/geometry/cone.py

    diff -r 2bccc5894319 -r 3bc5e812f2d4 sage/geometry/cone.py
    - +  
     1r"""
     2Convex rational polyhedral cones
     3
     4This module was designed as a part of framework for toric varieties
     5(:mod:`~sage.schemes.generic.toric_variety`,
     6:mod:`~sage.schemes.generic.fano_toric_variety`). While the emphasis is on
     7strictly convex cones, non-strictly convex cones are supported as well. Work
     8with distinct lattices (in the sense of discrete subgroups spanning vector
     9spaces) is supported. The default lattice is :class:`ToricLattice
     10<sage.geometry.toric_lattice.ToricLatticeFactory>` `N` of the appropriate
     11dimension. The only case when you must specify lattice explicitly is creation
     12of a 0-dimensional cone, where dimension of the ambient space cannot be
     13guessed.
     14
     15In addition to cones (:class:`ConvexRationalPolyhedralCone`), this module
     16provides the base class for cones and fans (:class:`IntegralRayCollection`)
     17and the function for computing Hasse diagrams of finite atomic and coatomic
     18lattices (in the sense of partially ordered sets where any two elements have
     19meet and joint) (:func:`hasse_diagram_from_incidences`).
     20
     21AUTHORS:
     22
     23- Andrey Novoseltsev (2010-05-13): initial version.
     24
     25EXAMPLES:
     26
     27Use :func:`Cone` to construct cones::
     28
     29    sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)])
     30    sage: halfspace = Cone([(1,0,0), (0,1,0), (-1,-1,0), (0,0,1)])
     31    sage: positive_xy = Cone([(1,0,0), (0,1,0)])
     32    sage: four_rays = Cone([(1,1,1), (1,-1,1), (-1,-1,1), (-1,1,1)])
     33   
     34For all of the cones above we have provided primitive generating rays, but in
     35fact this is not necessary - a cone can be constructed from any collection of
     36rays (from the same space, of course). If there are non-primitive (or even
     37non-integral) rays, they will be replaced with primitive ones. If there are
     38extra rays, they will be discarded. Of course, this means that :func:`Cone`
     39has to do some work before actually constructing the cone and sometimes it is
     40not desirable, if you know for sure that your input is already "good". In this
     41case you can use options ``check=False`` to force :func:`Cone` to use
     42exactly the directions that you have specified and ``normalize=False`` to
     43force it to use exactly the rays that you have specified. However, it is
     44better not to use these possibilities without necessity, since cones are
     45assumed to be represented by a minimal set of primitive generating rays.
     46See :func:`Cone` for further documentation on construction.
     47
     48Once you have a cone, you can perfrom numerous operations on it. The most
     49important ones are, probably, ray accessing methods::
     50
     51    sage: halfspace.rays()
     52    (N(0, 0, 1), N(1, 0, 0), N(-1, 0, 0), N(0, 1, 0), N(0, -1, 0))
     53    sage: halfspace.ray(2)
     54    N(-1, 0, 0)
     55    sage: halfspace.ray_matrix()
     56    [ 0  1 -1  0  0]
     57    [ 0  0  0  1 -1]
     58    [ 1  0  0  0  0]   
     59    sage: halfspace.ray_set()
     60    frozenset([N(1, 0, 0), N(-1, 0, 0), N(0, 1, 0), N(0, 0, 1), N(0, -1, 0)])
     61   
     62If you want to do something with each ray of a cone, you can write ::
     63
     64    sage: for ray in positive_xy: print ray
     65    N(1, 0, 0)
     66    N(0, 1, 0)   
     67   
     68You can also get an iterator over only some selected rays::
     69
     70    sage: for ray in halfspace.ray_iterator([1,2,1]): print ray
     71    N(1, 0, 0)
     72    N(-1, 0, 0)
     73    N(1, 0, 0)   
     74
     75There are two dimensions associated to each cone - the dimension of the
     76subspace spanned by the cone and the dimension of the ambient space where it
     77lives::
     78
     79    sage: positive_xy.dim()
     80    2
     81    sage: positive_xy.ambient_dim()
     82    3
     83   
     84You also may be interested in this dimension::
     85
     86    sage: dim(positive_xy.linear_subspace())
     87    0
     88    sage: dim(halfspace.linear_subspace())
     89    2
     90   
     91Or, perhaps, all you care about is whether it is zero or not::
     92   
     93    sage: positive_xy.is_strictly_convex()
     94    True
     95    sage: halfspace.is_strictly_convex()
     96    False
     97   
     98You can also perform these checks::
     99
     100    sage: positive_xy.is_simplicial()
     101    True
     102    sage: four_rays.is_simplicial()
     103    False
     104    sage: positive_xy.is_smooth()
     105    True
     106   
     107You can work with subcones that form faces of other cones::
     108
     109    sage: face = four_rays.faces(dim=2)[0]
     110    sage: face
     111    2-dimensional face of 3-dimensional cone
     112    sage: face.rays()
     113    (N(1, 1, 1), N(1, -1, 1))
     114    sage: face.cone_rays()
     115    (0, 1)
     116    sage: four_rays.rays(face.cone_rays())
     117    (N(1, 1, 1), N(1, -1, 1))
     118   
     119If you need to know inclusion relations between faces, use ::
     120
     121    sage: L = four_rays.face_lattice()
     122    sage: map(len, L.level_sets())
     123    [1, 4, 4, 1]
     124    sage: face = L.level_sets()[2][0]
     125    sage: face.element.rays()
     126    (N(1, 1, 1), N(1, -1, 1))
     127    sage: L.hasse_diagram().neighbors_in(face)
     128    [1-dimensional face of 3-dimensional cone,
     129     1-dimensional face of 3-dimensional cone]
     130   
     131When all the functionality provided by cones is not enough, you may want to
     132check if you can do necessary things using lattice polytopes and polyhedra
     133corresponding to cones::
     134
     135    sage: four_rays.lattice_polytope()
     136    A lattice polytope: 3-dimensional, 5 vertices.
     137    sage: four_rays.polyhedron()
     138    A 3-dimensional polyhedron in QQ^3 defined as
     139    the convex hull of 1 vertex and 4 rays.
     140   
     141And of course you are always welcome to suggest new features that should be
     142added to cones!
     143"""
     144
     145#*****************************************************************************
     146#       Copyright (C) 2010 Andrey Novoseltsev <novoselt@gmail.com>
     147#       Copyright (C) 2010 William Stein <wstein@gmail.com>
     148#
     149#  Distributed under the terms of the GNU General Public License (GPL)
     150#
     151#                  http://www.gnu.org/licenses/
     152#*****************************************************************************
     153
     154
     155import collections
     156import copy
     157import warnings
     158
     159from sage.combinat.posets.posets import FinitePoset
     160from sage.geometry.lattice_polytope import LatticePolytope
     161from sage.geometry.polyhedra import Polyhedron
     162from sage.geometry.toric_lattice import ToricLattice, is_ToricLattice
     163from sage.graphs.all import DiGraph
     164from sage.matrix.all import matrix, identity_matrix
     165from sage.misc.all import flatten
     166from sage.modules.all import span, vector
     167from sage.rings.all import QQ, RR, ZZ, gcd
     168from sage.structure.all import SageObject
     169from sage.structure.coerce import parent
     170
     171
     172def is_Cone(x):
     173    r"""
     174    Check if ``x`` is a cone.
     175   
     176    INPUT:
     177   
     178    - ``x`` -- anything.
     179   
     180    OUTPUT:
     181   
     182    - ``True`` if ``x`` is a cone and ``False`` otherwise.
     183   
     184    EXAMPLES::
     185   
     186        sage: from sage.geometry.cone import is_Cone
     187        sage: is_Cone(1)
     188        False
     189        sage: quadrant = Cone([(1,0), (0,1)])
     190        sage: quadrant
     191        2-dimensional cone
     192        sage: is_Cone(quadrant)
     193        True
     194    """
     195    return isinstance(x, ConvexRationalPolyhedralCone)
     196
     197
     198def Cone(data, lattice=None, check=True, normalize=True):
     199    r"""
     200    Construct a (not necessarily strictly) convex rational polyhedral cone.
     201   
     202    INPUT:
     203   
     204    - ``data`` -- one of the following:
     205        * :class:`~sage.geometry.polyhedra.Polyhedron` object representing
     206          a valid cone;
     207        * list of rays. Each ray should be given as a list or a vector
     208          convertible to the rational extension of the given ``lattice``;
     209       
     210    - ``lattice`` -- :class:`ToricLattice
     211      <sage.geometry.toric_lattice.ToricLatticeFactory>`, `\ZZ^n`, or any
     212      other object that behaves like these. If not specified, an attempt will
     213      be made to determine an appropriate toric lattice automatically;
     214   
     215    - ``check`` -- by default the input data will be checked for correctness
     216      (e.g. that all rays have the same number of components) and generating
     217      rays will be constructed from ``data``. If you know that the input is
     218      a minimal set of generators of a valid cone, you may significantly
     219      (up to 100 times on simple input) decrease construction time using
     220      ``check=False`` option;
     221   
     222    - ``normalize`` -- you can further speed up construction using
     223      ``normalize=False`` option. In this case ``data`` must be a list of
     224      immutable primitive rays in ``lattice``. In general, you should not use
     225      this option, it is designed for code optimization and does not give as
     226      drastic improvement in speed as the previous one.
     227       
     228    OUTPUT:
     229   
     230    - convex rational polyhedral cone determined by ``data``.
     231   
     232    EXAMPLES:
     233   
     234    Let's define a cone corresponding to the first quadrant of the plane
     235    (note, you can even mix objects of different types to represent rays, as
     236    long as you let this function to perform all the checks and necessary
     237    conversions!)::
     238   
     239        sage: quadrant = Cone([(1,0), [0,1]])
     240        sage: quadrant
     241        2-dimensional cone
     242        sage: quadrant.rays()
     243        (N(1, 0), N(0, 1))
     244       
     245    You may prefer to use :meth:`~IntegralRayCollection.ray_matrix` when you
     246    want to take a look at rays, they will be given as columns::
     247   
     248        sage: quadrant.ray_matrix()
     249        [1 0]
     250        [0 1]       
     251       
     252    If you give more rays than necessary, the extra ones will be discarded::
     253   
     254        sage: Cone([(1,0), (0,1), (1,1), (0,1)]).rays()
     255        (N(1, 0), N(0, 1))
     256       
     257    However, this work is not done with ``check=False`` option, so use it
     258    carefully! ::
     259   
     260        sage: Cone([(1,0), (0,1), (1,1), (0,1)], check=False).rays()
     261        (N(1, 0), N(0, 1), N(1, 1), N(0, 1))
     262       
     263    Even worse things can happen with ``normalize=False`` option::
     264   
     265        sage: Cone([(1,0), (0,1)], check=False, normalize=False)
     266        Traceback (most recent call last):
     267        ...
     268        AttributeError: 'tuple' object has no attribute 'parent'
     269           
     270    You can construct different "not" cones: not full-dimensional, not
     271    strictly convex, not containing any rays::
     272   
     273        sage: one_dimensional_cone = Cone([(1,0)])
     274        sage: one_dimensional_cone.dim()
     275        1
     276        sage: half_plane = Cone([(1,0), (0,1), (-1,0)])
     277        sage: half_plane.rays()
     278        (N(0, 1), N(1, 0), N(-1, 0))
     279        sage: half_plane.is_strictly_convex()
     280        False
     281        sage: origin = Cone([(0,0)])
     282        sage: origin.rays()
     283        ()
     284        sage: origin.dim()
     285        0
     286        sage: origin.ambient_dim()
     287        2
     288       
     289    You may construct the cone above without giving any rays, but in this case
     290    you must provide lattice explicitly::
     291   
     292        sage: origin = Cone([])
     293        Traceback (most recent call last):
     294        ...
     295        ValueError: lattice must be given explicitly if there are no rays!
     296        sage: origin = Cone([], lattice=ToricLattice(2))
     297        sage: origin.dim()
     298        0
     299        sage: origin.ambient_dim()
     300        2
     301        sage: origin.lattice()
     302        2-dimensional lattice N
     303       
     304    Of course, you can also provide lattice in other cases::
     305   
     306        sage: L = ToricLattice(3, "L")
     307        sage: c1 = Cone([(1,0,0),(1,1,1)], lattice=L)
     308        sage: c1.rays()
     309        (L(1, 0, 0), L(1, 1, 1))
     310       
     311    Or you can construct cones from rays of a particular lattice::
     312   
     313        sage: ray1 = L(1,0,0)
     314        sage: ray2 = L(1,1,1)
     315        sage: c2 = Cone([ray1, ray2])
     316        sage: c2.rays()
     317        (L(1, 0, 0), L(1, 1, 1))
     318        sage: c1 == c2
     319        True
     320       
     321    When the cone in question is not strictly convex, the standard form for
     322    the "generating rays" of the linear subspace is "basis vectors and their
     323    negatives", as in the following example::
     324       
     325        sage: plane = Cone([(1,0), (0,1), (-1,-1)])
     326        sage: plane.ray_matrix()
     327        [ 1 -1  0  0]
     328        [ 0  0  1 -1]       
     329    """
     330    # Cone from Polyhedron
     331    if isinstance(data, Polyhedron):
     332        polyhedron = data
     333        if lattice is None:
     334            lattice = ToricLattice(polyhedron.ambient_dim())
     335        if polyhedron.n_vertices() > 1:
     336            raise ValueError("%s is not a cone!" % polyhedron)
     337        apex = polyhedron.vertices()[0]
     338        if apex.count(0) != len(apex):
     339            raise ValueError("the apex of %s is not at the origin!"
     340                             % polyhedron)
     341        rays = normalize_rays(polyhedron.rays(), lattice)
     342        strict_rays = tuple(rays)
     343        lines = tuple(normalize_rays(polyhedron.lines(), lattice))
     344        for line in lines:
     345            rays.append(line)
     346            rays.append(-line)
     347            rays[-1].set_immutable()
     348        cone = ConvexRationalPolyhedralCone(rays, lattice)
     349        # Save constructed stuff for later use
     350        cone._polyhedron = polyhedron
     351        cone._strict_rays = strict_rays
     352        cone._lines = lines
     353        return cone
     354    # Cone from rays
     355    rays = data
     356    if check or normalize:
     357        # In principle, if check is True, this normalization is redundant,
     358        # since we will need to renormalize the output from Polyhedra, but
     359        # doing it now gives more consistent error messages (and it seems to
     360        # be fast anyway, compared to Polyhedron creation).
     361        rays = normalize_rays(rays, lattice)
     362    if lattice is None:
     363        if rays:
     364            lattice = rays[0].parent()
     365        else:
     366            raise ValueError(
     367                "lattice must be given explicitly if there are no rays!")
     368    if check and rays:
     369        # Any set of rays forms a cone, but we want to keep only generators
     370        return Cone(Polyhedron(rays=rays), lattice)
     371    return ConvexRationalPolyhedralCone(rays, lattice)
     372
     373
     374def normalize_rays(rays, lattice):
     375    r"""
     376    Normalize a list of rational rays, i.e. make them integral.
     377   
     378    INPUT:
     379   
     380    - ``rays`` -- list of rays which can be converted to the rational
     381      extension of ``lattice``;
     382   
     383    - ``lattice`` -- :class:`ToricLattice
     384      <sage.geometry.toric_lattice.ToricLatticeFactory>`, `\ZZ^n`, or any
     385      other object that behaves like these. If ``None``, an attempt will
     386      be made to determine an appropriate toric lattice automatically.   
     387       
     388    OUTPUT:
     389   
     390    - list of immutable primitive vectors of the ``lattice`` in the same
     391      directions as original ``rays``.
     392     
     393    EXAMPLES::
     394   
     395        sage: from sage.geometry.cone import normalize_rays
     396        sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], None)
     397        [N(0, 1), N(0, 1), N(3, 2), N(3, 14)]
     398        sage: L = ToricLattice(2, "L")
     399        sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], L.dual())
     400        [L*(0, 1), L*(0, 1), L*(3, 2), L*(3, 14)]       
     401        sage: ray_in_L = L(0,1)
     402        sage: normalize_rays([ray_in_L, (0, 2), (3, 2), (5/7, 10/3)], None)
     403        [L(0, 1), L(0, 1), L(3, 2), L(3, 14)]               
     404        sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], ZZ^2)
     405        [(0, 1), (0, 1), (3, 2), (3, 14)]
     406        sage: normalize_rays([(0, 1), (0, 2), (3, 2), (5/7, 10/3)], ZZ^3)
     407        Traceback (most recent call last):
     408        ...
     409        TypeError: cannot convert (0, 1) to
     410        Vector space of dimension 3 over Rational Field!
     411        sage: normalize_rays([], ZZ^3)
     412        []
     413    """
     414    if rays is None:
     415        rays = []
     416    try:
     417        rays = list(rays)
     418    except TypeError:
     419        raise TypeError(
     420                    "rays must be given as a list or a compatible structure!"
     421                    "\nGot: %s" % rays)
     422    if rays:
     423        if lattice is None:
     424            ray_parent = parent(rays[0])
     425            lattice = (ray_parent if is_ToricLattice(ray_parent)
     426                                  else ToricLattice(len(rays[0])))
     427        V = lattice.base_extend(QQ)
     428        for n, ray in enumerate(rays):
     429            try:
     430                ray = V(ray)
     431            except TypeError:
     432                raise TypeError("cannot convert %s to %s!" % (ray, V))
     433            if ray.is_zero():
     434                ray = lattice(0)
     435            else:
     436                ray = ray.denominator() * ray
     437                ray = lattice(ray / gcd(lattice(ray)))
     438            ray.set_immutable()
     439            rays[n] = ray
     440    return rays
     441
     442   
     443class IntegralRayCollection(SageObject,
     444                            collections.Container,
     445                            collections.Hashable,
     446                            collections.Iterable):
     447    r"""
     448    Create a collection of integral rays.
     449   
     450    This is a base class for rational polyhedral cones and fans.
     451   
     452    Ray collections are immutable, but they cache most of the returned values.
     453   
     454    INPUT:
     455   
     456    - ``rays`` -- list of immutable vectors in ``lattice``;
     457   
     458    - ``lattice`` -- :class:`ToricLattice
     459      <sage.geometry.toric_lattice.ToricLatticeFactory>`, `\ZZ^n`, or any
     460      other object that behaves like these. If ``None``, it will be determined
     461      as :func:`parent` of the first ray. Of course, this cannot be done if
     462      there are no rays, so in this case you must give an appropriate
     463      ``lattice`` directly.
     464     
     465    OUTPUT:
     466   
     467    - collection of given integral rays.
     468   
     469    .. WARNING::
     470   
     471        No correctness check or normalization is performed on the input data.
     472        This class is designed for internal operations and you probably should
     473        not use it directly.
     474       
     475    TESTS::
     476   
     477        sage: v = vector([0,1])
     478        sage: v.set_immutable()
     479        sage: c = sage.geometry.cone.IntegralRayCollection([v], None)
     480        sage: c.rays()
     481        ((0, 1),)
     482        sage: TestSuite(c).run()
     483    """   
     484   
     485    def __init__(self, rays, lattice):
     486        r"""
     487        See :class:`IntegralRayCollection` for documentation.
     488       
     489        TESTS::
     490       
     491            sage: v = vector([0,1])
     492            sage: v.set_immutable()
     493            sage: sage.geometry.cone.IntegralRayCollection([v], None).rays()
     494            ((0, 1),)
     495        """
     496        self._rays = tuple(rays)
     497        self._lattice = self._rays[0].parent() if lattice is None else lattice
     498
     499    def __cmp__(self, right):
     500        r"""
     501        Compare ``self`` and ``right``.
     502       
     503        INPUT:
     504       
     505        - ``right`` -- anything.
     506       
     507        OUTPUT:
     508       
     509        - 0 if ``right`` is of the same type as ``self`` and their rays are
     510          the same and listed in the same order. 1 or -1 otherwise.
     511       
     512        TESTS::
     513       
     514            sage: c1 = Cone([(1,0), (0,1)])
     515            sage: c2 = Cone([(0,1), (1,0)])
     516            sage: c3 = Cone([(0,1), (1,0)])
     517            sage: cmp(c1, c2)           
     518            1
     519            sage: cmp(c2, c1)           
     520            -1
     521            sage: cmp(c2, c3)           
     522            0
     523            sage: c2 is c3
     524            False
     525            sage: cmp(c1, 1)
     526            -1
     527        """
     528        c = cmp(type(self), type(right))
     529        if c:
     530            return c       
     531        return cmp((self.lattice(), self.rays()),
     532                   (right.lattice(), right.rays()))
     533                   
     534    def __hash__(self):
     535        r"""
     536        Return the hash of ``self`` computed from rays.
     537       
     538        OUTPUT:
     539       
     540        - integer.
     541       
     542        TESTS::
     543       
     544            sage: c = Cone([(1,0), (0,1)])
     545            sage: hash(c)  # 64-bit
     546            4372618627376133801
     547        """
     548        if "_hash" not in self.__dict__:
     549            self._hash = hash(self._rays)
     550        return self._hash
     551   
     552    def __iter__(self):
     553        r"""
     554        Return an iterator over rays of ``self``.
     555       
     556        OUTPUT:
     557       
     558        -  iterator.
     559       
     560        TESTS::
     561       
     562            sage: c = Cone([(1,0), (0,1)])
     563            sage: for ray in c: print ray
     564            N(1, 0)
     565            N(0, 1)
     566        """
     567        return self.ray_iterator()
     568                   
     569    def ambient_dim(self):
     570        r"""
     571        Return the dimension of the ambient lattice of ``self``.
     572       
     573        OUTPUT:
     574       
     575        - integer.
     576       
     577        EXAMPLES::
     578       
     579            sage: c = Cone([(1,0)])
     580            sage: c.ambient_dim()
     581            2
     582            sage: c.dim()
     583            1
     584        """
     585        return self.lattice().dimension()
     586   
     587    def dim(self):
     588        r"""
     589        Return the dimension of the subspace spanned by rays of ``self``.
     590       
     591        OUTPUT:
     592       
     593        - integer.
     594       
     595        EXAMPLES::
     596       
     597            sage: c = Cone([(1,0)])
     598            sage: c.ambient_dim()
     599            2
     600            sage: c.dim()
     601            1
     602        """
     603        if "_dim" not in self.__dict__:
     604            self._dim = self.ray_matrix().rank()
     605        return self._dim
     606   
     607    def lattice(self):
     608        r"""
     609        Return the ambient lattice of ``self``.
     610       
     611        OUTPUT:
     612       
     613        - lattice.
     614         
     615        EXAMPLES::
     616       
     617            sage: c = Cone([(1,0)])
     618            sage: c.lattice()
     619            2-dimensional lattice N
     620            sage: Cone([], ZZ^3).lattice()
     621            Ambient free module of rank 3
     622            over the principal ideal domain Integer Ring
     623        """
     624        return self._lattice
     625   
     626    def nrays(self):
     627        r"""
     628        Return the number of rays of ``self``.
     629       
     630        OUTPUT:
     631       
     632        - integer.
     633       
     634        EXAMPLES::
     635       
     636            sage: c = Cone([(1,0), (0,1)])
     637            sage: c.nrays()
     638            2
     639        """
     640        return len(self._rays)
     641   
     642    def ray(self, n):
     643        r"""
     644        Return the ``n``-th ray of ``self``.
     645       
     646        INPUT:
     647       
     648        - ``n`` -- integer, an index of a ray of ``self``. Enumeration of rays
     649          starts with zero.
     650       
     651        OUTPUT:
     652       
     653        - ray, an element of the lattice of ``self``.
     654       
     655        EXAMPLES::
     656       
     657            sage: c = Cone([(1,0), (0,1)])
     658            sage: c.ray(0)
     659            N(1, 0)
     660        """
     661        return self._rays[n]
     662   
     663    def ray_iterator(self, ray_list=None):
     664        r"""
     665        Return an iterator over (some of) the rays of ``self``.
     666       
     667        INPUT:
     668       
     669        - ``ray_list`` -- list of integers, the indices of the requested rays.
     670          If not specified, an iterator over all rays of ``self`` will be
     671          returned.
     672       
     673        OUTPUT:
     674       
     675        - iterator.
     676       
     677        EXAMPLES::
     678       
     679            sage: c = Cone([(1,0), (0,1), (-1, 0)])
     680            sage: for ray in c.ray_iterator(): print ray
     681            N(0, 1)
     682            N(1, 0)
     683            N(-1, 0)
     684            sage: for ray in c.ray_iterator([2,1]): print ray
     685            N(-1, 0)
     686            N(1, 0)
     687        """       
     688        if ray_list is None:
     689            for ray in self._rays:
     690                yield ray
     691        else:
     692            rays = self._rays
     693            for n in ray_list:
     694                yield rays[n]
     695   
     696    def ray_matrix(self):
     697        r"""
     698        Return a matrix whose columns are rays of ``self``.
     699       
     700        It can be convenient for linear algebra operations on rays, as well as
     701        for easy-to-read output.
     702       
     703        OUTPUT:
     704       
     705        - matrix.
     706       
     707        EXAMPLES::
     708       
     709            sage: c = Cone([(1,0), (0,1), (-1, 0)])
     710            sage: c.ray_matrix()
     711            [ 0  1 -1]
     712            [ 1  0  0]           
     713        """
     714        if "_ray_matrix" not in self.__dict__:
     715            self._ray_matrix = matrix(self.nrays(), self.ambient_dim(),
     716                                      self._rays).transpose()
     717            self._ray_matrix.set_immutable()
     718        return self._ray_matrix
     719   
     720    def ray_set(self):
     721        r"""
     722        Return rays of ``self`` as a :class:`frozenset`.
     723       
     724        Use :meth:`rays` if you want to get rays in the fixed order.
     725       
     726        OUTPUT:
     727       
     728        - :class:`frozenset` of rays.
     729       
     730        EXAMPLES::
     731       
     732            sage: c = Cone([(1,0), (0,1), (-1, 0)])
     733            sage: c.ray_set()
     734            frozenset([N(0, 1), N(1, 0), N(-1, 0)])
     735        """
     736        if "_ray_set" not in self.__dict__:
     737            self._ray_set = frozenset(self._rays)
     738        return self._ray_set
     739       
     740    def rays(self, ray_list=None):
     741        r"""
     742        Return rays of ``self`` as a :class:`tuple`.
     743       
     744        INPUT:
     745       
     746        - ``ray_list`` -- list of integers, the indices of the requested rays.
     747          If not specified, all rays of ``self`` will be returned. You may
     748          want to use :meth:`ray_set` if you do not care about the order of
     749          rays. See also :meth:`ray_iterator`.
     750       
     751        OUTPUT:
     752       
     753        - :class:`tuple` of rays.
     754       
     755        EXAMPLES::
     756       
     757            sage: c = Cone([(1,0), (0,1), (-1, 0)])
     758            sage: c.rays()
     759            (N(0, 1), N(1, 0), N(-1, 0))
     760            sage: c.rays([0, 2])
     761            (N(0, 1), N(-1, 0))
     762        """
     763        if ray_list is None:
     764            return self._rays
     765        else:
     766            return tuple(self.ray_iterator(ray_list))
     767       
     768   
     769class ConvexRationalPolyhedralCone(IntegralRayCollection):
     770    r"""
     771    Create a convex rational polyhedral cone.
     772   
     773    .. WARNING::
     774   
     775        This class does not perform any checks of correctness of input nor
     776        does it convert input into the standard representation. Use
     777        :func:`Cone` to construct cones.
     778       
     779    Cones are immutable, but they cache most of the returned values.
     780   
     781    INPUT:
     782   
     783    - same as for :class:`IntegralRayCollection`.
     784   
     785    OUTPUT:
     786   
     787    - convex rational polyhedral cone.
     788   
     789    TESTS::
     790   
     791        sage: v = vector([0,1])
     792        sage: v.set_immutable()
     793        sage: c = sage.geometry.cone.ConvexRationalPolyhedralCone([v], None)
     794        sage: c.rays()
     795        ((0, 1),)   
     796        sage: TestSuite(c).run()
     797       
     798        sage: c = Cone([(0,1)])
     799        sage: TestSuite(c).run()
     800    """
     801   
     802    # No __init__ method, just use the base class.
     803   
     804    def __contains__(self, point):
     805        r"""
     806        Check if ``point`` is contained in ``self``.
     807       
     808        See :meth:`_contains` (which is called by this function) for
     809        documentation.
     810       
     811        TESTS::
     812       
     813            sage: c = Cone([(1,0), (0,1)])
     814            sage: (1,1) in c
     815            True
     816            sage: [1,1] in c
     817            True
     818            sage: (-1,0) in c
     819            False
     820        """
     821        return self._contains(point)
     822   
     823    def __getstate__(self):
     824        r"""
     825        Return the dictionary that should be pickled.
     826       
     827        OUTPUT:
     828       
     829        - :class:`dict`.
     830       
     831        TESTS::
     832       
     833            sage: loads(dumps(Cone([(1,0)])))       
     834            1-dimensional cone
     835        """
     836        state = copy.copy(self.__dict__)
     837        state.pop("_polyhedron", None) # Polyhedron is not picklable.
     838        state.pop("_lattice_polytope", None) # Just to save time and space.
     839        return state
     840       
     841    def _contains(self, point):
     842        r"""
     843        Check if ``point`` is contained in ``self``.
     844       
     845        This function is called by :meth:`__contains__` and :meth:`contains`
     846        to ensure the same call depth for warning messages.
     847       
     848        INPUT:
     849       
     850        - ``point`` -- anything. An attempt will be made to convert it into a
     851          single element of the ambient space of ``self``. If it fails,
     852          ``False`` will be returned.
     853         
     854        OUTPUT:
     855       
     856        - ``True`` if ``point`` is contained in ``self``, ``False`` otherwise.
     857       
     858        TESTS::
     859           
     860            sage: c = Cone([(1,0), (0,1)])
     861            sage: c._contains((1,1))
     862            True
     863        """
     864        if is_ToricLattice(parent(point)):
     865            # Special treatment for elements of OTHER toric lattices
     866            if point not in self.lattice():
     867                # This is due to the discussion in Trac #8986
     868                warnings.warn("you have checked if a cone contains a point "
     869                              "from another lattice, this is always False!",
     870                              stacklevel=3)
     871                return False
     872        else:           
     873            try: # to cook up a point being exact ...
     874                point = self.lattice().base_extend(QQ)(point)
     875            except TypeError:
     876                try: # ... or at least numeric
     877                    point = self.lattice().base_extend(RR)(point)
     878                except TypeError:
     879                    # Give up, input is really strange
     880                    return False
     881        return all(n * point >= 0 for n in self.facet_normals())
     882       
     883    def _latex_(self):
     884        r"""
     885        Return a LaTeX representation of ``self``.
     886       
     887        OUTPUT:
     888       
     889        - string.
     890       
     891        TESTS::
     892       
     893            sage: quadrant = Cone([(1,0), (0,1)])
     894            sage: quadrant._latex_()
     895            '\\sigma^{2}'
     896        """
     897        return r"\sigma^{%d}" % self.dim()
     898   
     899    def _repr_(self):
     900        r"""
     901        Return a string representation of ``self``.
     902       
     903        OUTPUT:
     904       
     905        - string.
     906       
     907        TESTS::
     908       
     909            sage: quadrant = Cone([(1,0), (0,1)])
     910            sage: quadrant._repr_()
     911            '2-dimensional cone'
     912            sage: quadrant
     913            2-dimensional cone
     914        """
     915        return "%d-dimensional cone" % self.dim()
     916   
     917    def contains(self, *args):
     918        r"""
     919        Check if a given point is contained in ``self``.
     920       
     921        INPUT:
     922       
     923        - anything. An attempt will be made to convert all arguments into a
     924          single element of the ambient space of ``self``. If it fails,
     925          ``False`` will be returned.
     926         
     927        OUTPUT:
     928       
     929        - ``True`` if the given point is contained in ``self``, ``False``
     930          otherwise.
     931       
     932        EXAMPLES::
     933           
     934            sage: c = Cone([(1,0), (0,1)])
     935            sage: c.contains(c.lattice()(1,0))
     936            True
     937            sage: c.contains((1,0))
     938            True
     939            sage: c.contains((1,1))
     940            True
     941            sage: c.contains(1,1)
     942            True
     943            sage: c.contains((-1,0))
     944            False
     945            sage: c.contains(c.lattice().dual()(1,0)) #random output (warning)
     946            False
     947            sage: c.contains(c.lattice().dual()(1,0))
     948            False
     949            sage: c.contains(1)
     950            False
     951            sage: c.contains(1/2, sqrt(3))
     952            True
     953            sage: c.contains(-1/2, sqrt(3))
     954            False
     955        """
     956        point = flatten(args)
     957        if len(point) == 1:
     958           point = point[0]
     959        return self._contains(point)
     960
     961    def face_lattice(self):
     962        r"""
     963        Return the face lattice of ``self``.
     964       
     965        This lattice will have the origin as the bottom (we do not include the
     966        empty set as a face) and this cone itself as the top.
     967       
     968        OUTPUT:
     969       
     970        - :class:`~sage.combinat.posets.posets.FinitePoset` of
     971          :class:`ConeFace` objects, behaving like other cones, but also
     972          containing the information about their relation to this one, namely,
     973          the generating rays and containing facets.
     974         
     975        EXAMPLES:
     976       
     977        Let's take a look at the face lattice of the first quadrant::
     978       
     979            sage: quadrant = Cone([(1,0), (0,1)])
     980            sage: L = quadrant.face_lattice()
     981            sage: L
     982            Finite poset containing 4 elements
     983           
     984        To see all faces arranged by dimension, you can do this::
     985       
     986            sage: for level in L.level_sets(): print level
     987            [0-dimensional face of 2-dimensional cone]
     988            [1-dimensional face of 2-dimensional cone,
     989             1-dimensional face of 2-dimensional cone]
     990            [2-dimensional face of 2-dimensional cone]
     991           
     992        To work with a particular face of a particular dimension it is not
     993        enough to do just ::
     994       
     995            sage: face = L.level_sets()[1][0]
     996            sage: face
     997            1-dimensional face of 2-dimensional cone
     998            sage: face.rays()
     999            Traceback (most recent call last):
     1000            ...
     1001            AttributeError: 'PosetElement' object has no attribute 'rays'
     1002           
     1003        To get the actual face you need one more step::
     1004       
     1005            sage: face = face.element
     1006           
     1007        Now you can look at the actual rays of this face... ::
     1008       
     1009            sage: face.rays()
     1010            (N(1, 0),)
     1011           
     1012        ... or you can see indices of the rays of the orginal cone that
     1013        correspond to the above ray::
     1014       
     1015            sage: face.cone_rays()
     1016            (0,)
     1017            sage: quadrant.ray(0)
     1018            N(1, 0)
     1019           
     1020        You can also get the list of facets of the original cone containing
     1021        this face::
     1022       
     1023            sage: face.cone_facets()
     1024            (1,)
     1025           
     1026        An alternative to extracting faces from the face lattice is to use
     1027        :meth:`faces` method::
     1028       
     1029            sage: face is quadrant.faces(dim=1)[0]
     1030            True
     1031           
     1032        The advantage of working with the face lattice directly is that you
     1033        can (relatively easily) get faces that are related to the given one::
     1034       
     1035            sage: face = L.level_sets()[1][0]
     1036            sage: D = L.hasse_diagram()
     1037            sage: D.neighbors(face)
     1038            [0-dimensional face of 2-dimensional cone,
     1039             2-dimensional face of 2-dimensional cone]
     1040        """       
     1041        if "_face_lattice" not in self.__dict__:
     1042            if not self.is_strictly_convex():
     1043                raise NotImplementedError("face lattice is currently "
     1044                                "implemented only for strictly convex cones!")
     1045            # Should we build it from PALP complete incidences? May be faster.
     1046            # On the other hand this version is also quite nice ;-)
     1047            ray_to_facets = []
     1048            facet_to_rays = []
     1049            for ray in self:
     1050                ray_to_facets.append([])
     1051            for normal in self.facet_normals():
     1052                facet_to_rays.append([])
     1053            for i, ray in enumerate(self):
     1054                for j, normal in enumerate(self.facet_normals()):
     1055                    if ray * normal == 0:
     1056                        ray_to_facets[i].append(j)
     1057                        facet_to_rays[j].append(i)
     1058            self._face_lattice = hasse_diagram_from_incidences(
     1059                        ray_to_facets, facet_to_rays, ConeFace, cone=self)
     1060        return self._face_lattice
     1061           
     1062    def faces(self, dim=None, codim=None):
     1063        r"""
     1064        Return faces of ``self`` of specified (co)dimension.
     1065       
     1066        INPUT:
     1067       
     1068        - ``dim`` -- integer, dimension of the requested faces;
     1069       
     1070        - ``codim`` -- integer, codimension of the requested faces.
     1071       
     1072        .. NOTE::
     1073       
     1074            You can specify at most one parameter. If you don't give any, then
     1075            all faces will be returned.
     1076       
     1077        OUTPUT:
     1078       
     1079        - if either ``dim`` or ``codim`` is given, the output will be a
     1080          :class:`tuple` of :class:`ConeFace` objects, behaving like other
     1081          cones, but also containing the information about their relation to
     1082          this one, namely, the generating rays and containing facets;
     1083         
     1084        - if neither ``dim`` nor ``codim`` is given, the output will be the
     1085          :class:`tuple` of tuples as above, giving faces of all dimension. If
     1086          you care about inclusion relations between faces, consider using
     1087          :meth:`face_lattice`.
     1088         
     1089        EXAMPLES:
     1090       
     1091        Let's take a look at the faces of the first quadrant::
     1092       
     1093            sage: quadrant = Cone([(1,0), (0,1)])
     1094            sage: quadrant.faces()
     1095            ((0-dimensional face of 2-dimensional cone,),
     1096             (1-dimensional face of 2-dimensional cone,
     1097              1-dimensional face of 2-dimensional cone),
     1098             (2-dimensional face of 2-dimensional cone,))
     1099            sage: quadrant.faces(dim=1)
     1100            (1-dimensional face of 2-dimensional cone,
     1101             1-dimensional face of 2-dimensional cone)
     1102            sage: face = quadrant.faces(dim=1)[0]
     1103           
     1104        Now you can look at the actual rays of this face... ::
     1105       
     1106            sage: face.rays()
     1107            (N(1, 0),)
     1108           
     1109        ... or you can see indices of the rays of the orginal cone that
     1110        correspond to the above ray::
     1111       
     1112            sage: face.cone_rays()
     1113            (0,)
     1114            sage: quadrant.ray(0)
     1115            N(1, 0)
     1116           
     1117        You can also get the list of facets of the original cone containing
     1118        this face::
     1119       
     1120            sage: face.cone_facets()
     1121            (1,)
     1122        """
     1123        if "_faces" not in self.__dict__:
     1124            self._faces = tuple(tuple(e.element
     1125                                      for e in level)
     1126                                for level in self.face_lattice().level_sets())
     1127        if dim is None and codim is None:
     1128            return self._faces
     1129        elif dim is None:
     1130            return self._faces[self.dim() - codim]
     1131        elif codim is None:
     1132            return self._faces[dim]
     1133        raise ValueError(
     1134                    "dimension and codimension cannot be specified together!")
     1135           
     1136    def facet_normals(self):
     1137        r"""
     1138        Return normals to facets of ``self``.
     1139       
     1140        .. NOTE::
     1141       
     1142            For a not full-dimensional cone facet normals will be parallel to
     1143            the subspace spanned by the cone.
     1144       
     1145        OUTPUT:
     1146       
     1147        - :class:`tuple` of vectors.
     1148       
     1149        EXAMPLES::
     1150       
     1151            sage: cone = Cone([(1,0), (-1,3)])
     1152            sage: cone.facet_normals()
     1153            ((3, 1), (0, 1))
     1154        """
     1155        if "_facet_normals" not in self.__dict__:
     1156            if not self.is_strictly_convex():
     1157                raise NotImplementedError("facet normals are currently "
     1158                                "implemented only for strictly convex cones!")
     1159            lp = self.lattice_polytope()
     1160            self._facet_normals = tuple(lp.facet_normal(i)
     1161                                        for i in range(lp.nfacets())
     1162                                        if lp.facet_constant(i) == 0)
     1163        return self._facet_normals       
     1164           
     1165    def facets(self):
     1166        r"""
     1167        Return facets (faces of codimension 1) of ``self``.
     1168       
     1169        This function is a synonym for ``self.faces(codim=1)``.
     1170       
     1171        OUTPUT:
     1172       
     1173        - :class:`tuple` of :class:`ConeFace` objects.
     1174       
     1175        EXAMPLES::
     1176       
     1177            sage: quadrant = Cone([(1,0), (0,1)])
     1178            sage: quadrant.facets()
     1179            (1-dimensional face of 2-dimensional cone,
     1180             1-dimensional face of 2-dimensional cone)
     1181            sage: quadrant.facets() is quadrant.faces(codim=1)
     1182            True
     1183        """ 
     1184        return self.faces(codim=1)   
     1185           
     1186    def intersection(self, other):
     1187        r"""
     1188        Compute the intersection of two cones.
     1189       
     1190        INPUT:
     1191       
     1192        - ``other`` - cone.
     1193       
     1194        OUTPUT:
     1195       
     1196        - cone.
     1197       
     1198        EXAMPLES::
     1199       
     1200            sage: cone1 = Cone([(1,0), (-1, 3)])
     1201            sage: cone2 = Cone([(-1,0), (2, 5)])
     1202            sage: cone1.intersection(cone2).rays()
     1203            (N(2, 5), N(-1, 3))
     1204        """
     1205        return Cone(self.polyhedron().intersection(other.polyhedron()),
     1206                    self.lattice())
     1207                   
     1208    def is_equivalent(self, other):
     1209        r"""
     1210        Check if ``self`` is "mathematically" the same as ``other``.
     1211       
     1212        INPUT:
     1213       
     1214        - ``other`` - cone.
     1215       
     1216        OUTPUT:
     1217       
     1218        - ``True`` if ``self`` and ``other`` define the same cones as sets of
     1219          points in the same lattice, ``False`` otherwise.
     1220       
     1221        There are three different equivalences between cones `C_1` and `C_2`
     1222        in the same lattice:
     1223       
     1224        #. They have the same generating rays in the same order.
     1225           This is tested by ``C1 == C2``.
     1226        #. They describe the same sets of points.
     1227           This is tested by ``C1.is_equivalent(C2)``.
     1228        #. They are in the same orbit of `GL(n,\ZZ)` (and, therefore,
     1229           correspond to isomorphic affine toric varieties).
     1230           This is tested by ``C1.is_isomorphic(C2)``.
     1231           
     1232        EXAMPLES::
     1233       
     1234            sage: cone1 = Cone([(1,0), (-1, 3)])
     1235            sage: cone2 = Cone([(-1,3), (1, 0)])
     1236            sage: cone1.rays()
     1237            (N(1, 0), N(-1, 3))
     1238            sage: cone2.rays()
     1239            (N(-1, 3), N(1, 0))
     1240            sage: cone1 == cone2
     1241            False
     1242            sage: cone1.is_equivalent(cone2)
     1243            True
     1244        """
     1245        if self.lattice() != other.lattice():
     1246            return False
     1247        if self.dim() != other.dim(): # Not necessary, but very fast
     1248            return False
     1249        if self.ray_set() == other.ray_set():
     1250            return True
     1251        if self.is_strictly_convex() or other.is_strictly_convex():
     1252            return False # For strictly convex cones ray sets must coincide
     1253        if self.linear_subspace() != other.linear_subspace():
     1254            return False
     1255        return self.strict_quotient().is_equivalent(other.strict_quotient())
     1256
     1257    def is_face_of(self, cone):
     1258        r"""
     1259        Check if ``self`` forms a face of another ``cone``.
     1260       
     1261        INPUT:
     1262       
     1263        - ``cone`` -- cone.
     1264       
     1265        OUTPUT:
     1266       
     1267        - ``True`` if ``self`` is a face of ``cone``, ``False`` otherwise.
     1268       
     1269        EXAMPLES::
     1270       
     1271            sage: quadrant = Cone([(1,0), (0,1)])
     1272            sage: cone1 = Cone([(1,0)])
     1273            sage: cone2 = Cone([(1,2)])           
     1274            sage: quadrant.is_face_of(quadrant)
     1275            True
     1276            sage: cone1.is_face_of(quadrant)
     1277            True
     1278            sage: cone2.is_face_of(quadrant)
     1279            False
     1280        """
     1281        if self.lattice() != cone.lattice():
     1282            return False
     1283        # Cases of the origin and the whole cone (we don't have empty cones)
     1284        if self.nrays() == 0 or self.is_equivalent(cone):
     1285            return True
     1286        # Obviously False cases
     1287        if (self.dim() >= cone.dim() # if == and face, we return True above
     1288            or self.is_strictly_convex() != cone.is_strictly_convex()
     1289            or self.linear_subspace() != cone.linear_subspace()):
     1290            return False
     1291        # Now we need to worry only about strict quotients
     1292        if not self.strict_quotient().ray_set().issubset(
     1293                                            cone.strict_quotient().ray_set()):
     1294            return False
     1295        # Maybe this part can be written in a more efficient way
     1296        ph_self = self.strict_quotient().polyhedron()
     1297        ph_cone = cone.strict_quotient().polyhedron()
     1298        containing = []
     1299        for n, H in enumerate(ph_cone.Hrepresentation()):
     1300            containing.append(n)
     1301            for V in ph_self.Vrepresentation():
     1302                if H.eval(V) != 0:
     1303                    containing.pop()
     1304                    break
     1305        m = ph_cone.incidence_matrix().matrix_from_columns(containing)
     1306        # The intersection of all containing hyperplanes should contain only
     1307        # rays of the potential face and the origin
     1308        return map(sum, m.rows()).count(m.ncols()) == ph_self.n_rays() + 1
     1309       
     1310    def is_isomorphic(self, other):
     1311        r"""
     1312        Check if ``self`` is in the same `GL(n, \ZZ)`-orbit as ``other``.
     1313       
     1314        INPUT:
     1315       
     1316        - ``other`` - cone.
     1317       
     1318        OUTPUT:
     1319       
     1320        - ``True`` if ``self`` and ``other`` are in the same
     1321          `GL(n, \ZZ)`-orbit, ``False`` otherwise.
     1322       
     1323        There are three different equivalences between cones `C_1` and `C_2`
     1324        in the same lattice:
     1325       
     1326        #. They have the same generating rays in the same order.
     1327           This is tested by ``C1 == C2``.
     1328        #. They describe the same sets of points.
     1329           This is tested by ``C1.is_equivalent(C2)``.
     1330        #. They are in the same orbit of `GL(n,\ZZ)` (and, therefore,
     1331           correspond to isomorphic affine toric varieties).
     1332           This is tested by ``C1.is_isomorphic(C2)``.
     1333           
     1334        EXAMPLES::
     1335       
     1336            sage: cone1 = Cone([(1,0), (0, 3)])
     1337            sage: cone2 = Cone([(-1,3), (1, 0)])
     1338            sage: cone1.is_isomorphic(cone2)
     1339            Traceback (most recent call last):
     1340            ...
     1341            NotImplementedError: cone isomorphism is not implemented yet!
     1342        """
     1343        if self.lattice() != other.lattice():
     1344            return False
     1345        raise NotImplementedError("cone isomorphism is not implemented yet!")
     1346       
     1347    def is_simplicial(self):
     1348        r"""
     1349        Check if ``self`` is simplicial.
     1350       
     1351        A cone is called *simplicial* if primitive vectors along its
     1352        generating rays form a part of a rational basis of the ambient space.
     1353       
     1354        OUTPUT:
     1355       
     1356        - ``True`` if ``self`` is simplicial, ``False`` otherwise.
     1357       
     1358        EXAMPLES::
     1359       
     1360            sage: cone1 = Cone([(1,0), (0, 3)])
     1361            sage: cone2 = Cone([(1,0), (0, 3), (-1,-1)])
     1362            sage: cone1.is_simplicial()
     1363            True
     1364            sage: cone2.is_simplicial()
     1365            False
     1366        """
     1367        return self.nrays() == self.dim()
     1368       
     1369    def is_smooth(self):
     1370        r"""
     1371        Check if ``self`` is smooth.
     1372       
     1373        A cone is called *smooth* if primitive vectors along its generating
     1374        rays form a part of an *integral* basis of the ambient space.
     1375
     1376        OUTPUT:
     1377       
     1378        - ``True`` if ``self`` is smooth, ``False`` otherwise.
     1379       
     1380        EXAMPLES::
     1381       
     1382            sage: cone1 = Cone([(1,0), (0, 1)])
     1383            sage: cone2 = Cone([(1,0), (-1, 3)])
     1384            sage: cone1.is_smooth()
     1385            True
     1386            sage: cone2.is_smooth()
     1387            False
     1388        """
     1389        if "_is_smooth" not in self.__dict__:
     1390            if not self.is_simplicial():
     1391                self._is_smooth = False
     1392            else:
     1393                m = self.ray_matrix()
     1394                if self.dim() != self.ambient_dim():
     1395                    m = m.augment(identity_matrix(self.ambient_dim()))
     1396                    m = m.matrix_from_columns(m.pivots())
     1397                self._is_smooth = abs(m.det()) == 1
     1398        return self._is_smooth
     1399   
     1400    def is_strictly_convex(self):
     1401        r"""
     1402        Check if ``self`` is strictly convex.
     1403       
     1404        A cone is called *strictly convex* if it does not contain any lines.
     1405
     1406        OUTPUT:
     1407       
     1408        - ``True`` if ``self`` is strictly convex, ``False`` otherwise.
     1409       
     1410        EXAMPLES::
     1411       
     1412            sage: cone1 = Cone([(1,0), (0, 1)])
     1413            sage: cone2 = Cone([(1,0), (-1, 0)])
     1414            sage: cone1.is_strictly_convex()
     1415            True
     1416            sage: cone2.is_strictly_convex()
     1417            False
     1418        """
     1419        if "_is_strictly_convex" not in self.__dict__:
     1420            self._is_strictly_convex = self.polyhedron().n_lines() == 0
     1421        return self._is_strictly_convex     
     1422     
     1423    def lattice_polytope(self):
     1424        r"""
     1425        Return the lattice polytope associated to ``self``.
     1426       
     1427        The vertices of this polytope are primitive vectors along the
     1428        generating rays of ``self`` and the origin, if ``self`` is strictly
     1429        convex. In this case the origin is the last vertex, so the `i`-th ray
     1430        of the cone always corresponds to the `i`-th vertex of the polytope.
     1431       
     1432        See also :meth:`polyhedron`.
     1433       
     1434        OUTPUT:
     1435       
     1436        - :class:`LatticePolytope
     1437          <sage.geometry.lattice_polytope.LatticePolytopeClass>`.
     1438       
     1439        EXAMPLES::
     1440       
     1441            sage: quadrant = Cone([(1,0), (0,1)])
     1442            sage: lp = quadrant.lattice_polytope()
     1443            sage: lp
     1444            A lattice polytope: 2-dimensional, 3 vertices.
     1445            sage: lp.vertices()
     1446            [1 0 0]
     1447            [0 1 0]
     1448           
     1449            sage: line = Cone([(1,0), (-1,0)])
     1450            sage: lp = line.lattice_polytope()
     1451            sage: lp
     1452            A lattice polytope: 1-dimensional, 2 vertices.
     1453            sage: lp.vertices()
     1454            [ 1 -1]
     1455            [ 0  0]
     1456        """
     1457        if "_lattice_polytope" not in self.__dict__:
     1458            m = self.ray_matrix()
     1459            if self.is_strictly_convex():
     1460                m = m.augment(matrix(ZZ, self.ambient_dim(), 1)) # the origin
     1461            self._lattice_polytope = LatticePolytope(m,
     1462                                compute_vertices=False, copy_vertices=False)
     1463        return self._lattice_polytope
     1464   
     1465    def line_set(self):
     1466        r"""
     1467        Return a set of lines generating the linear subspace of ``self``.
     1468       
     1469        OUTPUT:
     1470       
     1471        - :class:`frozenset` of primitive vectors in the lattice of ``self``
     1472          giving directions of lines that span the linear subspace of
     1473          ``self``. These lines are arbitrary, but fixed. See also
     1474          :meth:`lines`.
     1475         
     1476        EXAMPLES::
     1477       
     1478            sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
     1479            sage: halfplane.line_set()
     1480            frozenset([N(1, 0)])
     1481            sage: fullplane = Cone([(1,0), (0,1), (-1,-1)])
     1482            sage: fullplane.line_set()
     1483            frozenset([N(0, 1), N(1, 0)])
     1484        """
     1485        if "_line_set" not in self.__dict__:
     1486            self._line_set = frozenset(self.lines())
     1487        return self._line_set
     1488       
     1489    def linear_subspace(self):
     1490        r"""
     1491        Return the largest linear subspace contained inside of ``self``.
     1492       
     1493        OUTPUT:
     1494       
     1495        - subspace of the ambient space of ``self``.
     1496       
     1497        EXAMPLES::
     1498       
     1499            sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
     1500            sage: halfplane.linear_subspace()
     1501            Vector space of degree 2 and dimension 1 over Rational Field
     1502            Basis matrix:
     1503            [1 0]
     1504        """
     1505        if "_linear_subspace" not in self.__dict__:
     1506            if self.is_strictly_convex():
     1507                self._linear_subspace = span([vector(QQ, self.ambient_dim())],
     1508                                             QQ)
     1509            else:
     1510                self._linear_subspace = span(self.lines(), QQ)
     1511        return self._linear_subspace       
     1512   
     1513    def lines(self):
     1514        r"""
     1515        Return lines generating the linear subspace of ``self``.
     1516       
     1517        OUTPUT:
     1518       
     1519        - :class:`tuple` of primitive vectors in the lattice of ``self``
     1520          giving directions of lines that span the linear subspace of
     1521          ``self``. These lines are arbitrary, but fixed. If you do not care
     1522          about the order, see also :meth:`line_set`.
     1523         
     1524        EXAMPLES::
     1525       
     1526            sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
     1527            sage: halfplane.lines()
     1528            (N(1, 0),)
     1529            sage: fullplane = Cone([(1,0), (0,1), (-1,-1)])
     1530            sage: fullplane.lines()
     1531            (N(1, 0), N(0, 1))
     1532        """
     1533        if "_lines" not in self.__dict__:
     1534            if self.is_strictly_convex():
     1535                self._lines = ()
     1536            else:
     1537                self._lines = tuple(normalize_rays(self.polyhedron().lines(),
     1538                                                   self.lattice()))
     1539        return self._lines
     1540       
     1541    def polyhedron(self):
     1542        r"""
     1543        Return the polyhedron associated to ``self``.
     1544       
     1545        Mathematically this polyhedron is the same as ``self``.
     1546       
     1547        See also :meth:`lattice_polytope`.
     1548       
     1549        OUTPUT:
     1550       
     1551        - :class:`~sage.geometry.polyhedra.Polyhedron`.
     1552       
     1553        EXAMPLES::
     1554       
     1555            sage: quadrant = Cone([(1,0), (0,1)])
     1556            sage: ph = quadrant.polyhedron()
     1557            sage: ph
     1558            A 2-dimensional polyhedron in QQ^2 defined as the convex hull
     1559            of 1 vertex and 2 rays.
     1560            sage: line = Cone([(1,0), (-1,0)])
     1561            sage: ph = line.polyhedron()
     1562            sage: ph
     1563            A 1-dimensional polyhedron in QQ^2 defined as the convex hull
     1564            of 1 vertex and 1 line.
     1565        """
     1566        if "_polyhedron" not in self.__dict__:
     1567            self._polyhedron = Polyhedron(rays=self.rays())
     1568        return self._polyhedron
     1569   
     1570    def strict_quotient(self):
     1571        r"""
     1572        Return the quotient of ``self`` by the linear subspace.
     1573       
     1574        We define the **strict quotient** of a cone to be the image of this
     1575        cone in the quotient of the ambient space by the linear subspace of
     1576        the cone, i.e. it is the "complementary part" to the linear subspace.
     1577       
     1578        OUTPUT:
     1579       
     1580        - cone.
     1581       
     1582        EXAMPLES::
     1583       
     1584            sage: halfplane = Cone([(1,0), (0,1), (-1,0)])
     1585            sage: ssc = halfplane.strict_quotient()           
     1586            sage: ssc
     1587            1-dimensional cone
     1588            sage: ssc.rays()
     1589            (N(1),)
     1590            sage: line = Cone([(1,0), (-1,0)])
     1591            sage: ssc = line.strict_quotient()           
     1592            sage: ssc
     1593            0-dimensional cone
     1594            sage: ssc.rays()
     1595            ()
     1596        """
     1597        if "_strict_quotient" not in self.__dict__:
     1598            if self.is_strictly_convex():
     1599                self._strict_quotient = self
     1600            else:
     1601                L = self.lattice()
     1602                Q = L.base_extend(QQ) / self.linear_subspace()
     1603                # Maybe we can improve this one if we create something special
     1604                # for sublattices. But it seems to be the most natural choice
     1605                # for names. If many subcones land in the same lattice -
     1606                # that's just how it goes.
     1607                if is_ToricLattice(L):
     1608                    S = ToricLattice(Q.dimension(), L._name, L._dual_name,
     1609                                     L._latex_name, L._latex_dual_name)
     1610                else:
     1611                    S = ZZ**Q.dimension()
     1612                rays = [Q(ray) for ray in self.rays() if not Q(ray).is_zero()]
     1613                quotient = Cone(rays, S, check=False)
     1614                quotient._is_strictly_convex = True
     1615                self._strict_quotient = quotient
     1616        return self._strict_quotient
     1617               
     1618               
     1619class ConeFace(ConvexRationalPolyhedralCone):
     1620    r"""   
     1621    Constuct a face of a cone (which is again a cone).
     1622   
     1623    This class provides access to the containing cone and face incidence
     1624    information.
     1625   
     1626    .. WARNING::
     1627   
     1628        This class will sort the incidence information, but it does not check
     1629        that the input defines a valid face. You should not construct objects
     1630        of this class directly.   
     1631       
     1632    INPUT:
     1633   
     1634    - ``cone_rays`` -- indices of rays of ``cone`` contained in this face;
     1635   
     1636    - ``cone_facets`` -- indices of facets of ``cone`` containing this face;
     1637   
     1638    - ``cone`` -- cone whose face is constructed.
     1639   
     1640    OUTPUT:
     1641   
     1642    - face of ``cone``.
     1643   
     1644    TESTS:
     1645   
     1646    The following code is likely to construct an invalid face, we just test
     1647    that face creation is working::
     1648   
     1649        sage: quadrant = Cone([(1,0), (0,1)])
     1650        sage: face = sage.geometry.cone.ConeFace([0], [0], quadrant)
     1651        sage: face
     1652        1-dimensional face of 2-dimensional cone
     1653        sage: TestSuite(face).run()
     1654       
     1655    The intended way to get objects of this class is the following::
     1656   
     1657        sage: face = quadrant.facets()[0]
     1658        sage: face
     1659        1-dimensional face of 2-dimensional cone
     1660        sage: face.cone_rays()
     1661        (0,)
     1662        sage: face.cone_facets()
     1663        (1,)
     1664    """               
     1665   
     1666    def __init__(self, cone_rays, cone_facets, cone):
     1667        r"""
     1668        See :class:`ConeFace` for documentation.
     1669       
     1670        TESTS::
     1671       
     1672        The following code is likely to construct an invalid face, we just
     1673        test that face creation is working::
     1674       
     1675            sage: quadrant = Cone([(1,0), (0,1)])
     1676            sage: face = sage.geometry.cone.ConeFace([0], [0], quadrant)
     1677            sage: face
     1678            1-dimensional face of 2-dimensional cone
     1679        """
     1680        self._cone_rays = tuple(sorted(cone_rays))
     1681        self._cone_facets = tuple(sorted(cone_facets))
     1682        self._cone = cone
     1683        super(ConeFace, self).__init__(cone.rays(self._cone_rays),
     1684                                       cone.lattice())
     1685
     1686    def _latex_(self):
     1687        r"""
     1688        Return a LaTeX representation of ``self``.
     1689       
     1690        OUTPUT:
     1691       
     1692        - string.
     1693       
     1694        TESTS::
     1695       
     1696            sage: quadrant = Cone([(1,0), (0,1)])
     1697            sage: face = quadrant.facets()[0]
     1698            sage: face._latex_()
     1699            '\\sigma^{1} \\subset \\sigma^{2}'
     1700        """
     1701        return r"%s \subset %s" % (super(ConeFace, self)._latex_(),
     1702                                   self.cone()._latex_())
     1703       
     1704    def _repr_(self):
     1705        r"""
     1706        Return a string representation of ``self``.
     1707       
     1708        OUTPUT:
     1709       
     1710        - string.
     1711       
     1712        TESTS::
     1713       
     1714            sage: quadrant = Cone([(1,0), (0,1)])
     1715            sage: face = quadrant.facets()[0]
     1716            sage: face
     1717            1-dimensional face of 2-dimensional cone
     1718            sage: face._repr_()
     1719            '1-dimensional face of 2-dimensional cone'
     1720        """
     1721        return "%d-dimensional face of %s" % (self.dim(), self.cone())
     1722   
     1723    def cone(self):
     1724        r"""
     1725        Return the "ambient cone", i.e. the cone whose face ``self`` is.
     1726       
     1727        OUTPUT:
     1728       
     1729        - cone.
     1730       
     1731        EXAMPLES::
     1732
     1733            sage: quadrant = Cone([(1,0), (0,1)])
     1734            sage: face = quadrant.facets()[0]
     1735            sage: face.cone() is quadrant
     1736            True
     1737        """
     1738        return self._cone
     1739               
     1740    def cone_facets(self):
     1741        r"""
     1742        Return indices of facets of the "ambient cone" containing ``self``.
     1743       
     1744        OUTPUT:
     1745       
     1746        - increasing :class:`tuple` of integers.
     1747
     1748        EXAMPLES::
     1749
     1750            sage: quadrant = Cone([(1,0), (0,1)])
     1751            sage: face = quadrant.faces(dim=0)[0]
     1752            sage: face.cone_facets()
     1753            (0, 1)
     1754        """
     1755        return self._cone_facets
     1756               
     1757    def cone_rays(self):
     1758        r"""
     1759        Return indices of rays of the "ambient cone" contained in ``self``.
     1760       
     1761        OUTPUT:
     1762       
     1763        - increasing :class:`tuple` of integers.
     1764
     1765        EXAMPLES::
     1766
     1767            sage: quadrant = Cone([(1,0), (0,1)])
     1768            sage: face = quadrant.faces(dim=2)[0]
     1769            sage: face.cone_rays()
     1770            (0, 1)
     1771        """
     1772        return self._cone_rays
     1773               
     1774
     1775def hasse_diagram_from_incidences(atom_to_coatoms, coatom_to_atoms,
     1776                                  face_constructor=None, **kwds):
     1777    r"""
     1778    Compute the Hasse diagram of an atomic and coatomic lattice.
     1779   
     1780    INPUT:
     1781   
     1782    - ``atom_to_coatoms`` - list, ``atom_to_coatom[i]`` should list all
     1783      coatoms over the ``i``-th atom;
     1784   
     1785    - ``coatom_to_atoms`` - list, ``coatom_to_atom[i]`` should list all
     1786      atoms under the ``i``-th coatom;
     1787   
     1788    - ``face_constructor`` - function or class taking as the first two
     1789      arguments :class:`frozenset` and any number of keyword arguments. It
     1790      will be called to construct a face over atoms passed as the first
     1791      argument and under coatoms passed as the second argument;
     1792     
     1793    - all other keyword arguments will be passed to ``face_constructor`` on
     1794      each call.
     1795   
     1796    OUTPUT:
     1797   
     1798    - :class:`~sage.combinat.posets.posets.FinitePoset` with elements
     1799      constructed by ``face_constructor``.   
     1800     
     1801    ALGORITHM:
     1802   
     1803    The detailed description of the used algorithm is given in [KP2002]_.
     1804   
     1805    The code of this function follows the pseudocode description in the
     1806    section 2.5 of the paper, although it is mostly based on frozen sets
     1807    instead of sorted lists - this makes the implementation easier and should
     1808    not cost a big performance penalty. (If one wants to make this function
     1809    faster, it should be probably written in Cython.)
     1810   
     1811    While the title of the paper mentions only polytopes, the algorithm (and
     1812    the implementation provided here) is applicable to any atomic and coatomic
     1813    lattice if both incidences are given, see Section 3.4.
     1814   
     1815    In particular, this function can be used for cones and complete fans.
     1816   
     1817    REFERENCES:
     1818   
     1819    ..  [KP2002]
     1820        Volker Kaibel and Marc E. Pfetsch,
     1821        "Computing the Face Lattice of a Polytope from its Vertex-Facet
     1822        Incidences", Computational Geometry: Theory and Applications,
     1823        Volume 23, Issue 3 (November 2002), 281-290.
     1824        Available at http://portal.acm.org/citation.cfm?id=763203
     1825       
     1826    AUTHORS:
     1827   
     1828    - Andrey Novoseltsev (2010-05-13) with thanks to Marshall Hampton for the
     1829      reference.
     1830     
     1831    EXAMPLES:
     1832   
     1833    Let's construct the Hasse diagram of a lattice of subsets of {0, 1, 2}.
     1834    Our atoms are {0}, {1}, and {2}, while our coatoms are {0,1}, {0,2}, and
     1835    {1,2}. Then incidences are ::
     1836   
     1837        sage: atom_to_coatoms = [(0,1), (0,2), (1,2)]
     1838        sage: coatom_to_atoms = [(0,1), (0,2), (1,2)]
     1839       
     1840    Let's store elements of the lattice as pairs of sorted tuples::
     1841   
     1842        sage: face_constructor = \
     1843        ...      lambda a, b: (tuple(sorted(a)), tuple(sorted(b)))
     1844       
     1845    Then we can compute the Hasse diagram as ::
     1846   
     1847        sage: L = sage.geometry.cone.hasse_diagram_from_incidences(
     1848        ...       atom_to_coatoms, coatom_to_atoms, face_constructor)
     1849        sage: L
     1850        Finite poset containing 8 elements
     1851        sage: for level in L.level_sets(): print level   
     1852        [((), (0, 1, 2))]
     1853        [((0,), (0, 1)), ((1,), (0, 2)), ((2,), (1, 2))]
     1854        [((0, 1), (0,)), ((0, 2), (1,)), ((1, 2), (2,))]
     1855        [((0, 1, 2), ())]
     1856   
     1857    For more involved examples see the source code of
     1858    :meth:`~ConvexRationalPolyhedralCone.face_lattice` and
     1859    :meth:`~RationalPolyhedralFan.cone_lattice`.
     1860    """
     1861    def default_face_constructor(atoms, coatoms, **kwds):
     1862        return (atoms, coatoms)
     1863    if face_constructor is None:
     1864        face_constructor = default_face_constructor
     1865       
     1866    atom_to_coatoms = [frozenset(atc) for atc in atom_to_coatoms]
     1867    A = frozenset(range(len(atom_to_coatoms)))  # All atoms
     1868    coatom_to_atoms = [frozenset(cta) for cta in coatom_to_atoms]
     1869    C = frozenset(range(len(coatom_to_atoms)))  # All coatoms
     1870    # Comments with numbers correspond to steps in Section 2.5 of the article
     1871    L = DiGraph()       # 3: initialize L
     1872    faces = dict()
     1873    atoms = frozenset()
     1874    coatoms = C
     1875    faces[atoms, coatoms] = 0
     1876    next_index = 1
     1877    Q = [(atoms, coatoms)]              # 4: initialize Q with the empty face
     1878    while Q:                            # 5
     1879        q_atoms, q_coatoms = Q.pop()    # 6: remove some q from Q
     1880        q = faces[q_atoms, q_coatoms]
     1881        # 7: compute H = {closure(q+atom) : atom not in atoms of q}
     1882        H = dict()
     1883        candidates = set(A.difference(q_atoms))
     1884        for atom in candidates:
     1885            coatoms = q_coatoms.intersection(atom_to_coatoms[atom])
     1886            atoms = A
     1887            for coatom in coatoms:
     1888                atoms = atoms.intersection(coatom_to_atoms[coatom])
     1889            H[atom] = (atoms, coatoms)
     1890        # 8: compute the set G of minimal sets in H
     1891        minimals = set([])
     1892        while candidates:
     1893            candidate = candidates.pop()
     1894            atoms = H[candidate][0]
     1895            if atoms.isdisjoint(candidates) and atoms.isdisjoint(minimals):
     1896                minimals.add(candidate)
     1897        # Now G == {H[atom] : atom in minimals}
     1898        for atom in minimals:   # 9: for g in G:
     1899            g_atoms, g_coatoms = H[atom]
     1900            if (g_atoms, g_coatoms) in faces:
     1901                g = faces[g_atoms, g_coatoms]
     1902            else:               # 11: if g was newly created
     1903                g = next_index
     1904                faces[g_atoms, g_coatoms] = g
     1905                next_index += 1
     1906                Q.append((g_atoms, g_coatoms))  # 12
     1907            L.add_edge(q, g)                    # 14
     1908    # End of algorithm, now construct a FinitePoset
     1909    # In principle, it is recommended to use Poset or in this case perhaps
     1910    # even LatticePoset, but it seems to take several times more time
     1911    # than the above computation, makes unnecessary copies, and crashes.
     1912    # So for now we will mimick the relevant code from Poset.
     1913    # Relabel vertices so {0,...,n} becomes a linear extension of the poset.
     1914    labels = dict()
     1915    for new, old in enumerate(L.topological_sort()):
     1916        labels[old] = new
     1917    L.relabel(labels)
     1918    # Set element labels.
     1919    elements = [None] * next_index
     1920    for face, index in faces.items():
     1921        elements[labels[index]] = face_constructor(*face, **kwds)
     1922    return FinitePoset(L, elements)
  • sage/geometry/toric_lattice.py

    diff -r 2bccc5894319 -r 3bc5e812f2d4 sage/geometry/toric_lattice.py
    a b  
    116116    sage: h = N.hom(identity_matrix(3), M)
    117117    sage: h(n)
    118118    M(1, 2, 3)
     119   
     120.. WARNING::
     121
     122    While integer vectors (elements of `\ZZ^n`) are printed as ``(1,2,3)``,
     123    in the code ``(1,2,3)`` is a :class:`tuple`, which has nothing to do
     124    neither with vectors, nor with toric lattices, so the following is
     125    probably not what you want while working with toric geometry objects::
     126   
     127        sage: (1,2,3) + (1,2,3)
     128        (1, 2, 3, 1, 2, 3)
     129       
     130    Instead, use syntax like ::
     131   
     132        sage: N(1,2,3) + N(1,2,3)
     133        N(2, 4, 6)
    119134"""
    120135# Parts of the "tutorial" above are also in toric_lattice_element.pyx.
    121136
     
    137152from sage.structure.factory import UniqueFactory
    138153
    139154
     155def is_ToricLattice(x):
     156    r"""
     157    Check if ``x`` is a toric lattice.
     158   
     159    INPUT:
     160   
     161    - ``x`` -- anything.
     162   
     163    OUTPUT:
     164   
     165    - ``True`` if ``x`` is a toric lattice and ``False`` otherwise.
     166   
     167    EXAMPLES::
     168   
     169        sage: from sage.geometry.toric_lattice import (
     170        ...     is_ToricLattice)
     171        sage: is_ToricLattice(1)
     172        False
     173        sage: N = ToricLattice(3)
     174        sage: N
     175        3-dimensional lattice N
     176        sage: is_ToricLattice(N)
     177        True
     178    """
     179    return isinstance(x, ToricLatticeClass)
     180
     181
    140182class ToricLatticeFactory(UniqueFactory):
    141183    r"""
    142184    Create a lattice for toric geometry objects.
     
    435477                   [right._name, right._dual_name,
    436478                    right._latex_name, right._latex_dual_name])
    437479                   
     480    def __contains__(self, point):
     481        r"""
     482        Check if ``point`` is an element of ``self``.
     483       
     484        INPUT:
     485       
     486        - ``point`` -- anything.
     487       
     488        OUTPUT:
     489       
     490        - ``True`` if ``point`` is an element of ``self``, ``False``
     491          otherwise.
     492         
     493        TESTS::
     494       
     495            sage: N = ToricLattice(3)
     496            sage: M = N.dual()
     497            sage: L = ToricLattice(3, "L")
     498            sage: 1 in N
     499            False
     500            sage: (1,0) in N
     501            False
     502            sage: (1,0,0) in N
     503            True
     504            sage: N(1,0,0) in N
     505            True
     506            sage: M(1,0,0) in N
     507            False
     508            sage: L(1,0,0) in N
     509            False
     510            sage: (1/2,0,0) in N
     511            False
     512            sage: (2/2,0,0) in N
     513            True
     514        """
     515        try:
     516            self(point)
     517        except TypeError:
     518            return False
     519        return True
     520                   
    438521    def _latex_(self):
    439522        r"""
    440523        Return a LaTeX representation of ``self``.
  • sage/geometry/toric_lattice_element.pyx

    diff -r 2bccc5894319 -r 3bc5e812f2d4 sage/geometry/toric_lattice_element.pyx
    a b  
    213213            return c
    214214        # Now use the real comparison of vectors
    215215        return self._cmp_c_impl(right)
     216   
     217    # For some reason, vectors work just fine without redefining this function
     218    # from the base class, but if it is not here, we get "unhashable type"...
     219    def __hash__(self):
     220        r"""
     221        Return the hash of ``self``.
     222       
     223        OUTPUT:
     224       
     225        - integer.
     226       
     227        TESTS::
     228       
     229            sage: N = ToricLattice(3)
     230            sage: n = N(1,2,3)
     231            sage: hash(n)
     232            Traceback (most recent call last):
     233            ...
     234            TypeError: mutable vectors are unhashable           
     235            sage: n.set_immutable()
     236            sage: hash(n)  # 64-bit
     237            2528502973977326415
     238        """
     239        return Vector_integer_dense.__hash__(self)
    216240
    217241    cpdef _act_on_(self, other, bint self_on_left):
    218242        """
     
    240264            sage: m = M(1,2,3)
    241265            sage: n * m # indirect doctest
    242266            14
     267           
     268        Now we test behaviour with other types::
     269       
     270            sage: v = vector([1, 2, 3])
     271            sage: v * n == n * v
     272            True
    243273            sage: v = vector([1, 1/2, 3/4])
    244274            sage: v * n == n * v
    245275            True
     276            sage: A = matrix(3, range(9))
     277            sage: A * n
     278            (8, 26, 44)
     279            sage: n * A
     280            (24, 30, 36)
     281            sage: B = A / 3
     282            sage: B * n
     283            (8/3, 26/3, 44/3)
     284            sage: n * B
     285            (8, 10, 12)
    246286        """
    247         # We deal only with the case of two lattice elements
     287        # We try to deal only with the case of two lattice elements...
    248288        if is_ToricLatticeElement(other):
    249289            if other.parent() is self.parent().dual():
    250290                # Our own _dot_product_ is disabled
    251291                return Vector_integer_dense._dot_product_(self, other)               
    252292            raise CoercionException("only elements of dual toric lattices "
    253293                                    "can act on each other!")
     294        # ... however we also need to treat the case when other is an integral
     295        # vector, since otherwise it will be coerced to the parent of self and
     296        # then the dot product will be called for elements of the same lattice
     297        if isinstance(other, Vector_integer_dense):
     298            return Vector_integer_dense._dot_product_(self, other)
    254299        # Now let the standard framework work...
    255300        return Vector_integer_dense._act_on_(self, other, self_on_left)
    256301