Morphisms between toric lattices compatible with fans

This module is a part of the framework for toric varieties (toric_variety, fano_toric_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.

EXAMPLES:

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

sage: diamond = lattice_polytope.octahedron(2)
sage: face = FaceFan(diamond)
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!

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: 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().ray_matrix()
[-1  1 -1  1  0  0]
[ 1  1 -1 -1 -1  1]    
sage: normal.ray_matrix()
[-1  1 -1  1]
[ 1  1 -1 -1]

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: sage.modules.free_module_morphism.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 – (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 – (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 – (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: diamond = lattice_polytope.octahedron(2)
sage: face = FaceFan(diamond)
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

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

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().ngenerating_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: fm = FanMorphism(phi, face)
sage: fm.codomain_fan()
Rational polyhedral fan in 2-d lattice N
sage: fm.codomain_fan().ray_matrix()
[ 1 -1]
[ 0  0]

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: 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!

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: fm = FanMorphism(phi, F1, F2, subdivide=True,
...                    check=False, verbose=True)
Placing ray images...
Computing chambers...
Subdividing cone 1 of 1...            
sage: fm.domain_fan().is_equivalent(F2)
True
codomain_fan()

Return the codomain fan of self.

OUTPUT:

EXAMPLES:

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
domain_fan()

Return the codomain fan of self.

OUTPUT:

EXAMPLES:

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
image_cone(cone)

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

INPUT:

OUTPUT:

EXAMPLES:

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

TESTS:

We check that complete codomain fans are handled correctly, since a different algorithm is used in this case:

sage: diamond = lattice_polytope.octahedron(2)
sage: face = FaceFan(diamond)
sage: normal = NormalFan(diamond)
sage: N = face.lattice()
sage: fm = FanMorphism(identity_matrix(2),
...           normal, face, subdivide=True)
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
preimage_cones(cone)

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

INPUT:

OUTPUT:

EXAMPLES:

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)

TESTS:

We check that reviewer’s example from Trac #9972 is handled correctly:

sage: N1 = ToricLattice(1)
sage: N2 = ToricLattice(2)
sage: Hom21 = Hom(N2, N1)
sage: pr = Hom21([N1.0,0])
sage: P1xP1 = toric_varieties.P1xP1()
sage: f = FanMorphism(pr, P1xP1.fan())
sage: c = f.image_cone(Cone([(1,0), (0,1)]))
sage: c
1-d cone of Rational polyhedral fan in 1-d lattice N
sage: f.preimage_cones(c)
(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)

Previous topic

Rational polyhedral fans

Next topic

Toric plotter

This Page