Morphisms between toric lattices compatible with fans - Combinatorial and Discrete Geometry

Skip to content
Back to top
View this page
Edit this page

# Morphisms between toric lattices compatible with fans

This module is a part of the framework for toric varieties (`variety`, `fano_variety`). Its main purpose is to provide support for working with lattice morphisms compatible with fans via `FanMorphism` class.

AUTHORS:

- Andrey Novoseltsev (2010-10-17): initial version.

- Andrey Novoseltsev (2011-04-11): added tests for injectivity/surjectivity,

    fibration, bundle, as well as some related methods.

EXAMPLES:

Let’s consider the face and normal fans of the “diamond” and the projection to the \(x\)-axis:

Sage

    sage: diamond = lattice_polytope.cross_polytope(2)
    sage: face = FaceFan(diamond, lattice=ToricLattice(2))
    sage: normal = NormalFan(diamond)
    sage: N = face.lattice()
    sage: H = End(N)
    sage: phi = H([N.0, 0])
    sage: phi
    Free module morphism defined by the matrix
    [1 0]
    [0 0]
    Domain: 2-d lattice N
    Codomain: 2-d lattice N
    sage: FanMorphism(phi, normal, face)
    Traceback (most recent call last):
    ...
    ValueError: the image of generating cone #1 of the domain fan
    is not contained in a single cone of the codomain fan!

Python

    >>> from sage.all import *
    >>> diamond = lattice_polytope.cross_polytope(Integer(2))
    >>> face = FaceFan(diamond, lattice=ToricLattice(Integer(2)))
    >>> normal = NormalFan(diamond)
    >>> N = face.lattice()
    >>> H = End(N)
    >>> phi = H([N.gen(0), Integer(0)])
    >>> phi
    Free module morphism defined by the matrix
    [1 0]
    [0 0]
    Domain: 2-d lattice N
    Codomain: 2-d lattice N
    >>> FanMorphism(phi, normal, face)
    Traceback (most recent call last):
    ...
    ValueError: the image of generating cone #1 of the domain fan
    is not contained in a single cone of the codomain fan!

Some of the cones of the normal fan fail to be mapped to a single cone of the face fan. We can rectify the situation in the following way:

Sage

    sage: fm = FanMorphism(phi, normal, face, subdivide=True)
    sage: fm
    Fan morphism defined by the matrix
    [1 0]
    [0 0]
    Domain fan: Rational polyhedral fan in 2-d lattice N
    Codomain fan: Rational polyhedral fan in 2-d lattice N
    sage: fm.domain_fan().rays()
    N( 1,  1),
    N( 1, -1),
    N(-1, -1),
    N(-1,  1),
    N( 0, -1),
    N( 0,  1)
    in 2-d lattice N
    sage: normal.rays()
    N( 1,  1),
    N( 1, -1),
    N(-1, -1),
    N(-1,  1)
    in 2-d lattice N

[Python]

    >>> from sage.all import *
    >>> fm = FanMorphism(phi, normal, face, subdivide=True)
    >>> fm
    Fan morphism defined by the matrix
    [1 0]
    [0 0]
    Domain fan: Rational polyhedral fan in 2-d lattice N
    Codomain fan: Rational polyhedral fan in 2-d lattice N
    >>> fm.domain_fan().rays()
    N( 1,  1),
    N( 1, -1),
    N(-1, -1),
    N(-1,  1),
    N( 0, -1),
    N( 0,  1)
    in 2-d lattice N
    >>> normal.rays()
    N( 1,  1),
    N( 1, -1),
    N(-1, -1),
    N(-1,  1)
    in 2-d lattice N

As you see, it was necessary to insert two new rays (to prevent “upper” and “lower” cones of the normal fan from being mapped to the whole \(x\)-axis).

class sage.geometry.fan_morphism.FanMorphism(morphism, domain_fan, codomain=None, subdivide=False, check=True, verbose=False)

  Bases: `FreeModuleMorphism`

  Create a fan morphism.

  Let \(\Sigma_1\) and \(\Sigma_2\) be two fans in lattices \(N_1\) and \(N_2\) respectively. Let \(\phi\) be a morphism (i.e. a linear map) from \(N_1\) to \(N_2\). We say that \(\phi\) is compatible with \(\Sigma_1\) and \(\Sigma_2\) if every cone \(\sigma_1\in\Sigma_1\) is mapped by \(\phi\) into a single cone \(\sigma_2\in\Sigma_2\), i.e. \(\phi(\sigma_1)\subset\sigma_2\) (\(\sigma_2\) may be different for different \(\sigma_1\)).

  By a fan morphism we understand a morphism between two lattices compatible with specified fans in these lattices. Such morphisms behave in exactly the same way as “regular” morphisms between lattices, but:

  - fan morphisms have a special constructor allowing some automatic adjustments to the initial fans (see below);

  - fan morphisms are aware of the associated fans and they can be accessed via `codomain_fan()` and `domain_fan()`;

  - fan morphisms can efficiently compute `image_cone()` of a given cone of the domain fan and `preimage_cones()` of a given cone of the codomain fan.

  INPUT:

  - `morphism` – either a morphism between domain and codomain, or an integral matrix defining such a morphism;

  - `domain_fan` – a `fan` in the domain

  - `codomain` – (default: `None`) either a codomain lattice or a fan in the codomain. If the codomain fan is not given, the image fan (fan generated by images of generating cones) of `domain_fan` will be used, if possible.

  - `subdivide` – boolean (default: `False`); if `True` and `domain_fan` is not compatible with the codomain fan because it is too coarse, it will be automatically refined to become compatible (the minimal refinement is canonical, so there are no choices involved)

  - `check` – boolean (default: `True`); if `False`, given fans and morphism will be assumed to be compatible. Be careful when using this option, since wrong assumptions can lead to wrong and hard-to-detect errors. On the other hand, this option may save you some time.

  - `verbose` – boolean (default: `False`); if `True`, some information may be printed during construction of the fan morphism

  OUTPUT: a fan morphism

  EXAMPLES:

  Here we consider the face and normal fans of the “diamond” and the projection to the \(x\)-axis:

  Sage

      sage: diamond = lattice_polytope.cross_polytope(2)
      sage: face = FaceFan(diamond, lattice=ToricLattice(2))
      sage: normal = NormalFan(diamond)
      sage: N = face.lattice()
      sage: H = End(N)
      sage: phi = H([N.0, 0])
      sage: phi
      Free module morphism defined by the matrix
      [1 0]
      [0 0]
      Domain: 2-d lattice N
      Codomain: 2-d lattice N
      sage: fm = FanMorphism(phi, face, normal)
      sage: fm.domain_fan() is face
      True

  Python

      >>> from sage.all import *
      >>> diamond = lattice_polytope.cross_polytope(Integer(2))
      >>> face = FaceFan(diamond, lattice=ToricLattice(Integer(2)))
      >>> normal = NormalFan(diamond)
      >>> N = face.lattice()
      >>> H = End(N)
      >>> phi = H([N.gen(0), Integer(0)])
      >>> phi
      Free module morphism defined by the matrix
      [1 0]
      [0 0]
      Domain: 2-d lattice N
      Codomain: 2-d lattice N
      >>> fm = FanMorphism(phi, face, normal)
      >>> fm.domain_fan() is face
      True

  Note, that since `phi` is compatible with these fans, the returned fan is exactly the same object as the initial `domain_fan`.

  Sage

      sage: FanMorphism(phi, normal, face)
      Traceback (most recent call last):
      ...
      ValueError: the image of generating cone #1 of the domain fan
      is not contained in a single cone of the codomain fan!
      sage: fm = FanMorphism(phi, normal, face, subdivide=True)
      sage: fm.domain_fan() is normal
      False
      sage: fm.domain_fan().n_generating_cones()
      6

  [Python]

      >>> from sage.all import *
      >>> FanMorphism(phi, normal, face)
      Traceback (most recent call last):
      ...
      ValueError: the image of generating cone #1 of the domain fan
      is not contained in a single cone of the codomain fan!
      >>> fm = FanMorphism(phi, normal, face, subdivide=True)
      >>> fm.domain_fan() is normal
      False
      >>> fm.domain_fan().n_generating_cones()
      6

  We had to subdivide two of the four cones of the normal fan, since they were mapped by `phi` into non-strictly convex cones.

  It is possible to omit the codomain fan, in which case the image fan will be used instead of it:

  Sage

      sage: fm = FanMorphism(phi, face)
      sage: fm.codomain_fan()
      Rational polyhedral fan in 2-d lattice N
      sage: fm.codomain_fan().rays()
      N( 1, 0),
      N(-1, 0)
      in 2-d lattice N

  Python

      >>> from sage.all import *
      >>> fm = FanMorphism(phi, face)
      >>> fm.codomain_fan()
      Rational polyhedral fan in 2-d lattice N
      >>> fm.codomain_fan().rays()
      N( 1, 0),
      N(-1, 0)
      in 2-d lattice N

  Now we demonstrate a more subtle example. We take the first quadrant as our domain fan. Then we divide the first quadrant into three cones, throw away the middle one and take the other two as our codomain fan. These fans are incompatible with the identity lattice morphism since the image of the domain fan is out of the support of the codomain fan:

  Sage

      sage: N = ToricLattice(2)
      sage: phi = End(N).identity()
      sage: F1 = Fan(cones=[(0,1)], rays=[(1,0), (0,1)])
      sage: F2 = Fan(cones=[(0,1), (2,3)],
      ....:          rays=[(1,0), (2,1), (1,2), (0,1)])
      sage: FanMorphism(phi, F1, F2)
      Traceback (most recent call last):
      ...
      ValueError: the image of generating cone #0 of the domain fan
      is not contained in a single cone of the codomain fan!
      sage: FanMorphism(phi, F1, F2, subdivide=True)
      Traceback (most recent call last):
      ...
      ValueError: morphism defined by
      [1 0]
      [0 1]
      does not map
      Rational polyhedral fan in 2-d lattice N
      into the support of
      Rational polyhedral fan in 2-d lattice N!

  [Python]

      >>> from sage.all import *
      >>> N = ToricLattice(Integer(2))
      >>> phi = End(N).identity()
      >>> F1 = Fan(cones=[(Integer(0),Integer(1))], rays=[(Integer(1),Integer(0)), (Integer(0),Integer(1))])
      >>> F2 = Fan(cones=[(Integer(0),Integer(1)), (Integer(2),Integer(3))],
      ...          rays=[(Integer(1),Integer(0)), (Integer(2),Integer(1)), (Integer(1),Integer(2)), (Integer(0),Integer(1))])
      >>> FanMorphism(phi, F1, F2)
      Traceback (most recent call last):
      ...
      ValueError: the image of generating cone #0 of the domain fan
      is not contained in a single cone of the codomain fan!
      >>> FanMorphism(phi, F1, F2, subdivide=True)
      Traceback (most recent call last):
      ...
      ValueError: morphism defined by
      [1 0]
      [0 1]
      does not map
      Rational polyhedral fan in 2-d lattice N
      into the support of
      Rational polyhedral fan in 2-d lattice N!

  The problem was detected and handled correctly (i.e. an exception was raised). However, the used algorithm requires extra checks for this situation after constructing a potential subdivision and this can take significant time. You can save about half the time using `check=False` option, if you know in advance that it is possible to make fans compatible with the morphism by subdividing the domain fan. Of course, if your assumption was incorrect, the result will be wrong and you will get a fan which does map into the support of the codomain fan, but is not a subdivision of the domain fan. You can test it on the example above:

  Sage

      sage: fm = FanMorphism(phi, F1, F2, subdivide=True,
      ....:                  check=False, verbose=True)
      Placing ray images (... ms)
      Computing chambers (... ms)
      Number of domain cones: 1.
      Number of chambers: 2.
      Cone 0 sits in chambers 0 1 (... ms)
      sage: fm.domain_fan().is_equivalent(F2)
      True

  Python

      >>> from sage.all import *
      >>> fm = FanMorphism(phi, F1, F2, subdivide=True,
      ...                  check=False, verbose=True)
      Placing ray images (... ms)
      Computing chambers (... ms)
      Number of domain cones: 1.
      Number of chambers: 2.
      Cone 0 sits in chambers 0 1 (... ms)
      >>> fm.domain_fan().is_equivalent(F2)
      True

  codomain_fan(dim=None, codim=None)

    Return the codomain fan of `self`.

    INPUT:

    - `dim` – dimension of the requested cones

    - `codim` – codimension of the requested cones

    OUTPUT:

    - `rational polyhedral fan` if no parameters were given, `tuple` of `cones` otherwise.

    EXAMPLES:

    Sage

        sage: quadrant = Cone([(1,0), (0,1)])
        sage: quadrant = Fan([quadrant])
        sage: quadrant_bl = quadrant.subdivide([(1,1)])
        sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
        sage: fm.codomain_fan()
        Rational polyhedral fan in 2-d lattice N
        sage: fm.codomain_fan() is quadrant
        True

    Python

        >>> from sage.all import *
        >>> quadrant = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))])
        >>> quadrant = Fan([quadrant])
        >>> quadrant_bl = quadrant.subdivide([(Integer(1),Integer(1))])
        >>> fm = FanMorphism(identity_matrix(Integer(2)), quadrant_bl, quadrant)
        >>> fm.codomain_fan()
        Rational polyhedral fan in 2-d lattice N
        >>> fm.codomain_fan() is quadrant
        True

  domain_fan(dim=None, codim=None)

    Return the codomain fan of `self`.

    INPUT:

    - `dim` – dimension of the requested cones

    - `codim` – codimension of the requested cones

    OUTPUT:

    - `rational polyhedral fan` if no parameters were given, `tuple` of `cones` otherwise.

    EXAMPLES:

    Sage

        sage: quadrant = Cone([(1,0), (0,1)])
        sage: quadrant = Fan([quadrant])
        sage: quadrant_bl = quadrant.subdivide([(1,1)])
        sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
        sage: fm.domain_fan()
        Rational polyhedral fan in 2-d lattice N
        sage: fm.domain_fan() is quadrant_bl
        True

    Python

        >>> from sage.all import *
        >>> quadrant = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))])
        >>> quadrant = Fan([quadrant])
        >>> quadrant_bl = quadrant.subdivide([(Integer(1),Integer(1))])
        >>> fm = FanMorphism(identity_matrix(Integer(2)), quadrant_bl, quadrant)
        >>> fm.domain_fan()
        Rational polyhedral fan in 2-d lattice N
        >>> fm.domain_fan() is quadrant_bl
        True

  factor()

    Factor `self` into injective * birational * surjective morphisms.

    OUTPUT:

    - a triple of `FanMorphism` \((\phi_i, \phi_b, \phi_s)\), such that \(\phi_s\) is surjective, \(\phi_b\) is birational, \(\phi_i\) is injective, and `self` is equal to \(\phi_i \circ \phi_b \circ \phi_s\).

    Intermediate fans live in the saturation of the image of `self` as a map between lattices and are the image of the `domain_fan()` and the restriction of the `codomain_fan()`, i.e. if `self` maps \(\Sigma \to \Sigma'\), then we have factorization into

    \[\Sigma \twoheadrightarrow \Sigma_s \to \Sigma_i \hookrightarrow \Sigma.\]

    Note

    - \(\Sigma_s\) is the finest fan with the smallest support that is compatible with `self`: any fan morphism from \(\Sigma\) given by the same map of lattices as `self` factors through \(\Sigma_s\).

    - \(\Sigma_i\) is the coarsest fan of the largest support that is compatible with `self`: any fan morphism into \(\Sigma'\) given by the same map of lattices as `self` factors though \(\Sigma_i\).

    EXAMPLES:

    We map an affine plane into a projective 3-space in such a way, that it becomes “a double cover of a chart of the blow up of one of the coordinate planes”:

    Sage

        sage: A2 = toric_varieties.A2()
        sage: P3 = toric_varieties.P(3)                                             # needs palp
        sage: m = matrix([(2,0,0), (1,1,0)])
        sage: phi = A2.hom(m, P3)                                                   # needs palp
        sage: phi.as_polynomial_map()                                               # needs palp
        Scheme morphism:
          From: 2-d affine toric variety
          To:   3-d CPR-Fano toric variety covered by 4 affine patches
          Defn: Defined on coordinates by sending [x : y] to
                [x^2*y : y : 1 : 1]

    Python

        >>> from sage.all import *
        >>> A2 = toric_varieties.A2()
        >>> P3 = toric_varieties.P(Integer(3))                                             # needs palp
        >>> m = matrix([(Integer(2),Integer(0),Integer(0)), (Integer(1),Integer(1),Integer(0))])
        >>> phi = A2.hom(m, P3)                                                   # needs palp
        >>> phi.as_polynomial_map()                                               # needs palp
        Scheme morphism:
          From: 2-d affine toric variety
          To:   3-d CPR-Fano toric variety covered by 4 affine patches
          Defn: Defined on coordinates by sending [x : y] to
                [x^2*y : y : 1 : 1]

    Now we will work with the underlying fan morphism:

    Sage

        sage: # needs palp
        sage: phi = phi.fan_morphism(); phi
        Fan morphism defined by the matrix
        [2 0 0]
        [1 1 0]
        Domain fan: Rational polyhedral fan in 2-d lattice N
        Codomain fan: Rational polyhedral fan in 3-d lattice N
        sage: phi.is_surjective(), phi.is_birational(), phi.is_injective()
        (False, False, False)
        sage: phi_i, phi_b, phi_s = phi.factor()
        sage: phi_s.is_surjective(), phi_b.is_birational(), phi_i.is_injective()
        (True, True, True)
        sage: prod(phi.factor()) == phi
        True

    [Python]

        >>> from sage.all import *
        >>> # needs palp
        >>> phi = phi.fan_morphism(); phi
        Fan morphism defined by the matrix
        [2 0 0]
        [1 1 0]
        Domain fan: Rational polyhedral fan in 2-d lattice N
        Codomain fan: Rational polyhedral fan in 3-d lattice N
        >>> phi.is_surjective(), phi.is_birational(), phi.is_injective()
        (False, False, False)
        >>> phi_i, phi_b, phi_s = phi.factor()
        >>> phi_s.is_surjective(), phi_b.is_birational(), phi_i.is_injective()
        (True, True, True)
        >>> prod(phi.factor()) == phi
        True

    Double cover (surjective):

    Sage

        sage: A2.fan().rays()
        N(1, 0),
        N(0, 1)
        in 2-d lattice N
        sage: phi_s                                                                 # needs palp
        Fan morphism defined by the matrix
        [2 0]
        [1 1]
        Domain fan: Rational polyhedral fan in 2-d lattice N
        Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        sage: phi_s.codomain_fan().rays()                                           # needs palp
        N(1, 0, 0),
        N(1, 1, 0)
        in Sublattice <N(1, 0, 0), N(0, 1, 0)>

    Python

        >>> from sage.all import *
        >>> A2.fan().rays()
        N(1, 0),
        N(0, 1)
        in 2-d lattice N
        >>> phi_s                                                                 # needs palp
        Fan morphism defined by the matrix
        [2 0]
        [1 1]
        Domain fan: Rational polyhedral fan in 2-d lattice N
        Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        >>> phi_s.codomain_fan().rays()                                           # needs palp
        N(1, 0, 0),
        N(1, 1, 0)
        in Sublattice <N(1, 0, 0), N(0, 1, 0)>

    Blowup chart (birational):

    Sage

        sage: phi_b                                                                 # needs palp
        Fan morphism defined by the matrix
        [1 0]
        [0 1]
        Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        sage: phi_b.codomain_fan().rays()                                           # needs palp
        N(-1, -1, 0),
        N( 0,  1, 0),
        N( 1,  0, 0)
        in Sublattice <N(1, 0, 0), N(0, 1, 0)>

    [Python]

        >>> from sage.all import *
        >>> phi_b                                                                 # needs palp
        Fan morphism defined by the matrix
        [1 0]
        [0 1]
        Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        >>> phi_b.codomain_fan().rays()                                           # needs palp
        N(-1, -1, 0),
        N( 0,  1, 0),
        N( 1,  0, 0)
        in Sublattice <N(1, 0, 0), N(0, 1, 0)>

    Coordinate plane inclusion (injective):

    Sage

        sage: phi_i                                                                 # needs palp
        Fan morphism defined by the matrix
        [1 0 0]
        [0 1 0]
        Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        Codomain fan: Rational polyhedral fan in 3-d lattice N
        sage: phi.codomain_fan().rays()                                             # needs palp
        N( 1,  0,  0),
        N( 0,  1,  0),
        N( 0,  0,  1),
        N(-1, -1, -1)
        in 3-d lattice N

    Python

        >>> from sage.all import *
        >>> phi_i                                                                 # needs palp
        Fan morphism defined by the matrix
        [1 0 0]
        [0 1 0]
        Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
        Codomain fan: Rational polyhedral fan in 3-d lattice N
        >>> phi.codomain_fan().rays()                                             # needs palp
        N( 1,  0,  0),
        N( 0,  1,  0),
        N( 0,  0,  1),
        N(-1, -1, -1)
        in 3-d lattice N

  image_cone(cone)

    Return the cone of the codomain fan containing the image of `cone`.

    INPUT:

    - `cone` – a `cone` equivalent to a cone of the `domain_fan()` of `self`.

    OUTPUT:

    - a `cone` of the `codomain_fan()` of `self`.

    EXAMPLES:

    Sage

        sage: quadrant = Cone([(1,0), (0,1)])
        sage: quadrant = Fan([quadrant])
        sage: quadrant_bl = quadrant.subdivide([(1,1)])
        sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
        sage: fm.image_cone(Cone([(1,0)]))
        1-d cone of Rational polyhedral fan in 2-d lattice N
        sage: fm.image_cone(Cone([(1,1)]))
        2-d cone of Rational polyhedral fan in 2-d lattice N

    Python

        >>> from sage.all import *
        >>> quadrant = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))])
        >>> quadrant = Fan([quadrant])
        >>> quadrant_bl = quadrant.subdivide([(Integer(1),Integer(1))])
        >>> fm = FanMorphism(identity_matrix(Integer(2)), quadrant_bl, quadrant)
        >>> fm.image_cone(Cone([(Integer(1),Integer(0))]))
        1-d cone of Rational polyhedral fan in 2-d lattice N
        >>> fm.image_cone(Cone([(Integer(1),Integer(1))]))
        2-d cone of Rational polyhedral fan in 2-d lattice N

  index(cone=None)

    Return the index of `self` as a map between lattices.

    INPUT:

    - `cone` – (default: `None`) a `cone` of the `codomain_fan()` of `self`.

    OUTPUT: integer, infinity, or `None`

    If no cone was specified, this function computes the index of the image of `self` in the codomain. If a cone \(\sigma\) was given, the index of `self` over \(\sigma\) is computed in the sense of Definition 2.1.7 of [HLY2002]: if \(\sigma'\) is any cone of the `domain_fan()` of `self` whose relative interior is mapped to the relative interior of \(\sigma\), it is the index of the image of \(N'(\sigma')\) in \(N(\sigma)\), where \(N'\) and \(N\) are domain and codomain lattices respectively. While that definition was formulated for the case of the finite index only, we extend it to the infinite one as well and return `None` if there is no \(\sigma'\) at all. See examples below for situations when such things happen. Note also that the index of `self` is the same as index over the trivial cone.

    EXAMPLES:

    Sage

        sage: # needs palp
        sage: Sigma = toric_varieties.dP8().fan()
        sage: Sigma_p = toric_varieties.P1().fan()
        sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
        sage: phi.index()
        1
        sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
        sage: psi.index()
        2
        sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
        sage: xi.index()
        +Infinity

    Python

        >>> from sage.all import *
        >>> # needs palp
        >>> Sigma = toric_varieties.dP8().fan()
        >>> Sigma_p = toric_varieties.P1().fan()
        >>> phi = FanMorphism(matrix([[Integer(1)], [-Integer(1)]]), Sigma, Sigma_p)
        >>> phi.index()
        1
        >>> psi = FanMorphism(matrix([[Integer(2)], [-Integer(2)]]), Sigma, Sigma_p)
        >>> psi.index()
        2
        >>> xi = FanMorphism(matrix([[Integer(1), Integer(0)]]), Sigma_p, Sigma)
        >>> xi.index()
        +Infinity

    Infinite index in the last example indicates that the image has positive codimension in the codomain. Let’s look at the rays of our fans:

    Sage

        sage: Sigma_p.rays()                                                        # needs palp
        N( 1),
        N(-1)
        in 1-d lattice N
        sage: Sigma.rays()                                                          # needs palp
        N( 1,  1),
        N( 0,  1),
        N(-1, -1),
        N( 1,  0)
        in 2-d lattice N
        sage: xi.factor()[0].domain_fan().rays()                                    # needs palp
        N(-1, 0),
        N( 1, 0)
        in Sublattice <N(1, 0)>

    [Python]

        >>> from sage.all import *
        >>> Sigma_p.rays()                                                        # needs palp
        N( 1),
        N(-1)
        in 1-d lattice N
        >>> Sigma.rays()                                                          # needs palp
        N( 1,  1),
        N( 0,  1),
        N(-1, -1),
        N( 1,  0)
        in 2-d lattice N
        >>> xi.factor()[Integer(0)].domain_fan().rays()                                    # needs palp
        N(-1, 0),
        N( 1, 0)
        in Sublattice <N(1, 0)>

    We see that one of the rays of the fan of `P1` is mapped to a ray, while the other one to the interior of some 2-d cone. Both rays correspond to single points on `P1`, yet one is mapped to the distinguished point of a torus invariant curve of `dP8` (with the rest of this curve being uncovered) and the other to a fixed point of `dP8` (thus completely covering this torus orbit in `dP8`).

    We should therefore expect the following behaviour: all indices over 1-d cones are `None`, except for one which is infinite, and all indices over 2-d cones are `None`, except for one which is 1:

    Sage

        sage: [xi.index(cone) for cone in Sigma(1)]                                 # needs palp
        [None, None, None, +Infinity]
        sage: [xi.index(cone) for cone in Sigma(2)]                                 # needs palp
        [None, 1, None, None]

    Python

        >>> from sage.all import *
        >>> [xi.index(cone) for cone in Sigma(Integer(1))]                                 # needs palp
        [None, None, None, +Infinity]
        >>> [xi.index(cone) for cone in Sigma(Integer(2))]                                 # needs palp
        [None, 1, None, None]

  is_birational()

    Check if `self` is birational.

    OUTPUT: `True` if `self` is birational, `False` otherwise

    For fan morphisms this check is equivalent to `self.index() == 1` and means that the corresponding map between toric varieties is birational.

    EXAMPLES:

    Sage

        sage: # needs palp
        sage: Sigma = toric_varieties.dP8().fan()
        sage: Sigma_p = toric_varieties.P1().fan()
        sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
        sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
        sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
        sage: phi.index(), psi.index(), xi.index()
        (1, 2, +Infinity)
        sage: phi.is_birational(), psi.is_birational(), xi.is_birational()
        (True, False, False)

    Python

        >>> from sage.all import *
        >>> # needs palp
        >>> Sigma = toric_varieties.dP8().fan()
        >>> Sigma_p = toric_varieties.P1().fan()
        >>> phi = FanMorphism(matrix([[Integer(1)], [-Integer(1)]]), Sigma, Sigma_p)
        >>> psi = FanMorphism(matrix([[Integer(2)], [-Integer(2)]]), Sigma, Sigma_p)
        >>> xi = FanMorphism(matrix([[Integer(1), Integer(0)]]), Sigma_p, Sigma)
        >>> phi.index(), psi.index(), xi.index()
        (1, 2, +Infinity)
        >>> phi.is_birational(), psi.is_birational(), xi.is_birational()
        (True, False, False)

  is_bundle()

    Check if `self` is a bundle.

    OUTPUT: `True` if `self` is a bundle, `False` otherwise

    Let \(\phi: \Sigma \to \Sigma'\) be a fan morphism such that the underlying lattice morphism \(\phi: N \to N'\) is surjective. Let \(\Sigma_0\) be the kernel fan of \(\phi\). Then \(\phi\) is a bundle (or splitting) if there is a subfan \(\widehat{\Sigma}\) of \(\Sigma\) such that the following two conditions are satisfied:

    > 1. Cones of \(\Sigma\) are precisely the cones of the form \(\sigma_0 + \widehat{\sigma}\), where \(\sigma_0 \in \Sigma_0\) and \(\widehat{\sigma} \in \widehat{\Sigma}\).

    > 2. Cones of \(\widehat{\Sigma}\) are in bijection with cones of \(\Sigma'\) induced by \(\phi\) and \(\phi\) maps lattice points in every cone \(\widehat{\sigma} \in \widehat{\Sigma}\) bijectively onto lattice points in \(\phi(\widehat{\sigma})\).

    If a fan morphism \(\phi: \Sigma \to \Sigma'\) is a bundle, then \(X_\Sigma\) is a fiber bundle over \(X_{\Sigma'}\) with fibers \(X_{\Sigma_0, N_0}\), where \(N_0\) is the kernel lattice of \(\phi\). See [CLS2011] for more details.

    See also

    `is_fibration()`, `kernel_fan()`.

    EXAMPLES:

    We consider several maps between fans of a del Pezzo surface and the projective line:

    Sage

        sage: # needs palp
        sage: Sigma = toric_varieties.dP8().fan()
        sage: Sigma_p = toric_varieties.P1().fan()
        sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
        sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
        sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
        sage: phi.is_bundle()
        True
        sage: phi.is_fibration()
        True
        sage: phi.index()
        1
        sage: psi.is_bundle()
        False
        sage: psi.is_fibration()
        True
        sage: psi.index()
        2
        sage: xi.is_fibration()
        False
        sage: xi.index()
        +Infinity

    Python

        >>> from sage.all import *
        >>> # needs palp
        >>> Sigma = toric_varieties.dP8().fan()
        >>> Sigma_p = toric_varieties.P1().fan()
        >>> phi = FanMorphism(matrix([[Integer(1)], [-Integer(1)]]), Sigma, Sigma_p)
        >>> psi = FanMorphism(matrix([[Integer(2)], [-Integer(2)]]), Sigma, Sigma_p)
        >>> xi = FanMorphism(matrix([[Integer(1), Integer(0)]]), Sigma_p, Sigma)
        >>> phi.is_bundle()
        True
        >>> phi.is_fibration()
        True
        >>> phi.index()
        1
        >>> psi.is_bundle()
        False
        >>> psi.is_fibration()
        True
        >>> psi.index()
        2
        >>> xi.is_fibration()
        False
        >>> xi.index()
        +Infinity

    The first of these maps induces not only a fibration, but a fiber bundle structure. The second map is very similar, yet it fails to be a bundle, as its index is 2. The last map is not even a fibration.

  is_dominant()

    Return whether the fan morphism is dominant.

    A fan morphism \(\phi\) is dominant if it is surjective as a map of vector spaces. That is, \(\phi_\RR: N_\RR \to N'_\RR\) is surjective.

    If the domain fan is `complete`, then this implies that the fan morphism is `surjective`.

    If the fan morphism is dominant, then the associated morphism of toric varieties is dominant in the algebraic-geometric sense (that is, surjective onto a dense subset).

    OUTPUT: boolean

    EXAMPLES:

    Sage

        sage: P1 = toric_varieties.P1()
        sage: A1 = toric_varieties.A1()
        sage: phi = FanMorphism(matrix([[1]]), A1.fan(), P1.fan())
        sage: phi.is_dominant()
        True
        sage: phi.is_surjective()
        False

    Python

        >>> from sage.all import *
        >>> P1 = toric_varieties.P1()
        >>> A1 = toric_varieties.A1()
        >>> phi = FanMorphism(matrix([[Integer(1)]]), A1.fan(), P1.fan())
        >>> phi.is_dominant()
        True
        >>> phi.is_surjective()
        False

  is_fibration()

    Check if `self` is a fibration.

    OUTPUT: `True` if `self` is a fibration, `False` otherwise

    A fan morphism \(\phi: \Sigma \to \Sigma'\) is a fibration if for any cone \(\sigma' \in \Sigma'\) and any primitive preimage cone \(\sigma \in \Sigma\) corresponding to \(\sigma'\) the linear map of vector spaces \(\phi_\RR\) induces a bijection between \(\sigma\) and \(\sigma'\), and, in addition, \(\phi\) is `dominant` (that is, \(\phi_\RR: N_\RR \to N'_\RR\) is surjective).

    If a fan morphism \(\phi: \Sigma \to \Sigma'\) is a fibration, then the associated morphism between toric varieties \(\tilde{\phi}: X_\Sigma \to X_{\Sigma'}\) is a fibration in the sense that it is surjective and all of its fibers have the same dimension, namely \(\dim X_\Sigma - \dim X_{\Sigma'}\). These fibers do not have to be isomorphic, i.e. a fibration is not necessarily a fiber bundle. See [HLY2002] for more details.

    See also

    `is_bundle()`, `primitive_preimage_cones()`.

    EXAMPLES:

    We consider several maps between fans of a del Pezzo surface and the projective line:

    Sage

        sage: # needs palp
        sage: Sigma = toric_varieties.dP8().fan()
        sage: Sigma_p = toric_varieties.P1().fan()
        sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
        sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
        sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
        sage: phi.is_bundle()
        True
        sage: phi.is_fibration()
        True
        sage: phi.index()
        1
        sage: psi.is_bundle()
        False
        sage: psi.is_fibration()
        True
        sage: psi.index()
        2
        sage: xi.is_fibration()
        False
        sage: xi.index()
        +Infinity

    Python

        >>> from sage.all import *
        >>> # needs palp
        >>> Sigma = toric_varieties.dP8().fan()
        >>> Sigma_p = toric_varieties.P1().fan()
        >>> phi = FanMorphism(matrix([[Integer(1)], [-Integer(1)]]), Sigma, Sigma_p)
        >>> psi = FanMorphism(matrix([[Integer(2)], [-Integer(2)]]), Sigma, Sigma_p)
        >>> xi = FanMorphism(matrix([[Integer(1), Integer(0)]]), Sigma_p, Sigma)
        >>> phi.is_bundle()
        True
        >>> phi.is_fibration()
        True
        >>> phi.index()
        1
        >>> psi.is_bundle()
        False
        >>> psi.is_fibration()
        True
        >>> psi.index()
        2
        >>> xi.is_fibration()
        False
        >>> xi.index()
        +Infinity

    The first of these maps induces not only a fibration, but a fiber bundle structure. The second map is very similar, yet it fails to be a bundle, as its index is 2. The last map is not even a fibration.

  is_injective()

    Check if `self` is injective.

    OUTPUT: `True` if `self` is injective, `False` otherwise

    Let \(\phi: \Sigma \to \Sigma'\) be a fan morphism such that the underlying lattice morphism \(\phi: N \to N'\) bijectively maps \(N\) to a saturated sublattice of \(N'\). Let \(\psi: \Sigma \to \Sigma'_0\) be the restriction of \(\phi\) to the image. Then \(\phi\) is injective if the map between cones corresponding to \(\psi\) (injectively) maps each cone of \(\Sigma\) to a cone of the same dimension.

    If a fan morphism \(\phi: \Sigma \to \Sigma'\) is injective, then the associated morphism between toric varieties \(\tilde{\phi}: X_\Sigma \to X_{\Sigma'}\) is injective.

    See also

    `factor()`.

    EXAMPLES:

    Consider the fan of the affine plane:

    Sage

        sage: A2 = toric_varieties.A(2).fan()

    Python

        >>> from sage.all import *
        >>> A2 = toric_varieties.A(Integer(2)).fan()

    We will map several fans consisting of a single ray into the interior of the 2-cone:

    Sage

        sage: Sigma = Fan([Cone([(1,1)])])
        sage: m = identity_matrix(2)
        sage: FanMorphism(m, Sigma, A2).is_injective()
        False

    [Python]

        >>> from sage.all import *
        >>> Sigma = Fan([Cone([(Integer(1),Integer(1))])])
        >>> m = identity_matrix(Integer(2))
        >>> FanMorphism(m, Sigma, A2).is_injective()
        False

    This morphism was not injective since (in the toric varieties interpretation) the 1-dimensional orbit corresponding to the ray was mapped to the 0-dimensional orbit corresponding to the 2-cone.

    Sage

        sage: Sigma = Fan([Cone([(1,)])])
        sage: m = matrix(1, 2, [1,1])
        sage: FanMorphism(m, Sigma, A2).is_injective()
        True

    Python

        >>> from sage.all import *
        >>> Sigma = Fan([Cone([(Integer(1),)])])
        >>> m = matrix(Integer(1), Integer(2), [Integer(1),Integer(1)])
        >>> FanMorphism(m, Sigma, A2).is_injective()
        True

    While the fans in this example are close to the previous one, here the ray corresponds to a 0-dimensional orbit.

    Sage

        sage: Sigma = Fan([Cone([(1,)])])
        sage: m = matrix(1, 2, [2,2])
        sage: FanMorphism(m, Sigma, A2).is_injective()
        False

    [Python]

        >>> from sage.all import *
        >>> Sigma = Fan([Cone([(Integer(1),)])])
        >>> m = matrix(Integer(1), Integer(2), [Integer(2),Integer(2)])
        >>> FanMorphism(m, Sigma, A2).is_injective()
        False

    Here the problem is that `m` maps the domain lattice to a non-saturated sublattice of the codomain. The corresponding map of the toric varieties is a two-sheeted cover of its image.

    We also embed the affine plane into the projective one:

    Sage

        sage: P2 = toric_varieties.P(2).fan()                                       # needs palp
        sage: m = identity_matrix(2)
        sage: FanMorphism(m, A2, P2).is_injective()                                 # needs palp
        True

    Python

        >>> from sage.all import *
        >>> P2 = toric_varieties.P(Integer(2)).fan()                                       # needs palp
        >>> m = identity_matrix(Integer(2))
        >>> FanMorphism(m, A2, P2).is_injective()                                 # needs palp
        True

  is_surjective()

    Check if `self` is surjective.

    OUTPUT: `True` if `self` is surjective, `False` otherwise

    A fan morphism \(\phi: \Sigma \to \Sigma'\) is surjective if the corresponding map between cones is surjective, i.e. for each cone \(\sigma' \in \Sigma'\) there is at least one preimage cone \(\sigma \in \Sigma\) such that the relative interior of \(\sigma\) is mapped to the relative interior of \(\sigma'\) and, in addition, \(\phi_\RR: N_\RR \to N'_\RR\) is surjective.

    If a fan morphism \(\phi: \Sigma \to \Sigma'\) is surjective, then the associated morphism between toric varieties \(\tilde{\phi}: X_\Sigma \to X_{\Sigma'}\) is surjective.

    See also

    `is_bundle()`, `is_fibration()`, `preimage_cones()`, `is_complete()`.

    EXAMPLES:

    We check that the blow up of the affine plane at the origin is surjective:

    Sage

        sage: A2 = toric_varieties.A(2).fan()
        sage: Bl = A2.subdivide([(1,1)])
        sage: m = identity_matrix(2)
        sage: FanMorphism(m, Bl, A2).is_surjective()
        True

    Python

        >>> from sage.all import *
        >>> A2 = toric_varieties.A(Integer(2)).fan()
        >>> Bl = A2.subdivide([(Integer(1),Integer(1))])
        >>> m = identity_matrix(Integer(2))
        >>> FanMorphism(m, Bl, A2).is_surjective()
        True

    It remains surjective if we throw away “south and north poles” of the exceptional divisor:

    Sage

        sage: FanMorphism(m, Fan(Bl.cones(1)), A2).is_surjective()
        True

    [Python]

        >>> from sage.all import *
        >>> FanMorphism(m, Fan(Bl.cones(Integer(1))), A2).is_surjective()
        True

    But a single patch of the blow up does not cover the plane:

    Sage

        sage: F = Fan([Bl.generating_cone(0)])
        sage: FanMorphism(m, F, A2).is_surjective()
        False

    Python

        >>> from sage.all import *
        >>> F = Fan([Bl.generating_cone(Integer(0))])
        >>> FanMorphism(m, F, A2).is_surjective()
        False

  kernel_fan()

    Return the subfan of the domain fan mapped into the origin.

    OUTPUT: a `fan`

    Note

    The lattice of the kernel fan is the `kernel()` sublattice of `self`.

    See also

    `preimage_fan()`.

    EXAMPLES:

    Sage

        sage: fan = Fan(rays=[(1,0), (1,1), (0,1)], cones=[(0,1), (1,2)])
        sage: fm = FanMorphism(matrix(2, 1, [1,-1]), fan, ToricLattice(1))
        sage: fm.kernel_fan()
        Rational polyhedral fan in Sublattice <N(1, 1)>
        sage: _.rays()
        N(1, 1)
        in Sublattice <N(1, 1)>
        sage: fm.kernel_fan().cones()
        ((0-d cone of Rational polyhedral fan in Sublattice <N(1, 1)>,),
         (1-d cone of Rational polyhedral fan in Sublattice <N(1, 1)>,))

    Python

        >>> from sage.all import *
        >>> fan = Fan(rays=[(Integer(1),Integer(0)), (Integer(1),Integer(1)), (Integer(0),Integer(1))], cones=[(Integer(0),Integer(1)), (Integer(1),Integer(2))])
        >>> fm = FanMorphism(matrix(Integer(2), Integer(1), [Integer(1),-Integer(1)]), fan, ToricLattice(Integer(1)))
        >>> fm.kernel_fan()
        Rational polyhedral fan in Sublattice <N(1, 1)>
        >>> _.rays()
        N(1, 1)
        in Sublattice <N(1, 1)>
        >>> fm.kernel_fan().cones()
        ((0-d cone of Rational polyhedral fan in Sublattice <N(1, 1)>,),
         (1-d cone of Rational polyhedral fan in Sublattice <N(1, 1)>,))

  preimage_cones(cone)

    Return cones of the domain fan whose `image_cone()` is `cone`.

    INPUT:

    - `cone` – a `cone` equivalent to a cone of the `codomain_fan()` of `self`.

    OUTPUT:

    - a `tuple` of `cones` of the `domain_fan()` of `self`, sorted by dimension.

    See also

    `preimage_fan()`.

    EXAMPLES:

    Sage

        sage: quadrant = Cone([(1,0), (0,1)])
        sage: quadrant = Fan([quadrant])
        sage: quadrant_bl = quadrant.subdivide([(1,1)])
        sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
        sage: fm.preimage_cones(Cone([(1,0)]))
        (1-d cone of Rational polyhedral fan in 2-d lattice N,)
        sage: fm.preimage_cones(Cone([(1,0), (0,1)]))
        (1-d cone of Rational polyhedral fan in 2-d lattice N,
         2-d cone of Rational polyhedral fan in 2-d lattice N,
         2-d cone of Rational polyhedral fan in 2-d lattice N)

    Python

        >>> from sage.all import *
        >>> quadrant = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))])
        >>> quadrant = Fan([quadrant])
        >>> quadrant_bl = quadrant.subdivide([(Integer(1),Integer(1))])
        >>> fm = FanMorphism(identity_matrix(Integer(2)), quadrant_bl, quadrant)
        >>> fm.preimage_cones(Cone([(Integer(1),Integer(0))]))
        (1-d cone of Rational polyhedral fan in 2-d lattice N,)
        >>> fm.preimage_cones(Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]))
        (1-d cone of Rational polyhedral fan in 2-d lattice N,
         2-d cone of Rational polyhedral fan in 2-d lattice N,
         2-d cone of Rational polyhedral fan in 2-d lattice N)

  preimage_fan(cone)

    Return the subfan of the domain fan mapped into `cone`.

    INPUT:

    - `cone` – a `cone` equivalent to a cone of the `codomain_fan()` of `self`

    OUTPUT: a `fan`

    Note

    The preimage fan of `cone` consists of all cones of the `domain_fan()` which are mapped into `cone`, including those that are mapped into its boundary. So this fan is not necessarily generated by `preimage_cones()` of `cone`.

    See also

    `kernel_fan()`, `preimage_cones()`.

    EXAMPLES:

    Sage

        sage: quadrant_cone = Cone([(1,0), (0,1)])
        sage: quadrant_fan = Fan([quadrant_cone])
        sage: quadrant_bl = quadrant_fan.subdivide([(1,1)])
        sage: fm = FanMorphism(identity_matrix(2),
        ....:                  quadrant_bl, quadrant_fan)
        sage: fm.preimage_fan(Cone([(1,0)])).cones()
        ((0-d cone of Rational polyhedral fan in 2-d lattice N,),
         (1-d cone of Rational polyhedral fan in 2-d lattice N,))
        sage: fm.preimage_fan(quadrant_cone).n_generating_cones()
        2
        sage: len(fm.preimage_cones(quadrant_cone))
        3

    Python

        >>> from sage.all import *
        >>> quadrant_cone = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))])
        >>> quadrant_fan = Fan([quadrant_cone])
        >>> quadrant_bl = quadrant_fan.subdivide([(Integer(1),Integer(1))])
        >>> fm = FanMorphism(identity_matrix(Integer(2)),
        ...                  quadrant_bl, quadrant_fan)
        >>> fm.preimage_fan(Cone([(Integer(1),Integer(0))])).cones()
        ((0-d cone of Rational polyhedral fan in 2-d lattice N,),
         (1-d cone of Rational polyhedral fan in 2-d lattice N,))
        >>> fm.preimage_fan(quadrant_cone).n_generating_cones()
        2
        >>> len(fm.preimage_cones(quadrant_cone))
        3

  primitive_preimage_cones(cone)

    Return the primitive cones of the domain fan corresponding to `cone`.

    INPUT:

    - `cone` – a `cone` equivalent to a cone of the `codomain_fan()` of `self`

    OUTPUT: a `cone`

    Let \(\phi: \Sigma \to \Sigma'\) be a fan morphism, let \(\sigma \in \Sigma\), and let \(\sigma' = \phi(\sigma)\). Then \(\sigma\) is a primitive cone corresponding to \(\sigma'\) if there is no proper face \(\tau\) of \(\sigma\) such that \(\phi(\tau) = \sigma'\).

    Primitive cones play an important role for fibration morphisms.

    See also

    `is_fibration()`, `preimage_cones()`, `preimage_fan()`.

    EXAMPLES:

    Consider a projection of a del Pezzo surface onto the projective line:

    Sage

        sage: Sigma = toric_varieties.dP6().fan()                                   # needs palp
        sage: Sigma.rays()                                                          # needs palp
        N( 0,  1),
        N(-1,  0),
        N(-1, -1),
        N( 0, -1),
        N( 1,  0),
        N( 1,  1)
        in 2-d lattice N
        sage: Sigma_p = toric_varieties.P1().fan()
        sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)                # needs palp

    Python

        >>> from sage.all import *
        >>> Sigma = toric_varieties.dP6().fan()                                   # needs palp
        >>> Sigma.rays()                                                          # needs palp
        N( 0,  1),
        N(-1,  0),
        N(-1, -1),
        N( 0, -1),
        N( 1,  0),
        N( 1,  1)
        in 2-d lattice N
        >>> Sigma_p = toric_varieties.P1().fan()
        >>> phi = FanMorphism(matrix([[Integer(1)], [-Integer(1)]]), Sigma, Sigma_p)                # needs palp

    Under this map, one pair of rays is mapped to the origin, one in the positive direction, and one in the negative one. Also three 2-dimensional cones are mapped in the positive direction and three in the negative one, so there are 5 preimage cones corresponding to either of the rays of the codomain fan `Sigma_p`:

    Sage

        sage: len(phi.preimage_cones(Cone([(1,)])))                                 # needs palp
        5

    [Python]

        >>> from sage.all import *
        >>> len(phi.preimage_cones(Cone([(Integer(1),)])))                                 # needs palp
        5

    Yet only rays are primitive:

    Sage

        sage: phi.primitive_preimage_cones(Cone([(1,)]))                            # needs palp
        (1-d cone of Rational polyhedral fan in 2-d lattice N,
         1-d cone of Rational polyhedral fan in 2-d lattice N)

    Python

        >>> from sage.all import *
        >>> phi.primitive_preimage_cones(Cone([(Integer(1),)]))                            # needs palp
        (1-d cone of Rational polyhedral fan in 2-d lattice N,
         1-d cone of Rational polyhedral fan in 2-d lattice N)

    Since all primitive cones are mapped onto their images bijectively, we get a fibration:

    Sage

        sage: phi.is_fibration()                                                    # needs palp
        True

    [Python]

        >>> from sage.all import *
        >>> phi.is_fibration()                                                    # needs palp
        True

    But since there are several primitive cones corresponding to the same cone of the codomain fan, this map is not a bundle, even though its index is 1:

    Sage

        sage: phi.is_bundle()                                                       # needs palp
        False
        sage: phi.index()                                                           # needs palp
        1

    Python

        >>> from sage.all import *
        >>> phi.is_bundle()                                                       # needs palp
        False
        >>> phi.index()                                                           # needs palp
        1

  relative_star_generators(domain_cone)

    Return the relative star generators of `domain_cone`.

    INPUT:

    - `domain_cone` – a cone of the `domain_fan()` of `self`

    OUTPUT:

    - `star_generators()` of `domain_cone` viewed as a cone of `preimage_fan()` of `image_cone()` of `domain_cone`.

    EXAMPLES:

    Sage

        sage: A2 = toric_varieties.A(2).fan()
        sage: Bl = A2.subdivide([(1,1)])
        sage: f = FanMorphism(identity_matrix(2), Bl, A2)
        sage: for c1 in Bl(1):
        ....:     print(f.relative_star_generators(c1))
        (1-d cone of Rational polyhedral fan in 2-d lattice N,)
        (1-d cone of Rational polyhedral fan in 2-d lattice N,)
        (2-d cone of Rational polyhedral fan in 2-d lattice N,
         2-d cone of Rational polyhedral fan in 2-d lattice N)

    Python

        >>> from sage.all import *
        >>> A2 = toric_varieties.A(Integer(2)).fan()
        >>> Bl = A2.subdivide([(Integer(1),Integer(1))])
        >>> f = FanMorphism(identity_matrix(Integer(2)), Bl, A2)
        >>> for c1 in Bl(Integer(1)):
        ...     print(f.relative_star_generators(c1))
        (1-d cone of Rational polyhedral fan in 2-d lattice N,)
        (1-d cone of Rational polyhedral fan in 2-d lattice N,)
        (2-d cone of Rational polyhedral fan in 2-d lattice N,
         2-d cone of Rational polyhedral fan in 2-d lattice N)