Ticket #13191: trac_13191_auto_fan_2d.patch

File trac_13191_auto_fan_2d.patch, 7.2 KB (added by vbraun, 10 years ago)

Updated patch

  • sage/geometry/all.py

    # HG changeset patch
    # User Volker Braun <vbraun@stp.dias.ie>
    # Date 1341765476 -3600
    # Node ID e5d40497a7e4a5a431be861171dfb2ed40a22d33
    # Parent  7b3862293a3b7ecc030a524376e1b1e559ebc343
    Automatically construct a 2d-fan from rays (without specifying cones)
    
    diff --git a/sage/geometry/all.py b/sage/geometry/all.py
    a b  
    11from cone import Cone
    22
    3 from fan import Fan, FaceFan, NormalFan
     3from fan import Fan, FaceFan, NormalFan, Fan2d
    44
    55from fan_morphism import FanMorphism
    66
  • sage/geometry/fan.py

    diff --git a/sage/geometry/fan.py b/sage/geometry/fan.py
    a b  
    3636first using :func:`~sage.geometry.cone.Cone` and then combine them into a fan.
    3737See the documentation of :func:`Fan` for details.
    3838
    39 Instead of building a fan from scratch, for this tutorial we will use an easy
    40 way to get two fans assosiated to :class:`lattice polytopes
    41 <sage.geometry.lattice_polytope.LatticePolytopeClass>`: :func:`FaceFan` and
    42 :func:`NormalFan`::
     39In 2 dimensions there is a unique maximal fan determined by rays, and
     40you can use :func:`Fan2d` to construct it::
     41
     42    sage: fan2d = Fan2d(rays=[(1,0), (0,1), (-1,0)])
     43    sage: fan2d.is_equivalent(fan)
     44    True
     45
     46But keep in mind that in higher dimensions the cone data is essential
     47and cannot be omitted. Instead of building a fan from scratch, for
     48this tutorial we will use an easy way to get two fans assosiated to
     49:class:`lattice polytopes
     50<sage.geometry.lattice_polytope.LatticePolytopeClass>`:
     51:func:`FaceFan` and :func:`NormalFan`::
    4352
    4453    sage: fan1 = FaceFan(lattice_polytope.octahedron(3))
    4554    sage: fan2 = NormalFan(lattice_polytope.octahedron(3))
     
    363372
    364373    - a :class:`fan <RationalPolyhedralFan>`.
    365374
     375    .. SEEALSO::
     376
     377        In 2 dimensions you can cyclically order the rays. Hence the
     378        rays determine a unique maximal fan without having to specify
     379        the cones, and you can use :func:`Fan2d` to construct this
     380        fan from just the rays.
     381
    366382    EXAMPLES:
    367383
    368384    Let's construct a fan corresponding to the projective plane in several
     
    681697    return fan
    682698
    683699
     700def Fan2d(rays, lattice=None):
     701    """
     702    Construct the maximal 2-d fan with given ``rays``.
     703
     704    In two dimensions we can uniquely construct a fan from just rays,
     705    just by cyclically ordering the rays and constructing as many
     706    cones as possible. This is why we implement a special constructor
     707    for this case.
     708
     709    INPUT:
     710
     711    - ``rays`` -- list of rays given as list or vectors convertible to
     712      the rational extension of ``lattice``. Duplicate rays are
     713      removed without changing the ordering of the remaining rays.
     714
     715    - ``lattice`` -- :class:`ToricLattice
     716      <sage.geometry.toric_lattice.ToricLatticeFactory>`, `\ZZ^n`, or any
     717      other object that behaves like these. If not specified, an attempt will
     718      be made to determine an appropriate toric lattice automatically.
     719
     720    EXAMPLES::
     721   
     722        sage: Fan2d([(0,1), (1,0)])
     723        Rational polyhedral fan in 2-d lattice N
     724        sage: Fan2d([], lattice=ToricLattice(2, 'myN'))
     725        Rational polyhedral fan in 2-d lattice myN
     726
     727    The ray order is as specified, even if it is not the cyclic order::
     728
     729        sage: fan1 = Fan2d([(0,1), (1,0)])
     730        sage: fan1.rays()
     731        N(0, 1),
     732        N(1, 0)
     733        in 2-d lattice N
     734
     735        sage: fan2 = Fan2d([(1,0), (0,1)])
     736        sage: fan2.rays()
     737        N(1, 0),
     738        N(0, 1)
     739        in 2-d lattice N
     740       
     741        sage: fan1 == fan2, fan1.is_equivalent(fan2)
     742        (False, True)
     743
     744        sage: fan = Fan2d([(1,1), (-1,-1), (1,-1), (-1,1)])
     745        sage: [ cone.ambient_ray_indices() for cone in fan ]
     746        [(2, 1), (1, 3), (3, 0), (0, 2)]
     747        sage: fan.is_complete()
     748        True
     749
     750    TESTS::
     751
     752        sage: Fan2d([(0,1), (0,1)]).generating_cones()
     753        (1-d cone of Rational polyhedral fan in 2-d lattice N,)
     754
     755        sage: Fan2d([(1,1), (-1,-1)]).generating_cones()
     756        (1-d cone of Rational polyhedral fan in 2-d lattice N,
     757         1-d cone of Rational polyhedral fan in 2-d lattice N)
     758
     759        sage: Fan2d([])
     760        Traceback (most recent call last):
     761        ...
     762        ValueError: you must specify a 2-dimensional lattice
     763        when you construct a fan without rays.
     764
     765        sage: Fan2d([(3,4)]).rays()
     766        N(3, 4)
     767        in 2-d lattice N
     768
     769        sage: Fan2d([(0,1,0)])
     770        Traceback (most recent call last):
     771        ...
     772        ValueError: the lattice must be 2-dimensional.
     773
     774        sage: Fan2d([(0,1), (1,0), (0,0)])
     775        Traceback (most recent call last):
     776        ...
     777        ValueError: only non-zero vectors define rays
     778
     779        sage: Fan2d([(0, -2), (2, -10), (1, -3), (2, -9), (2, -12), (1, 1),
     780        ...          (2, 1), (1, -5), (0, -6), (1, -7), (0, 1), (2, -4),
     781        ...          (2, -2), (1, -9), (1, -8), (2, -6), (0, -1), (0, -3),
     782        ...          (2, -11), (2, -8), (1, 0), (0, -5), (1, -4), (2, 0),
     783        ...          (1, -6), (2, -7), (2, -5), (-1, -3), (1, -1), (1, -2),
     784        ...          (0, -4), (2, -3), (2, -1)]).cone_lattice()
     785        Finite poset containing 44 elements
     786       
     787        sage: Fan2d([(1,1)]).is_complete()
     788        False
     789        sage: Fan2d([(1,1), (-1,-1)]).is_complete()
     790        False
     791        sage: Fan2d([(1,0), (0,1)]).is_complete()
     792        False
     793    """
     794    if len(rays) == 0:
     795        if lattice is None or lattice.dimension() != 2:
     796            raise ValueError('you must specify a 2-dimensional lattice when you construct a fan without rays.')
     797        return RationalPolyhedralFan(cones=((), ), rays=(), lattice=lattice)
     798
     799    # remove multiple rays without changing order
     800    rays = normalize_rays(rays, lattice)
     801    rays = sorted( (r,i) for i,r in enumerate(rays) )
     802    distinct_rays = [ rays[i] for i in range(len(rays)) if rays[i][0] != rays[i-1][0] ]
     803    if distinct_rays:
     804        rays = sorted( (i,r) for r,i in distinct_rays )
     805        rays = [ r[1] for r in rays ]
     806    else: # all given rays were the same
     807        rays = [ rays[0][0] ]
     808    lattice = rays[0].parent()
     809    if lattice.dimension() != 2:
     810        raise ValueError('the lattice must be 2-dimensional.')
     811         
     812    import math
     813    # each sorted_rays entry = (angle, ray, original_ray_index)
     814    sorted_rays = sorted( (math.atan2(r[0],r[1]), r, i) for i,r in enumerate(rays) )
     815    sorted_rays = [ r for i,r in enumerate(sorted_rays) if r[1] != sorted_rays[i-1][1] ]
     816    cones = []
     817    is_complete = True
     818    for i in range(len(sorted_rays)):
     819        r0 = sorted_rays[i-1][1]
     820        r1 = sorted_rays[i][1]
     821        if r1.is_zero():
     822            raise ValueError('only non-zero vectors define rays')
     823        assert r0 != r1
     824        cross_prod = r0[0]*r1[1]-r0[1]*r1[0]
     825        if cross_prod < 0:
     826            r0_index = (i-1) % len(sorted_rays)
     827            r1_index = i
     828            cones.append((sorted_rays[r0_index][2], sorted_rays[r1_index][2]))
     829        else:
     830            is_complete = False
     831    if cones == []:
     832        cones = [ (i,) for i in range(len(rays)) ]
     833        is_complete = False
     834    return RationalPolyhedralFan(cones, rays, lattice, is_complete)
     835
     836
    684837class Cone_of_fan(ConvexRationalPolyhedralCone):
    685838    r"""
    686839    Construct a cone belonging to a fan.