Rational polyhedral fans#
This module was designed as a part of the framework for toric varieties
(variety
,
fano_variety
). While the emphasis is on
complete full-dimensional fans, arbitrary fans are supported. Work
with distinct lattices. The default lattice is ToricLattice
\(N\) of the appropriate
dimension. The only case when you must specify lattice explicitly is creation
of a 0-dimensional fan, where dimension of the ambient space cannot be
guessed.
A rational polyhedral fan is a finite collection of strictly convex rational polyhedral cones, such that the intersection of any two cones of the fan is a face of each of them and each face of each cone is also a cone of the fan.
AUTHORS:
Andrey Novoseltsev (2010-05-15): initial version.
Andrey Novoseltsev (2010-06-17): substantial improvement during review by Volker Braun.
EXAMPLES:
Use Fan()
to construct fans “explicitly”:
sage: fan = Fan(cones=[(0,1), (1,2)],
....: rays=[(1,0), (0,1), (-1,0)])
sage: fan
Rational polyhedral fan in 2-d lattice N
>>> from sage.all import *
>>> fan = Fan(cones=[(Integer(0),Integer(1)), (Integer(1),Integer(2))],
... rays=[(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),Integer(0))])
>>> fan
Rational polyhedral fan in 2-d lattice N
In addition to giving such lists of cones and rays you can also create cones
first using Cone()
and then combine them into a fan.
See the documentation of Fan()
for details.
In 2 dimensions there is a unique maximal fan determined by rays, and
you can use Fan2d()
to construct it:
sage: fan2d = Fan2d(rays=[(1,0), (0,1), (-1,0)])
sage: fan2d.is_equivalent(fan)
True
>>> from sage.all import *
>>> fan2d = Fan2d(rays=[(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),Integer(0))])
>>> fan2d.is_equivalent(fan)
True
But keep in mind that in higher dimensions the cone data is essential
and cannot be omitted. Instead of building a fan from scratch, for
this tutorial we will use an easy way to get two fans associated to
lattice polytopes
:
FaceFan()
and NormalFan()
:
sage: fan1 = FaceFan(lattice_polytope.cross_polytope(3))
sage: fan2 = NormalFan(lattice_polytope.cross_polytope(3))
>>> from sage.all import *
>>> fan1 = FaceFan(lattice_polytope.cross_polytope(Integer(3)))
>>> fan2 = NormalFan(lattice_polytope.cross_polytope(Integer(3)))
Given such “automatic” fans, you may wonder what are their rays and cones:
sage: fan1.rays()
M( 1, 0, 0),
M( 0, 1, 0),
M( 0, 0, 1),
M(-1, 0, 0),
M( 0, -1, 0),
M( 0, 0, -1)
in 3-d lattice M
sage: fan1.generating_cones()
(3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M)
>>> from sage.all import *
>>> fan1.rays()
M( 1, 0, 0),
M( 0, 1, 0),
M( 0, 0, 1),
M(-1, 0, 0),
M( 0, -1, 0),
M( 0, 0, -1)
in 3-d lattice M
>>> fan1.generating_cones()
(3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M)
The last output is not very illuminating. Let’s try to improve it:
sage: for cone in fan1: print(cone.rays())
M( 0, 1, 0),
M( 0, 0, 1),
M(-1, 0, 0)
in 3-d lattice M
M( 0, 0, 1),
M(-1, 0, 0),
M( 0, -1, 0)
in 3-d lattice M
M(-1, 0, 0),
M( 0, -1, 0),
M( 0, 0, -1)
in 3-d lattice M
M( 0, 1, 0),
M(-1, 0, 0),
M( 0, 0, -1)
in 3-d lattice M
M(1, 0, 0),
M(0, 1, 0),
M(0, 0, -1)
in 3-d lattice M
M(1, 0, 0),
M(0, 1, 0),
M(0, 0, 1)
in 3-d lattice M
M(1, 0, 0),
M(0, 0, 1),
M(0, -1, 0)
in 3-d lattice M
M(1, 0, 0),
M(0, -1, 0),
M(0, 0, -1)
in 3-d lattice M
>>> from sage.all import *
>>> for cone in fan1: print(cone.rays())
M( 0, 1, 0),
M( 0, 0, 1),
M(-1, 0, 0)
in 3-d lattice M
M( 0, 0, 1),
M(-1, 0, 0),
M( 0, -1, 0)
in 3-d lattice M
M(-1, 0, 0),
M( 0, -1, 0),
M( 0, 0, -1)
in 3-d lattice M
M( 0, 1, 0),
M(-1, 0, 0),
M( 0, 0, -1)
in 3-d lattice M
M(1, 0, 0),
M(0, 1, 0),
M(0, 0, -1)
in 3-d lattice M
M(1, 0, 0),
M(0, 1, 0),
M(0, 0, 1)
in 3-d lattice M
M(1, 0, 0),
M(0, 0, 1),
M(0, -1, 0)
in 3-d lattice M
M(1, 0, 0),
M(0, -1, 0),
M(0, 0, -1)
in 3-d lattice M
You can also do
sage: for cone in fan1: print(cone.ambient_ray_indices())
(1, 2, 3)
(2, 3, 4)
(3, 4, 5)
(1, 3, 5)
(0, 1, 5)
(0, 1, 2)
(0, 2, 4)
(0, 4, 5)
>>> from sage.all import *
>>> for cone in fan1: print(cone.ambient_ray_indices())
(1, 2, 3)
(2, 3, 4)
(3, 4, 5)
(1, 3, 5)
(0, 1, 5)
(0, 1, 2)
(0, 2, 4)
(0, 4, 5)
to see indices of rays of the fan corresponding to each cone.
While the above cycles were over “cones in fan”, it is obvious that we did not
get ALL the cones: every face of every cone in a fan must also be in the fan,
but all of the above cones were of dimension three. The reason for this
behaviour is that in many cases it is enough to work with generating cones of
the fan, i.e. cones which are not faces of bigger cones. When you do need to
work with lower dimensional cones, you can easily get access to them using
cones()
:
sage: [cone.ambient_ray_indices() for cone in fan1.cones(2)]
[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (0, 4),
(2, 4), (3, 4), (0, 5), (1, 5), (3, 5), (4, 5)]
>>> from sage.all import *
>>> [cone.ambient_ray_indices() for cone in fan1.cones(Integer(2))]
[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (0, 4),
(2, 4), (3, 4), (0, 5), (1, 5), (3, 5), (4, 5)]
In fact, you do not have to type .cones
:
sage: [cone.ambient_ray_indices() for cone in fan1(2)]
[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (0, 4),
(2, 4), (3, 4), (0, 5), (1, 5), (3, 5), (4, 5)]
>>> from sage.all import *
>>> [cone.ambient_ray_indices() for cone in fan1(Integer(2))]
[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (0, 4),
(2, 4), (3, 4), (0, 5), (1, 5), (3, 5), (4, 5)]
You may also need to know the inclusion relations between all of the cones of
the fan. In this case check out
cone_lattice()
:
sage: L = fan1.cone_lattice()
sage: L
Finite lattice containing 28 elements with distinguished linear extension
sage: L.bottom()
0-d cone of Rational polyhedral fan in 3-d lattice M
sage: L.top()
Rational polyhedral fan in 3-d lattice M
sage: cone = L.level_sets()[2][0]
sage: cone
2-d cone of Rational polyhedral fan in 3-d lattice M
sage: sorted(L.hasse_diagram().neighbors(cone))
[1-d cone of Rational polyhedral fan in 3-d lattice M,
1-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M]
>>> from sage.all import *
>>> L = fan1.cone_lattice()
>>> L
Finite lattice containing 28 elements with distinguished linear extension
>>> L.bottom()
0-d cone of Rational polyhedral fan in 3-d lattice M
>>> L.top()
Rational polyhedral fan in 3-d lattice M
>>> cone = L.level_sets()[Integer(2)][Integer(0)]
>>> cone
2-d cone of Rational polyhedral fan in 3-d lattice M
>>> sorted(L.hasse_diagram().neighbors(cone))
[1-d cone of Rational polyhedral fan in 3-d lattice M,
1-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M,
3-d cone of Rational polyhedral fan in 3-d lattice M]
You can check how “good” a fan is:
sage: fan1.is_complete()
True
sage: fan1.is_simplicial()
True
sage: fan1.is_smooth()
True
>>> from sage.all import *
>>> fan1.is_complete()
True
>>> fan1.is_simplicial()
True
>>> fan1.is_smooth()
True
The face fan of the octahedron is really good! Time to remember that we have also constructed its normal fan:
sage: fan2.is_complete()
True
sage: fan2.is_simplicial()
False
sage: fan2.is_smooth()
False
>>> from sage.all import *
>>> fan2.is_complete()
True
>>> fan2.is_simplicial()
False
>>> fan2.is_smooth()
False
This one does have some “problems,” but we can fix them:
sage: fan3 = fan2.make_simplicial()
sage: fan3.is_simplicial()
True
sage: fan3.is_smooth()
False
>>> from sage.all import *
>>> fan3 = fan2.make_simplicial()
>>> fan3.is_simplicial()
True
>>> fan3.is_smooth()
False
Note that we had to save the result of
make_simplicial()
in a new fan.
Fans in Sage are immutable, so any operation that does change them constructs
a new fan.
We can also make fan3
smooth, but it will take a bit more work:
sage: # needs palp
sage: cube = lattice_polytope.cross_polytope(3).polar()
sage: sk = cube.skeleton_points(2)
sage: rays = [cube.point(p) for p in sk]
sage: fan4 = fan3.subdivide(new_rays=rays)
sage: fan4.is_smooth()
True
>>> from sage.all import *
>>> # needs palp
>>> cube = lattice_polytope.cross_polytope(Integer(3)).polar()
>>> sk = cube.skeleton_points(Integer(2))
>>> rays = [cube.point(p) for p in sk]
>>> fan4 = fan3.subdivide(new_rays=rays)
>>> fan4.is_smooth()
True
Let’s see how “different” are fan2
and fan4
:
sage: fan2.ngenerating_cones()
6
sage: fan2.nrays()
8
sage: fan4.ngenerating_cones() # needs palp
48
sage: fan4.nrays() # needs palp
26
>>> from sage.all import *
>>> fan2.ngenerating_cones()
6
>>> fan2.nrays()
8
>>> fan4.ngenerating_cones() # needs palp
48
>>> fan4.nrays() # needs palp
26
Smoothness does not come for free!
Please take a look at the rest of the available functions below and their complete descriptions. If you need any features that are missing, feel free to suggest them. (Or implement them on your own and submit a patch to Sage for inclusion!)
- class sage.geometry.fan.Cone_of_fan(ambient, ambient_ray_indices)[source]#
Bases:
ConvexRationalPolyhedralCone
Construct a cone belonging to a fan.
Warning
This class does not check that the input defines a valid cone of a fan. You must not construct objects of this class directly.
In addition to all of the properties of “regular”
cones
, such cones know their relation to the fan.INPUT:
ambient
– fan whose cone is constructed;ambient_ray_indices
– increasing list or tuple of integers, indices of rays ofambient
generating this cone.
OUTPUT:
cone of
ambient
EXAMPLES:
The intended way to get objects of this class is the following:
sage: # needs palp sage: fan = toric_varieties.P1xP1().fan() sage: cone = fan.generating_cone(0); cone 2-d cone of Rational polyhedral fan in 2-d lattice N sage: cone.ambient_ray_indices() (0, 2) sage: cone.star_generator_indices() (0,)
>>> from sage.all import * >>> # needs palp >>> fan = toric_varieties.P1xP1().fan() >>> cone = fan.generating_cone(Integer(0)); cone 2-d cone of Rational polyhedral fan in 2-d lattice N >>> cone.ambient_ray_indices() (0, 2) >>> cone.star_generator_indices() (0,)
- star_generator_indices()[source]#
Return indices of generating cones of the “ambient fan” containing
self
.OUTPUT:
increasing
tuple
of integersEXAMPLES:
sage: P1xP1 = toric_varieties.P1xP1() # needs palp sage: cone = P1xP1.fan().generating_cone(0) # needs palp sage: cone.star_generator_indices() # needs palp (0,)
>>> from sage.all import * >>> P1xP1 = toric_varieties.P1xP1() # needs palp >>> cone = P1xP1.fan().generating_cone(Integer(0)) # needs palp >>> cone.star_generator_indices() # needs palp (0,)
- star_generators()[source]#
Return indices of generating cones of the “ambient fan” containing
self
.OUTPUT:
increasing
tuple
of integersEXAMPLES:
sage: P1xP1 = toric_varieties.P1xP1() # needs palp sage: cone = P1xP1.fan().generating_cone(0) # needs palp sage: cone.star_generators() # needs palp (2-d cone of Rational polyhedral fan in 2-d lattice N,)
>>> from sage.all import * >>> P1xP1 = toric_varieties.P1xP1() # needs palp >>> cone = P1xP1.fan().generating_cone(Integer(0)) # needs palp >>> cone.star_generators() # needs palp (2-d cone of Rational polyhedral fan in 2-d lattice N,)
- sage.geometry.fan.FaceFan(polytope, lattice=None)[source]#
Construct the face fan of the given rational
polytope
.INPUT:
polytope
– apolytope
over \(\QQ\) or alattice polytope
. A (not necessarily full-dimensional) polytope containing the origin in itsrelative interior
.lattice
–ToricLattice
, \(\ZZ^n\), or any other object that behaves like these. If not specified, an attempt will be made to determine an appropriate toric lattice automatically.
OUTPUT:
See also
NormalFan()
.EXAMPLES:
Let’s construct the fan corresponding to the product of two projective lines:
sage: diamond = lattice_polytope.cross_polytope(2) sage: P1xP1 = FaceFan(diamond) sage: P1xP1.rays() M( 1, 0), M( 0, 1), M(-1, 0), M( 0, -1) in 2-d lattice M sage: for cone in P1xP1: print(cone.rays()) M(-1, 0), M( 0, -1) in 2-d lattice M M( 0, 1), M(-1, 0) in 2-d lattice M M(1, 0), M(0, 1) in 2-d lattice M M(1, 0), M(0, -1) in 2-d lattice M
>>> from sage.all import * >>> diamond = lattice_polytope.cross_polytope(Integer(2)) >>> P1xP1 = FaceFan(diamond) >>> P1xP1.rays() M( 1, 0), M( 0, 1), M(-1, 0), M( 0, -1) in 2-d lattice M >>> for cone in P1xP1: print(cone.rays()) M(-1, 0), M( 0, -1) in 2-d lattice M M( 0, 1), M(-1, 0) in 2-d lattice M M(1, 0), M(0, 1) in 2-d lattice M M(1, 0), M(0, -1) in 2-d lattice M
- sage.geometry.fan.Fan(cones, rays=None, lattice=None, check=True, normalize=True, is_complete=None, virtual_rays=None, discard_faces=False, allow_arrangement=False)[source]#
Construct a rational polyhedral fan.
Note
Approximate time to construct a fan consisting of \(n\) cones is \(n^2/5\) seconds. That is half an hour for 100 cones. This time can be significantly reduced in the future, but it is still likely to be \(\sim n^2\) (with, say, \(/500\) instead of \(/5\)). If you know that your input does form a valid fan, use
check=False
option to skip consistency checks.INPUT:
cones
– list of eitherCone
objects or lists of integers interpreted as indices of generating rays inrays
. These must be only maximal cones of the fan, unlessdiscard_faces=True
orallow_arrangement=True
option is specified;rays
– list of rays given as list or vectors convertible to the rational extension oflattice
. Ifcones
are given byCone
objectsrays
may be determined automatically. You still may give them explicitly to ensure a particular order of rays in the fan. In this case you must list all rays that appear incones
. You can give “extra” ones if it is convenient (e.g. if you have a big list of rays for several fans), but all “extra” rays will be discarded;lattice
–ToricLattice
, \(\ZZ^n\), or any other object that behaves like these. If not specified, an attempt will be made to determine an appropriate toric lattice automatically;check
– by default the input data will be checked for correctness (e.g. that intersection of any two given cones is a face of each), unlessallow_arrangement=True
option is specified. If you know for sure that the input is correct, you may significantly decrease construction time usingcheck=False
option;normalize
– you can further speed up construction usingnormalize=False
option. In this casecones
must be a list of sortedtuples
andrays
must be immutable primitive vectors inlattice
. In general, you should not use this option, it is designed for code optimization and does not give as drastic improvement in speed as the previous one;is_complete
– every fan can determine on its own if it is complete or not, however it can take quite a bit of time for “big” fans with many generating cones. On the other hand, in some situations it is known in advance that a certain fan is complete. In this case you can passis_complete=True
option to speed up some computations. You may also passis_complete=False
option, although it is less likely to be beneficial. Of course, passing a wrong value can compromise the integrity of data structures of the fan and lead to wrong results, so you should be very careful if you decide to use this option;virtual_rays
– (optional, computed automatically if needed) a list of ray generators to be used forvirtual_rays()
;discard_faces
– by default, the fan constructor expects the list of maximal cones, unlessallow_arrangement=True
option is specified. If you provide “extra” ones and leaveallow_arrangement=False
(default) andcheck=True
(default), an exception will be raised. If you provide “extra” cones and setallow_arrangement=False
(default) andcheck=False
, you may get wrong results as assumptions on internal data structures will be invalid. If you want the fan constructor to select the maximal cones from the given input, you may providediscard_faces=True
option (it works both forcheck=True
andcheck=False
).allow_arrangement
– by default (allow_arrangement=False
), the fan constructor expects that the intersection of any two given cones is a face of each. Ifallow_arrangement=True
option is specified, then construct a rational polyhedralfan from the cone arrangement, so that the union of the cones in the polyhedral fan equals to the union of the given cones, and each given cone is the union of some cones in the polyhedral fan.
OUTPUT:
a
fan
See also
In 2 dimensions you can cyclically order the rays. Hence the rays determine a unique maximal fan without having to specify the cones, and you can use
Fan2d()
to construct this fan from just the rays.EXAMPLES:
Let’s construct a fan corresponding to the projective plane in several ways:
sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(0,1), (-1,-1)]) sage: cone3 = Cone([(-1,-1), (1,0)]) sage: P2 = Fan([cone1, cone2, cone2]) Traceback (most recent call last): ... ValueError: you have provided 3 cones, but only 2 of them are maximal! Use discard_faces=True if you indeed need to construct a fan from these cones.
>>> from sage.all import * >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(Integer(0),Integer(1)), (-Integer(1),-Integer(1))]) >>> cone3 = Cone([(-Integer(1),-Integer(1)), (Integer(1),Integer(0))]) >>> P2 = Fan([cone1, cone2, cone2]) Traceback (most recent call last): ... ValueError: you have provided 3 cones, but only 2 of them are maximal! Use discard_faces=True if you indeed need to construct a fan from these cones.
Oops! There was a typo and
cone2
was listed twice as a generating cone of the fan. If it was intentional (e.g. the list of cones was generated automatically and it is possible that it contains repetitions or faces of other cones), usediscard_faces=True
option:sage: P2 = Fan([cone1, cone2, cone2], discard_faces=True) sage: P2.ngenerating_cones() 2
>>> from sage.all import * >>> P2 = Fan([cone1, cone2, cone2], discard_faces=True) >>> P2.ngenerating_cones() 2
However, in this case it was definitely a typo, since the fan of \(\mathbb{P}^2\) has 3 maximal cones:
sage: P2 = Fan([cone1, cone2, cone3]) sage: P2.ngenerating_cones() 3
>>> from sage.all import * >>> P2 = Fan([cone1, cone2, cone3]) >>> P2.ngenerating_cones() 3
Looks better. An alternative way is
sage: rays = [(1,0), (0,1), (-1,-1)] sage: cones = [(0,1), (1,2), (2,0)] sage: P2a = Fan(cones, rays) sage: P2a.ngenerating_cones() 3 sage: P2 == P2a False
>>> from sage.all import * >>> rays = [(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),-Integer(1))] >>> cones = [(Integer(0),Integer(1)), (Integer(1),Integer(2)), (Integer(2),Integer(0))] >>> P2a = Fan(cones, rays) >>> P2a.ngenerating_cones() 3 >>> P2 == P2a False
That may seem wrong, but it is not:
sage: P2.is_equivalent(P2a) True
>>> from sage.all import * >>> P2.is_equivalent(P2a) True
See
is_equivalent()
for details.Yet another way to construct this fan is
sage: P2b = Fan(cones, rays, check=False) sage: P2b.ngenerating_cones() 3 sage: P2a == P2b True
>>> from sage.all import * >>> P2b = Fan(cones, rays, check=False) >>> P2b.ngenerating_cones() 3 >>> P2a == P2b True
If you try the above examples, you are likely to notice the difference in speed, so when you are sure that everything is correct, it is a good idea to use
check=False
option. On the other hand, it is usually NOT a good idea to usenormalize=False
option:sage: P2c = Fan(cones, rays, check=False, normalize=False) Traceback (most recent call last): ... AttributeError: 'tuple' object has no attribute 'parent'...
>>> from sage.all import * >>> P2c = Fan(cones, rays, check=False, normalize=False) Traceback (most recent call last): ... AttributeError: 'tuple' object has no attribute 'parent'...
Yet another way is to use functions
FaceFan()
andNormalFan()
to construct fans fromlattice polytopes
.We have not yet used
lattice
argument, since if was determined automatically:sage: P2.lattice() 2-d lattice N sage: P2b.lattice() 2-d lattice N
>>> from sage.all import * >>> P2.lattice() 2-d lattice N >>> P2b.lattice() 2-d lattice N
However, it is necessary to specify it explicitly if you want to construct a fan without rays or cones:
sage: Fan([], []) Traceback (most recent call last): ... ValueError: you must specify the lattice when you construct a fan without rays and cones! sage: F = Fan([], [], lattice=ToricLattice(2, "L")) sage: F Rational polyhedral fan in 2-d lattice L sage: F.lattice_dim() 2 sage: F.dim() 0
>>> from sage.all import * >>> Fan([], []) Traceback (most recent call last): ... ValueError: you must specify the lattice when you construct a fan without rays and cones! >>> F = Fan([], [], lattice=ToricLattice(Integer(2), "L")) >>> F Rational polyhedral fan in 2-d lattice L >>> F.lattice_dim() 2 >>> F.dim() 0
In the following examples, we test the
allow_arrangement=True
option. See Issue #25122.The intersection of the two cones is not a face of each. Therefore, they do not belong to the same rational polyhedral fan:
sage: c1 = Cone([(-2,-1,1), (-2,1,1), (2,1,1), (2,-1,1)]) sage: c2 = Cone([(-1,-2,1), (-1,2,1), (1,2,1), (1,-2,1)]) sage: c1.intersection(c2).is_face_of(c1) False sage: c1.intersection(c2).is_face_of(c2) False sage: Fan([c1, c2]) Traceback (most recent call last): ... ValueError: these cones cannot belong to the same fan! ...
>>> from sage.all import * >>> c1 = Cone([(-Integer(2),-Integer(1),Integer(1)), (-Integer(2),Integer(1),Integer(1)), (Integer(2),Integer(1),Integer(1)), (Integer(2),-Integer(1),Integer(1))]) >>> c2 = Cone([(-Integer(1),-Integer(2),Integer(1)), (-Integer(1),Integer(2),Integer(1)), (Integer(1),Integer(2),Integer(1)), (Integer(1),-Integer(2),Integer(1))]) >>> c1.intersection(c2).is_face_of(c1) False >>> c1.intersection(c2).is_face_of(c2) False >>> Fan([c1, c2]) Traceback (most recent call last): ... ValueError: these cones cannot belong to the same fan! ...
Let’s construct the fan using
allow_arrangement=True
option:sage: fan = Fan([c1, c2], allow_arrangement=True) sage: fan.ngenerating_cones() 5
>>> from sage.all import * >>> fan = Fan([c1, c2], allow_arrangement=True) >>> fan.ngenerating_cones() 5
Another example where cone c2 is inside cone c1:
sage: c1 = Cone([(4, 0, 0), (0, 4, 0), (0, 0, 4)]) sage: c2 = Cone([(2, 1, 1), (1, 2, 1), (1, 1, 2)]) sage: fan = Fan([c1, c2], allow_arrangement=True) sage: fan.ngenerating_cones() 7 sage: fan.plot() # needs sage.plot Graphics3d Object
>>> from sage.all import * >>> c1 = Cone([(Integer(4), Integer(0), Integer(0)), (Integer(0), Integer(4), Integer(0)), (Integer(0), Integer(0), Integer(4))]) >>> c2 = Cone([(Integer(2), Integer(1), Integer(1)), (Integer(1), Integer(2), Integer(1)), (Integer(1), Integer(1), Integer(2))]) >>> fan = Fan([c1, c2], allow_arrangement=True) >>> fan.ngenerating_cones() 7 >>> fan.plot() # needs sage.plot Graphics3d Object
Cones of different dimension:
sage: c1 = Cone([(1,0), (0,1)]) sage: c2 = Cone([(2,1)]) sage: c3 = Cone([(-1,-2)]) sage: fan = Fan([c1, c2, c3], allow_arrangement=True) sage: for cone in sorted(fan.generating_cones()): print(sorted(cone.rays())) [N(-1, -2)] [N(0, 1), N(1, 2)] [N(1, 0), N(2, 1)] [N(1, 2), N(2, 1)]
>>> from sage.all import * >>> c1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> c2 = Cone([(Integer(2),Integer(1))]) >>> c3 = Cone([(-Integer(1),-Integer(2))]) >>> fan = Fan([c1, c2, c3], allow_arrangement=True) >>> for cone in sorted(fan.generating_cones()): print(sorted(cone.rays())) [N(-1, -2)] [N(0, 1), N(1, 2)] [N(1, 0), N(2, 1)] [N(1, 2), N(2, 1)]
A 3-d cone and a 1-d cone:
sage: c3 = Cone([[0, 1, 1], [1, 0, 1], [0, -1, 1], [-1, 0, 1]]) sage: c1 = Cone([[0, 0, 1]]) sage: fan1 = Fan([c1, c3], allow_arrangement=True) sage: fan1.plot() # needs sage.plot Graphics3d Object
>>> from sage.all import * >>> c3 = Cone([[Integer(0), Integer(1), Integer(1)], [Integer(1), Integer(0), Integer(1)], [Integer(0), -Integer(1), Integer(1)], [-Integer(1), Integer(0), Integer(1)]]) >>> c1 = Cone([[Integer(0), Integer(0), Integer(1)]]) >>> fan1 = Fan([c1, c3], allow_arrangement=True) >>> fan1.plot() # needs sage.plot Graphics3d Object
A 3-d cone and two 2-d cones:
sage: c2v = Cone([[0, 1, 1], [0, -1, 1]]) sage: c2h = Cone([[1, 0, 1], [-1, 0, 1]]) sage: fan2 = Fan([c2v, c2h, c3], allow_arrangement=True) sage: fan2.is_simplicial() True sage: fan2.is_equivalent(fan1) True
>>> from sage.all import * >>> c2v = Cone([[Integer(0), Integer(1), Integer(1)], [Integer(0), -Integer(1), Integer(1)]]) >>> c2h = Cone([[Integer(1), Integer(0), Integer(1)], [-Integer(1), Integer(0), Integer(1)]]) >>> fan2 = Fan([c2v, c2h, c3], allow_arrangement=True) >>> fan2.is_simplicial() True >>> fan2.is_equivalent(fan1) True
- sage.geometry.fan.Fan2d(rays, lattice=None)[source]#
Construct the maximal 2-d fan with given
rays
.In two dimensions we can uniquely construct a fan from just rays, just by cyclically ordering the rays and constructing as many cones as possible. This is why we implement a special constructor for this case.
INPUT:
rays
– list of rays given as list or vectors convertible to the rational extension oflattice
. Duplicate rays are removed without changing the ordering of the remaining rays.lattice
–ToricLattice
, \(\ZZ^n\), or any other object that behaves like these. If not specified, an attempt will be made to determine an appropriate toric lattice automatically.
EXAMPLES:
sage: Fan2d([(0,1), (1,0)]) Rational polyhedral fan in 2-d lattice N sage: Fan2d([], lattice=ToricLattice(2, 'myN')) Rational polyhedral fan in 2-d lattice myN
>>> from sage.all import * >>> Fan2d([(Integer(0),Integer(1)), (Integer(1),Integer(0))]) Rational polyhedral fan in 2-d lattice N >>> Fan2d([], lattice=ToricLattice(Integer(2), 'myN')) Rational polyhedral fan in 2-d lattice myN
The ray order is as specified, even if it is not the cyclic order:
sage: fan1 = Fan2d([(0,1), (1,0)]) sage: fan1.rays() N(0, 1), N(1, 0) in 2-d lattice N sage: fan2 = Fan2d([(1,0), (0,1)]) sage: fan2.rays() N(1, 0), N(0, 1) in 2-d lattice N sage: fan1 == fan2, fan1.is_equivalent(fan2) (False, True) sage: fan = Fan2d([(1,1), (-1,-1), (1,-1), (-1,1)]) sage: [cone.ambient_ray_indices() for cone in fan] [(2, 1), (1, 3), (3, 0), (0, 2)] sage: fan.is_complete() True
>>> from sage.all import * >>> fan1 = Fan2d([(Integer(0),Integer(1)), (Integer(1),Integer(0))]) >>> fan1.rays() N(0, 1), N(1, 0) in 2-d lattice N >>> fan2 = Fan2d([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> fan2.rays() N(1, 0), N(0, 1) in 2-d lattice N >>> fan1 == fan2, fan1.is_equivalent(fan2) (False, True) >>> fan = Fan2d([(Integer(1),Integer(1)), (-Integer(1),-Integer(1)), (Integer(1),-Integer(1)), (-Integer(1),Integer(1))]) >>> [cone.ambient_ray_indices() for cone in fan] [(2, 1), (1, 3), (3, 0), (0, 2)] >>> fan.is_complete() True
- sage.geometry.fan.NormalFan(polytope, lattice=None)[source]#
Construct the normal fan of the given rational
polytope
.This returns the inner normal fan. For the outer normal fan, use
NormalFan(-P)
.INPUT:
polytope
– a full-dimensionalpolytope
over \(\QQ\) or:class:\(lattice polytope <sage.geometry.lattice_polytope.LatticePolytopeClass>\).lattice
–ToricLattice
, \(\ZZ^n\), or any other object that behaves like these. If not specified, an attempt will be made to determine an appropriate toric lattice automatically.
OUTPUT:
See also
FaceFan()
.EXAMPLES:
Let’s construct the fan corresponding to the product of two projective lines:
sage: square = LatticePolytope([(1,1), (-1,1), (-1,-1), (1,-1)]) sage: P1xP1 = NormalFan(square) sage: P1xP1.rays() N( 1, 0), N( 0, 1), N(-1, 0), N( 0, -1) in 2-d lattice N sage: for cone in P1xP1: print(cone.rays()) N(-1, 0), N( 0, -1) in 2-d lattice N N(1, 0), N(0, -1) in 2-d lattice N N(1, 0), N(0, 1) in 2-d lattice N N( 0, 1), N(-1, 0) in 2-d lattice N sage: cuboctahed = polytopes.cuboctahedron() sage: NormalFan(cuboctahed) Rational polyhedral fan in 3-d lattice N
>>> from sage.all import * >>> square = LatticePolytope([(Integer(1),Integer(1)), (-Integer(1),Integer(1)), (-Integer(1),-Integer(1)), (Integer(1),-Integer(1))]) >>> P1xP1 = NormalFan(square) >>> P1xP1.rays() N( 1, 0), N( 0, 1), N(-1, 0), N( 0, -1) in 2-d lattice N >>> for cone in P1xP1: print(cone.rays()) N(-1, 0), N( 0, -1) in 2-d lattice N N(1, 0), N(0, -1) in 2-d lattice N N(1, 0), N(0, 1) in 2-d lattice N N( 0, 1), N(-1, 0) in 2-d lattice N >>> cuboctahed = polytopes.cuboctahedron() >>> NormalFan(cuboctahed) Rational polyhedral fan in 3-d lattice N
- class sage.geometry.fan.RationalPolyhedralFan(cones, rays, lattice, is_complete=None, virtual_rays=None)[source]#
Bases:
IntegralRayCollection
,Callable
,Container
Create a rational polyhedral fan.
Warning
This class does not perform any checks of correctness of input nor does it convert input into the standard representation. Use
Fan()
to construct fans from “raw data” orFaceFan()
andNormalFan()
to get fans associated to polytopes.Fans are immutable, but they cache most of the returned values.
INPUT:
cones
– list of generating cones of the fan, each cone given as a list of indices of its generating rays inrays
;rays
– list of immutable primitive vectors inlattice
consisting of exactly the rays of the fan (i.e. no “extra” ones);lattice
–ToricLattice
, \(\ZZ^n\), or any other object that behaves like these. IfNone
, it will be determined asparent()
of the first ray. Of course, this cannot be done if there are no rays, so in this case you must give an appropriatelattice
directly;is_complete
– if given, must beTrue
orFalse
depending on whether this fan is complete or not. By default, it will be determined automatically if necessary;virtual_rays
– if given, must be a list of immutable primitive vectors inlattice
, seevirtual_rays()
for details. By default, it will be determined automatically if necessary.
OUTPUT:
rational polyhedral fan
- Gale_transform()[source]#
Return the Gale transform of
self
.OUTPUT:
A matrix over \(ZZ\)
EXAMPLES:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: fan.Gale_transform() # needs palp [ 1 1 0 0 -2] [ 0 0 1 1 -2] sage: _.base_ring() # needs palp Integer Ring
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> fan.Gale_transform() # needs palp [ 1 1 0 0 -2] [ 0 0 1 1 -2] >>> _.base_ring() # needs palp Integer Ring
- Stanley_Reisner_ideal(ring)[source]#
Return the Stanley-Reisner ideal.
INPUT:
A polynomial ring in
self.nrays()
variables.
OUTPUT:
The Stanley-Reisner ideal in the given polynomial ring
EXAMPLES:
sage: fan = Fan([[0,1,3], [3,4], [2,0], [1,2,4]], ....: [(-3, -2, 1), (0, 0, 1), (3, -2, 1), (-1, -1, 1), (1, -1, 1)]) sage: fan.Stanley_Reisner_ideal(PolynomialRing(QQ, 5, 'A, B, C, D, E')) Ideal (A*E, C*D, A*B*C, B*D*E) of Multivariate Polynomial Ring in A, B, C, D, E over Rational Field
>>> from sage.all import * >>> fan = Fan([[Integer(0),Integer(1),Integer(3)], [Integer(3),Integer(4)], [Integer(2),Integer(0)], [Integer(1),Integer(2),Integer(4)]], ... [(-Integer(3), -Integer(2), Integer(1)), (Integer(0), Integer(0), Integer(1)), (Integer(3), -Integer(2), Integer(1)), (-Integer(1), -Integer(1), Integer(1)), (Integer(1), -Integer(1), Integer(1))]) >>> fan.Stanley_Reisner_ideal(PolynomialRing(QQ, Integer(5), 'A, B, C, D, E')) Ideal (A*E, C*D, A*B*C, B*D*E) of Multivariate Polynomial Ring in A, B, C, D, E over Rational Field
- cartesian_product(other, lattice=None)[source]#
Return the Cartesian product of
self
withother
.INPUT:
other
– arational polyhedral fan
;lattice
– (optional) the ambient lattice for the Cartesian product fan. By default, the direct sum of the ambient lattices ofself
andother
is constructed.
OUTPUT:
a
fan
whose cones are all pairwise Cartesian products of the cones ofself
andother
EXAMPLES:
sage: K = ToricLattice(1, 'K') sage: fan1 = Fan([[0],[1]], [(1,),(-1,)], lattice=K) sage: L = ToricLattice(2, 'L') sage: fan2 = Fan(rays=[(1,0), (0,1), (-1,-1)], ....: cones=[[0,1], [1,2], [2,0]], lattice=L) sage: fan1.cartesian_product(fan2) Rational polyhedral fan in 3-d lattice K+L sage: _.ngenerating_cones() 6
>>> from sage.all import * >>> K = ToricLattice(Integer(1), 'K') >>> fan1 = Fan([[Integer(0)],[Integer(1)]], [(Integer(1),),(-Integer(1),)], lattice=K) >>> L = ToricLattice(Integer(2), 'L') >>> fan2 = Fan(rays=[(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),-Integer(1))], ... cones=[[Integer(0),Integer(1)], [Integer(1),Integer(2)], [Integer(2),Integer(0)]], lattice=L) >>> fan1.cartesian_product(fan2) Rational polyhedral fan in 3-d lattice K+L >>> _.ngenerating_cones() 6
- common_refinement(other)[source]#
Return the common refinement of this fan and
other
.INPUT:
other
– afan
in the samelattice()
and with the same support as this fan
OUTPUT:
a
fan
EXAMPLES:
Refining a fan with itself gives itself:
sage: F0 = Fan2d([(1,0), (0,1), (-1,0), (0,-1)]) sage: F0.common_refinement(F0) == F0 True
>>> from sage.all import * >>> F0 = Fan2d([(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),Integer(0)), (Integer(0),-Integer(1))]) >>> F0.common_refinement(F0) == F0 True
A more complex example with complete fans:
sage: F1 = Fan([[0],[1]], [(1,),(-1,)]) sage: F2 = Fan2d([(1,0), (1,1), (0,1), (-1,0), (0,-1)]) sage: F3 = F2.cartesian_product(F1) sage: F4 = F1.cartesian_product(F2) sage: FF = F3.common_refinement(F4) sage: F3.ngenerating_cones() 10 sage: F4.ngenerating_cones() 10 sage: FF.ngenerating_cones() 13
>>> from sage.all import * >>> F1 = Fan([[Integer(0)],[Integer(1)]], [(Integer(1),),(-Integer(1),)]) >>> F2 = Fan2d([(Integer(1),Integer(0)), (Integer(1),Integer(1)), (Integer(0),Integer(1)), (-Integer(1),Integer(0)), (Integer(0),-Integer(1))]) >>> F3 = F2.cartesian_product(F1) >>> F4 = F1.cartesian_product(F2) >>> FF = F3.common_refinement(F4) >>> F3.ngenerating_cones() 10 >>> F4.ngenerating_cones() 10 >>> FF.ngenerating_cones() 13
An example with two non-complete fans with the same support:
sage: F5 = Fan2d([(1,0), (1,2), (0,1)]) sage: F6 = Fan2d([(1,0), (2,1), (0,1)]) sage: F5.common_refinement(F6).ngenerating_cones() 3
>>> from sage.all import * >>> F5 = Fan2d([(Integer(1),Integer(0)), (Integer(1),Integer(2)), (Integer(0),Integer(1))]) >>> F6 = Fan2d([(Integer(1),Integer(0)), (Integer(2),Integer(1)), (Integer(0),Integer(1))]) >>> F5.common_refinement(F6).ngenerating_cones() 3
Both fans must live in the same lattice:
sage: F0.common_refinement(F1) Traceback (most recent call last): ... ValueError: the fans are not in the same lattice
>>> from sage.all import * >>> F0.common_refinement(F1) Traceback (most recent call last): ... ValueError: the fans are not in the same lattice
- complex(base_ring=Integer Ring, extended=False)[source]#
Return the chain complex of the fan.
To a \(d\)-dimensional fan \(\Sigma\), one can canonically associate a chain complex \(K^\bullet\)
\[0 \longrightarrow \ZZ^{\Sigma(d)} \longrightarrow \ZZ^{\Sigma(d-1)} \longrightarrow \cdots \longrightarrow \ZZ^{\Sigma(0)} \longrightarrow 0\]where the leftmost non-zero entry is in degree \(0\) and the rightmost entry in degree \(d\). See [Kly1990], eq. (3.2). This complex computes the homology of \(|\Sigma|\subset N_\RR\) with arbitrary support,
\[H_i(K) = H_{d-i}(|\Sigma|, \ZZ)_{\text{non-cpct}}\]For a complete fan, this is just the non-compactly supported homology of \(\RR^d\). In this case, \(H_0(K)=\ZZ\) and \(0\) in all non-zero degrees.
For a complete fan, there is an extended chain complex
\[0 \longrightarrow \ZZ \longrightarrow \ZZ^{\Sigma(d)} \longrightarrow \ZZ^{\Sigma(d-1)} \longrightarrow \cdots \longrightarrow \ZZ^{\Sigma(0)} \longrightarrow 0\]where we take the first \(\ZZ\) term to be in degree -1. This complex is an exact sequence, that is, all homology groups vanish.
The orientation of each cone is chosen as in
oriented_boundary()
.INPUT:
extended
– Boolean (default:False
). Whether to construct the extended complex, that is, including the \(\ZZ\)-term at degree -1 or not.base_ring
– A ring (default:ZZ
). The ring to use instead of \(\ZZ\).
OUTPUT:
The complex associated to the fan as a
ChainComplex
. This raises aValueError
if the extended complex is requested for a non-complete fan.EXAMPLES:
sage: # needs palp sage: fan = toric_varieties.P(3).fan() sage: K_normal = fan.complex(); K_normal Chain complex with at most 4 nonzero terms over Integer Ring sage: K_normal.homology() {0: Z, 1: 0, 2: 0, 3: 0} sage: K_extended = fan.complex(extended=True); K_extended Chain complex with at most 5 nonzero terms over Integer Ring sage: K_extended.homology() {-1: 0, 0: 0, 1: 0, 2: 0, 3: 0}
>>> from sage.all import * >>> # needs palp >>> fan = toric_varieties.P(Integer(3)).fan() >>> K_normal = fan.complex(); K_normal Chain complex with at most 4 nonzero terms over Integer Ring >>> K_normal.homology() {0: Z, 1: 0, 2: 0, 3: 0} >>> K_extended = fan.complex(extended=True); K_extended Chain complex with at most 5 nonzero terms over Integer Ring >>> K_extended.homology() {-1: 0, 0: 0, 1: 0, 2: 0, 3: 0}
Homology computations are much faster over \(\QQ\) if you do not care about the torsion coefficients:
sage: toric_varieties.P2_123().fan().complex(extended=True, # needs palp ....: base_ring=QQ) Chain complex with at most 4 nonzero terms over Rational Field sage: _.homology() # needs palp {-1: Vector space of dimension 0 over Rational Field, 0: Vector space of dimension 0 over Rational Field, 1: Vector space of dimension 0 over Rational Field, 2: Vector space of dimension 0 over Rational Field}
>>> from sage.all import * >>> toric_varieties.P2_123().fan().complex(extended=True, # needs palp ... base_ring=QQ) Chain complex with at most 4 nonzero terms over Rational Field >>> _.homology() # needs palp {-1: Vector space of dimension 0 over Rational Field, 0: Vector space of dimension 0 over Rational Field, 1: Vector space of dimension 0 over Rational Field, 2: Vector space of dimension 0 over Rational Field}
The extended complex is only defined for complete fans:
sage: fan = Fan([Cone([(1,0)])]) sage: fan.is_complete() False sage: fan.complex(extended=True) Traceback (most recent call last): ... ValueError: The extended complex is only defined for complete fans!
>>> from sage.all import * >>> fan = Fan([Cone([(Integer(1),Integer(0))])]) >>> fan.is_complete() False >>> fan.complex(extended=True) Traceback (most recent call last): ... ValueError: The extended complex is only defined for complete fans!
The definition of the complex does not refer to the ambient space of the fan, so it does not distinguish a fan from the same fan embedded in a subspace:
sage: K1 = Fan([Cone([(-1,)]), Cone([(1,)])]).complex() sage: K2 = Fan([Cone([(-1,0,0)]), Cone([(1,0,0)])]).complex() sage: K1 == K2 True
>>> from sage.all import * >>> K1 = Fan([Cone([(-Integer(1),)]), Cone([(Integer(1),)])]).complex() >>> K2 = Fan([Cone([(-Integer(1),Integer(0),Integer(0))]), Cone([(Integer(1),Integer(0),Integer(0))])]).complex() >>> K1 == K2 True
Things get more complicated for non-complete fans:
sage: fan = Fan([Cone([(1,1,1)]), ....: Cone([(1,0,0), (0,1,0)]), ....: Cone([(-1,0,0), (0,-1,0), (0,0,-1)])]) sage: fan.complex().homology() {0: 0, 1: 0, 2: Z x Z, 3: 0} sage: fan = Fan([Cone([(1,0,0), (0,1,0)]), ....: Cone([(-1,0,0), (0,-1,0), (0,0,-1)])]) sage: fan.complex().homology() {0: 0, 1: 0, 2: Z, 3: 0} sage: fan = Fan([Cone([(-1,0,0), (0,-1,0), (0,0,-1)])]) sage: fan.complex().homology() {0: 0, 1: 0, 2: 0, 3: 0}
>>> from sage.all import * >>> fan = Fan([Cone([(Integer(1),Integer(1),Integer(1))]), ... Cone([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0))]), ... Cone([(-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),-Integer(1))])]) >>> fan.complex().homology() {0: 0, 1: 0, 2: Z x Z, 3: 0} >>> fan = Fan([Cone([(Integer(1),Integer(0),Integer(0)), (Integer(0),Integer(1),Integer(0))]), ... Cone([(-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),-Integer(1))])]) >>> fan.complex().homology() {0: 0, 1: 0, 2: Z, 3: 0} >>> fan = Fan([Cone([(-Integer(1),Integer(0),Integer(0)), (Integer(0),-Integer(1),Integer(0)), (Integer(0),Integer(0),-Integer(1))])]) >>> fan.complex().homology() {0: 0, 1: 0, 2: 0, 3: 0}
- cone_containing(*points)[source]#
Return the smallest cone of
self
containing all given points.INPUT:
either one or more indices of rays of
self
, or one or more objects representing points of the ambient space ofself
, or a list of such objects (you CANNOT give a list of indices).
OUTPUT:
A
cone of fan
whose ambient fan isself
Note
We think of the origin as of the smallest cone containing no rays at all. If there is no ray in
self
that contains allrays
, aValueError
exception will be raised.EXAMPLES:
sage: cone1 = Cone([(0,-1), (1,0)]) sage: cone2 = Cone([(1,0), (0,1)]) sage: f = Fan([cone1, cone2]) sage: f.rays() N(0, -1), N(0, 1), N(1, 0) in 2-d lattice N sage: f.cone_containing(0) # ray index 1-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing(0, 1) # ray indices Traceback (most recent call last): ... ValueError: there is no cone in Rational polyhedral fan in 2-d lattice N containing all of the given rays! Ray indices: [0, 1] sage: f.cone_containing(0, 2) # ray indices 2-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing((0,1)) # point 1-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing([(0,1)]) # point 1-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing((1,1)) 2-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing((1,1), (1,0)) 2-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing() 0-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing((0,0)) 0-d cone of Rational polyhedral fan in 2-d lattice N sage: f.cone_containing((-1,1)) Traceback (most recent call last): ... ValueError: there is no cone in Rational polyhedral fan in 2-d lattice N containing all of the given points! Points: [N(-1, 1)]
>>> from sage.all import * >>> cone1 = Cone([(Integer(0),-Integer(1)), (Integer(1),Integer(0))]) >>> cone2 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> f = Fan([cone1, cone2]) >>> f.rays() N(0, -1), N(0, 1), N(1, 0) in 2-d lattice N >>> f.cone_containing(Integer(0)) # ray index 1-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing(Integer(0), Integer(1)) # ray indices Traceback (most recent call last): ... ValueError: there is no cone in Rational polyhedral fan in 2-d lattice N containing all of the given rays! Ray indices: [0, 1] >>> f.cone_containing(Integer(0), Integer(2)) # ray indices 2-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing((Integer(0),Integer(1))) # point 1-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing([(Integer(0),Integer(1))]) # point 1-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing((Integer(1),Integer(1))) 2-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing((Integer(1),Integer(1)), (Integer(1),Integer(0))) 2-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing() 0-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing((Integer(0),Integer(0))) 0-d cone of Rational polyhedral fan in 2-d lattice N >>> f.cone_containing((-Integer(1),Integer(1))) Traceback (most recent call last): ... ValueError: there is no cone in Rational polyhedral fan in 2-d lattice N containing all of the given points! Points: [N(-1, 1)]
- cone_lattice()[source]#
Return the cone lattice of
self
.This lattice will have the origin as the bottom (we do not include the empty set as a cone) and the fan itself as the top.
OUTPUT:
finite poset <sage.combinat.posets.posets.FinitePoset
ofcones of fan
, behaving like “regular” cones, but also containing the information about their relation to this fan, namely, the contained rays and containing generating cones. The top of the lattice will be this fan itself (which is not acone of fan
).See also
cones()
.EXAMPLES:
Cone lattices can be computed for arbitrary fans:
sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(-1,0)]) sage: fan = Fan([cone1, cone2]) sage: fan.rays() N(-1, 0), N( 0, 1), N( 1, 0) in 2-d lattice N sage: for cone in fan: print(cone.ambient_ray_indices()) (1, 2) (0,) sage: L = fan.cone_lattice() sage: L Finite poset containing 6 elements with distinguished linear extension
>>> from sage.all import * >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(-Integer(1),Integer(0))]) >>> fan = Fan([cone1, cone2]) >>> fan.rays() N(-1, 0), N( 0, 1), N( 1, 0) in 2-d lattice N >>> for cone in fan: print(cone.ambient_ray_indices()) (1, 2) (0,) >>> L = fan.cone_lattice() >>> L Finite poset containing 6 elements with distinguished linear extension
These 6 elements are the origin, three rays, one two-dimensional cone, and the fan itself. Since we do add the fan itself as the largest face, you should be a little bit careful with this last element:
sage: for face in L: print(face.ambient_ray_indices()) Traceback (most recent call last): ... AttributeError: 'RationalPolyhedralFan' object has no attribute 'ambient_ray_indices' sage: L.top() Rational polyhedral fan in 2-d lattice N
>>> from sage.all import * >>> for face in L: print(face.ambient_ray_indices()) Traceback (most recent call last): ... AttributeError: 'RationalPolyhedralFan' object has no attribute 'ambient_ray_indices' >>> L.top() Rational polyhedral fan in 2-d lattice N
For example, you can do
sage: for l in L.level_sets()[:-1]: ....: print([f.ambient_ray_indices() for f in l]) [()] [(0,), (1,), (2,)] [(1, 2)]
>>> from sage.all import * >>> for l in L.level_sets()[:-Integer(1)]: ... print([f.ambient_ray_indices() for f in l]) [()] [(0,), (1,), (2,)] [(1, 2)]
If the fan is complete, its cone lattice is atomic and coatomic and can (and will!) be computed in a much more efficient way, but the interface is exactly the same:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: L = fan.cone_lattice() # needs palp sage: for l in L.level_sets()[:-1]: # needs palp ....: print([f.ambient_ray_indices() for f in l]) [()] [(0,), (1,), (2,), (3,)] [(0, 2), (1, 2), (1, 3), (0, 3)]
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> L = fan.cone_lattice() # needs palp >>> for l in L.level_sets()[:-Integer(1)]: # needs palp ... print([f.ambient_ray_indices() for f in l]) [()] [(0,), (1,), (2,), (3,)] [(0, 2), (1, 2), (1, 3), (0, 3)]
Let’s also consider the cone lattice of a fan generated by a single cone:
sage: fan = Fan([cone1]) sage: L = fan.cone_lattice() sage: L Finite poset containing 5 elements with distinguished linear extension
>>> from sage.all import * >>> fan = Fan([cone1]) >>> L = fan.cone_lattice() >>> L Finite poset containing 5 elements with distinguished linear extension
Here these 5 elements correspond to the origin, two rays, one generating cone of dimension two, and the whole fan. While this single cone “is” the whole fan, it is consistent and convenient to distinguish them in the cone lattice.
- cones(dim=None, codim=None)[source]#
Return the specified cones of
self
.INPUT:
dim
– dimension of the requested cones;codim
– codimension of the requested cones.
Note
You can specify at most one input parameter.
OUTPUT:
tuple
of cones ofself
of the specified (co)dimension, if eitherdim
orcodim
is given. Otherwisetuple
of such tuples for all existing dimensions.EXAMPLES:
sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(-1,0)]) sage: fan = Fan([cone1, cone2]) sage: fan(dim=0) (0-d cone of Rational polyhedral fan in 2-d lattice N,) sage: fan(codim=2) (0-d cone of Rational polyhedral fan in 2-d lattice N,) sage: for cone in fan.cones(1): cone.ray(0) N(-1, 0) N(0, 1) N(1, 0) sage: fan.cones(2) (2-d cone of Rational polyhedral fan in 2-d lattice N,)
>>> from sage.all import * >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(-Integer(1),Integer(0))]) >>> fan = Fan([cone1, cone2]) >>> fan(dim=Integer(0)) (0-d cone of Rational polyhedral fan in 2-d lattice N,) >>> fan(codim=Integer(2)) (0-d cone of Rational polyhedral fan in 2-d lattice N,) >>> for cone in fan.cones(Integer(1)): cone.ray(Integer(0)) N(-1, 0) N(0, 1) N(1, 0) >>> fan.cones(Integer(2)) (2-d cone of Rational polyhedral fan in 2-d lattice N,)
You cannot specify both dimension and codimension, even if they “agree”:
sage: fan(dim=1, codim=1) Traceback (most recent call last): ... ValueError: dimension and codimension cannot be specified together!
>>> from sage.all import * >>> fan(dim=Integer(1), codim=Integer(1)) Traceback (most recent call last): ... ValueError: dimension and codimension cannot be specified together!
But it is OK to ask for cones of too high or low (co)dimension:
sage: fan(-1) () sage: fan(3) () sage: fan(codim=4) ()
>>> from sage.all import * >>> fan(-Integer(1)) () >>> fan(Integer(3)) () >>> fan(codim=Integer(4)) ()
- contains(cone)[source]#
Check if a given
cone
is equivalent to a cone of the fan.INPUT:
cone
– anything.
OUTPUT:
False
ifcone
is not a cone or ifcone
is not equivalent to a cone of the fan,True
otherwiseNote
Recall that a fan is a (finite) collection of cones. A cone is contained in a fan if it is equivalent to one of the cones of the fan. In particular, it is possible that all rays of the cone are in the fan, but the cone itself is not.
If you want to know whether a point is in the support of the fan, you should use
support_contains()
.EXAMPLES:
We first construct a simple fan:
sage: cone1 = Cone([(0,-1), (1,0)]) sage: cone2 = Cone([(1,0), (0,1)]) sage: f = Fan([cone1, cone2])
>>> from sage.all import * >>> cone1 = Cone([(Integer(0),-Integer(1)), (Integer(1),Integer(0))]) >>> cone2 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> f = Fan([cone1, cone2])
Now we check if some cones are in this fan. First, we make sure that the order of rays of the input cone does not matter (
check=False
option ensures that rays of these cones will be listed exactly as they are given):sage: f.contains(Cone([(1,0), (0,1)], check=False)) True sage: f.contains(Cone([(0,1), (1,0)], check=False)) True
>>> from sage.all import * >>> f.contains(Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))], check=False)) True >>> f.contains(Cone([(Integer(0),Integer(1)), (Integer(1),Integer(0))], check=False)) True
Now we check that a non-generating cone is in our fan:
sage: f.contains(Cone([(1,0)])) True sage: Cone([(1,0)]) in f # equivalent to the previous command True
>>> from sage.all import * >>> f.contains(Cone([(Integer(1),Integer(0))])) True >>> Cone([(Integer(1),Integer(0))]) in f # equivalent to the previous command True
Finally, we test some cones which are not in this fan:
sage: f.contains(Cone([(1,1)])) False sage: f.contains(Cone([(1,0), (-0,1)])) True
>>> from sage.all import * >>> f.contains(Cone([(Integer(1),Integer(1))])) False >>> f.contains(Cone([(Integer(1),Integer(0)), (-Integer(0),Integer(1))])) True
A point is not a cone:
sage: n = f.lattice()(1,1); n N(1, 1) sage: f.contains(n) False
>>> from sage.all import * >>> n = f.lattice()(Integer(1),Integer(1)); n N(1, 1) >>> f.contains(n) False
- embed(cone)[source]#
Return the cone equivalent to the given one, but sitting in
self
.You may need to use this method before calling methods of
cone
that depend on the ambient structure, such asambient_ray_indices()
orfacet_of()
. The cone returned by this method will haveself
as ambient. Ifcone
does not represent a valid cone ofself
,ValueError
exception is raised.Note
This method is very quick if
self
is already the ambient structure ofcone
, so you can use without extra checks and performance hit even ifcone
is likely to sit inself
but in principle may not.INPUT:
cone
– acone
.
OUTPUT:
a
cone of fan
, equivalent tocone
but sitting insideself
EXAMPLES:
Let’s take a 3-d fan generated by a cone on 4 rays:
sage: f = Fan([Cone([(1,0,1), (0,1,1), (-1,0,1), (0,-1,1)])])
>>> from sage.all import * >>> f = Fan([Cone([(Integer(1),Integer(0),Integer(1)), (Integer(0),Integer(1),Integer(1)), (-Integer(1),Integer(0),Integer(1)), (Integer(0),-Integer(1),Integer(1))])])
Then any ray generates a 1-d cone of this fan, but if you construct such a cone directly, it will not “sit” inside the fan:
sage: ray = Cone([(0,-1,1)]) sage: ray 1-d cone in 3-d lattice N sage: ray.ambient_ray_indices() (0,) sage: ray.adjacent() () sage: ray.ambient() 1-d cone in 3-d lattice N
>>> from sage.all import * >>> ray = Cone([(Integer(0),-Integer(1),Integer(1))]) >>> ray 1-d cone in 3-d lattice N >>> ray.ambient_ray_indices() (0,) >>> ray.adjacent() () >>> ray.ambient() 1-d cone in 3-d lattice N
If we want to operate with this ray as a part of the fan, we need to embed it first:
sage: e_ray = f.embed(ray) sage: e_ray 1-d cone of Rational polyhedral fan in 3-d lattice N sage: e_ray.rays() N(0, -1, 1) in 3-d lattice N sage: e_ray is ray False sage: e_ray.is_equivalent(ray) True sage: e_ray.ambient_ray_indices() (3,) sage: e_ray.adjacent() (1-d cone of Rational polyhedral fan in 3-d lattice N, 1-d cone of Rational polyhedral fan in 3-d lattice N) sage: e_ray.ambient() Rational polyhedral fan in 3-d lattice N
>>> from sage.all import * >>> e_ray = f.embed(ray) >>> e_ray 1-d cone of Rational polyhedral fan in 3-d lattice N >>> e_ray.rays() N(0, -1, 1) in 3-d lattice N >>> e_ray is ray False >>> e_ray.is_equivalent(ray) True >>> e_ray.ambient_ray_indices() (3,) >>> e_ray.adjacent() (1-d cone of Rational polyhedral fan in 3-d lattice N, 1-d cone of Rational polyhedral fan in 3-d lattice N) >>> e_ray.ambient() Rational polyhedral fan in 3-d lattice N
Not every cone can be embedded into a fixed fan:
sage: f.embed(Cone([(0,0,1)])) Traceback (most recent call last): ... ValueError: 1-d cone in 3-d lattice N does not belong to Rational polyhedral fan in 3-d lattice N! sage: f.embed(Cone([(1,0,1), (-1,0,1)])) Traceback (most recent call last): ... ValueError: 2-d cone in 3-d lattice N does not belong to Rational polyhedral fan in 3-d lattice N!
>>> from sage.all import * >>> f.embed(Cone([(Integer(0),Integer(0),Integer(1))])) Traceback (most recent call last): ... ValueError: 1-d cone in 3-d lattice N does not belong to Rational polyhedral fan in 3-d lattice N! >>> f.embed(Cone([(Integer(1),Integer(0),Integer(1)), (-Integer(1),Integer(0),Integer(1))])) Traceback (most recent call last): ... ValueError: 2-d cone in 3-d lattice N does not belong to Rational polyhedral fan in 3-d lattice N!
- f_vector()[source]#
Return the f-vector of the fan.
This is the tuple \((f_0, f_1, \ldots, f_d)\) where \(f_i\) is the number of cones of dimension \(i\).
EXAMPLES:
sage: F = ClusterAlgebra(['A',2]).cluster_fan() sage: F.f_vector() (1, 5, 5)
>>> from sage.all import * >>> F = ClusterAlgebra(['A',Integer(2)]).cluster_fan() >>> F.f_vector() (1, 5, 5)
- generating_cone(n)[source]#
Return the
n
-th generating cone ofself
.INPUT:
n
– integer, the index of a generating cone.
OUTPUT:
EXAMPLES:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: fan.generating_cone(0) # needs palp 2-d cone of Rational polyhedral fan in 2-d lattice N
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> fan.generating_cone(Integer(0)) # needs palp 2-d cone of Rational polyhedral fan in 2-d lattice N
- generating_cones()[source]#
Return generating cones of
self
.OUTPUT:
tuple
ofcones of fan
EXAMPLES:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: fan.generating_cones() # needs palp (2-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, 2-d cone of Rational polyhedral fan in 2-d lattice N) sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(-1,0)]) sage: fan = Fan([cone1, cone2]) sage: fan.generating_cones() (2-d cone of Rational polyhedral fan in 2-d lattice N, 1-d cone of Rational polyhedral fan in 2-d lattice N)
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> fan.generating_cones() # needs palp (2-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, 2-d cone of Rational polyhedral fan in 2-d lattice N) >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(-Integer(1),Integer(0))]) >>> fan = Fan([cone1, cone2]) >>> fan.generating_cones() (2-d cone of Rational polyhedral fan in 2-d lattice N, 1-d cone of Rational polyhedral fan in 2-d lattice N)
- is_complete()[source]#
Check if
self
is complete.A rational polyhedral fan is complete if its cones fill the whole space.
OUTPUT:
True
ifself
is complete andFalse
otherwiseEXAMPLES:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: fan.is_complete() # needs palp True sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(-1,0)]) sage: fan = Fan([cone1, cone2]) sage: fan.is_complete() False
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> fan.is_complete() # needs palp True >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(-Integer(1),Integer(0))]) >>> fan = Fan([cone1, cone2]) >>> fan.is_complete() False
- is_equivalent(other)[source]#
Check if
self
is “mathematically” the same asother
.INPUT:
other
– fan.
OUTPUT:
True
ifself
andother
define the same fans as collections of equivalent cones in the same lattice,False
otherwise.There are three different equivalences between fans \(F_1\) and \(F_2\) in the same lattice:
They have the same rays in the same order and the same generating cones in the same order. This is tested by
F1 == F2
.They have the same rays and the same generating cones without taking into account any order. This is tested by
F1.is_equivalent(F2)
.They are in the same orbit of \(GL(n,\ZZ)\) (and, therefore, correspond to isomorphic toric varieties). This is tested by
F1.is_isomorphic(F2)
.
Note that
virtual_rays()
are included into consideration for all of the above equivalences.EXAMPLES:
sage: fan1 = Fan(cones=[(0,1), (1,2)], ....: rays=[(1,0), (0,1), (-1,-1)], ....: check=False) sage: fan2 = Fan(cones=[(2,1), (0,2)], ....: rays=[(1,0), (-1,-1), (0,1)], ....: check=False) sage: fan3 = Fan(cones=[(0,1), (1,2)], ....: rays=[(1,0), (0,1), (-1,1)], ....: check=False) sage: fan1 == fan2 False sage: fan1.is_equivalent(fan2) True sage: fan1 == fan3 False sage: fan1.is_equivalent(fan3) False
>>> from sage.all import * >>> fan1 = Fan(cones=[(Integer(0),Integer(1)), (Integer(1),Integer(2))], ... rays=[(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),-Integer(1))], ... check=False) >>> fan2 = Fan(cones=[(Integer(2),Integer(1)), (Integer(0),Integer(2))], ... rays=[(Integer(1),Integer(0)), (-Integer(1),-Integer(1)), (Integer(0),Integer(1))], ... check=False) >>> fan3 = Fan(cones=[(Integer(0),Integer(1)), (Integer(1),Integer(2))], ... rays=[(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),Integer(1))], ... check=False) >>> fan1 == fan2 False >>> fan1.is_equivalent(fan2) True >>> fan1 == fan3 False >>> fan1.is_equivalent(fan3) False
- is_isomorphic(other)[source]#
Check if
self
is in the same \(GL(n, \ZZ)\)-orbit asother
.There are three different equivalences between fans \(F_1\) and \(F_2\) in the same lattice:
They have the same rays in the same order and the same generating cones in the same order. This is tested by
F1 == F2
.They have the same rays and the same generating cones without taking into account any order. This is tested by
F1.is_equivalent(F2)
.They are in the same orbit of \(GL(n,\ZZ)\) (and, therefore, correspond to isomorphic toric varieties). This is tested by
F1.is_isomorphic(F2)
.
Note that
virtual_rays()
are included into consideration for all of the above equivalences.INPUT:
other
– afan
.
OUTPUT:
True
ifself
andother
are in the same \(GL(n, \ZZ)\)-orbit,False
otherwiseSee also
If you want to obtain the actual fan isomorphism, use
isomorphism()
.EXAMPLES:
Here we pick an \(SL(2,\ZZ)\) matrix
m
and then verify that the image fan is isomorphic:sage: rays = ((1, 1), (0, 1), (-1, -1), (1, 0)) sage: cones = [(0,1), (1,2), (2,3), (3,0)] sage: fan1 = Fan(cones, rays) sage: m = matrix([[-2,3], [1,-1]]) sage: fan2 = Fan(cones, [vector(r)*m for r in rays]) sage: fan1.is_isomorphic(fan2) True sage: fan1.is_equivalent(fan2) False sage: fan1 == fan2 False
>>> from sage.all import * >>> rays = ((Integer(1), Integer(1)), (Integer(0), Integer(1)), (-Integer(1), -Integer(1)), (Integer(1), Integer(0))) >>> cones = [(Integer(0),Integer(1)), (Integer(1),Integer(2)), (Integer(2),Integer(3)), (Integer(3),Integer(0))] >>> fan1 = Fan(cones, rays) >>> m = matrix([[-Integer(2),Integer(3)], [Integer(1),-Integer(1)]]) >>> fan2 = Fan(cones, [vector(r)*m for r in rays]) >>> fan1.is_isomorphic(fan2) True >>> fan1.is_equivalent(fan2) False >>> fan1 == fan2 False
These fans are “mirrors” of each other:
sage: fan1 = Fan(cones=[(0,1), (1,2)], ....: rays=[(1,0), (0,1), (-1,-1)], ....: check=False) sage: fan2 = Fan(cones=[(0,1), (1,2)], ....: rays=[(1,0), (0,-1), (-1,1)], ....: check=False) sage: fan1 == fan2 False sage: fan1.is_equivalent(fan2) False sage: fan1.is_isomorphic(fan2) True sage: fan1.is_isomorphic(fan1) True
>>> from sage.all import * >>> fan1 = Fan(cones=[(Integer(0),Integer(1)), (Integer(1),Integer(2))], ... rays=[(Integer(1),Integer(0)), (Integer(0),Integer(1)), (-Integer(1),-Integer(1))], ... check=False) >>> fan2 = Fan(cones=[(Integer(0),Integer(1)), (Integer(1),Integer(2))], ... rays=[(Integer(1),Integer(0)), (Integer(0),-Integer(1)), (-Integer(1),Integer(1))], ... check=False) >>> fan1 == fan2 False >>> fan1.is_equivalent(fan2) False >>> fan1.is_isomorphic(fan2) True >>> fan1.is_isomorphic(fan1) True
- is_simplicial()[source]#
Check if
self
is simplicial.A rational polyhedral fan is simplicial if all of its cones are, i.e. primitive vectors along generating rays of every cone form a part of a rational basis of the ambient space.
OUTPUT:
True
ifself
is simplicial andFalse
otherwiseEXAMPLES:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: fan.is_simplicial() # needs palp True sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(-1,0)]) sage: fan = Fan([cone1, cone2]) sage: fan.is_simplicial() True
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> fan.is_simplicial() # needs palp True >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(-Integer(1),Integer(0))]) >>> fan = Fan([cone1, cone2]) >>> fan.is_simplicial() True
In fact, any fan in a two-dimensional ambient space is simplicial. This is no longer the case in dimension three:
sage: fan = NormalFan(lattice_polytope.cross_polytope(3)) sage: fan.is_simplicial() False sage: fan.generating_cone(0).nrays() 4
>>> from sage.all import * >>> fan = NormalFan(lattice_polytope.cross_polytope(Integer(3))) >>> fan.is_simplicial() False >>> fan.generating_cone(Integer(0)).nrays() 4
- is_smooth(codim=None)[source]#
Check if
self
is smooth.A rational polyhedral fan is smooth if all of its cones are, i.e. primitive vectors along generating rays of every cone form a part of an integral basis of the ambient space. In this case the corresponding toric variety is smooth.
A fan in an \(n\)-dimensional lattice is smooth up to codimension \(c\) if all cones of codimension greater than or equal to \(c\) are smooth, i.e. if all cones of dimension less than or equal to \(n-c\) are smooth. In this case the singular set of the corresponding toric variety is of dimension less than \(c\).
INPUT:
codim
– codimension in which smoothness has to be checked, by default complete smoothness will be checked.
OUTPUT:
True
ifself
is smooth (in codimensioncodim
, if it was given) andFalse
otherwise.EXAMPLES:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: fan.is_smooth() # needs palp True sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(-1,0)]) sage: fan = Fan([cone1, cone2]) sage: fan.is_smooth() True sage: fan = NormalFan(lattice_polytope.cross_polytope(2)) sage: fan.is_smooth() False sage: fan.is_smooth(codim=1) True sage: fan.generating_cone(0).rays() N(-1, -1), N(-1, 1) in 2-d lattice N sage: fan.generating_cone(0).rays().matrix().det() -2
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> fan.is_smooth() # needs palp True >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(-Integer(1),Integer(0))]) >>> fan = Fan([cone1, cone2]) >>> fan.is_smooth() True >>> fan = NormalFan(lattice_polytope.cross_polytope(Integer(2))) >>> fan.is_smooth() False >>> fan.is_smooth(codim=Integer(1)) True >>> fan.generating_cone(Integer(0)).rays() N(-1, -1), N(-1, 1) in 2-d lattice N >>> fan.generating_cone(Integer(0)).rays().matrix().det() -2
- isomorphism(other)[source]#
Return a fan isomorphism from
self
toother
.INPUT:
other
– fan.
OUTPUT:
A fan isomorphism. If no such isomorphism exists, a
FanNotIsomorphicError
is raised.EXAMPLES:
sage: rays = ((1, 1), (0, 1), (-1, -1), (3, 1)) sage: cones = [(0,1), (1,2), (2,3), (3,0)] sage: fan1 = Fan(cones, rays) sage: m = matrix([[-2,3], [1,-1]]) sage: fan2 = Fan(cones, [vector(r)*m for r in rays]) sage: fan1.isomorphism(fan2) Fan morphism defined by the matrix [-2 3] [ 1 -1] Domain fan: Rational polyhedral fan in 2-d lattice N Codomain fan: Rational polyhedral fan in 2-d lattice N sage: fan2.isomorphism(fan1) Fan morphism defined by the matrix [1 3] [1 2] Domain fan: Rational polyhedral fan in 2-d lattice N Codomain fan: Rational polyhedral fan in 2-d lattice N sage: fan1.isomorphism(toric_varieties.P2().fan()) # needs palp Traceback (most recent call last): ... FanNotIsomorphicError
>>> from sage.all import * >>> rays = ((Integer(1), Integer(1)), (Integer(0), Integer(1)), (-Integer(1), -Integer(1)), (Integer(3), Integer(1))) >>> cones = [(Integer(0),Integer(1)), (Integer(1),Integer(2)), (Integer(2),Integer(3)), (Integer(3),Integer(0))] >>> fan1 = Fan(cones, rays) >>> m = matrix([[-Integer(2),Integer(3)], [Integer(1),-Integer(1)]]) >>> fan2 = Fan(cones, [vector(r)*m for r in rays]) >>> fan1.isomorphism(fan2) Fan morphism defined by the matrix [-2 3] [ 1 -1] Domain fan: Rational polyhedral fan in 2-d lattice N Codomain fan: Rational polyhedral fan in 2-d lattice N >>> fan2.isomorphism(fan1) Fan morphism defined by the matrix [1 3] [1 2] Domain fan: Rational polyhedral fan in 2-d lattice N Codomain fan: Rational polyhedral fan in 2-d lattice N >>> fan1.isomorphism(toric_varieties.P2().fan()) # needs palp Traceback (most recent call last): ... FanNotIsomorphicError
- linear_equivalence_ideal(ring)[source]#
Return the ideal generated by linear relations.
INPUT:
A polynomial ring in
self.nrays()
variables.
OUTPUT:
Return the ideal, in the given
ring
, generated by the linear relations of the rays. In toric geometry, this corresponds to rational equivalence of divisors.EXAMPLES:
sage: fan = Fan([[0,1,3],[3,4],[2,0],[1,2,4]], ....: [(-3, -2, 1), (0, 0, 1), (3, -2, 1), (-1, -1, 1), (1, -1, 1)]) sage: fan.linear_equivalence_ideal(PolynomialRing(QQ, 5, 'A, B, C, D, E')) Ideal (-3*A + 3*C - D + E, -2*A - 2*C - D - E, A + B + C + D + E) of Multivariate Polynomial Ring in A, B, C, D, E over Rational Field
>>> from sage.all import * >>> fan = Fan([[Integer(0),Integer(1),Integer(3)],[Integer(3),Integer(4)],[Integer(2),Integer(0)],[Integer(1),Integer(2),Integer(4)]], ... [(-Integer(3), -Integer(2), Integer(1)), (Integer(0), Integer(0), Integer(1)), (Integer(3), -Integer(2), Integer(1)), (-Integer(1), -Integer(1), Integer(1)), (Integer(1), -Integer(1), Integer(1))]) >>> fan.linear_equivalence_ideal(PolynomialRing(QQ, Integer(5), 'A, B, C, D, E')) Ideal (-3*A + 3*C - D + E, -2*A - 2*C - D - E, A + B + C + D + E) of Multivariate Polynomial Ring in A, B, C, D, E over Rational Field
- make_simplicial(**kwds)[source]#
Construct a simplicial fan subdividing
self
.It is a synonym for
subdivide()
withmake_simplicial=True
option.INPUT:
this functions accepts only keyword arguments. See
subdivide()
for documentation.
OUTPUT:
EXAMPLES:
sage: fan = NormalFan(lattice_polytope.cross_polytope(3)) sage: fan.is_simplicial() False sage: fan.ngenerating_cones() 6 sage: new_fan = fan.make_simplicial() sage: new_fan.is_simplicial() True sage: new_fan.ngenerating_cones() 12
>>> from sage.all import * >>> fan = NormalFan(lattice_polytope.cross_polytope(Integer(3))) >>> fan.is_simplicial() False >>> fan.ngenerating_cones() 6 >>> new_fan = fan.make_simplicial() >>> new_fan.is_simplicial() True >>> new_fan.ngenerating_cones() 12
- ngenerating_cones()[source]#
Return the number of generating cones of
self
.OUTPUT:
integer
EXAMPLES:
sage: fan = toric_varieties.P1xP1().fan() # needs palp sage: fan.ngenerating_cones() # needs palp 4 sage: cone1 = Cone([(1,0), (0,1)]) sage: cone2 = Cone([(-1,0)]) sage: fan = Fan([cone1, cone2]) sage: fan.ngenerating_cones() 2
>>> from sage.all import * >>> fan = toric_varieties.P1xP1().fan() # needs palp >>> fan.ngenerating_cones() # needs palp 4 >>> cone1 = Cone([(Integer(1),Integer(0)), (Integer(0),Integer(1))]) >>> cone2 = Cone([(-Integer(1),Integer(0))]) >>> fan = Fan([cone1, cone2]) >>> fan.ngenerating_cones() 2
- oriented_boundary(cone)[source]#
Return the facets bounding
cone
with their induced orientation.INPUT:
cone
– a cone of the fan or the whole fan.
OUTPUT:
The boundary cones of
cone
as a formal linear combination of cones with coefficients \(\pm 1\). Each summand is a facet ofcone
and the coefficient indicates whether their (chosen) orientation agrees or disagrees with the “outward normal first” boundary orientation. Note that the orientation of any individual cone is arbitrary. This method once and for all picks orientations for all cones and then computes the boundaries relative to that chosen orientation.If
cone
is the fan itself, the generating cones with their orientation relative to the ambient space are returned.See
complex()
for the associated chain complex. If you do not require the orientation, usecone.facets()
instead.EXAMPLES:
sage: # needs palp sage: fan = toric_varieties.P(3).fan() sage: cone = fan(2)[0] sage: bdry = fan.oriented_boundary(cone); bdry -1-d cone of Rational polyhedral fan in 3-d lattice N + 1-d cone of Rational polyhedral fan in 3-d lattice N sage: bdry[0] (-1, 1-d cone of Rational polyhedral fan in 3-d lattice N) sage: bdry[1] (1, 1-d cone of Rational polyhedral fan in 3-d lattice N) sage: fan.oriented_boundary(bdry[0][1]) -0-d cone of Rational polyhedral fan in 3-d lattice N sage: fan.oriented_boundary(bdry[1][1]) -0-d cone of Rational polyhedral fan in 3-d lattice N
>>> from sage.all import * >>> # needs palp >>> fan = toric_varieties.P(Integer(3)).fan() >>> cone = fan(Integer(2))[Integer(0)] >>> bdry = fan.oriented_boundary(cone); bdry -1-d cone of Rational polyhedral fan in 3-d lattice N + 1-d cone of Rational polyhedral fan in 3-d lattice N >>> bdry[Integer(0)] (-1, 1-d cone of Rational polyhedral fan in 3-d lattice N) >>> bdry[Integer(1)] (1, 1-d cone of Rational polyhedral fan in 3-d lattice N) >>> fan.oriented_boundary(bdry[Integer(0)][Integer(1)]) -0-d cone of Rational polyhedral fan in 3-d lattice N >>> fan.oriented_boundary(bdry[Integer(1)][Integer(1)]) -0-d cone of Rational polyhedral fan in 3-d lattice N
If you pass the fan itself, this method returns the orientation of the generating cones which is determined by the order of the rays in
cone.ray_basis()
sage: fan.oriented_boundary(fan) # needs palp -3-d cone of Rational polyhedral fan in 3-d lattice N + 3-d cone of Rational polyhedral fan in 3-d lattice N - 3-d cone of Rational polyhedral fan in 3-d lattice N + 3-d cone of Rational polyhedral fan in 3-d lattice N sage: [cone.rays().basis().matrix().det() # needs palp ....: for cone in fan.generating_cones()] [-1, 1, -1, 1]
>>> from sage.all import * >>> fan.oriented_boundary(fan) # needs palp -3-d cone of Rational polyhedral fan in 3-d lattice N + 3-d cone of Rational polyhedral fan in 3-d lattice N - 3-d cone of Rational polyhedral fan in 3-d lattice N + 3-d cone of Rational polyhedral fan in 3-d lattice N >>> [cone.rays().basis().matrix().det() # needs palp ... for cone in fan.generating_cones()] [-1, 1, -1, 1]
A non-full dimensional fan:
sage: cone = Cone([(4,5)]) sage: fan = Fan([cone]) sage: fan.oriented_boundary(cone) 0-d cone of Rational polyhedral fan in 2-d lattice N sage: fan.oriented_boundary(fan) 1-d cone of Rational polyhedral fan in 2-d lattice N
>>> from sage.all import * >>> cone = Cone([(Integer(4),Integer(5))]) >>> fan = Fan([cone]) >>> fan.oriented_boundary(cone) 0-d cone of Rational polyhedral fan in 2-d lattice N >>> fan.oriented_boundary(fan) 1-d cone of Rational polyhedral fan in 2-d lattice N
- plot(**options)[source]#
Plot
self
.INPUT:
any options for toric plots (see
toric_plotter.options
), none are mandatory.
OUTPUT:
a plot
EXAMPLES:
sage: fan = toric_varieties.dP6().fan() # needs palp sage: fan.plot() # needs palp sage.plot Graphics object consisting of 31 graphics primitives
>>> from sage.all import * >>> fan = toric_varieties.dP6().fan() # needs palp >>> fan.plot() # needs palp sage.plot Graphics object consisting of 31 graphics primitives
- primitive_collections()[source]#
Return the primitive collections.
OUTPUT:
Return the subsets \(\{i_1,\dots,i_k\} \subset \{ 1,\dots,n\}\) such that
The points \(\{p_{i_1},\dots,p_{i_k}\}\) do not span a cone of the fan.
If you remove any one \(p_{i_j}\) from the set, then they do span a cone of the fan.
Note
By replacing the multiindices \(\{i_1,\dots,i_k\}\) of each primitive collection with the monomials \(x_{i_1}\cdots x_{i_k}\) one generates the Stanley-Reisner ideal in \(\ZZ[x_1,\dots]\).
REFERENCES:
EXAMPLES:
sage: fan = Fan([[0,1,3], [3,4], [2,0], [1,2,4]], ....: [(-3, -2, 1), (0, 0, 1), (3, -2, 1), (-1, -1, 1), (1, -1, 1)]) sage: fan.primitive_collections() [frozenset({0, 4}), frozenset({2, 3}), frozenset({0, 1, 2}), frozenset({1, 3, 4})]
>>> from sage.all import * >>> fan = Fan([[Integer(0),Integer(1),Integer(3)], [Integer(3),Integer(4)], [Integer(2),Integer(0)], [Integer(1),Integer(2),Integer(4)]], ... [(-Integer(3), -Integer(2), Integer(1)), (Integer(0), Integer(0), Integer(1)), (Integer(3), -Integer(2), Integer(1)), (-Integer(1), -Integer(1), Integer(1)), (Integer(1), -Integer(1), Integer(1))]) >>> fan.primitive_collections() [frozenset({0, 4}), frozenset({2, 3}), frozenset({0, 1, 2}), frozenset({1, 3, 4})]
- subdivide(new_rays=None, make_simplicial=False, algorithm='default', verbose=False)[source]#
Construct a new fan subdividing
self
.INPUT:
new_rays
– list of new rays to be added during subdivision, each ray must be a list or a vector. May be empty orNone
(default);make_simplicial
– ifTrue
, the returned fan is guaranteed to be simplicial, default isFalse
;algorithm
– string with the name of the algorithm used for subdivision. Currently there is only one available algorithm called “default”;verbose
– ifTrue
, some timing information may be printed during the process of subdivision.
OUTPUT:
Currently the “default” algorithm corresponds to iterative stellar subdivision for each ray in
new_rays
.EXAMPLES:
sage: fan = NormalFan(lattice_polytope.cross_polytope(3)) sage: fan.is_simplicial() False sage: fan.ngenerating_cones() 6 sage: fan.nrays() 8 sage: new_fan = fan.subdivide(new_rays=[(1,0,0)]) sage: new_fan.is_simplicial() False sage: new_fan.ngenerating_cones() 9 sage: new_fan.nrays() 9
>>> from sage.all import * >>> fan = NormalFan(lattice_polytope.cross_polytope(Integer(3))) >>> fan.is_simplicial() False >>> fan.ngenerating_cones() 6 >>> fan.nrays() 8 >>> new_fan = fan.subdivide(new_rays=[(Integer(1),Integer(0),Integer(0))]) >>> new_fan.is_simplicial() False >>> new_fan.ngenerating_cones() 9 >>> new_fan.nrays() 9
- support_contains(*args)[source]#
Check if a point is contained in the support of the fan.
The support of a fan is the union of all cones of the fan. If you want to know whether the fan contains a given cone, you should use
contains()
instead.INPUT:
*args
– an element ofself.lattice()
or something that can be converted to it (for example, a list of coordinates).
OUTPUT:
True
ifpoint
is contained in the support of the fan,False
otherwise
- toric_variety(*args, **kwds)[source]#
Return the associated toric variety.
INPUT:
same arguments as
ToricVariety()
OUTPUT:
a toric variety
This is equivalent to the command
ToricVariety(self)
and is provided only as a convenient alternative method to go from the fan to the associated toric variety.EXAMPLES:
sage: Fan([Cone([(1,0)]), Cone([(0,1)])]).toric_variety() 2-d toric variety covered by 2 affine patches
>>> from sage.all import * >>> Fan([Cone([(Integer(1),Integer(0))]), Cone([(Integer(0),Integer(1))])]).toric_variety() 2-d toric variety covered by 2 affine patches
- vertex_graph()[source]#
Return the graph of 1- and 2-cones.
OUTPUT:
An edge-colored graph. The vertices correspond to the 1-cones (i.e. rays) of the fan. Two vertices are joined by an edge iff the rays span a 2-cone of the fan. The edges are colored by pairs of integers that classify the 2-cones up to \(GL(2,\ZZ)\) transformation, see
classify_cone_2d()
.EXAMPLES:
sage: # needs palp sage: dP8 = toric_varieties.dP8() sage: g = dP8.fan().vertex_graph(); g Graph on 4 vertices sage: set(dP8.fan(1)) == set(g.vertices(sort=False)) True sage: g.edge_labels() # all edge labels the same since every cone is smooth [(1, 0), (1, 0), (1, 0), (1, 0)] sage: g = toric_varieties.Cube_deformation(10).fan().vertex_graph() sage: g.automorphism_group().order() # needs sage.groups 48 sage: g.automorphism_group(edge_labels=True).order() # needs sage.groups 4
>>> from sage.all import * >>> # needs palp >>> dP8 = toric_varieties.dP8() >>> g = dP8.fan().vertex_graph(); g Graph on 4 vertices >>> set(dP8.fan(Integer(1))) == set(g.vertices(sort=False)) True >>> g.edge_labels() # all edge labels the same since every cone is smooth [(1, 0), (1, 0), (1, 0), (1, 0)] >>> g = toric_varieties.Cube_deformation(Integer(10)).fan().vertex_graph() >>> g.automorphism_group().order() # needs sage.groups 48 >>> g.automorphism_group(edge_labels=True).order() # needs sage.groups 4
- virtual_rays(*args)[source]#
Return (some of the) virtual rays of
self
.Let \(N\) be the \(D\)-dimensional
lattice()
of a \(d\)-dimensional fan \(\Sigma\) in \(N_\RR\). Then the corresponding toric variety is of the form \(X \times (\CC^*)^{D-d}\). The actualrays()
of \(\Sigma\) give a canonical choice of homogeneous coordinates on \(X\). This function returns an arbitrary but fixed choice of virtual rays corresponding to a (non-canonical) choice of homogeneous coordinates on the torus factor. Combinatorially primitive integral generators of virtual rays span the \(D-d\) dimensions of \(N_\QQ\) “missed” by the actual rays. (In general addition of virtual rays is not sufficient to span \(N\) over \(\ZZ\).)Note
You may use a particular choice of virtual rays by passing optional argument
virtual_rays
to theFan()
constructor.INPUT:
ray_list
– a list of integers, the indices of the requested virtual rays. If not specified, all virtual rays ofself
will be returned.
OUTPUT:
a
PointCollection
of primitive integral ray generators. Usually (if the fan is full-dimensional) this will be empty.EXAMPLES:
sage: f = Fan([Cone([(1,0,1,0), (0,1,1,0)])]) sage: f.virtual_rays() N(1, 0, 0, 0), N(0, 0, 0, 1) in 4-d lattice N sage: f.rays() N(1, 0, 1, 0), N(0, 1, 1, 0) in 4-d lattice N sage: f.virtual_rays([0]) N(1, 0, 0, 0) in 4-d lattice N
>>> from sage.all import * >>> f = Fan([Cone([(Integer(1),Integer(0),Integer(1),Integer(0)), (Integer(0),Integer(1),Integer(1),Integer(0))])]) >>> f.virtual_rays() N(1, 0, 0, 0), N(0, 0, 0, 1) in 4-d lattice N >>> f.rays() N(1, 0, 1, 0), N(0, 1, 1, 0) in 4-d lattice N >>> f.virtual_rays([Integer(0)]) N(1, 0, 0, 0) in 4-d lattice N
You can also give virtual ray indices directly, without packing them into a list:
sage: f.virtual_rays(0) N(1, 0, 0, 0) in 4-d lattice N
>>> from sage.all import * >>> f.virtual_rays(Integer(0)) N(1, 0, 0, 0) in 4-d lattice N
Make sure that Issue #16344 is fixed and one can compute the virtual rays of fans in non-saturated lattices:
sage: N = ToricLattice(1) sage: B = N.submodule([(2,)]).basis() sage: f = Fan([Cone([B[0]])]) sage: len(f.virtual_rays()) 0
>>> from sage.all import * >>> N = ToricLattice(Integer(1)) >>> B = N.submodule([(Integer(2),)]).basis() >>> f = Fan([Cone([B[Integer(0)]])]) >>> len(f.virtual_rays()) 0
- sage.geometry.fan.discard_faces(cones)[source]#
Return the cones of the given list which are not faces of each other.
INPUT:
cones
– a list ofcones
.
OUTPUT:
a list of
cones
, sorted by dimension in decreasing orderEXAMPLES:
Consider all cones of a fan:
sage: Sigma = toric_varieties.P2().fan() # needs palp sage: cones = flatten(Sigma.cones()) # needs palp sage: len(cones) # needs palp 7
>>> from sage.all import * >>> Sigma = toric_varieties.P2().fan() # needs palp >>> cones = flatten(Sigma.cones()) # needs palp >>> len(cones) # needs palp 7
Most of them are not necessary to generate this fan:
sage: from sage.geometry.fan import discard_faces sage: len(discard_faces(cones)) # needs palp 3 sage: Sigma.ngenerating_cones() # needs palp 3
>>> from sage.all import * >>> from sage.geometry.fan import discard_faces >>> len(discard_faces(cones)) # needs palp 3 >>> Sigma.ngenerating_cones() # needs palp 3
- sage.geometry.fan.is_Fan(x)[source]#
Check if
x
is a Fan.INPUT:
x
– anything.
OUTPUT:
True
ifx
is a fan andFalse
otherwiseEXAMPLES:
sage: from sage.geometry.fan import is_Fan sage: is_Fan(1) doctest:warning... DeprecationWarning: The function is_Fan is deprecated; use 'isinstance(..., RationalPolyhedralFan)' instead. See https://github.com/sagemath/sage/issues/38126 for details. False sage: fan = toric_varieties.P2().fan(); fan # needs palp Rational polyhedral fan in 2-d lattice N sage: is_Fan(fan) # needs palp True
>>> from sage.all import * >>> from sage.geometry.fan import is_Fan >>> is_Fan(Integer(1)) doctest:warning... DeprecationWarning: The function is_Fan is deprecated; use 'isinstance(..., RationalPolyhedralFan)' instead. See https://github.com/sagemath/sage/issues/38126 for details. False >>> fan = toric_varieties.P2().fan(); fan # needs palp Rational polyhedral fan in 2-d lattice N >>> is_Fan(fan) # needs palp True