passagemath-polyhedra 10.6.31rc3__cp314-cp314-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-polyhedra might be problematic. Click here for more details.
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +208 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_polyhedra.libs/libgcc_s-2d945d6c.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-28992bcb.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-1ede7ee7.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-85f2cd6d.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-aarch64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-aarch64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-aarch64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
|
@@ -0,0 +1,1884 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
# sage.doctest: needs sage.combinat sage.graphs
|
|
3
|
+
r"""
|
|
4
|
+
Morphisms between toric lattices compatible with fans
|
|
5
|
+
|
|
6
|
+
This module is a part of the framework for toric varieties
|
|
7
|
+
(:mod:`~sage.schemes.toric.variety`,
|
|
8
|
+
:mod:`~sage.schemes.toric.fano_variety`). Its main purpose is to
|
|
9
|
+
provide support for working with lattice morphisms compatible with fans via
|
|
10
|
+
:class:`FanMorphism` class.
|
|
11
|
+
|
|
12
|
+
AUTHORS:
|
|
13
|
+
|
|
14
|
+
- Andrey Novoseltsev (2010-10-17): initial version.
|
|
15
|
+
- Andrey Novoseltsev (2011-04-11): added tests for injectivity/surjectivity,
|
|
16
|
+
fibration, bundle, as well as some related methods.
|
|
17
|
+
|
|
18
|
+
EXAMPLES:
|
|
19
|
+
|
|
20
|
+
Let's consider the face and normal fans of the "diamond" and the projection
|
|
21
|
+
to the `x`-axis::
|
|
22
|
+
|
|
23
|
+
sage: diamond = lattice_polytope.cross_polytope(2)
|
|
24
|
+
sage: face = FaceFan(diamond, lattice=ToricLattice(2))
|
|
25
|
+
sage: normal = NormalFan(diamond)
|
|
26
|
+
sage: N = face.lattice()
|
|
27
|
+
sage: H = End(N)
|
|
28
|
+
sage: phi = H([N.0, 0])
|
|
29
|
+
sage: phi
|
|
30
|
+
Free module morphism defined by the matrix
|
|
31
|
+
[1 0]
|
|
32
|
+
[0 0]
|
|
33
|
+
Domain: 2-d lattice N
|
|
34
|
+
Codomain: 2-d lattice N
|
|
35
|
+
sage: FanMorphism(phi, normal, face)
|
|
36
|
+
Traceback (most recent call last):
|
|
37
|
+
...
|
|
38
|
+
ValueError: the image of generating cone #1 of the domain fan
|
|
39
|
+
is not contained in a single cone of the codomain fan!
|
|
40
|
+
|
|
41
|
+
Some of the cones of the normal fan fail to be mapped to a single cone of the
|
|
42
|
+
face fan. We can rectify the situation in the following way::
|
|
43
|
+
|
|
44
|
+
sage: fm = FanMorphism(phi, normal, face, subdivide=True)
|
|
45
|
+
sage: fm
|
|
46
|
+
Fan morphism defined by the matrix
|
|
47
|
+
[1 0]
|
|
48
|
+
[0 0]
|
|
49
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
50
|
+
Codomain fan: Rational polyhedral fan in 2-d lattice N
|
|
51
|
+
sage: fm.domain_fan().rays()
|
|
52
|
+
N( 1, 1),
|
|
53
|
+
N( 1, -1),
|
|
54
|
+
N(-1, -1),
|
|
55
|
+
N(-1, 1),
|
|
56
|
+
N( 0, -1),
|
|
57
|
+
N( 0, 1)
|
|
58
|
+
in 2-d lattice N
|
|
59
|
+
sage: normal.rays()
|
|
60
|
+
N( 1, 1),
|
|
61
|
+
N( 1, -1),
|
|
62
|
+
N(-1, -1),
|
|
63
|
+
N(-1, 1)
|
|
64
|
+
in 2-d lattice N
|
|
65
|
+
|
|
66
|
+
As you see, it was necessary to insert two new rays (to prevent "upper" and
|
|
67
|
+
"lower" cones of the normal fan from being mapped to the whole `x`-axis).
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ****************************************************************************
|
|
72
|
+
# Copyright (C) 2010 Andrey Novoseltsev <novoselt@gmail.com>
|
|
73
|
+
# Copyright (C) 2010 William Stein <wstein@gmail.com>
|
|
74
|
+
#
|
|
75
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
76
|
+
#
|
|
77
|
+
# https://www.gnu.org/licenses/
|
|
78
|
+
# ****************************************************************************
|
|
79
|
+
|
|
80
|
+
import operator
|
|
81
|
+
|
|
82
|
+
from sage.categories.homset import Hom
|
|
83
|
+
from sage.geometry.cone import Cone
|
|
84
|
+
from sage.geometry.fan import Fan, RationalPolyhedralFan
|
|
85
|
+
from sage.matrix.constructor import matrix
|
|
86
|
+
from sage.matrix.special import identity_matrix
|
|
87
|
+
from sage.structure.element import Matrix
|
|
88
|
+
from sage.misc.cachefunc import cached_method
|
|
89
|
+
from sage.misc.misc_c import prod
|
|
90
|
+
from sage.modules.free_module_morphism import FreeModuleMorphism
|
|
91
|
+
from sage.rings.infinity import Infinity
|
|
92
|
+
from sage.rings.integer_ring import ZZ
|
|
93
|
+
from sage.rings.infinity import InfinityElement
|
|
94
|
+
from functools import reduce
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class FanMorphism(FreeModuleMorphism):
|
|
98
|
+
r"""
|
|
99
|
+
Create a fan morphism.
|
|
100
|
+
|
|
101
|
+
Let `\Sigma_1` and `\Sigma_2` be two fans in lattices `N_1` and `N_2`
|
|
102
|
+
respectively. Let `\phi` be a morphism (i.e. a linear map) from `N_1` to
|
|
103
|
+
`N_2`. We say that `\phi` is *compatible* with `\Sigma_1` and `\Sigma_2`
|
|
104
|
+
if every cone `\sigma_1\in\Sigma_1` is mapped by `\phi` into a single cone
|
|
105
|
+
`\sigma_2\in\Sigma_2`, i.e. `\phi(\sigma_1)\subset\sigma_2` (`\sigma_2`
|
|
106
|
+
may be different for different `\sigma_1`).
|
|
107
|
+
|
|
108
|
+
By a **fan morphism** we understand a morphism between two lattices
|
|
109
|
+
compatible with specified fans in these lattices. Such morphisms behave in
|
|
110
|
+
exactly the same way as "regular" morphisms between lattices, but:
|
|
111
|
+
|
|
112
|
+
* fan morphisms have a special constructor allowing some automatic
|
|
113
|
+
adjustments to the initial fans (see below);
|
|
114
|
+
* fan morphisms are aware of the associated fans and they can be
|
|
115
|
+
accessed via :meth:`codomain_fan` and :meth:`domain_fan`;
|
|
116
|
+
* fan morphisms can efficiently compute :meth:`image_cone` of a given
|
|
117
|
+
cone of the domain fan and :meth:`preimage_cones` of a given cone of
|
|
118
|
+
the codomain fan.
|
|
119
|
+
|
|
120
|
+
INPUT:
|
|
121
|
+
|
|
122
|
+
- ``morphism`` -- either a morphism between domain and codomain, or an
|
|
123
|
+
integral matrix defining such a morphism;
|
|
124
|
+
|
|
125
|
+
- ``domain_fan`` -- a :class:`fan
|
|
126
|
+
<sage.geometry.fan.RationalPolyhedralFan>` in the domain
|
|
127
|
+
|
|
128
|
+
- ``codomain`` -- (default: ``None``) either a codomain lattice or a fan in
|
|
129
|
+
the codomain. If the codomain fan is not given, the image fan (fan
|
|
130
|
+
generated by images of generating cones) of ``domain_fan`` will be used,
|
|
131
|
+
if possible.
|
|
132
|
+
|
|
133
|
+
- ``subdivide`` -- boolean (default: ``False``); if ``True`` and ``domain_fan`` is
|
|
134
|
+
not compatible with the codomain fan because it is too coarse, it will be
|
|
135
|
+
automatically refined to become compatible (the minimal refinement is
|
|
136
|
+
canonical, so there are no choices involved)
|
|
137
|
+
|
|
138
|
+
- ``check`` -- boolean (default: ``True``); if ``False``, given fans and morphism
|
|
139
|
+
will be assumed to be compatible. Be careful when using this option,
|
|
140
|
+
since wrong assumptions can lead to wrong and hard-to-detect errors. On
|
|
141
|
+
the other hand, this option may save you some time.
|
|
142
|
+
|
|
143
|
+
- ``verbose`` -- boolean (default: ``False``); if ``True``, some information may be
|
|
144
|
+
printed during construction of the fan morphism
|
|
145
|
+
|
|
146
|
+
OUTPUT: a fan morphism
|
|
147
|
+
|
|
148
|
+
EXAMPLES:
|
|
149
|
+
|
|
150
|
+
Here we consider the face and normal fans of the "diamond" and the
|
|
151
|
+
projection to the `x`-axis::
|
|
152
|
+
|
|
153
|
+
sage: diamond = lattice_polytope.cross_polytope(2)
|
|
154
|
+
sage: face = FaceFan(diamond, lattice=ToricLattice(2))
|
|
155
|
+
sage: normal = NormalFan(diamond)
|
|
156
|
+
sage: N = face.lattice()
|
|
157
|
+
sage: H = End(N)
|
|
158
|
+
sage: phi = H([N.0, 0])
|
|
159
|
+
sage: phi
|
|
160
|
+
Free module morphism defined by the matrix
|
|
161
|
+
[1 0]
|
|
162
|
+
[0 0]
|
|
163
|
+
Domain: 2-d lattice N
|
|
164
|
+
Codomain: 2-d lattice N
|
|
165
|
+
sage: fm = FanMorphism(phi, face, normal)
|
|
166
|
+
sage: fm.domain_fan() is face
|
|
167
|
+
True
|
|
168
|
+
|
|
169
|
+
Note, that since ``phi`` is compatible with these fans, the returned
|
|
170
|
+
fan is exactly the same object as the initial ``domain_fan``. ::
|
|
171
|
+
|
|
172
|
+
sage: FanMorphism(phi, normal, face)
|
|
173
|
+
Traceback (most recent call last):
|
|
174
|
+
...
|
|
175
|
+
ValueError: the image of generating cone #1 of the domain fan
|
|
176
|
+
is not contained in a single cone of the codomain fan!
|
|
177
|
+
sage: fm = FanMorphism(phi, normal, face, subdivide=True)
|
|
178
|
+
sage: fm.domain_fan() is normal
|
|
179
|
+
False
|
|
180
|
+
sage: fm.domain_fan().ngenerating_cones()
|
|
181
|
+
6
|
|
182
|
+
|
|
183
|
+
We had to subdivide two of the four cones of the normal fan, since
|
|
184
|
+
they were mapped by ``phi`` into non-strictly convex cones.
|
|
185
|
+
|
|
186
|
+
It is possible to omit the codomain fan, in which case the image fan will
|
|
187
|
+
be used instead of it::
|
|
188
|
+
|
|
189
|
+
sage: fm = FanMorphism(phi, face)
|
|
190
|
+
sage: fm.codomain_fan()
|
|
191
|
+
Rational polyhedral fan in 2-d lattice N
|
|
192
|
+
sage: fm.codomain_fan().rays()
|
|
193
|
+
N( 1, 0),
|
|
194
|
+
N(-1, 0)
|
|
195
|
+
in 2-d lattice N
|
|
196
|
+
|
|
197
|
+
Now we demonstrate a more subtle example. We take the first quadrant as our
|
|
198
|
+
domain fan. Then we divide the first quadrant into three cones, throw away
|
|
199
|
+
the middle one and take the other two as our codomain fan. These fans are
|
|
200
|
+
incompatible with the identity lattice morphism since the image of the
|
|
201
|
+
domain fan is out of the support of the codomain fan::
|
|
202
|
+
|
|
203
|
+
sage: N = ToricLattice(2)
|
|
204
|
+
sage: phi = End(N).identity()
|
|
205
|
+
sage: F1 = Fan(cones=[(0,1)], rays=[(1,0), (0,1)])
|
|
206
|
+
sage: F2 = Fan(cones=[(0,1), (2,3)],
|
|
207
|
+
....: rays=[(1,0), (2,1), (1,2), (0,1)])
|
|
208
|
+
sage: FanMorphism(phi, F1, F2)
|
|
209
|
+
Traceback (most recent call last):
|
|
210
|
+
...
|
|
211
|
+
ValueError: the image of generating cone #0 of the domain fan
|
|
212
|
+
is not contained in a single cone of the codomain fan!
|
|
213
|
+
sage: FanMorphism(phi, F1, F2, subdivide=True)
|
|
214
|
+
Traceback (most recent call last):
|
|
215
|
+
...
|
|
216
|
+
ValueError: morphism defined by
|
|
217
|
+
[1 0]
|
|
218
|
+
[0 1]
|
|
219
|
+
does not map
|
|
220
|
+
Rational polyhedral fan in 2-d lattice N
|
|
221
|
+
into the support of
|
|
222
|
+
Rational polyhedral fan in 2-d lattice N!
|
|
223
|
+
|
|
224
|
+
The problem was detected and handled correctly (i.e. an exception was
|
|
225
|
+
raised). However, the used algorithm requires extra checks for this
|
|
226
|
+
situation after constructing a potential subdivision and this can take
|
|
227
|
+
significant time. You can save about half the time using
|
|
228
|
+
``check=False`` option, if you know in advance that it is possible to
|
|
229
|
+
make fans compatible with the morphism by subdividing the domain fan.
|
|
230
|
+
Of course, if your assumption was incorrect, the result will be wrong
|
|
231
|
+
and you will get a fan which *does* map into the support of the
|
|
232
|
+
codomain fan, but is **not** a subdivision of the domain fan. You
|
|
233
|
+
can test it on the example above::
|
|
234
|
+
|
|
235
|
+
sage: fm = FanMorphism(phi, F1, F2, subdivide=True,
|
|
236
|
+
....: check=False, verbose=True)
|
|
237
|
+
Placing ray images (... ms)
|
|
238
|
+
Computing chambers (... ms)
|
|
239
|
+
Number of domain cones: 1.
|
|
240
|
+
Number of chambers: 2.
|
|
241
|
+
Cone 0 sits in chambers 0 1 (... ms)
|
|
242
|
+
sage: fm.domain_fan().is_equivalent(F2)
|
|
243
|
+
True
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
def __init__(self, morphism, domain_fan,
|
|
247
|
+
codomain=None,
|
|
248
|
+
subdivide=False,
|
|
249
|
+
check=True,
|
|
250
|
+
verbose=False):
|
|
251
|
+
r"""
|
|
252
|
+
Create a fan morphism.
|
|
253
|
+
|
|
254
|
+
See :class:`FanMorphism` for documentation.
|
|
255
|
+
|
|
256
|
+
TESTS::
|
|
257
|
+
|
|
258
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
259
|
+
sage: quadrant = Fan([quadrant])
|
|
260
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
261
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
|
|
262
|
+
sage: fm
|
|
263
|
+
Fan morphism defined by the matrix
|
|
264
|
+
[1 0]
|
|
265
|
+
[0 1]
|
|
266
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
267
|
+
Codomain fan: Rational polyhedral fan in 2-d lattice N
|
|
268
|
+
sage: TestSuite(fm).run(skip='_test_category')
|
|
269
|
+
"""
|
|
270
|
+
assert isinstance(domain_fan, RationalPolyhedralFan)
|
|
271
|
+
if isinstance(codomain, RationalPolyhedralFan):
|
|
272
|
+
codomain, codomain_fan = codomain.lattice(), codomain
|
|
273
|
+
else:
|
|
274
|
+
codomain_fan = None
|
|
275
|
+
if isinstance(morphism, FreeModuleMorphism):
|
|
276
|
+
parent = morphism.parent()
|
|
277
|
+
A = morphism.matrix()
|
|
278
|
+
elif isinstance(morphism, Matrix):
|
|
279
|
+
A = morphism
|
|
280
|
+
if codomain is None:
|
|
281
|
+
raise ValueError("codomain (fan) must be given explicitly if "
|
|
282
|
+
"morphism is given by a matrix!")
|
|
283
|
+
parent = Hom(domain_fan.lattice(), codomain)
|
|
284
|
+
else:
|
|
285
|
+
raise TypeError("morphism must be either a FreeModuleMorphism "
|
|
286
|
+
"or a matrix!\nGot: %s" % morphism)
|
|
287
|
+
super().__init__(parent, A)
|
|
288
|
+
self._domain_fan = domain_fan
|
|
289
|
+
self._image_cone = dict()
|
|
290
|
+
self._preimage_cones = dict()
|
|
291
|
+
self._preimage_fans = dict()
|
|
292
|
+
self._primitive_preimage_cones = dict()
|
|
293
|
+
if codomain_fan is None:
|
|
294
|
+
self._construct_codomain_fan(check)
|
|
295
|
+
else:
|
|
296
|
+
self._codomain_fan = codomain_fan
|
|
297
|
+
if subdivide:
|
|
298
|
+
self._subdivide_domain_fan(check, verbose)
|
|
299
|
+
elif check:
|
|
300
|
+
self._validate()
|
|
301
|
+
|
|
302
|
+
def __mul__(self, right):
|
|
303
|
+
"""
|
|
304
|
+
Return the composition of ``self`` and ``right``.
|
|
305
|
+
|
|
306
|
+
INPUT:
|
|
307
|
+
|
|
308
|
+
- ``right`` -- a :class:`FanMorphism` whose :meth:`codomain_fan` can be
|
|
309
|
+
mapped to :meth:`domain_fan` of ``self`` via identity map of lattices
|
|
310
|
+
|
|
311
|
+
OUTPUT: a :class:`FanMorphism`
|
|
312
|
+
|
|
313
|
+
EXAMPLES::
|
|
314
|
+
|
|
315
|
+
sage: A2 = toric_varieties.A2()
|
|
316
|
+
sage: P3 = toric_varieties.P(3) # needs palp
|
|
317
|
+
sage: m = matrix([(2,0,0), (1,1,0)])
|
|
318
|
+
sage: phi = A2.hom(m, P3).fan_morphism(); phi # needs palp
|
|
319
|
+
Fan morphism defined by the matrix
|
|
320
|
+
[2 0 0]
|
|
321
|
+
[1 1 0]
|
|
322
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
323
|
+
Codomain fan: Rational polyhedral fan in 3-d lattice N
|
|
324
|
+
sage: prod(phi.factor()) # indirect test # needs palp
|
|
325
|
+
Fan morphism defined by the matrix
|
|
326
|
+
[2 0 0]
|
|
327
|
+
[1 1 0]
|
|
328
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
329
|
+
Codomain fan: Rational polyhedral fan in 3-d lattice N
|
|
330
|
+
"""
|
|
331
|
+
if not isinstance(right, FanMorphism):
|
|
332
|
+
raise TypeError(
|
|
333
|
+
"fan morphisms should be composed with fan morphisms")
|
|
334
|
+
# We don't need it, we just check compatibility of fans:
|
|
335
|
+
FanMorphism(identity_matrix(self.domain().dimension()),
|
|
336
|
+
right.codomain_fan(), self.domain_fan())
|
|
337
|
+
m = right.matrix() * self.matrix()
|
|
338
|
+
return FanMorphism(m, right.domain_fan(), self.codomain_fan())
|
|
339
|
+
|
|
340
|
+
def _RISGIS(self):
|
|
341
|
+
r"""
|
|
342
|
+
Return Ray Images Star Generator Indices Sets.
|
|
343
|
+
|
|
344
|
+
OUTPUT:
|
|
345
|
+
|
|
346
|
+
- a :class:`tuple` of :class:`frozenset`s of integers, the `i`-th set
|
|
347
|
+
is the set of indices of star generators for the minimal cone of the
|
|
348
|
+
:meth:`codomain_fan` containing the image of the `i`-th ray of the
|
|
349
|
+
:meth:`domain_fan`.
|
|
350
|
+
|
|
351
|
+
TESTS::
|
|
352
|
+
|
|
353
|
+
sage: diamond = lattice_polytope.cross_polytope(2)
|
|
354
|
+
sage: face = FaceFan(diamond)
|
|
355
|
+
sage: normal = NormalFan(diamond)
|
|
356
|
+
sage: N = face.lattice()
|
|
357
|
+
sage: fm = FanMorphism(identity_matrix(2),
|
|
358
|
+
....: normal, face, subdivide=True)
|
|
359
|
+
sage: fm._RISGIS()
|
|
360
|
+
(frozenset({2}),
|
|
361
|
+
frozenset({3}),
|
|
362
|
+
frozenset({0}),
|
|
363
|
+
frozenset({1}),
|
|
364
|
+
frozenset({0, 1}),
|
|
365
|
+
frozenset({0, 3}),
|
|
366
|
+
frozenset({2, 3}),
|
|
367
|
+
frozenset({1, 2}))
|
|
368
|
+
"""
|
|
369
|
+
if "_RISGIS_" not in self.__dict__:
|
|
370
|
+
try:
|
|
371
|
+
cones = [self._codomain_fan.cone_containing(self(ray))
|
|
372
|
+
for ray in self._domain_fan.rays()]
|
|
373
|
+
except ValueError:
|
|
374
|
+
self._support_error()
|
|
375
|
+
self._RISGIS_ = tuple(frozenset(cone.star_generator_indices())
|
|
376
|
+
for cone in cones)
|
|
377
|
+
return self._RISGIS_
|
|
378
|
+
|
|
379
|
+
def _chambers(self):
|
|
380
|
+
r"""
|
|
381
|
+
Return chambers in the domain corresponding to the codomain fan.
|
|
382
|
+
|
|
383
|
+
This function is used during automatic refinement of the domain fans,
|
|
384
|
+
see :meth:`_subdivide_domain_fan`.
|
|
385
|
+
|
|
386
|
+
OUTPUT:
|
|
387
|
+
|
|
388
|
+
- a :class:`tuple` ``(chambers, cone_to_chamber)``, where
|
|
389
|
+
|
|
390
|
+
- ``chambers`` is a :class:`list` of :class:`cones
|
|
391
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` in the domain of
|
|
392
|
+
``self``
|
|
393
|
+
|
|
394
|
+
- ``cone_to_chamber`` is a :class:`list` of integers, if its `i`-th
|
|
395
|
+
element is `j`, then the `j`-th element of ``chambers`` is the
|
|
396
|
+
inverse image of the `i`-th generating cone of the codomain fan.
|
|
397
|
+
|
|
398
|
+
TESTS::
|
|
399
|
+
|
|
400
|
+
sage: F = NormalFan(lattice_polytope.cross_polytope(2))
|
|
401
|
+
sage: N = F.lattice()
|
|
402
|
+
sage: H = End(N)
|
|
403
|
+
sage: phi = H([N.0, 0])
|
|
404
|
+
sage: fm = FanMorphism(phi, F, F, subdivide=True)
|
|
405
|
+
sage: fm._chambers()
|
|
406
|
+
([2-d cone in 2-d lattice N,
|
|
407
|
+
1-d cone in 2-d lattice N,
|
|
408
|
+
2-d cone in 2-d lattice N],
|
|
409
|
+
[0, 1, 2, 1])
|
|
410
|
+
"""
|
|
411
|
+
kernel_rays = []
|
|
412
|
+
for ray in self.kernel().basis():
|
|
413
|
+
kernel_rays.append(ray)
|
|
414
|
+
kernel_rays.append(-ray)
|
|
415
|
+
image_rays = []
|
|
416
|
+
for ray in self.image().basis():
|
|
417
|
+
image_rays.append(ray)
|
|
418
|
+
image_rays.append(-ray)
|
|
419
|
+
image = Cone(image_rays)
|
|
420
|
+
chambers = []
|
|
421
|
+
cone_to_chamber = []
|
|
422
|
+
for cone in self._codomain_fan:
|
|
423
|
+
chamber = Cone([self.lift(ray) for ray in cone.intersection(image)]
|
|
424
|
+
+ kernel_rays, lattice=self.domain())
|
|
425
|
+
cone_to_chamber.append(len(chambers))
|
|
426
|
+
for i, old_chamber in enumerate(chambers):
|
|
427
|
+
if old_chamber.is_equivalent(chamber):
|
|
428
|
+
cone_to_chamber[-1] = i
|
|
429
|
+
break
|
|
430
|
+
if cone_to_chamber[-1] == len(chambers):
|
|
431
|
+
chambers.append(chamber)
|
|
432
|
+
return (chambers, cone_to_chamber)
|
|
433
|
+
|
|
434
|
+
def _construct_codomain_fan(self, check):
|
|
435
|
+
r"""
|
|
436
|
+
Construct the codomain fan as the image of the domain one.
|
|
437
|
+
|
|
438
|
+
.. WARNING::
|
|
439
|
+
|
|
440
|
+
This method should be called only during initialization.
|
|
441
|
+
|
|
442
|
+
INPUT:
|
|
443
|
+
|
|
444
|
+
- ``check`` -- passed on to the fan constructor
|
|
445
|
+
|
|
446
|
+
OUTPUT: none, but the codomain fan of ``self`` is set to the constructed fan
|
|
447
|
+
|
|
448
|
+
TESTS::
|
|
449
|
+
|
|
450
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
451
|
+
sage: quadrant = Fan([quadrant])
|
|
452
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
453
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl)
|
|
454
|
+
Traceback (most recent call last):
|
|
455
|
+
...
|
|
456
|
+
ValueError: codomain (fan) must be given explicitly
|
|
457
|
+
if morphism is given by a matrix!
|
|
458
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, ZZ^2)
|
|
459
|
+
sage: fm.codomain_fan().rays() # indirect doctest
|
|
460
|
+
(1, 0),
|
|
461
|
+
(0, 1),
|
|
462
|
+
(1, 1)
|
|
463
|
+
in Ambient free module of rank 2
|
|
464
|
+
over the principal ideal domain Integer Ring
|
|
465
|
+
"""
|
|
466
|
+
# We literally try to construct the image fan and hope that it works.
|
|
467
|
+
# If it does not, the fan constructor will raise an exception.
|
|
468
|
+
domain_fan = self._domain_fan
|
|
469
|
+
self._codomain_fan = Fan(cones=(domain_cone.ambient_ray_indices()
|
|
470
|
+
for domain_cone in domain_fan),
|
|
471
|
+
rays=(self(ray) for ray in domain_fan.rays()),
|
|
472
|
+
lattice=self.codomain(),
|
|
473
|
+
discard_faces=True, check=check)
|
|
474
|
+
|
|
475
|
+
def _latex_(self):
|
|
476
|
+
r"""
|
|
477
|
+
Return the `\LaTeX` representation of ``self``.
|
|
478
|
+
|
|
479
|
+
OUTPUT: a :class:`string`
|
|
480
|
+
|
|
481
|
+
EXAMPLES::
|
|
482
|
+
|
|
483
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
484
|
+
sage: quadrant = Fan([quadrant])
|
|
485
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
486
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
|
|
487
|
+
sage: fm
|
|
488
|
+
Fan morphism defined by the matrix
|
|
489
|
+
[1 0]
|
|
490
|
+
[0 1]
|
|
491
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
492
|
+
Codomain fan: Rational polyhedral fan in 2-d lattice N
|
|
493
|
+
sage: print(fm._latex_())
|
|
494
|
+
\left(\begin{array}{rr}
|
|
495
|
+
1 & 0 \\
|
|
496
|
+
0 & 1
|
|
497
|
+
\end{array}\right) : \Sigma^{2} \to \Sigma^{2}
|
|
498
|
+
"""
|
|
499
|
+
from sage.misc.latex import latex
|
|
500
|
+
return (r"%s : %s \to %s" % (latex(self.matrix()),
|
|
501
|
+
latex(self.domain_fan()), latex(self.codomain_fan())))
|
|
502
|
+
|
|
503
|
+
@cached_method
|
|
504
|
+
def _ray_index_map(self):
|
|
505
|
+
r"""
|
|
506
|
+
Return the map between indices of rays in domain and codomain fans.
|
|
507
|
+
|
|
508
|
+
OUTPUT:
|
|
509
|
+
|
|
510
|
+
- a tuple of integers. If the `i`-th entry is -1, the `i`-th ray of the
|
|
511
|
+
domain fan is mapped to the origin. If it is `j`, then the `i`-th ray
|
|
512
|
+
of the domain fan is mapped onto the `j`-th ray of the codomain fan.
|
|
513
|
+
If there is a ray in the domain fan which is mapped into the relative
|
|
514
|
+
interior of a higher dimensional cone, a :exc:`ValueError`
|
|
515
|
+
exception is raised.
|
|
516
|
+
|
|
517
|
+
.. NOTE::
|
|
518
|
+
|
|
519
|
+
This method is used by :meth:`is_bundle` and :meth:`is_fibration`.
|
|
520
|
+
|
|
521
|
+
TESTS::
|
|
522
|
+
|
|
523
|
+
sage: # needs palp
|
|
524
|
+
sage: Sigma = toric_varieties.dP8().fan()
|
|
525
|
+
sage: Sigma_p = toric_varieties.P1().fan()
|
|
526
|
+
sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
|
|
527
|
+
sage: phi._ray_index_map()
|
|
528
|
+
(-1, 1, -1, 0)
|
|
529
|
+
sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
|
|
530
|
+
sage: xi._ray_index_map()
|
|
531
|
+
Traceback (most recent call last):
|
|
532
|
+
...
|
|
533
|
+
ValueError: ray #1 is mapped into a 2-d cone!
|
|
534
|
+
"""
|
|
535
|
+
Sigma = self.domain_fan()
|
|
536
|
+
ray_index_map = [-1] * Sigma.nrays()
|
|
537
|
+
for i, rho in enumerate(Sigma(1)):
|
|
538
|
+
sigma_p = self.image_cone(rho)
|
|
539
|
+
if sigma_p.nrays() > 1:
|
|
540
|
+
raise ValueError("ray #%d is mapped into a %d-d cone!" %
|
|
541
|
+
(i, sigma_p.dim()))
|
|
542
|
+
elif sigma_p.nrays() == 1:
|
|
543
|
+
ray_index_map[i] = sigma_p.ambient_ray_indices()[0]
|
|
544
|
+
return tuple(ray_index_map)
|
|
545
|
+
|
|
546
|
+
def _repr_(self):
|
|
547
|
+
r"""
|
|
548
|
+
Return the string representation of ``self``.
|
|
549
|
+
|
|
550
|
+
OUTPUT: a :class:`string`
|
|
551
|
+
|
|
552
|
+
EXAMPLES::
|
|
553
|
+
|
|
554
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
555
|
+
sage: quadrant = Fan([quadrant])
|
|
556
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
557
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
|
|
558
|
+
sage: print(fm._repr_())
|
|
559
|
+
Fan morphism defined by the matrix
|
|
560
|
+
[1 0]
|
|
561
|
+
[0 1]
|
|
562
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
563
|
+
Codomain fan: Rational polyhedral fan in 2-d lattice N
|
|
564
|
+
"""
|
|
565
|
+
return ("Fan morphism defined by the matrix\n"
|
|
566
|
+
"%s\n"
|
|
567
|
+
"Domain fan: %s\n"
|
|
568
|
+
"Codomain fan: %s"
|
|
569
|
+
% (self.matrix(), self.domain_fan(), self.codomain_fan()))
|
|
570
|
+
|
|
571
|
+
def _subdivide_domain_fan(self, check, verbose):
|
|
572
|
+
r"""
|
|
573
|
+
Subdivide the domain fan to make it compatible with the codomain fan.
|
|
574
|
+
|
|
575
|
+
.. WARNING::
|
|
576
|
+
|
|
577
|
+
This method should be called only during initialization.
|
|
578
|
+
|
|
579
|
+
INPUT:
|
|
580
|
+
|
|
581
|
+
- ``check`` -- boolean (default: ``True``); if ``False``, some of the
|
|
582
|
+
consistency checks will be omitted, which saves time but can
|
|
583
|
+
potentially lead to wrong results. Currently, with
|
|
584
|
+
``check=False`` option there will be no check that ``domain_fan``
|
|
585
|
+
maps to ``codomain_fan``
|
|
586
|
+
|
|
587
|
+
- ``verbose`` -- boolean (default: ``False``); if ``True``, some timing
|
|
588
|
+
information will be printed in the process
|
|
589
|
+
|
|
590
|
+
OUTPUT:
|
|
591
|
+
|
|
592
|
+
- none, but the domain fan of ``self`` is replaced with its minimal
|
|
593
|
+
refinement, if possible. Otherwise a :exc:`ValueError`
|
|
594
|
+
exception is raised.
|
|
595
|
+
|
|
596
|
+
TESTS::
|
|
597
|
+
|
|
598
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
599
|
+
sage: quadrant = Fan([quadrant])
|
|
600
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
601
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant, quadrant_bl)
|
|
602
|
+
Traceback (most recent call last):
|
|
603
|
+
...
|
|
604
|
+
ValueError: the image of generating cone #0 of the domain fan
|
|
605
|
+
is not contained in a single cone of the codomain fan!
|
|
606
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant,
|
|
607
|
+
....: quadrant_bl, subdivide=True)
|
|
608
|
+
sage: fm.domain_fan().rays() # indirect doctest
|
|
609
|
+
N(1, 0),
|
|
610
|
+
N(0, 1),
|
|
611
|
+
N(1, 1)
|
|
612
|
+
in 2-d lattice N
|
|
613
|
+
|
|
614
|
+
Now we demonstrate a more subtle example. We take the first quadrant
|
|
615
|
+
as our ``domain_fan``. Then we divide the first quadrant into three
|
|
616
|
+
cones, throw away the middle one and take the other two as our
|
|
617
|
+
``codomain_fan``. These fans are incompatible with the identity
|
|
618
|
+
lattice morphism since the image of ``domain_fan`` is out of the
|
|
619
|
+
support of ``codomain_fan``::
|
|
620
|
+
|
|
621
|
+
sage: N = ToricLattice(2)
|
|
622
|
+
sage: phi = End(N).identity()
|
|
623
|
+
sage: F1 = Fan(cones=[(0,1)], rays=[(1,0), (0,1)])
|
|
624
|
+
sage: F2 = Fan(cones=[(0,1), (2,3)],
|
|
625
|
+
....: rays=[(1,0), (2,1), (1,2), (0,1)])
|
|
626
|
+
sage: FanMorphism(phi, F1, F2)
|
|
627
|
+
Traceback (most recent call last):
|
|
628
|
+
...
|
|
629
|
+
ValueError: the image of generating cone #0 of the domain fan
|
|
630
|
+
is not contained in a single cone of the codomain fan!
|
|
631
|
+
sage: FanMorphism(phi, F1, F2, subdivide=True)
|
|
632
|
+
Traceback (most recent call last):
|
|
633
|
+
...
|
|
634
|
+
ValueError: morphism defined by
|
|
635
|
+
[1 0]
|
|
636
|
+
[0 1]
|
|
637
|
+
does not map
|
|
638
|
+
Rational polyhedral fan in 2-d lattice N
|
|
639
|
+
into the support of
|
|
640
|
+
Rational polyhedral fan in 2-d lattice N!
|
|
641
|
+
|
|
642
|
+
We check that :issue:`10943` is fixed::
|
|
643
|
+
|
|
644
|
+
sage: Sigma = Fan(rays=[(1,1,0), (1,-1,0)], cones=[(0,1)])
|
|
645
|
+
sage: Sigma_prime = FaceFan(lattice_polytope.cross_polytope(3))
|
|
646
|
+
sage: fm = FanMorphism(identity_matrix(3),
|
|
647
|
+
....: Sigma, Sigma_prime, subdivide=True)
|
|
648
|
+
sage: fm.domain_fan().rays()
|
|
649
|
+
N(1, 1, 0),
|
|
650
|
+
N(1, -1, 0),
|
|
651
|
+
N(1, 0, 0)
|
|
652
|
+
in 3-d lattice N
|
|
653
|
+
sage: [cone.ambient_ray_indices() for cone in fm.domain_fan()]
|
|
654
|
+
[(0, 2), (1, 2)]
|
|
655
|
+
|
|
656
|
+
sage: sigma = Cone([(0,1), (3,1)])
|
|
657
|
+
sage: Sigma = Fan([sigma])
|
|
658
|
+
sage: Sigma_prime = Sigma.subdivide([(1,1), (2,1)])
|
|
659
|
+
sage: FanMorphism(identity_matrix(2),
|
|
660
|
+
....: Sigma, Sigma_prime, subdivide=True)
|
|
661
|
+
Fan morphism defined by the matrix
|
|
662
|
+
[1 0]
|
|
663
|
+
[0 1]
|
|
664
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
665
|
+
Codomain fan: Rational polyhedral fan in 2-d lattice N
|
|
666
|
+
"""
|
|
667
|
+
domain_fan = self._domain_fan
|
|
668
|
+
lattice_dim = self.domain().dimension()
|
|
669
|
+
if verbose:
|
|
670
|
+
from sage.misc.timing import walltime
|
|
671
|
+
start = walltime()
|
|
672
|
+
print("Placing ray images", end=" ")
|
|
673
|
+
# Figure out where 1-dimensional cones (i.e. rays) are mapped.
|
|
674
|
+
RISGIS = self._RISGIS()
|
|
675
|
+
if verbose:
|
|
676
|
+
print("(%.3f ms)" % walltime(start))
|
|
677
|
+
# Subdivide cones that require it.
|
|
678
|
+
chambers = None # preimages of codomain cones, computed if necessary
|
|
679
|
+
new_cones = []
|
|
680
|
+
for cone_index, domain_cone in enumerate(domain_fan):
|
|
681
|
+
if reduce(operator.and_,
|
|
682
|
+
(RISGIS[i] for i in domain_cone.ambient_ray_indices())):
|
|
683
|
+
# There is a codomain cone containing all rays of this domain
|
|
684
|
+
# cone, no need to subdivide it.
|
|
685
|
+
new_cones.append(domain_cone)
|
|
686
|
+
continue
|
|
687
|
+
dim = domain_cone.dim()
|
|
688
|
+
if chambers is None:
|
|
689
|
+
if verbose:
|
|
690
|
+
start = walltime()
|
|
691
|
+
print("Computing chambers", end=" ")
|
|
692
|
+
chambers, cone_to_chamber = self._chambers()
|
|
693
|
+
if verbose:
|
|
694
|
+
print("(%.3f ms)" % walltime(start))
|
|
695
|
+
print("Number of domain cones: %d.\n"
|
|
696
|
+
"Number of chambers: %d." %
|
|
697
|
+
(domain_fan.ngenerating_cones(), len(chambers)))
|
|
698
|
+
# Subdivide domain_cone.
|
|
699
|
+
if verbose:
|
|
700
|
+
start = walltime()
|
|
701
|
+
print("Cone %d sits in chambers" % cone_index, end=" ")
|
|
702
|
+
# It seems that there is no quick way to determine which chambers
|
|
703
|
+
# do NOT intersect this domain cone, so we go over all of them.
|
|
704
|
+
parts = []
|
|
705
|
+
for chamber_index, chamber in enumerate(chambers):
|
|
706
|
+
# If chamber is small, intersection will be small.
|
|
707
|
+
if chamber.dim() < dim:
|
|
708
|
+
continue
|
|
709
|
+
new_part = domain_cone.intersection(chamber)
|
|
710
|
+
# If intersection is small, it is not a generating cone.
|
|
711
|
+
if new_part.dim() < dim:
|
|
712
|
+
continue
|
|
713
|
+
# Small cones may have repetitive intersections with chambers.
|
|
714
|
+
if (dim == lattice_dim or
|
|
715
|
+
not any(part.is_equivalent(new_part) for part in parts)):
|
|
716
|
+
parts.append(new_part)
|
|
717
|
+
if verbose:
|
|
718
|
+
print(chamber_index, end=" ")
|
|
719
|
+
if check:
|
|
720
|
+
# Check if the subdivision is complete, i.e. there are no
|
|
721
|
+
# missing pieces of domain_cone. To do this, we construct a
|
|
722
|
+
# fan from the obtained parts and check that interior points
|
|
723
|
+
# of boundary cones of this fan are in the interior of the
|
|
724
|
+
# original cone. In any case we know that we are constructing
|
|
725
|
+
# a valid fan, so passing check=False to Fan(...) is OK.
|
|
726
|
+
if verbose:
|
|
727
|
+
print("(%.3f ms)" % walltime(start))
|
|
728
|
+
start = walltime()
|
|
729
|
+
print("Checking for missing pieces", end=" ")
|
|
730
|
+
cone_subdivision = Fan(parts, check=False)
|
|
731
|
+
for cone in cone_subdivision(dim - 1):
|
|
732
|
+
if len(cone.star_generators()) == 1:
|
|
733
|
+
if domain_cone.relative_interior_contains(
|
|
734
|
+
sum(cone.rays())):
|
|
735
|
+
self._support_error()
|
|
736
|
+
new_cones.extend(parts)
|
|
737
|
+
if verbose:
|
|
738
|
+
print("(%.3f ms)" % walltime(start))
|
|
739
|
+
if len(new_cones) > domain_fan.ngenerating_cones():
|
|
740
|
+
# Construct a new fan keeping old rays in the same order
|
|
741
|
+
new_rays = list(domain_fan.rays())
|
|
742
|
+
for cone in new_cones:
|
|
743
|
+
for ray in cone:
|
|
744
|
+
if ray not in new_rays:
|
|
745
|
+
new_rays.append(ray)
|
|
746
|
+
# Replace domain_fan, this is OK since this method must be called
|
|
747
|
+
# only during initialization of the FanMorphism.
|
|
748
|
+
self._domain_fan = Fan(new_cones, new_rays, domain_fan.lattice(),
|
|
749
|
+
check=False)
|
|
750
|
+
# Also remove RISGIS for the old fan
|
|
751
|
+
del self._RISGIS_
|
|
752
|
+
|
|
753
|
+
def _support_error(self):
|
|
754
|
+
r"""
|
|
755
|
+
Raise a :exc:`ValueError` exception due to support incompatibility.
|
|
756
|
+
|
|
757
|
+
OUTPUT: none, a :exc:`ValueError` exception is raised
|
|
758
|
+
|
|
759
|
+
TESTS:
|
|
760
|
+
|
|
761
|
+
We deliberately construct an invalid morphism for this demonstration::
|
|
762
|
+
|
|
763
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
764
|
+
sage: quadrant = Fan([quadrant])
|
|
765
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
766
|
+
sage: fm = FanMorphism(identity_matrix(2),
|
|
767
|
+
....: quadrant, quadrant_bl, check=False)
|
|
768
|
+
|
|
769
|
+
Now we report that the morphism is invalid::
|
|
770
|
+
|
|
771
|
+
sage: fm._support_error()
|
|
772
|
+
Traceback (most recent call last):
|
|
773
|
+
...
|
|
774
|
+
ValueError: morphism defined by
|
|
775
|
+
[1 0]
|
|
776
|
+
[0 1]
|
|
777
|
+
does not map
|
|
778
|
+
Rational polyhedral fan in 2-d lattice N
|
|
779
|
+
into the support of
|
|
780
|
+
Rational polyhedral fan in 2-d lattice N!
|
|
781
|
+
"""
|
|
782
|
+
raise ValueError("morphism defined by\n"
|
|
783
|
+
"%s\n"
|
|
784
|
+
"does not map\n"
|
|
785
|
+
"%s\n"
|
|
786
|
+
"into the support of\n"
|
|
787
|
+
"%s!"
|
|
788
|
+
% (self.matrix(), self.domain_fan(), self.codomain_fan()))
|
|
789
|
+
|
|
790
|
+
def _validate(self):
|
|
791
|
+
r"""
|
|
792
|
+
Check if ``self`` is indeed compatible with domain and codomain fans.
|
|
793
|
+
|
|
794
|
+
OUTPUT:
|
|
795
|
+
|
|
796
|
+
- none, but a :exc:`ValueError` exception is raised if there is
|
|
797
|
+
a cone of the domain fan of ``self`` which is not completely
|
|
798
|
+
contained in a single cone of the codomain fan of ``self``,
|
|
799
|
+
or if one of these fans does not sit in the appropriate lattice.
|
|
800
|
+
|
|
801
|
+
EXAMPLES::
|
|
802
|
+
|
|
803
|
+
sage: N3 = ToricLattice(3, "N3")
|
|
804
|
+
sage: N2 = ToricLattice(2, "N2")
|
|
805
|
+
sage: H = Hom(N3, N2)
|
|
806
|
+
sage: phi = H([N2.0, N2.1, N2.0])
|
|
807
|
+
sage: F1 = Fan(cones=[(0,1,2), (1,2,3)],
|
|
808
|
+
....: rays=[(1,1,1), (1,1,-1), (1,-1,1), (1,-1,-1)],
|
|
809
|
+
....: lattice=N3)
|
|
810
|
+
sage: F1.rays()
|
|
811
|
+
N3(1, 1, 1),
|
|
812
|
+
N3(1, 1, -1),
|
|
813
|
+
N3(1, -1, 1),
|
|
814
|
+
N3(1, -1, -1)
|
|
815
|
+
in 3-d lattice N3
|
|
816
|
+
sage: [phi(ray) for ray in F1.rays()]
|
|
817
|
+
[N2(2, 1), N2(0, 1), N2(2, -1), N2(0, -1)]
|
|
818
|
+
sage: F2 = Fan(cones=[(0,1,2), (1,2,3)],
|
|
819
|
+
....: rays=[(1,1,1), (1,1,-1), (1,2,1), (1,2,-1)],
|
|
820
|
+
....: lattice=N3)
|
|
821
|
+
sage: F2.rays()
|
|
822
|
+
N3(1, 1, 1),
|
|
823
|
+
N3(1, 1, -1),
|
|
824
|
+
N3(1, 2, 1),
|
|
825
|
+
N3(1, 2, -1)
|
|
826
|
+
in 3-d lattice N3
|
|
827
|
+
sage: [phi(ray) for ray in F2.rays()]
|
|
828
|
+
[N2(2, 1), N2(0, 1), N2(2, 2), N2(0, 2)]
|
|
829
|
+
sage: F3 = Fan(cones=[(0,1), (1,2)],
|
|
830
|
+
....: rays=[(1,0), (2,1), (0,1)],
|
|
831
|
+
....: lattice=N2)
|
|
832
|
+
sage: FanMorphism(phi, F2, F3)
|
|
833
|
+
Fan morphism defined by the matrix
|
|
834
|
+
[1 0]
|
|
835
|
+
[0 1]
|
|
836
|
+
[1 0]
|
|
837
|
+
Domain fan: Rational polyhedral fan in 3-d lattice N3
|
|
838
|
+
Codomain fan: Rational polyhedral fan in 2-d lattice N2
|
|
839
|
+
sage: FanMorphism(phi, F1, F3) # indirect doctest
|
|
840
|
+
Traceback (most recent call last):
|
|
841
|
+
...
|
|
842
|
+
ValueError: morphism defined by
|
|
843
|
+
[1 0]
|
|
844
|
+
[0 1]
|
|
845
|
+
[1 0]
|
|
846
|
+
does not map
|
|
847
|
+
Rational polyhedral fan in 3-d lattice N3
|
|
848
|
+
into the support of
|
|
849
|
+
Rational polyhedral fan in 2-d lattice N2!
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
TESTS::
|
|
853
|
+
|
|
854
|
+
sage: trivialfan2 = Fan([], [], lattice=ToricLattice(2))
|
|
855
|
+
sage: trivialfan3 = Fan([], [], lattice=ToricLattice(3))
|
|
856
|
+
sage: FanMorphism(zero_matrix(2,3), trivialfan2, trivialfan3)
|
|
857
|
+
Fan morphism defined by the matrix
|
|
858
|
+
[0 0 0]
|
|
859
|
+
[0 0 0]
|
|
860
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
861
|
+
Codomain fan: Rational polyhedral fan in 3-d lattice N
|
|
862
|
+
"""
|
|
863
|
+
domain_fan = self._domain_fan
|
|
864
|
+
if domain_fan.lattice() is not self.domain():
|
|
865
|
+
raise ValueError("%s does not sit in %s!"
|
|
866
|
+
% (domain_fan, self.domain()))
|
|
867
|
+
codomain_fan = self._codomain_fan
|
|
868
|
+
if codomain_fan.lattice() is not self.codomain():
|
|
869
|
+
raise ValueError("%s does not sit in %s!"
|
|
870
|
+
% (codomain_fan, self.codomain()))
|
|
871
|
+
RISGIS = self._RISGIS()
|
|
872
|
+
for n, domain_cone in enumerate(domain_fan):
|
|
873
|
+
if not domain_cone.is_trivial() and \
|
|
874
|
+
not reduce(operator.and_,
|
|
875
|
+
(RISGIS[i] for i in domain_cone.ambient_ray_indices())):
|
|
876
|
+
raise ValueError("the image of generating cone #%d of the "
|
|
877
|
+
"domain fan is not contained in a single "
|
|
878
|
+
"cone of the codomain fan!" % n)
|
|
879
|
+
|
|
880
|
+
def codomain_fan(self, dim=None, codim=None):
|
|
881
|
+
r"""
|
|
882
|
+
Return the codomain fan of ``self``.
|
|
883
|
+
|
|
884
|
+
INPUT:
|
|
885
|
+
|
|
886
|
+
- ``dim`` -- dimension of the requested cones
|
|
887
|
+
|
|
888
|
+
- ``codim`` -- codimension of the requested cones
|
|
889
|
+
|
|
890
|
+
OUTPUT:
|
|
891
|
+
|
|
892
|
+
- :class:`rational polyhedral fan
|
|
893
|
+
<sage.geometry.fan.RationalPolyhedralFan>` if no parameters were
|
|
894
|
+
given, :class:`tuple` of :class:`cones
|
|
895
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` otherwise.
|
|
896
|
+
|
|
897
|
+
EXAMPLES::
|
|
898
|
+
|
|
899
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
900
|
+
sage: quadrant = Fan([quadrant])
|
|
901
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
902
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
|
|
903
|
+
sage: fm.codomain_fan()
|
|
904
|
+
Rational polyhedral fan in 2-d lattice N
|
|
905
|
+
sage: fm.codomain_fan() is quadrant
|
|
906
|
+
True
|
|
907
|
+
"""
|
|
908
|
+
return self._codomain_fan(dim=dim, codim=codim)
|
|
909
|
+
|
|
910
|
+
def domain_fan(self, dim=None, codim=None):
|
|
911
|
+
r"""
|
|
912
|
+
Return the codomain fan of ``self``.
|
|
913
|
+
|
|
914
|
+
INPUT:
|
|
915
|
+
|
|
916
|
+
- ``dim`` -- dimension of the requested cones
|
|
917
|
+
|
|
918
|
+
- ``codim`` -- codimension of the requested cones
|
|
919
|
+
|
|
920
|
+
OUTPUT:
|
|
921
|
+
|
|
922
|
+
- :class:`rational polyhedral fan
|
|
923
|
+
<sage.geometry.fan.RationalPolyhedralFan>` if no parameters were
|
|
924
|
+
given, :class:`tuple` of :class:`cones
|
|
925
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` otherwise.
|
|
926
|
+
|
|
927
|
+
EXAMPLES::
|
|
928
|
+
|
|
929
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
930
|
+
sage: quadrant = Fan([quadrant])
|
|
931
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
932
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
|
|
933
|
+
sage: fm.domain_fan()
|
|
934
|
+
Rational polyhedral fan in 2-d lattice N
|
|
935
|
+
sage: fm.domain_fan() is quadrant_bl
|
|
936
|
+
True
|
|
937
|
+
"""
|
|
938
|
+
return self._domain_fan(dim=dim, codim=codim)
|
|
939
|
+
|
|
940
|
+
def image_cone(self, cone):
|
|
941
|
+
r"""
|
|
942
|
+
Return the cone of the codomain fan containing the image of ``cone``.
|
|
943
|
+
|
|
944
|
+
INPUT:
|
|
945
|
+
|
|
946
|
+
- ``cone`` -- a :class:`cone
|
|
947
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` equivalent to a
|
|
948
|
+
cone of the :meth:`domain_fan` of ``self``.
|
|
949
|
+
|
|
950
|
+
OUTPUT:
|
|
951
|
+
|
|
952
|
+
- a :class:`cone <sage.geometry.cone.ConvexRationalPolyhedralCone>`
|
|
953
|
+
of the :meth:`codomain_fan` of ``self``.
|
|
954
|
+
|
|
955
|
+
EXAMPLES::
|
|
956
|
+
|
|
957
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
958
|
+
sage: quadrant = Fan([quadrant])
|
|
959
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
960
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
|
|
961
|
+
sage: fm.image_cone(Cone([(1,0)]))
|
|
962
|
+
1-d cone of Rational polyhedral fan in 2-d lattice N
|
|
963
|
+
sage: fm.image_cone(Cone([(1,1)]))
|
|
964
|
+
2-d cone of Rational polyhedral fan in 2-d lattice N
|
|
965
|
+
|
|
966
|
+
TESTS:
|
|
967
|
+
|
|
968
|
+
We check that complete codomain fans are handled correctly, since a
|
|
969
|
+
different algorithm is used in this case::
|
|
970
|
+
|
|
971
|
+
sage: diamond = lattice_polytope.cross_polytope(2)
|
|
972
|
+
sage: face = FaceFan(diamond, lattice=ToricLattice(2))
|
|
973
|
+
sage: normal = NormalFan(diamond)
|
|
974
|
+
sage: N = face.lattice()
|
|
975
|
+
sage: fm = FanMorphism(identity_matrix(2),
|
|
976
|
+
....: normal, face, subdivide=True)
|
|
977
|
+
sage: fm.image_cone(Cone([(1,0)]))
|
|
978
|
+
1-d cone of Rational polyhedral fan in 2-d lattice N
|
|
979
|
+
sage: fm.image_cone(Cone([(1,1)]))
|
|
980
|
+
2-d cone of Rational polyhedral fan in 2-d lattice N
|
|
981
|
+
"""
|
|
982
|
+
cone = self._domain_fan.embed(cone)
|
|
983
|
+
if cone not in self._image_cone:
|
|
984
|
+
codomain_fan = self._codomain_fan()
|
|
985
|
+
if cone.is_trivial():
|
|
986
|
+
self._image_cone[cone] = codomain_fan(0)[0]
|
|
987
|
+
elif codomain_fan.is_complete():
|
|
988
|
+
# Optimization for a common case
|
|
989
|
+
RISGIS = self._RISGIS()
|
|
990
|
+
CSGIS = set(reduce(operator.and_,
|
|
991
|
+
(RISGIS[i] for i in cone.ambient_ray_indices())))
|
|
992
|
+
image_cone = codomain_fan.generating_cone(CSGIS.pop())
|
|
993
|
+
for i in CSGIS:
|
|
994
|
+
image_cone = image_cone.intersection(
|
|
995
|
+
codomain_fan.generating_cone(i))
|
|
996
|
+
self._image_cone[cone] = image_cone
|
|
997
|
+
else:
|
|
998
|
+
self._image_cone[cone] = codomain_fan.cone_containing(
|
|
999
|
+
self(ray) for ray in cone)
|
|
1000
|
+
return self._image_cone[cone]
|
|
1001
|
+
|
|
1002
|
+
def index(self, cone=None):
|
|
1003
|
+
r"""
|
|
1004
|
+
Return the index of ``self`` as a map between lattices.
|
|
1005
|
+
|
|
1006
|
+
INPUT:
|
|
1007
|
+
|
|
1008
|
+
- ``cone`` -- (default: ``None``) a :class:`cone
|
|
1009
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` of the
|
|
1010
|
+
:meth:`codomain_fan` of ``self``.
|
|
1011
|
+
|
|
1012
|
+
OUTPUT: integer, infinity, or ``None``
|
|
1013
|
+
|
|
1014
|
+
If no cone was specified, this function computes the index of the
|
|
1015
|
+
image of ``self`` in the codomain. If a cone `\sigma` was given, the
|
|
1016
|
+
index of ``self`` over `\sigma` is computed in the sense of
|
|
1017
|
+
Definition 2.1.7 of [HLY2002]_: if `\sigma'` is any cone of the
|
|
1018
|
+
:meth:`domain_fan` of ``self`` whose relative interior is mapped to the
|
|
1019
|
+
relative interior of `\sigma`, it is the index of the image of
|
|
1020
|
+
`N'(\sigma')` in `N(\sigma)`, where `N'` and `N` are domain and codomain
|
|
1021
|
+
lattices respectively. While that definition was formulated for the case
|
|
1022
|
+
of the finite index only, we extend it to the infinite one as well and
|
|
1023
|
+
return ``None`` if there is no `\sigma'` at all. See examples below for
|
|
1024
|
+
situations when such things happen. Note also that the index of ``self``
|
|
1025
|
+
is the same as index over the trivial cone.
|
|
1026
|
+
|
|
1027
|
+
EXAMPLES::
|
|
1028
|
+
|
|
1029
|
+
sage: # needs palp
|
|
1030
|
+
sage: Sigma = toric_varieties.dP8().fan()
|
|
1031
|
+
sage: Sigma_p = toric_varieties.P1().fan()
|
|
1032
|
+
sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
|
|
1033
|
+
sage: phi.index()
|
|
1034
|
+
1
|
|
1035
|
+
sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
|
|
1036
|
+
sage: psi.index()
|
|
1037
|
+
2
|
|
1038
|
+
sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
|
|
1039
|
+
sage: xi.index()
|
|
1040
|
+
+Infinity
|
|
1041
|
+
|
|
1042
|
+
Infinite index in the last example indicates that the image has positive
|
|
1043
|
+
codimension in the codomain. Let's look at the rays of our fans::
|
|
1044
|
+
|
|
1045
|
+
sage: Sigma_p.rays() # needs palp
|
|
1046
|
+
N( 1),
|
|
1047
|
+
N(-1)
|
|
1048
|
+
in 1-d lattice N
|
|
1049
|
+
sage: Sigma.rays() # needs palp
|
|
1050
|
+
N( 1, 1),
|
|
1051
|
+
N( 0, 1),
|
|
1052
|
+
N(-1, -1),
|
|
1053
|
+
N( 1, 0)
|
|
1054
|
+
in 2-d lattice N
|
|
1055
|
+
sage: xi.factor()[0].domain_fan().rays() # needs palp
|
|
1056
|
+
N(-1, 0),
|
|
1057
|
+
N( 1, 0)
|
|
1058
|
+
in Sublattice <N(1, 0)>
|
|
1059
|
+
|
|
1060
|
+
We see that one of the rays of the fan of ``P1`` is mapped to a ray,
|
|
1061
|
+
while the other one to the interior of some 2-d cone. Both rays
|
|
1062
|
+
correspond to single points on ``P1``, yet one is mapped to the
|
|
1063
|
+
distinguished point of a torus invariant curve of ``dP8`` (with the
|
|
1064
|
+
rest of this curve being uncovered) and the other to a fixed point
|
|
1065
|
+
of ``dP8`` (thus completely covering this torus orbit in ``dP8``).
|
|
1066
|
+
|
|
1067
|
+
We should therefore expect the following behaviour: all indices over
|
|
1068
|
+
1-d cones are ``None``, except for one which is infinite, and all
|
|
1069
|
+
indices over 2-d cones are ``None``, except for one which is 1::
|
|
1070
|
+
|
|
1071
|
+
sage: [xi.index(cone) for cone in Sigma(1)] # needs palp
|
|
1072
|
+
[None, None, None, +Infinity]
|
|
1073
|
+
sage: [xi.index(cone) for cone in Sigma(2)] # needs palp
|
|
1074
|
+
[None, 1, None, None]
|
|
1075
|
+
|
|
1076
|
+
TESTS::
|
|
1077
|
+
|
|
1078
|
+
sage: # needs palp
|
|
1079
|
+
sage: Sigma = toric_varieties.dP8().fan()
|
|
1080
|
+
sage: Sigma_p = toric_varieties.Cube_nonpolyhedral().fan()
|
|
1081
|
+
sage: m = matrix([[2,6,10], [7,11,13]])
|
|
1082
|
+
sage: zeta = FanMorphism(m, Sigma, Sigma_p, subdivide=True)
|
|
1083
|
+
sage: [zeta.index(cone) for cone in flatten(Sigma_p.cones())]
|
|
1084
|
+
[+Infinity, None, None, None, None, None, None, None, None, None,
|
|
1085
|
+
4, 4, None, 4, None, None, 2, None, 4, None, 4, 1, 1, 1, 1, 1, 1]
|
|
1086
|
+
sage: zeta = prod(zeta.factor()[1:])
|
|
1087
|
+
sage: Sigma_p = zeta.codomain_fan()
|
|
1088
|
+
sage: [zeta.index(cone) for cone in flatten(Sigma_p.cones())]
|
|
1089
|
+
[4, 4, 4, 1, 4, 4, 4, 1, 1, 1, 1, 1, 1]
|
|
1090
|
+
sage: zeta.index() == zeta.index(Sigma_p(0)[0])
|
|
1091
|
+
True
|
|
1092
|
+
"""
|
|
1093
|
+
if cone is None:
|
|
1094
|
+
try:
|
|
1095
|
+
return self.matrix().image().index_in(self.codomain())
|
|
1096
|
+
except ArithmeticError:
|
|
1097
|
+
cone = Cone([], lattice=self.codomain())
|
|
1098
|
+
cone = self._codomain_fan.embed(cone)
|
|
1099
|
+
PPCs = self.primitive_preimage_cones(cone)
|
|
1100
|
+
if not PPCs:
|
|
1101
|
+
return None
|
|
1102
|
+
Q = cone.sublattice_quotient()
|
|
1103
|
+
S = Q.submodule([self(g)
|
|
1104
|
+
for g in PPCs[0].sublattice_complement().gens()])
|
|
1105
|
+
i = prod((Q/S).invariants())
|
|
1106
|
+
return i if i > 0 else Infinity
|
|
1107
|
+
|
|
1108
|
+
def is_birational(self):
|
|
1109
|
+
r"""
|
|
1110
|
+
Check if ``self`` is birational.
|
|
1111
|
+
|
|
1112
|
+
OUTPUT: ``True`` if ``self`` is birational, ``False`` otherwise
|
|
1113
|
+
|
|
1114
|
+
For fan morphisms this check is equivalent to ``self.index() == 1`` and
|
|
1115
|
+
means that the corresponding map between toric varieties is birational.
|
|
1116
|
+
|
|
1117
|
+
EXAMPLES::
|
|
1118
|
+
|
|
1119
|
+
sage: # needs palp
|
|
1120
|
+
sage: Sigma = toric_varieties.dP8().fan()
|
|
1121
|
+
sage: Sigma_p = toric_varieties.P1().fan()
|
|
1122
|
+
sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
|
|
1123
|
+
sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
|
|
1124
|
+
sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
|
|
1125
|
+
sage: phi.index(), psi.index(), xi.index()
|
|
1126
|
+
(1, 2, +Infinity)
|
|
1127
|
+
sage: phi.is_birational(), psi.is_birational(), xi.is_birational()
|
|
1128
|
+
(True, False, False)
|
|
1129
|
+
"""
|
|
1130
|
+
return self.index() == 1
|
|
1131
|
+
|
|
1132
|
+
@cached_method
|
|
1133
|
+
def is_bundle(self):
|
|
1134
|
+
r"""
|
|
1135
|
+
Check if ``self`` is a bundle.
|
|
1136
|
+
|
|
1137
|
+
OUTPUT: ``True`` if ``self`` is a bundle, ``False`` otherwise
|
|
1138
|
+
|
|
1139
|
+
Let `\phi: \Sigma \to \Sigma'` be a fan morphism such that the
|
|
1140
|
+
underlying lattice morphism `\phi: N \to N'` is surjective. Let
|
|
1141
|
+
`\Sigma_0` be the kernel fan of `\phi`. Then `\phi` is a **bundle** (or
|
|
1142
|
+
splitting) if there is a subfan `\widehat{\Sigma}` of `\Sigma` such
|
|
1143
|
+
that the following two conditions are satisfied:
|
|
1144
|
+
|
|
1145
|
+
#. Cones of `\Sigma` are precisely the cones of the form
|
|
1146
|
+
`\sigma_0 + \widehat{\sigma}`, where `\sigma_0 \in \Sigma_0` and
|
|
1147
|
+
`\widehat{\sigma} \in \widehat{\Sigma}`.
|
|
1148
|
+
|
|
1149
|
+
#. Cones of `\widehat{\Sigma}` are in bijection with cones of
|
|
1150
|
+
`\Sigma'` induced by `\phi` and `\phi` maps lattice points in
|
|
1151
|
+
every cone `\widehat{\sigma} \in \widehat{\Sigma}` bijectively
|
|
1152
|
+
onto lattice points in `\phi(\widehat{\sigma})`.
|
|
1153
|
+
|
|
1154
|
+
If a fan morphism `\phi: \Sigma \to \Sigma'` is a bundle, then
|
|
1155
|
+
`X_\Sigma` is a fiber bundle over `X_{\Sigma'}` with fibers
|
|
1156
|
+
`X_{\Sigma_0, N_0}`, where `N_0` is the kernel lattice of `\phi`. See
|
|
1157
|
+
[CLS2011]_ for more details.
|
|
1158
|
+
|
|
1159
|
+
.. SEEALSO:: :meth:`is_fibration`, :meth:`kernel_fan`.
|
|
1160
|
+
|
|
1161
|
+
EXAMPLES:
|
|
1162
|
+
|
|
1163
|
+
We consider several maps between fans of a del Pezzo surface and the
|
|
1164
|
+
projective line::
|
|
1165
|
+
|
|
1166
|
+
sage: # needs palp
|
|
1167
|
+
sage: Sigma = toric_varieties.dP8().fan()
|
|
1168
|
+
sage: Sigma_p = toric_varieties.P1().fan()
|
|
1169
|
+
sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
|
|
1170
|
+
sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
|
|
1171
|
+
sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
|
|
1172
|
+
sage: phi.is_bundle()
|
|
1173
|
+
True
|
|
1174
|
+
sage: phi.is_fibration()
|
|
1175
|
+
True
|
|
1176
|
+
sage: phi.index()
|
|
1177
|
+
1
|
|
1178
|
+
sage: psi.is_bundle()
|
|
1179
|
+
False
|
|
1180
|
+
sage: psi.is_fibration()
|
|
1181
|
+
True
|
|
1182
|
+
sage: psi.index()
|
|
1183
|
+
2
|
|
1184
|
+
sage: xi.is_fibration()
|
|
1185
|
+
False
|
|
1186
|
+
sage: xi.index()
|
|
1187
|
+
+Infinity
|
|
1188
|
+
|
|
1189
|
+
The first of these maps induces not only a fibration, but a fiber
|
|
1190
|
+
bundle structure. The second map is very similar, yet it fails to be
|
|
1191
|
+
a bundle, as its index is 2. The last map is not even a fibration.
|
|
1192
|
+
"""
|
|
1193
|
+
if self.index() != 1:
|
|
1194
|
+
return False # Not surjective between lattices.
|
|
1195
|
+
Sigma = self.domain_fan()
|
|
1196
|
+
Sigma_p = self.codomain_fan()
|
|
1197
|
+
Sigma_0 = self.kernel_fan()
|
|
1198
|
+
if (Sigma.ngenerating_cones() !=
|
|
1199
|
+
Sigma_0.ngenerating_cones() * Sigma_p.ngenerating_cones()):
|
|
1200
|
+
return False # Definitely no splitting.
|
|
1201
|
+
try:
|
|
1202
|
+
ray_index_map = self._ray_index_map()
|
|
1203
|
+
except ValueError:
|
|
1204
|
+
return False # Rays are not mapped onto rays or the origin.
|
|
1205
|
+
# Figure out how Sigma_0 sits inside Sigma in terms of ray indices.
|
|
1206
|
+
I_0s = [Sigma.embed(sigma_0).ambient_ray_indices()
|
|
1207
|
+
for sigma_0 in Sigma_0]
|
|
1208
|
+
# We examine only generating cones, this is sufficient.
|
|
1209
|
+
for sigma_p in Sigma_p:
|
|
1210
|
+
primitive_cones = self.primitive_preimage_cones(sigma_p)
|
|
1211
|
+
if len(primitive_cones) != 1: # Should be only sigma_hat.
|
|
1212
|
+
return False
|
|
1213
|
+
sigma_hat = primitive_cones[0]
|
|
1214
|
+
if sigma_p.dim() != sigma_hat.dim():
|
|
1215
|
+
return False # sigma -> sigma_p is not a bijection
|
|
1216
|
+
I_p = sigma_p.ambient_ray_indices()
|
|
1217
|
+
I_hat = sigma_hat.ambient_ray_indices()
|
|
1218
|
+
if I_p != tuple(sorted(ray_index_map[i] for i in I_hat)):
|
|
1219
|
+
return False # sigma -> sigma_p is not a bijection
|
|
1220
|
+
# Check that sigma_hat + sigma_0 is always in Sigma.
|
|
1221
|
+
for I_0 in I_0s:
|
|
1222
|
+
I = tuple(sorted(I_hat + I_0))
|
|
1223
|
+
if all(sigma.ambient_ray_indices() != I for sigma in Sigma):
|
|
1224
|
+
return False
|
|
1225
|
+
return True
|
|
1226
|
+
|
|
1227
|
+
@cached_method
|
|
1228
|
+
def is_fibration(self):
|
|
1229
|
+
r"""
|
|
1230
|
+
Check if ``self`` is a fibration.
|
|
1231
|
+
|
|
1232
|
+
OUTPUT: ``True`` if ``self`` is a fibration, ``False`` otherwise
|
|
1233
|
+
|
|
1234
|
+
A fan morphism `\phi: \Sigma \to \Sigma'` is a **fibration**
|
|
1235
|
+
if for any cone `\sigma' \in \Sigma'` and any primitive
|
|
1236
|
+
preimage cone `\sigma \in \Sigma` corresponding to `\sigma'`
|
|
1237
|
+
the linear map of vector spaces `\phi_\RR` induces a bijection
|
|
1238
|
+
between `\sigma` and `\sigma'`, and, in addition, `\phi` is
|
|
1239
|
+
:meth:`dominant <is_dominant>` (that is, `\phi_\RR: N_\RR \to
|
|
1240
|
+
N'_\RR` is surjective).
|
|
1241
|
+
|
|
1242
|
+
If a fan morphism `\phi: \Sigma \to \Sigma'` is a fibration, then the
|
|
1243
|
+
associated morphism between toric varieties `\tilde{\phi}: X_\Sigma \to
|
|
1244
|
+
X_{\Sigma'}` is a fibration in the sense that it is surjective and all
|
|
1245
|
+
of its fibers have the same dimension, namely `\dim X_\Sigma -
|
|
1246
|
+
\dim X_{\Sigma'}`. These fibers do *not* have to be isomorphic, i.e. a
|
|
1247
|
+
fibration is not necessarily a fiber bundle. See [HLY2002]_ for more
|
|
1248
|
+
details.
|
|
1249
|
+
|
|
1250
|
+
.. SEEALSO:: :meth:`is_bundle`, :meth:`primitive_preimage_cones`.
|
|
1251
|
+
|
|
1252
|
+
EXAMPLES:
|
|
1253
|
+
|
|
1254
|
+
We consider several maps between fans of a del Pezzo surface and the
|
|
1255
|
+
projective line::
|
|
1256
|
+
|
|
1257
|
+
sage: # needs palp
|
|
1258
|
+
sage: Sigma = toric_varieties.dP8().fan()
|
|
1259
|
+
sage: Sigma_p = toric_varieties.P1().fan()
|
|
1260
|
+
sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
|
|
1261
|
+
sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
|
|
1262
|
+
sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
|
|
1263
|
+
sage: phi.is_bundle()
|
|
1264
|
+
True
|
|
1265
|
+
sage: phi.is_fibration()
|
|
1266
|
+
True
|
|
1267
|
+
sage: phi.index()
|
|
1268
|
+
1
|
|
1269
|
+
sage: psi.is_bundle()
|
|
1270
|
+
False
|
|
1271
|
+
sage: psi.is_fibration()
|
|
1272
|
+
True
|
|
1273
|
+
sage: psi.index()
|
|
1274
|
+
2
|
|
1275
|
+
sage: xi.is_fibration()
|
|
1276
|
+
False
|
|
1277
|
+
sage: xi.index()
|
|
1278
|
+
+Infinity
|
|
1279
|
+
|
|
1280
|
+
The first of these maps induces not only a fibration, but a fiber
|
|
1281
|
+
bundle structure. The second map is very similar, yet it fails to be
|
|
1282
|
+
a bundle, as its index is 2. The last map is not even a fibration.
|
|
1283
|
+
|
|
1284
|
+
TESTS:
|
|
1285
|
+
|
|
1286
|
+
We check that reviewer's example on :issue:`11200` works as expected::
|
|
1287
|
+
|
|
1288
|
+
sage: P1 = toric_varieties.P1()
|
|
1289
|
+
sage: A1 = toric_varieties.A1()
|
|
1290
|
+
sage: phi = FanMorphism(matrix([[1]]), A1.fan(), P1.fan())
|
|
1291
|
+
sage: phi.is_fibration()
|
|
1292
|
+
False
|
|
1293
|
+
"""
|
|
1294
|
+
if not self.is_surjective():
|
|
1295
|
+
return False
|
|
1296
|
+
try:
|
|
1297
|
+
ray_index_map = self._ray_index_map()
|
|
1298
|
+
except ValueError:
|
|
1299
|
+
return False # Rays are not mapped onto rays or the origin.
|
|
1300
|
+
Sigma_p = self.codomain_fan()
|
|
1301
|
+
# Rays are already checked, the origin is trivial, start with 2-cones.
|
|
1302
|
+
for d in range(2, Sigma_p.dim() + 1):
|
|
1303
|
+
for sigma_p in Sigma_p(d):
|
|
1304
|
+
I_p = sigma_p.ambient_ray_indices()
|
|
1305
|
+
for sigma in self.primitive_preimage_cones(sigma_p):
|
|
1306
|
+
if sigma.dim() != d:
|
|
1307
|
+
return False # sigma -> sigma_p is not a bijection
|
|
1308
|
+
I = sigma.ambient_ray_indices()
|
|
1309
|
+
if I_p != tuple(sorted(ray_index_map[i] for i in I)):
|
|
1310
|
+
return False # sigma -> sigma_p is not a bijection
|
|
1311
|
+
return True
|
|
1312
|
+
|
|
1313
|
+
@cached_method
|
|
1314
|
+
def is_injective(self):
|
|
1315
|
+
r"""
|
|
1316
|
+
Check if ``self`` is injective.
|
|
1317
|
+
|
|
1318
|
+
OUTPUT: ``True`` if ``self`` is injective, ``False`` otherwise
|
|
1319
|
+
|
|
1320
|
+
Let `\phi: \Sigma \to \Sigma'` be a fan morphism such that the
|
|
1321
|
+
underlying lattice morphism `\phi: N \to N'` bijectively maps `N` to a
|
|
1322
|
+
*saturated* sublattice of `N'`. Let `\psi: \Sigma \to \Sigma'_0` be the
|
|
1323
|
+
restriction of `\phi` to the image. Then `\phi` is **injective** if the
|
|
1324
|
+
map between cones corresponding to `\psi` (injectively) maps each cone
|
|
1325
|
+
of `\Sigma` to a cone of the same dimension.
|
|
1326
|
+
|
|
1327
|
+
If a fan morphism `\phi: \Sigma \to \Sigma'` is injective, then the
|
|
1328
|
+
associated morphism between toric varieties `\tilde{\phi}: X_\Sigma \to
|
|
1329
|
+
X_{\Sigma'}` is injective.
|
|
1330
|
+
|
|
1331
|
+
.. SEEALSO:: :meth:`factor`.
|
|
1332
|
+
|
|
1333
|
+
EXAMPLES:
|
|
1334
|
+
|
|
1335
|
+
Consider the fan of the affine plane::
|
|
1336
|
+
|
|
1337
|
+
sage: A2 = toric_varieties.A(2).fan()
|
|
1338
|
+
|
|
1339
|
+
We will map several fans consisting of a single ray into the interior
|
|
1340
|
+
of the 2-cone::
|
|
1341
|
+
|
|
1342
|
+
sage: Sigma = Fan([Cone([(1,1)])])
|
|
1343
|
+
sage: m = identity_matrix(2)
|
|
1344
|
+
sage: FanMorphism(m, Sigma, A2).is_injective()
|
|
1345
|
+
False
|
|
1346
|
+
|
|
1347
|
+
This morphism was not injective since (in the toric varieties
|
|
1348
|
+
interpretation) the 1-dimensional orbit corresponding to the ray was
|
|
1349
|
+
mapped to the 0-dimensional orbit corresponding to the 2-cone. ::
|
|
1350
|
+
|
|
1351
|
+
sage: Sigma = Fan([Cone([(1,)])])
|
|
1352
|
+
sage: m = matrix(1, 2, [1,1])
|
|
1353
|
+
sage: FanMorphism(m, Sigma, A2).is_injective()
|
|
1354
|
+
True
|
|
1355
|
+
|
|
1356
|
+
While the fans in this example are close to the previous one, here the
|
|
1357
|
+
ray corresponds to a 0-dimensional orbit. ::
|
|
1358
|
+
|
|
1359
|
+
sage: Sigma = Fan([Cone([(1,)])])
|
|
1360
|
+
sage: m = matrix(1, 2, [2,2])
|
|
1361
|
+
sage: FanMorphism(m, Sigma, A2).is_injective()
|
|
1362
|
+
False
|
|
1363
|
+
|
|
1364
|
+
Here the problem is that ``m`` maps the domain lattice to a
|
|
1365
|
+
non-saturated sublattice of the codomain. The corresponding map of the
|
|
1366
|
+
toric varieties is a two-sheeted cover of its image.
|
|
1367
|
+
|
|
1368
|
+
We also embed the affine plane into the projective one::
|
|
1369
|
+
|
|
1370
|
+
sage: P2 = toric_varieties.P(2).fan() # needs palp
|
|
1371
|
+
sage: m = identity_matrix(2)
|
|
1372
|
+
sage: FanMorphism(m, A2, P2).is_injective() # needs palp
|
|
1373
|
+
True
|
|
1374
|
+
"""
|
|
1375
|
+
if self.matrix().index_in_saturation() != 1:
|
|
1376
|
+
return False
|
|
1377
|
+
if self.image().dimension() < self.codomain().dimension():
|
|
1378
|
+
return prod(self.factor()[1:]).is_injective()
|
|
1379
|
+
# Now we know that underlying lattice morphism is bijective.
|
|
1380
|
+
Sigma = self.domain_fan()
|
|
1381
|
+
return all(self.image_cone(sigma).dim() == d
|
|
1382
|
+
for d in range(1, Sigma.dim() + 1) for sigma in Sigma(d))
|
|
1383
|
+
|
|
1384
|
+
@cached_method
|
|
1385
|
+
def is_surjective(self):
|
|
1386
|
+
r"""
|
|
1387
|
+
Check if ``self`` is surjective.
|
|
1388
|
+
|
|
1389
|
+
OUTPUT: ``True`` if ``self`` is surjective, ``False`` otherwise
|
|
1390
|
+
|
|
1391
|
+
A fan morphism `\phi: \Sigma \to \Sigma'` is **surjective** if the
|
|
1392
|
+
corresponding map between cones is surjective, i.e. for each cone
|
|
1393
|
+
`\sigma' \in \Sigma'` there is at least one preimage cone `\sigma \in
|
|
1394
|
+
\Sigma` such that the relative interior of `\sigma` is mapped to the
|
|
1395
|
+
relative interior of `\sigma'` and, in addition,
|
|
1396
|
+
`\phi_\RR: N_\RR \to N'_\RR` is surjective.
|
|
1397
|
+
|
|
1398
|
+
If a fan morphism `\phi: \Sigma \to \Sigma'` is surjective, then the
|
|
1399
|
+
associated morphism between toric varieties `\tilde{\phi}: X_\Sigma \to
|
|
1400
|
+
X_{\Sigma'}` is surjective.
|
|
1401
|
+
|
|
1402
|
+
.. SEEALSO:: :meth:`is_bundle`, :meth:`is_fibration`,
|
|
1403
|
+
:meth:`preimage_cones`, :meth:`is_complete`.
|
|
1404
|
+
|
|
1405
|
+
EXAMPLES:
|
|
1406
|
+
|
|
1407
|
+
We check that the blow up of the affine plane at the origin is
|
|
1408
|
+
surjective::
|
|
1409
|
+
|
|
1410
|
+
sage: A2 = toric_varieties.A(2).fan()
|
|
1411
|
+
sage: Bl = A2.subdivide([(1,1)])
|
|
1412
|
+
sage: m = identity_matrix(2)
|
|
1413
|
+
sage: FanMorphism(m, Bl, A2).is_surjective()
|
|
1414
|
+
True
|
|
1415
|
+
|
|
1416
|
+
It remains surjective if we throw away "south and north poles" of the
|
|
1417
|
+
exceptional divisor::
|
|
1418
|
+
|
|
1419
|
+
sage: FanMorphism(m, Fan(Bl.cones(1)), A2).is_surjective()
|
|
1420
|
+
True
|
|
1421
|
+
|
|
1422
|
+
But a single patch of the blow up does not cover the plane::
|
|
1423
|
+
|
|
1424
|
+
sage: F = Fan([Bl.generating_cone(0)])
|
|
1425
|
+
sage: FanMorphism(m, F, A2).is_surjective()
|
|
1426
|
+
False
|
|
1427
|
+
|
|
1428
|
+
TESTS:
|
|
1429
|
+
|
|
1430
|
+
We check that reviewer's example on :issue:`11200` works as expected::
|
|
1431
|
+
|
|
1432
|
+
sage: P1 = toric_varieties.P1()
|
|
1433
|
+
sage: A1 = toric_varieties.A1()
|
|
1434
|
+
sage: phi = FanMorphism(matrix([[1]]), A1.fan(), P1.fan())
|
|
1435
|
+
sage: phi.is_surjective()
|
|
1436
|
+
False
|
|
1437
|
+
"""
|
|
1438
|
+
if isinstance(self.index(), InfinityElement):
|
|
1439
|
+
return False # Not surjective between vector spaces.
|
|
1440
|
+
for dcones in self.codomain_fan().cones():
|
|
1441
|
+
for sigma_p in dcones:
|
|
1442
|
+
if not self.preimage_cones(sigma_p):
|
|
1443
|
+
return False
|
|
1444
|
+
return True
|
|
1445
|
+
|
|
1446
|
+
@cached_method
|
|
1447
|
+
def is_dominant(self):
|
|
1448
|
+
r"""
|
|
1449
|
+
Return whether the fan morphism is dominant.
|
|
1450
|
+
|
|
1451
|
+
A fan morphism `\phi` is dominant if it is surjective as a map
|
|
1452
|
+
of vector spaces. That is, `\phi_\RR: N_\RR \to N'_\RR` is
|
|
1453
|
+
surjective.
|
|
1454
|
+
|
|
1455
|
+
If the domain fan is :meth:`complete
|
|
1456
|
+
<sage.geometry.fan.RationalPolyhedralFan.is_complete>`, then
|
|
1457
|
+
this implies that the fan morphism is :meth:`surjective
|
|
1458
|
+
<is_surjective>`.
|
|
1459
|
+
|
|
1460
|
+
If the fan morphism is dominant, then the associated morphism
|
|
1461
|
+
of toric varieties is dominant in the algebraic-geometric
|
|
1462
|
+
sense (that is, surjective onto a dense subset).
|
|
1463
|
+
|
|
1464
|
+
OUTPUT: boolean
|
|
1465
|
+
|
|
1466
|
+
EXAMPLES::
|
|
1467
|
+
|
|
1468
|
+
sage: P1 = toric_varieties.P1()
|
|
1469
|
+
sage: A1 = toric_varieties.A1()
|
|
1470
|
+
sage: phi = FanMorphism(matrix([[1]]), A1.fan(), P1.fan())
|
|
1471
|
+
sage: phi.is_dominant()
|
|
1472
|
+
True
|
|
1473
|
+
sage: phi.is_surjective()
|
|
1474
|
+
False
|
|
1475
|
+
"""
|
|
1476
|
+
return self.matrix().rank() == self.codomain_fan().dim()
|
|
1477
|
+
|
|
1478
|
+
@cached_method
|
|
1479
|
+
def kernel_fan(self):
|
|
1480
|
+
r"""
|
|
1481
|
+
Return the subfan of the domain fan mapped into the origin.
|
|
1482
|
+
|
|
1483
|
+
OUTPUT: a :class:`fan <sage.geometry.fan.RationalPolyhedralFan>`
|
|
1484
|
+
|
|
1485
|
+
.. NOTE::
|
|
1486
|
+
|
|
1487
|
+
The lattice of the kernel fan is the :meth:`kernel` sublattice of
|
|
1488
|
+
``self``.
|
|
1489
|
+
|
|
1490
|
+
.. SEEALSO:: :meth:`preimage_fan`.
|
|
1491
|
+
|
|
1492
|
+
EXAMPLES::
|
|
1493
|
+
|
|
1494
|
+
sage: fan = Fan(rays=[(1,0), (1,1), (0,1)], cones=[(0,1), (1,2)])
|
|
1495
|
+
sage: fm = FanMorphism(matrix(2, 1, [1,-1]), fan, ToricLattice(1))
|
|
1496
|
+
sage: fm.kernel_fan()
|
|
1497
|
+
Rational polyhedral fan in Sublattice <N(1, 1)>
|
|
1498
|
+
sage: _.rays()
|
|
1499
|
+
N(1, 1)
|
|
1500
|
+
in Sublattice <N(1, 1)>
|
|
1501
|
+
sage: fm.kernel_fan().cones()
|
|
1502
|
+
((0-d cone of Rational polyhedral fan in Sublattice <N(1, 1)>,),
|
|
1503
|
+
(1-d cone of Rational polyhedral fan in Sublattice <N(1, 1)>,))
|
|
1504
|
+
"""
|
|
1505
|
+
fan = self.preimage_fan(Cone([], lattice=self.codomain()))
|
|
1506
|
+
return Fan((cone.ambient_ray_indices() for cone in fan), fan.rays(),
|
|
1507
|
+
lattice=self.kernel(), check=False)
|
|
1508
|
+
|
|
1509
|
+
def preimage_cones(self, cone):
|
|
1510
|
+
r"""
|
|
1511
|
+
Return cones of the domain fan whose :meth:`image_cone` is ``cone``.
|
|
1512
|
+
|
|
1513
|
+
INPUT:
|
|
1514
|
+
|
|
1515
|
+
- ``cone`` -- a :class:`cone
|
|
1516
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` equivalent to a
|
|
1517
|
+
cone of the :meth:`codomain_fan` of ``self``.
|
|
1518
|
+
|
|
1519
|
+
OUTPUT:
|
|
1520
|
+
|
|
1521
|
+
- a :class:`tuple` of :class:`cones
|
|
1522
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` of the
|
|
1523
|
+
:meth:`domain_fan` of ``self``, sorted by dimension.
|
|
1524
|
+
|
|
1525
|
+
.. SEEALSO:: :meth:`preimage_fan`.
|
|
1526
|
+
|
|
1527
|
+
EXAMPLES::
|
|
1528
|
+
|
|
1529
|
+
sage: quadrant = Cone([(1,0), (0,1)])
|
|
1530
|
+
sage: quadrant = Fan([quadrant])
|
|
1531
|
+
sage: quadrant_bl = quadrant.subdivide([(1,1)])
|
|
1532
|
+
sage: fm = FanMorphism(identity_matrix(2), quadrant_bl, quadrant)
|
|
1533
|
+
sage: fm.preimage_cones(Cone([(1,0)]))
|
|
1534
|
+
(1-d cone of Rational polyhedral fan in 2-d lattice N,)
|
|
1535
|
+
sage: fm.preimage_cones(Cone([(1,0), (0,1)]))
|
|
1536
|
+
(1-d cone of Rational polyhedral fan in 2-d lattice N,
|
|
1537
|
+
2-d cone of Rational polyhedral fan in 2-d lattice N,
|
|
1538
|
+
2-d cone of Rational polyhedral fan in 2-d lattice N)
|
|
1539
|
+
|
|
1540
|
+
TESTS:
|
|
1541
|
+
|
|
1542
|
+
We check that reviewer's example from :issue:`9972` is handled correctly::
|
|
1543
|
+
|
|
1544
|
+
sage: # needs palp
|
|
1545
|
+
sage: N1 = ToricLattice(1)
|
|
1546
|
+
sage: N2 = ToricLattice(2)
|
|
1547
|
+
sage: Hom21 = Hom(N2, N1)
|
|
1548
|
+
sage: pr = Hom21([N1.0,0])
|
|
1549
|
+
sage: P1xP1 = toric_varieties.P1xP1()
|
|
1550
|
+
sage: f = FanMorphism(pr, P1xP1.fan())
|
|
1551
|
+
sage: c = f.image_cone(Cone([(1,0), (0,1)])); c
|
|
1552
|
+
1-d cone of Rational polyhedral fan in 1-d lattice N
|
|
1553
|
+
sage: f.preimage_cones(c)
|
|
1554
|
+
(1-d cone of Rational polyhedral fan in 2-d lattice N,
|
|
1555
|
+
2-d cone of Rational polyhedral fan in 2-d lattice N,
|
|
1556
|
+
2-d cone of Rational polyhedral fan in 2-d lattice N)
|
|
1557
|
+
"""
|
|
1558
|
+
cone = self._codomain_fan.embed(cone)
|
|
1559
|
+
if cone not in self._preimage_cones:
|
|
1560
|
+
# "Images of preimages" must sit in all of these generating cones
|
|
1561
|
+
CSGI = cone.star_generator_indices()
|
|
1562
|
+
RISGIS = self._RISGIS()
|
|
1563
|
+
domain_fan = self._domain_fan
|
|
1564
|
+
possible_rays = frozenset(i for i in range(domain_fan.nrays())
|
|
1565
|
+
if RISGIS[i].issuperset(CSGI))
|
|
1566
|
+
preimage_cones = []
|
|
1567
|
+
for dcones in domain_fan.cones():
|
|
1568
|
+
for dcone in dcones:
|
|
1569
|
+
if (possible_rays.issuperset(dcone.ambient_ray_indices())
|
|
1570
|
+
and self.image_cone(dcone) == cone):
|
|
1571
|
+
preimage_cones.append(dcone)
|
|
1572
|
+
self._preimage_cones[cone] = tuple(preimage_cones)
|
|
1573
|
+
return self._preimage_cones[cone]
|
|
1574
|
+
|
|
1575
|
+
def preimage_fan(self, cone):
|
|
1576
|
+
r"""
|
|
1577
|
+
Return the subfan of the domain fan mapped into ``cone``.
|
|
1578
|
+
|
|
1579
|
+
INPUT:
|
|
1580
|
+
|
|
1581
|
+
- ``cone`` -- a :class:`cone
|
|
1582
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` equivalent to a
|
|
1583
|
+
cone of the :meth:`codomain_fan` of ``self``
|
|
1584
|
+
|
|
1585
|
+
OUTPUT: a :class:`fan <sage.geometry.fan.RationalPolyhedralFan>`
|
|
1586
|
+
|
|
1587
|
+
.. NOTE::
|
|
1588
|
+
|
|
1589
|
+
The preimage fan of ``cone`` consists of all cones of the
|
|
1590
|
+
:meth:`domain_fan` which are mapped into ``cone``, including those
|
|
1591
|
+
that are mapped into its boundary. So this fan is not necessarily
|
|
1592
|
+
generated by :meth:`preimage_cones` of ``cone``.
|
|
1593
|
+
|
|
1594
|
+
.. SEEALSO:: :meth:`kernel_fan`, :meth:`preimage_cones`.
|
|
1595
|
+
|
|
1596
|
+
EXAMPLES::
|
|
1597
|
+
|
|
1598
|
+
sage: quadrant_cone = Cone([(1,0), (0,1)])
|
|
1599
|
+
sage: quadrant_fan = Fan([quadrant_cone])
|
|
1600
|
+
sage: quadrant_bl = quadrant_fan.subdivide([(1,1)])
|
|
1601
|
+
sage: fm = FanMorphism(identity_matrix(2),
|
|
1602
|
+
....: quadrant_bl, quadrant_fan)
|
|
1603
|
+
sage: fm.preimage_fan(Cone([(1,0)])).cones()
|
|
1604
|
+
((0-d cone of Rational polyhedral fan in 2-d lattice N,),
|
|
1605
|
+
(1-d cone of Rational polyhedral fan in 2-d lattice N,))
|
|
1606
|
+
sage: fm.preimage_fan(quadrant_cone).ngenerating_cones()
|
|
1607
|
+
2
|
|
1608
|
+
sage: len(fm.preimage_cones(quadrant_cone))
|
|
1609
|
+
3
|
|
1610
|
+
"""
|
|
1611
|
+
cone = self._codomain_fan.embed(cone)
|
|
1612
|
+
if cone not in self._preimage_fans:
|
|
1613
|
+
# Find all cones mapped into the given one, including its boundary.
|
|
1614
|
+
domain_fan = self._domain_fan
|
|
1615
|
+
cones = []
|
|
1616
|
+
for dcones in reversed(domain_fan.cones()):
|
|
1617
|
+
for dcone in dcones:
|
|
1618
|
+
if (not any(dcone.is_face_of(other) for other in cones) and
|
|
1619
|
+
self.image_cone(dcone).is_face_of(cone)):
|
|
1620
|
+
cones.append(dcone)
|
|
1621
|
+
# Now form the fan from these cones, keeping the ray order.
|
|
1622
|
+
ray_indices = set(cones[0].ambient_ray_indices())
|
|
1623
|
+
for c in cones[1:]:
|
|
1624
|
+
ray_indices.update(c.ambient_ray_indices())
|
|
1625
|
+
self._preimage_fans[cone] = Fan(cones,
|
|
1626
|
+
domain_fan.rays(sorted(ray_indices)), check=False)
|
|
1627
|
+
return self._preimage_fans[cone]
|
|
1628
|
+
|
|
1629
|
+
def primitive_preimage_cones(self, cone):
|
|
1630
|
+
r"""
|
|
1631
|
+
Return the primitive cones of the domain fan corresponding to ``cone``.
|
|
1632
|
+
|
|
1633
|
+
INPUT:
|
|
1634
|
+
|
|
1635
|
+
- ``cone`` -- a :class:`cone
|
|
1636
|
+
<sage.geometry.cone.ConvexRationalPolyhedralCone>` equivalent to a
|
|
1637
|
+
cone of the :meth:`codomain_fan` of ``self``
|
|
1638
|
+
|
|
1639
|
+
OUTPUT: a :class:`cone <sage.geometry.cone.ConvexRationalPolyhedralCone>`
|
|
1640
|
+
|
|
1641
|
+
Let `\phi: \Sigma \to \Sigma'` be a fan morphism, let `\sigma \in
|
|
1642
|
+
\Sigma`, and let `\sigma' = \phi(\sigma)`. Then `\sigma` is a
|
|
1643
|
+
**primitive cone corresponding to** `\sigma'` if there is no proper
|
|
1644
|
+
face `\tau` of `\sigma` such that `\phi(\tau) = \sigma'`.
|
|
1645
|
+
|
|
1646
|
+
Primitive cones play an important role for fibration morphisms.
|
|
1647
|
+
|
|
1648
|
+
.. SEEALSO:: :meth:`is_fibration`, :meth:`preimage_cones`,
|
|
1649
|
+
:meth:`preimage_fan`.
|
|
1650
|
+
|
|
1651
|
+
EXAMPLES:
|
|
1652
|
+
|
|
1653
|
+
Consider a projection of a del Pezzo surface onto the projective line::
|
|
1654
|
+
|
|
1655
|
+
sage: Sigma = toric_varieties.dP6().fan() # needs palp
|
|
1656
|
+
sage: Sigma.rays() # needs palp
|
|
1657
|
+
N( 0, 1),
|
|
1658
|
+
N(-1, 0),
|
|
1659
|
+
N(-1, -1),
|
|
1660
|
+
N( 0, -1),
|
|
1661
|
+
N( 1, 0),
|
|
1662
|
+
N( 1, 1)
|
|
1663
|
+
in 2-d lattice N
|
|
1664
|
+
sage: Sigma_p = toric_varieties.P1().fan()
|
|
1665
|
+
sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p) # needs palp
|
|
1666
|
+
|
|
1667
|
+
Under this map, one pair of rays is mapped to the origin, one in the
|
|
1668
|
+
positive direction, and one in the negative one. Also three
|
|
1669
|
+
2-dimensional cones are mapped in the positive direction and three in
|
|
1670
|
+
the negative one, so there are 5 preimage cones corresponding to either
|
|
1671
|
+
of the rays of the codomain fan ``Sigma_p``::
|
|
1672
|
+
|
|
1673
|
+
sage: len(phi.preimage_cones(Cone([(1,)]))) # needs palp
|
|
1674
|
+
5
|
|
1675
|
+
|
|
1676
|
+
Yet only rays are primitive::
|
|
1677
|
+
|
|
1678
|
+
sage: phi.primitive_preimage_cones(Cone([(1,)])) # needs palp
|
|
1679
|
+
(1-d cone of Rational polyhedral fan in 2-d lattice N,
|
|
1680
|
+
1-d cone of Rational polyhedral fan in 2-d lattice N)
|
|
1681
|
+
|
|
1682
|
+
Since all primitive cones are mapped onto their images bijectively, we
|
|
1683
|
+
get a fibration::
|
|
1684
|
+
|
|
1685
|
+
sage: phi.is_fibration() # needs palp
|
|
1686
|
+
True
|
|
1687
|
+
|
|
1688
|
+
But since there are several primitive cones corresponding to the same
|
|
1689
|
+
cone of the codomain fan, this map is not a bundle, even though its
|
|
1690
|
+
index is 1::
|
|
1691
|
+
|
|
1692
|
+
sage: phi.is_bundle() # needs palp
|
|
1693
|
+
False
|
|
1694
|
+
sage: phi.index() # needs palp
|
|
1695
|
+
1
|
|
1696
|
+
"""
|
|
1697
|
+
sigma_p = self._codomain_fan.embed(cone) # Necessary if used as a key
|
|
1698
|
+
if sigma_p not in self._primitive_preimage_cones:
|
|
1699
|
+
primitive_cones = []
|
|
1700
|
+
for sigma in self.preimage_cones(sigma_p): # Sorted by dimension
|
|
1701
|
+
if not any(tau.is_face_of(sigma) for tau in primitive_cones):
|
|
1702
|
+
primitive_cones.append(sigma)
|
|
1703
|
+
self._primitive_preimage_cones[sigma_p] = tuple(primitive_cones)
|
|
1704
|
+
return self._primitive_preimage_cones[sigma_p]
|
|
1705
|
+
|
|
1706
|
+
def factor(self):
|
|
1707
|
+
r"""
|
|
1708
|
+
Factor ``self`` into injective * birational * surjective morphisms.
|
|
1709
|
+
|
|
1710
|
+
OUTPUT:
|
|
1711
|
+
|
|
1712
|
+
- a triple of :class:`FanMorphism` `(\phi_i, \phi_b, \phi_s)`, such that
|
|
1713
|
+
`\phi_s` is surjective, `\phi_b` is birational, `\phi_i` is injective,
|
|
1714
|
+
and ``self`` is equal to `\phi_i \circ \phi_b \circ \phi_s`.
|
|
1715
|
+
|
|
1716
|
+
Intermediate fans live in the saturation of the image of ``self``
|
|
1717
|
+
as a map between lattices and are the image of the :meth:`domain_fan`
|
|
1718
|
+
and the restriction of the :meth:`codomain_fan`, i.e. if ``self`` maps
|
|
1719
|
+
`\Sigma \to \Sigma'`, then we have factorization into
|
|
1720
|
+
|
|
1721
|
+
.. MATH::
|
|
1722
|
+
|
|
1723
|
+
\Sigma
|
|
1724
|
+
\twoheadrightarrow
|
|
1725
|
+
\Sigma_s
|
|
1726
|
+
\to
|
|
1727
|
+
\Sigma_i
|
|
1728
|
+
\hookrightarrow
|
|
1729
|
+
\Sigma.
|
|
1730
|
+
|
|
1731
|
+
.. NOTE::
|
|
1732
|
+
|
|
1733
|
+
* `\Sigma_s` is the finest fan with the smallest support that is
|
|
1734
|
+
compatible with ``self``: any fan morphism from `\Sigma` given by
|
|
1735
|
+
the same map of lattices as ``self`` factors through `\Sigma_s`.
|
|
1736
|
+
|
|
1737
|
+
* `\Sigma_i` is the coarsest fan of the largest support that is
|
|
1738
|
+
compatible with ``self``: any fan morphism into `\Sigma'` given by
|
|
1739
|
+
the same map of lattices as ``self`` factors though `\Sigma_i`.
|
|
1740
|
+
|
|
1741
|
+
EXAMPLES:
|
|
1742
|
+
|
|
1743
|
+
We map an affine plane into a projective 3-space in such a way, that it
|
|
1744
|
+
becomes "a double cover of a chart of the blow up of one of the
|
|
1745
|
+
coordinate planes"::
|
|
1746
|
+
|
|
1747
|
+
sage: A2 = toric_varieties.A2()
|
|
1748
|
+
sage: P3 = toric_varieties.P(3) # needs palp
|
|
1749
|
+
sage: m = matrix([(2,0,0), (1,1,0)])
|
|
1750
|
+
sage: phi = A2.hom(m, P3) # needs palp
|
|
1751
|
+
sage: phi.as_polynomial_map() # needs palp
|
|
1752
|
+
Scheme morphism:
|
|
1753
|
+
From: 2-d affine toric variety
|
|
1754
|
+
To: 3-d CPR-Fano toric variety covered by 4 affine patches
|
|
1755
|
+
Defn: Defined on coordinates by sending [x : y] to
|
|
1756
|
+
[x^2*y : y : 1 : 1]
|
|
1757
|
+
|
|
1758
|
+
Now we will work with the underlying fan morphism::
|
|
1759
|
+
|
|
1760
|
+
sage: # needs palp
|
|
1761
|
+
sage: phi = phi.fan_morphism(); phi
|
|
1762
|
+
Fan morphism defined by the matrix
|
|
1763
|
+
[2 0 0]
|
|
1764
|
+
[1 1 0]
|
|
1765
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
1766
|
+
Codomain fan: Rational polyhedral fan in 3-d lattice N
|
|
1767
|
+
sage: phi.is_surjective(), phi.is_birational(), phi.is_injective()
|
|
1768
|
+
(False, False, False)
|
|
1769
|
+
sage: phi_i, phi_b, phi_s = phi.factor()
|
|
1770
|
+
sage: phi_s.is_surjective(), phi_b.is_birational(), phi_i.is_injective()
|
|
1771
|
+
(True, True, True)
|
|
1772
|
+
sage: prod(phi.factor()) == phi
|
|
1773
|
+
True
|
|
1774
|
+
|
|
1775
|
+
Double cover (surjective)::
|
|
1776
|
+
|
|
1777
|
+
sage: A2.fan().rays()
|
|
1778
|
+
N(1, 0),
|
|
1779
|
+
N(0, 1)
|
|
1780
|
+
in 2-d lattice N
|
|
1781
|
+
sage: phi_s # needs palp
|
|
1782
|
+
Fan morphism defined by the matrix
|
|
1783
|
+
[2 0]
|
|
1784
|
+
[1 1]
|
|
1785
|
+
Domain fan: Rational polyhedral fan in 2-d lattice N
|
|
1786
|
+
Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
|
|
1787
|
+
sage: phi_s.codomain_fan().rays() # needs palp
|
|
1788
|
+
N(1, 0, 0),
|
|
1789
|
+
N(1, 1, 0)
|
|
1790
|
+
in Sublattice <N(1, 0, 0), N(0, 1, 0)>
|
|
1791
|
+
|
|
1792
|
+
Blowup chart (birational)::
|
|
1793
|
+
|
|
1794
|
+
sage: phi_b # needs palp
|
|
1795
|
+
Fan morphism defined by the matrix
|
|
1796
|
+
[1 0]
|
|
1797
|
+
[0 1]
|
|
1798
|
+
Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
|
|
1799
|
+
Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
|
|
1800
|
+
sage: phi_b.codomain_fan().rays() # needs palp
|
|
1801
|
+
N(-1, -1, 0),
|
|
1802
|
+
N( 0, 1, 0),
|
|
1803
|
+
N( 1, 0, 0)
|
|
1804
|
+
in Sublattice <N(1, 0, 0), N(0, 1, 0)>
|
|
1805
|
+
|
|
1806
|
+
Coordinate plane inclusion (injective)::
|
|
1807
|
+
|
|
1808
|
+
sage: phi_i # needs palp
|
|
1809
|
+
Fan morphism defined by the matrix
|
|
1810
|
+
[1 0 0]
|
|
1811
|
+
[0 1 0]
|
|
1812
|
+
Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
|
|
1813
|
+
Codomain fan: Rational polyhedral fan in 3-d lattice N
|
|
1814
|
+
sage: phi.codomain_fan().rays() # needs palp
|
|
1815
|
+
N( 1, 0, 0),
|
|
1816
|
+
N( 0, 1, 0),
|
|
1817
|
+
N( 0, 0, 1),
|
|
1818
|
+
N(-1, -1, -1)
|
|
1819
|
+
in 3-d lattice N
|
|
1820
|
+
|
|
1821
|
+
TESTS::
|
|
1822
|
+
|
|
1823
|
+
sage: phi_s.matrix() * phi_b.matrix() * phi_i.matrix() == m # needs palp
|
|
1824
|
+
True
|
|
1825
|
+
|
|
1826
|
+
sage: # needs palp
|
|
1827
|
+
sage: phi.domain_fan() is phi_s.domain_fan()
|
|
1828
|
+
True
|
|
1829
|
+
sage: phi_s.codomain_fan() is phi_b.domain_fan()
|
|
1830
|
+
True
|
|
1831
|
+
sage: phi_b.codomain_fan() is phi_i.domain_fan()
|
|
1832
|
+
True
|
|
1833
|
+
sage: phi_i.codomain_fan() is phi.codomain_fan()
|
|
1834
|
+
True
|
|
1835
|
+
|
|
1836
|
+
sage: trivialfan2 = Fan([], [], lattice=ToricLattice(2))
|
|
1837
|
+
sage: trivialfan3 = Fan([], [], lattice=ToricLattice(3))
|
|
1838
|
+
sage: f = FanMorphism(zero_matrix(2,3), trivialfan2, trivialfan3)
|
|
1839
|
+
sage: [phi.matrix().dimensions() for phi in f.factor()]
|
|
1840
|
+
[(0, 3), (0, 0), (2, 0)]
|
|
1841
|
+
"""
|
|
1842
|
+
L = self.image().saturation()
|
|
1843
|
+
d = L.dimension()
|
|
1844
|
+
m = self.matrix()
|
|
1845
|
+
m = matrix(ZZ, m.nrows(), d, (L.coordinates(c) for c in m.rows()))
|
|
1846
|
+
phi_s = FanMorphism(m, self.domain_fan(), L, check=False)
|
|
1847
|
+
Sigma_prime = self.codomain_fan()
|
|
1848
|
+
L_cone = Cone(sum(([g, -g] for g in L.gens()), []), lattice=L)
|
|
1849
|
+
Sigma_i = Fan(cones=(L_cone.intersection(cone) for cone in Sigma_prime),
|
|
1850
|
+
lattice=L, discard_faces=True, check=False)
|
|
1851
|
+
phi_b = FanMorphism(identity_matrix(d), phi_s.codomain_fan(), Sigma_i,
|
|
1852
|
+
check=False)
|
|
1853
|
+
phi_i = FanMorphism(L.basis_matrix(), Sigma_i, Sigma_prime, check=False)
|
|
1854
|
+
return (phi_i, phi_b, phi_s)
|
|
1855
|
+
|
|
1856
|
+
def relative_star_generators(self, domain_cone):
|
|
1857
|
+
"""
|
|
1858
|
+
Return the relative star generators of ``domain_cone``.
|
|
1859
|
+
|
|
1860
|
+
INPUT:
|
|
1861
|
+
|
|
1862
|
+
- ``domain_cone`` -- a cone of the :meth:`domain_fan` of ``self``
|
|
1863
|
+
|
|
1864
|
+
OUTPUT:
|
|
1865
|
+
|
|
1866
|
+
- :meth:`~RationalPolyhedralFan.star_generators` of ``domain_cone``
|
|
1867
|
+
viewed as a cone of :meth:`preimage_fan` of :meth:`image_cone` of
|
|
1868
|
+
``domain_cone``.
|
|
1869
|
+
|
|
1870
|
+
EXAMPLES::
|
|
1871
|
+
|
|
1872
|
+
sage: A2 = toric_varieties.A(2).fan()
|
|
1873
|
+
sage: Bl = A2.subdivide([(1,1)])
|
|
1874
|
+
sage: f = FanMorphism(identity_matrix(2), Bl, A2)
|
|
1875
|
+
sage: for c1 in Bl(1):
|
|
1876
|
+
....: print(f.relative_star_generators(c1))
|
|
1877
|
+
(1-d cone of Rational polyhedral fan in 2-d lattice N,)
|
|
1878
|
+
(1-d cone of Rational polyhedral fan in 2-d lattice N,)
|
|
1879
|
+
(2-d cone of Rational polyhedral fan in 2-d lattice N,
|
|
1880
|
+
2-d cone of Rational polyhedral fan in 2-d lattice N)
|
|
1881
|
+
"""
|
|
1882
|
+
base_cone = self.image_cone(domain_cone)
|
|
1883
|
+
preimg_fan = self.preimage_fan(base_cone)
|
|
1884
|
+
return preimg_fan.embed(domain_cone).star_generators()
|