Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/schemes/toric/fano_variety.py
8820 views
1
r"""
2
Fano toric varieties
3
4
This module provides support for (Crepant Partial Resolutions of) Fano toric
5
varieties, corresponding to crepant subdivisions of face fans of reflexive
6
:class:`lattice polytopes
7
<sage.geometry.lattice_polytope.LatticePolytopeClass>`.
8
The interface is provided via :func:`CPRFanoToricVariety`.
9
10
A careful exposition of different flavours of Fano varieties can be found in
11
the paper by Benjamin Nill [Nill2005]_. The main goal of this module is to
12
support work with **Gorenstein weak Fano toric varieties**. Such a variety
13
corresponds to a **coherent crepant refinement of the normal fan of a
14
reflexive polytope** `\Delta`, where crepant means that primitive generators
15
of the refining rays lie on the facets of the polar polytope `\Delta^\circ`
16
and coherent (a.k.a. regular or projective) means that there exists a strictly
17
upper convex piecewise linear function whose domains of linearity are
18
precisely the maximal cones of the subdivision. These varieties are important
19
for string theory in physics, as they serve as ambient spaces for mirror pairs
20
of Calabi-Yau manifolds via constructions due to Victor V. Batyrev
21
[Batyrev1994]_ and Lev A. Borisov [Borisov1993]_.
22
23
From the combinatorial point of view "crepant" requirement is much more simple
24
and natural to work with than "coherent." For this reason, the code in this
25
module will allow work with arbitrary crepant subdivisions without checking
26
whether they are coherent or not. We refer to corresponding toric varieties as
27
**CPR-Fano toric varieties**.
28
29
REFERENCES:
30
31
.. [Batyrev1994]
32
Victor V. Batyrev,
33
"Dual polyhedra and mirror symmetry for Calabi-Yau hypersurfaces in toric
34
varieties",
35
J. Algebraic Geom. 3 (1994), no. 3, 493-535.
36
arXiv:alg-geom/9310003v1
37
38
.. [Borisov1993]
39
Lev A. Borisov,
40
"Towards the mirror symmetry for Calabi-Yau complete intersections in
41
Gorenstein Fano toric varieties", 1993.
42
arXiv:alg-geom/9310001v1
43
44
.. [CD2007]
45
Adrian Clingher and Charles F. Doran,
46
"Modular invariants for lattice polarized K3 surfaces",
47
Michigan Math. J. 55 (2007), no. 2, 355-393.
48
arXiv:math/0602146v1 [math.AG]
49
50
.. [Nill2005]
51
Benjamin Nill,
52
"Gorenstein toric Fano varieties",
53
Manuscripta Math. 116 (2005), no. 2, 183-210.
54
arXiv:math/0405448v1 [math.AG]
55
56
AUTHORS:
57
58
- Andrey Novoseltsev (2010-05-18): initial version.
59
60
EXAMPLES:
61
62
Most of the functions available for Fano toric varieties are the same as
63
for general toric varieties, so here we will concentrate only on
64
Calabi-Yau subvarieties, which were the primary goal for creating this
65
module.
66
67
For our first example we realize the projective plane as a Fano toric
68
variety::
69
70
sage: simplex = lattice_polytope.projective_space(2)
71
sage: P2 = CPRFanoToricVariety(Delta_polar=simplex)
72
73
Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau
74
manifold::
75
76
sage: P2.anticanonical_hypersurface(
77
... monomial_points="all")
78
Closed subscheme of 2-d CPR-Fano toric variety
79
covered by 3 affine patches defined by:
80
a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2
81
+ a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2
82
+ a4*z1^2*z2 + a5*z0*z2^2
83
+ a3*z1*z2^2 + a2*z2^3
84
85
In many cases it is sufficient to work with the "simplified polynomial
86
moduli space" of anticanonical hypersurfaces::
87
88
sage: P2.anticanonical_hypersurface(
89
... monomial_points="simplified")
90
Closed subscheme of 2-d CPR-Fano toric variety
91
covered by 3 affine patches defined by:
92
a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3
93
94
The mirror family to these hypersurfaces lives inside the Fano toric
95
variety obtained using ``simplex`` as ``Delta`` instead of ``Delta_polar``::
96
97
sage: FTV = CPRFanoToricVariety(Delta=simplex,
98
... coordinate_points="all")
99
sage: FTV.anticanonical_hypersurface(
100
... monomial_points="simplified")
101
Closed subscheme of 2-d CPR-Fano toric variety
102
covered by 9 affine patches defined by:
103
a2*z2^3*z3^2*z4*z5^2*z8
104
+ a1*z1^3*z3*z4^2*z7^2*z9
105
+ a3*z0*z1*z2*z3*z4*z5*z7*z8*z9
106
+ a0*z0^3*z5*z7*z8^2*z9^2
107
108
Here we have taken the resolved version of the ambient space for the
109
mirror family, but in fact we don't have to resolve singularities
110
corresponding to the interior points of facets - they are singular
111
points which do not lie on a generic anticanonical hypersurface::
112
113
sage: FTV = CPRFanoToricVariety(Delta=simplex,
114
... coordinate_points="all but facets")
115
sage: FTV.anticanonical_hypersurface(
116
... monomial_points="simplified")
117
Closed subscheme of 2-d CPR-Fano toric variety
118
covered by 3 affine patches defined by:
119
a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3
120
121
This looks very similar to our second version of the anticanonical
122
hypersurface of the projective plane, as expected, since all
123
one-dimensional Calabi-Yau manifolds are elliptic curves!
124
125
Now let's take a look at a toric realization of `M`-polarized K3 surfaces
126
studied by Adrian Clingher and Charles F. Doran in [CD2007]_::
127
128
sage: p4318 = ReflexivePolytope(3, 4318) # long time
129
sage: FTV = CPRFanoToricVariety(Delta_polar=p4318) # long time
130
sage: FTV.anticanonical_hypersurface() # long time
131
Closed subscheme of 3-d CPR-Fano toric variety
132
covered by 4 affine patches defined by:
133
a3*z2^12 + a4*z2^6*z3^6 + a2*z3^12
134
+ a8*z0*z1*z2*z3 + a0*z1^3 + a1*z0^2
135
136
Below you will find detailed descriptions of available functions. Current
137
functionality of this module is very basic, but it is under active
138
development and hopefully will improve in future releases of Sage. If there
139
are some particular features that you would like to see implemented ASAP,
140
please consider reporting them to the Sage Development Team or even
141
implementing them on your own as a patch for inclusion!
142
"""
143
# The first example of the tutorial is taken from
144
# CPRFanoToricVariety_field.anticanonical_hypersurface
145
146
147
#*****************************************************************************
148
# Copyright (C) 2010 Andrey Novoseltsev <[email protected]>
149
# Copyright (C) 2010 William Stein <[email protected]>
150
#
151
# Distributed under the terms of the GNU General Public License (GPL)
152
#
153
# http://www.gnu.org/licenses/
154
#*****************************************************************************
155
156
import re
157
158
from sage.geometry.all import Cone, FaceFan, Fan, LatticePolytope
159
from sage.misc.all import latex, prod
160
from sage.rings.all import (PolynomialRing, QQ)
161
162
from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing
163
from sage.rings.polynomial.polynomial_ring import is_PolynomialRing
164
from sage.rings.fraction_field import is_FractionField
165
166
from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme_toric
167
from sage.schemes.toric.variety import (
168
ToricVariety_field,
169
normalize_names)
170
from sage.structure.all import get_coercion_model
171
from sage.categories.fields import Fields
172
_Fields = Fields()
173
174
175
# Default coefficient for anticanonical hypersurfaces
176
DEFAULT_COEFFICIENT = "a"
177
# Default coefficients for nef complete intersections
178
DEFAULT_COEFFICIENTS = tuple(chr(i) for i in range(ord("a"), ord("z") + 1))
179
180
181
def is_CPRFanoToricVariety(x):
182
r"""
183
Check if ``x`` is a CPR-Fano toric variety.
184
185
INPUT:
186
187
- ``x`` -- anything.
188
189
OUTPUT:
190
191
- ``True`` if ``x`` is a :class:`CPR-Fano toric variety
192
<CPRFanoToricVariety_field>` and ``False`` otherwise.
193
194
.. NOTE::
195
196
While projective spaces are Fano toric varieties mathematically, they
197
are not toric varieties in Sage due to efficiency considerations, so
198
this function will return ``False``.
199
200
EXAMPLES::
201
202
sage: from sage.schemes.toric.fano_variety import (
203
... is_CPRFanoToricVariety)
204
sage: is_CPRFanoToricVariety(1)
205
False
206
sage: FTV = CPRFanoToricVariety(lattice_polytope.octahedron(2))
207
sage: FTV
208
2-d CPR-Fano toric variety covered by 4 affine patches
209
sage: is_CPRFanoToricVariety(FTV)
210
True
211
sage: is_CPRFanoToricVariety(ProjectiveSpace(2))
212
False
213
"""
214
return isinstance(x, CPRFanoToricVariety_field)
215
216
217
def CPRFanoToricVariety(Delta=None,
218
Delta_polar=None,
219
coordinate_points=None,
220
charts=None,
221
coordinate_names=None,
222
names=None,
223
coordinate_name_indices=None,
224
make_simplicial=False,
225
base_ring=None,
226
base_field=None,
227
check=True):
228
r"""
229
Construct a CPR-Fano toric variety.
230
231
.. NOTE::
232
233
See documentation of the module
234
:mod:`~sage.schemes.toric.fano_variety` for the used
235
definitions and supported varieties.
236
237
Due to the large number of available options, it is recommended to always
238
use keyword parameters.
239
240
INPUT:
241
242
- ``Delta`` -- reflexive :class:`lattice polytope
243
<sage.geometry.lattice_polytope.LatticePolytopeClass>`. The fan of the
244
constructed CPR-Fano toric variety will be a crepant subdivision of the
245
*normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be
246
given, but not both at the same time, since one is completely determined
247
by another via :meth:`polar
248
<sage.geometry.lattice_polytope.LatticePolytopeClass.polar>` method;
249
250
- ``Delta_polar`` -- reflexive :class:`lattice polytope
251
<sage.geometry.lattice_polytope.LatticePolytopeClass>`. The fan of the
252
constructed CPR-Fano toric variety will be a crepant subdivision of the
253
*face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must
254
be given, but not both at the same time, since one is completely
255
determined by another via :meth:`polar
256
<sage.geometry.lattice_polytope.LatticePolytopeClass.polar>` method;
257
258
- ``coordinate_points`` -- list of integers or string. A list will be
259
interpreted as indices of (boundary) points of ``Delta_polar`` which
260
should be used as rays of the underlying fan. It must include all
261
vertices of ``Delta_polar`` and no repetitions are allowed. A string
262
must be one of the following descriptions of points of ``Delta_polar``:
263
264
* "vertices" (default),
265
* "all" (will not include the origin),
266
* "all but facets" (will not include points in the relative interior of
267
facets);
268
269
- ``charts`` -- list of lists of elements from ``coordinate_points``. Each
270
of these lists must define a generating cone of a fan subdividing the
271
normal fan of ``Delta``. Default ``charts`` correspond to the normal fan
272
of ``Delta`` without subdivision. The fan specified by ``charts`` will
273
be subdivided to include all of the requested ``coordinate_points``;
274
275
- ``coordinate_names`` -- names of variables for the coordinate ring, see
276
:func:`~sage.schemes.toric.variety.normalize_names`
277
for acceptable formats. If not given, indexed variable names will be
278
created automatically;
279
280
- ``names`` -- an alias of ``coordinate_names`` for internal
281
use. You may specify either ``names`` or ``coordinate_names``,
282
but not both;
283
284
- ``coordinate_name_indices`` -- list of integers, indices for indexed
285
variables. If not given, the index of each variable will coincide with
286
the index of the corresponding point of ``Delta_polar``;
287
288
- ``make_simplicial`` -- if ``True``, the underlying fan will be made
289
simplicial (default: ``False``);
290
291
- ``base_ring`` -- base field of the CPR-Fano toric variety
292
(default: `\QQ`);
293
294
- ``base_field`` -- alias for ``base_ring``. Takes precedence if
295
both are specified.
296
297
- ``check`` -- by default the input data will be checked for correctness
298
(e.g. that ``charts`` do form a subdivision of the normal fan of
299
``Delta``). If you know for sure that the input is valid, you may
300
significantly decrease construction time using ``check=False`` option.
301
302
OUTPUT:
303
304
- :class:`CPR-Fano toric variety <CPRFanoToricVariety_field>`.
305
306
EXAMPLES:
307
308
We start with the product of two projective lines::
309
310
sage: diamond = lattice_polytope.octahedron(2)
311
sage: diamond.vertices()
312
[ 1 0 -1 0]
313
[ 0 1 0 -1]
314
sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond)
315
sage: P1xP1
316
2-d CPR-Fano toric variety covered by 4 affine patches
317
sage: P1xP1.fan()
318
Rational polyhedral fan in 2-d lattice N
319
sage: P1xP1.fan().rays()
320
N( 1, 0),
321
N( 0, 1),
322
N(-1, 0),
323
N( 0, -1)
324
in 2-d lattice N
325
326
"Unfortunately," this variety is smooth to start with and we cannot
327
perform any subdivisions of the underlying fan without leaving the
328
category of CPR-Fano toric varieties. Our next example starts with a
329
square::
330
331
sage: square = diamond.polar()
332
sage: square.vertices()
333
[-1 1 -1 1]
334
[ 1 1 -1 -1]
335
sage: square.points()
336
[-1 1 -1 1 -1 0 0 0 1]
337
[ 1 1 -1 -1 0 -1 0 1 0]
338
339
We will construct several varieties associated to it::
340
341
sage: FTV = CPRFanoToricVariety(Delta_polar=square)
342
sage: FTV.fan().rays()
343
N(-1, 1),
344
N( 1, 1),
345
N(-1, -1),
346
N( 1, -1)
347
in 2-d lattice N
348
sage: FTV.gens()
349
(z0, z1, z2, z3)
350
351
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
352
... coordinate_points=[0,1,2,3,8])
353
sage: FTV.fan().rays()
354
N(-1, 1),
355
N( 1, 1),
356
N(-1, -1),
357
N( 1, -1),
358
N( 1, 0)
359
in 2-d lattice N
360
sage: FTV.gens()
361
(z0, z1, z2, z3, z8)
362
363
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
364
... coordinate_points=[8,0,2,1,3],
365
... coordinate_names="x+")
366
sage: FTV.fan().rays()
367
N( 1, 0),
368
N(-1, 1),
369
N(-1, -1),
370
N( 1, 1),
371
N( 1, -1)
372
in 2-d lattice N
373
sage: FTV.gens()
374
(x8, x0, x2, x1, x3)
375
376
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
377
... coordinate_points="all",
378
... coordinate_names="x y Z+")
379
sage: FTV.fan().rays()
380
N(-1, 1),
381
N( 1, 1),
382
N(-1, -1),
383
N( 1, -1),
384
N(-1, 0),
385
N( 0, -1),
386
N( 0, 1),
387
N( 1, 0)
388
in 2-d lattice N
389
sage: FTV.gens()
390
(x, y, Z2, Z3, Z4, Z5, Z7, Z8)
391
392
Note that ``Z6`` is "missing". This is due to the fact that the 6-th point
393
of ``square`` is the origin, and all automatically created names have the
394
same indices as corresponding points of
395
:meth:`~CPRFanoToricVariety_field.Delta_polar`. This is usually very
396
convenient, especially if you have to work with several partial
397
resolutions of the same Fano toric variety. However, you can change it, if
398
you want::
399
400
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
401
... coordinate_points="all",
402
... coordinate_names="x y Z+",
403
... coordinate_name_indices=range(8))
404
sage: FTV.gens()
405
(x, y, Z2, Z3, Z4, Z5, Z6, Z7)
406
407
Note that you have to provide indices for *all* variables, including those
408
that have "completely custom" names. Again, this is usually convenient,
409
because you can add or remove "custom" variables without disturbing too
410
much "automatic" ones::
411
412
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
413
... coordinate_points="all",
414
... coordinate_names="x Z+",
415
... coordinate_name_indices=range(8))
416
sage: FTV.gens()
417
(x, Z1, Z2, Z3, Z4, Z5, Z6, Z7)
418
419
If you prefer to always start from zero, you will have to shift indices
420
accordingly::
421
422
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
423
... coordinate_points="all",
424
... coordinate_names="x Z+",
425
... coordinate_name_indices=[0] + range(7))
426
sage: FTV.gens()
427
(x, Z0, Z1, Z2, Z3, Z4, Z5, Z6)
428
429
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
430
... coordinate_points="all",
431
... coordinate_names="x y Z+",
432
... coordinate_name_indices=[0]*2 + range(6))
433
sage: FTV.gens()
434
(x, y, Z0, Z1, Z2, Z3, Z4, Z5)
435
436
So you always can get any names you want, somewhat complicated default
437
behaviour was designed with the hope that in most cases you will have no
438
desire to provide different names.
439
440
Now we will use the possibility to specify initial charts::
441
442
sage: charts = [(0,1), (1,3), (3,2), (2,0)]
443
444
(these charts actually form exactly the face fan of our square) ::
445
446
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
447
... coordinate_points=[0,1,2,3,4],
448
... charts=charts)
449
sage: FTV.fan().rays()
450
N(-1, 1),
451
N( 1, 1),
452
N(-1, -1),
453
N( 1, -1),
454
N(-1, 0)
455
in 2-d lattice N
456
sage: [cone.ambient_ray_indices() for cone in FTV.fan()]
457
[(0, 1), (1, 3), (2, 3), (2, 4), (0, 4)]
458
459
If charts are wrong, it should be detected::
460
461
sage: bad_charts = charts + [(2,0)]
462
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
463
... coordinate_points=[0,1,2,3,4],
464
... charts=bad_charts)
465
Traceback (most recent call last):
466
...
467
ValueError: you have provided 5 cones, but only 4 of them are maximal!
468
Use discard_faces=True if you indeed need to construct a fan from
469
these cones.
470
471
These charts are technically correct, they just happened to list one of
472
them twice, but it is assumed that such a situation will not happen. It is
473
especially important when you try to speed up your code::
474
475
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
476
... coordinate_points=[0,1,2,3,4],
477
... charts=bad_charts,
478
... check=False)
479
sage: FTV.fan().rays()
480
N(-1, 1),
481
N( 1, 1),
482
N(-1, -1),
483
N( 1, -1),
484
N(-1, 0)
485
in 2-d lattice N
486
sage: [cone.ambient_ray_indices() for cone in FTV.fan()]
487
[(0, 1), (1, 3), (2, 3), (2, 4), (0, 4), (2, 4), (0, 4)]
488
489
The last line shows two of the generating cones twice. While "everything
490
still works" in the sense "it does not crash," any work with such a
491
variety may lead to mathematically wrong results, so use ``check=False``
492
carefully!
493
494
Here are some other possible mistakes::
495
496
sage: bad_charts = charts + [(0,3)]
497
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
498
... coordinate_points=[0,1,2,3,4],
499
... charts=bad_charts)
500
Traceback (most recent call last):
501
...
502
ValueError: (0, 3) does not form a chart of a
503
subdivision of the face fan of A polytope polar
504
to An octahedron: 2-dimensional, 4 vertices.!
505
506
sage: bad_charts = charts[:-1]
507
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
508
... coordinate_points=[0,1,2,3,4],
509
... charts=bad_charts)
510
Traceback (most recent call last):
511
...
512
ValueError: given charts do not form a complete fan!
513
514
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
515
... coordinate_points=[1,2,3,4])
516
Traceback (most recent call last):
517
...
518
ValueError: all 4 vertices of Delta_polar
519
must be used for coordinates!
520
Got: [1, 2, 3, 4]
521
522
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
523
... coordinate_points=[0,0,1,2,3,4])
524
Traceback (most recent call last):
525
...
526
ValueError: no repetitions are
527
allowed for coordinate points!
528
Got: [0, 0, 1, 2, 3, 4]
529
530
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
531
... coordinate_points=[0,1,2,3,6])
532
Traceback (most recent call last):
533
...
534
ValueError: the origin (point #6)
535
cannot be used for a coordinate!
536
Got: [0, 1, 2, 3, 6]
537
538
Here is a shorthand for defining the toric variety and homogeneous
539
coordinates in one go::
540
541
sage: P1xP1.<a,b,c,d> = CPRFanoToricVariety(Delta_polar=diamond)
542
sage: (a^2+b^2) * (c+d)
543
a^2*c + b^2*c + a^2*d + b^2*d
544
"""
545
if names is not None:
546
if coordinate_names is not None:
547
raise ValueError('You must not specify both coordinate_names and names!')
548
coordinate_names = names
549
# Check/normalize Delta_polar
550
if Delta is None and Delta_polar is None:
551
raise ValueError("either Delta or Delta_polar must be given!")
552
elif Delta is not None and Delta_polar is not None:
553
raise ValueError("Delta and Delta_polar cannot be given together!")
554
elif Delta_polar is None:
555
Delta_polar = Delta.polar()
556
elif not Delta_polar.is_reflexive():
557
raise ValueError("Delta_polar must be reflexive!")
558
# Check/normalize coordinate_points and construct fan rays
559
if coordinate_points is None:
560
coordinate_points = range(Delta_polar.nvertices())
561
if charts is not None:
562
for chart in charts:
563
for point in chart:
564
if point not in coordinate_points:
565
coordinate_points.append(point)
566
elif coordinate_points == "vertices":
567
coordinate_points = range(Delta_polar.nvertices())
568
elif coordinate_points == "all":
569
coordinate_points = range(Delta_polar.npoints())
570
coordinate_points.remove(Delta_polar.origin())
571
elif coordinate_points == "all but facets":
572
coordinate_points = Delta_polar.skeleton_points(Delta_polar.dim() - 2)
573
elif isinstance(coordinate_points, str):
574
raise ValueError("unrecognized description of the coordinate points!"
575
"\nGot: %s" % coordinate_points)
576
elif check:
577
cp_set = set(coordinate_points)
578
if len(cp_set) != len(coordinate_points):
579
raise ValueError(
580
"no repetitions are allowed for coordinate points!\nGot: %s"
581
% coordinate_points)
582
if not cp_set.issuperset(range(Delta_polar.nvertices())):
583
raise ValueError("all %d vertices of Delta_polar must be used "
584
"for coordinates!\nGot: %s"
585
% (Delta_polar.nvertices(), coordinate_points))
586
if Delta_polar.origin() in cp_set:
587
raise ValueError("the origin (point #%d) cannot be used for a "
588
"coordinate!\nGot: %s"
589
% (Delta_polar.origin(), coordinate_points))
590
point_to_ray = dict()
591
for n, point in enumerate(coordinate_points):
592
point_to_ray[point] = n
593
# This can be simplified if LatticePolytopeClass is adjusted.
594
rays = [Delta_polar.point(p) for p in coordinate_points]
595
for ray in rays:
596
ray.set_immutable()
597
# Check/normalize charts and construct the fan based on them.
598
if charts is None:
599
# Start with the face fan
600
fan = FaceFan(Delta_polar)
601
else:
602
# First of all, check that each chart is completely contained in a
603
# single facet of Delta_polar, otherwise they do not form a
604
# subdivision of the face fan of Delta_polar
605
if check:
606
facet_sets = [frozenset(facet.points())
607
for facet in Delta_polar.facets()]
608
for chart in charts:
609
is_bad = True
610
for fset in facet_sets:
611
if fset.issuperset(chart):
612
is_bad = False
613
break
614
if is_bad:
615
raise ValueError(
616
"%s does not form a chart of a subdivision of the "
617
"face fan of %s!" % (chart, Delta_polar))
618
# We will construct the initial fan from Cone objects: since charts
619
# may not use all of the necessary rays, alternative form is tedious
620
# With check=False it should not be long anyway.
621
cones = [Cone((rays[point_to_ray[point]] for point in chart),
622
check=check)
623
for chart in charts]
624
fan = Fan(cones, check=check)
625
if check and not fan.is_complete():
626
raise ValueError("given charts do not form a complete fan!")
627
# Subdivide this fan to use all required points
628
fan = fan.subdivide(new_rays=(ray for ray in rays
629
if ray not in fan.rays().set()),
630
make_simplicial=make_simplicial)
631
# Now create yet another fan making sure that the order of the rays is
632
# the same as requested (it is a bit difficult to get it from the start)
633
trans = dict()
634
for n, ray in enumerate(fan.rays()):
635
trans[n] = rays.index(ray)
636
cones = tuple(tuple(sorted(trans[r] for r in cone.ambient_ray_indices()))
637
for cone in fan)
638
fan = Fan(cones, rays, check=False)
639
# Check/normalize base_field
640
if base_field is not None:
641
base_ring = base_field
642
if base_ring is None:
643
base_ring = QQ
644
elif base_ring not in _Fields:
645
raise TypeError("need a field to construct a Fano toric variety!"
646
"\n Got %s" % base_ring)
647
fan._is_complete = True # At this point it must be for sure
648
return CPRFanoToricVariety_field(
649
Delta_polar, fan, coordinate_points,
650
point_to_ray, coordinate_names, coordinate_name_indices, base_ring)
651
652
653
class CPRFanoToricVariety_field(ToricVariety_field):
654
r"""
655
Construct a CPR-Fano toric variety associated to a reflexive polytope.
656
657
.. WARNING::
658
659
This class does not perform any checks of correctness of input and it
660
does assume that the internal structure of the given parameters is
661
coordinated in a certain way. Use
662
:func:`CPRFanoToricVariety` to construct CPR-Fano toric varieties.
663
664
.. NOTE::
665
666
See documentation of the module
667
:mod:`~sage.schemes.toric.fano_variety` for the used
668
definitions and supported varieties.
669
670
INPUT:
671
672
- ``Delta_polar`` -- reflexive polytope;
673
674
- ``fan`` -- rational polyhedral fan subdividing the face fan of
675
``Delta_polar``;
676
677
- ``coordinate_points`` -- list of indices of points of ``Delta_polar``
678
used for rays of ``fan``;
679
680
- ``point_to_ray`` -- dictionary mapping the index of a coordinate point
681
to the index of the corresponding ray;
682
683
- ``coordinate_names`` -- names of the variables of the coordinate ring in
684
the format accepted by
685
:func:`~sage.schemes.toric.variety.normalize_names`;
686
687
- ``coordinate_name_indices`` -- indices for indexed variables,
688
if ``None``, will be equal to ``coordinate_points``;
689
690
- ``base_field`` -- base field of the CPR-Fano toric variety.
691
692
OUTPUT:
693
694
- :class:`CPR-Fano toric variety <CPRFanoToricVariety_field>`.
695
696
TESTS::
697
698
sage: P1xP1 = CPRFanoToricVariety(
699
... Delta_polar=lattice_polytope.octahedron(2))
700
sage: P1xP1
701
2-d CPR-Fano toric variety covered by 4 affine patches
702
"""
703
704
def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray,
705
coordinate_names, coordinate_name_indices, base_field):
706
r"""
707
See :class:`CPRFanoToricVariety_field` for documentation.
708
709
Use ``CPRFanoToricVariety`` to construct CPR-Fano toric varieties.
710
711
TESTS::
712
713
sage: P1xP1 = CPRFanoToricVariety(
714
... Delta_polar=lattice_polytope.octahedron(2))
715
sage: P1xP1
716
2-d CPR-Fano toric variety covered by 4 affine patches
717
"""
718
self._Delta_polar = Delta_polar
719
self._coordinate_points = tuple(coordinate_points)
720
self._point_to_ray = point_to_ray
721
# Check/normalize coordinate_indices
722
if coordinate_name_indices is None:
723
coordinate_name_indices = coordinate_points
724
super(CPRFanoToricVariety_field, self).__init__(fan, coordinate_names,
725
coordinate_name_indices, base_field)
726
727
def _latex_(self):
728
r"""
729
Return a LaTeX representation of ``self``.
730
731
OUTPUT:
732
733
- string.
734
735
TESTS::
736
737
sage: P1xP1 = CPRFanoToricVariety(
738
... Delta_polar=lattice_polytope.octahedron(2))
739
sage: P1xP1._latex_()
740
'\\mathbb{P}_{\\Delta^{2}}'
741
"""
742
return r"\mathbb{P}_{%s}" % latex(self.Delta())
743
744
def _repr_(self):
745
r"""
746
Return a string representation of ``self``.
747
748
OUTPUT:
749
750
- string.
751
752
TESTS::
753
754
sage: P1xP1 = CPRFanoToricVariety(
755
... Delta_polar=lattice_polytope.octahedron(2))
756
sage: P1xP1._repr_()
757
'2-d CPR-Fano toric variety covered by 4 affine patches'
758
"""
759
return ("%d-d CPR-Fano toric variety covered by %d affine patches"
760
% (self.dimension_relative(), self.fan().ngenerating_cones()))
761
762
def anticanonical_hypersurface(self, **kwds):
763
r"""
764
Return an anticanonical hypersurface of ``self``.
765
766
.. NOTE::
767
768
The returned hypersurface may be actually a subscheme of
769
**another** CPR-Fano toric variety: if the base field of ``self``
770
does not include all of the required names for generic monomial
771
coefficients, it will be automatically extended.
772
773
Below `\Delta` is the reflexive polytope corresponding to ``self``,
774
i.e. the fan of ``self`` is a refinement of the normal fan of
775
`\Delta`. This function accepts only keyword parameters.
776
777
INPUT:
778
779
- ``monomial points`` -- a list of integers or a string. A list will be
780
interpreted as indices of points of `\Delta` which should be used
781
for monomials of this hypersurface. A string must be one of the
782
following descriptions of points of `\Delta`:
783
784
* "vertices",
785
* "vertices+origin",
786
* "all",
787
* "simplified" (default) -- all points of `\Delta` except for
788
the interior points of facets, this choice corresponds to working
789
with the "simplified polynomial moduli space" of anticanonical
790
hypersurfaces;
791
792
- ``coefficient_names`` -- names for the monomial coefficients, see
793
:func:`~sage.schemes.toric.variety.normalize_names`
794
for acceptable formats. If not given, indexed coefficient names will
795
be created automatically;
796
797
- ``coefficient_name_indices`` -- a list of integers, indices for
798
indexed coefficients. If not given, the index of each coefficient
799
will coincide with the index of the corresponding point of `\Delta`;
800
801
- ``coefficients`` -- as an alternative to specifying coefficient
802
names and/or indices, you can give the coefficients themselves as
803
arbitrary expressions and/or strings. Using strings allows you to
804
easily add "parameters": the base field of ``self`` will be extended
805
to include all necessary names.
806
807
OUTPUT:
808
809
- an :class:`anticanonical hypersurface <AnticanonicalHypersurface>` of
810
``self`` (with the extended base field, if necessary).
811
812
EXAMPLES:
813
814
We realize the projective plane as a Fano toric variety::
815
816
sage: simplex = lattice_polytope.projective_space(2)
817
sage: P2 = CPRFanoToricVariety(Delta_polar=simplex)
818
819
Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau
820
manifold::
821
822
sage: P2.anticanonical_hypersurface(
823
... monomial_points="all")
824
Closed subscheme of 2-d CPR-Fano toric variety
825
covered by 3 affine patches defined by:
826
a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2
827
+ a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2
828
+ a4*z1^2*z2 + a5*z0*z2^2
829
+ a3*z1*z2^2 + a2*z2^3
830
831
In many cases it is sufficient to work with the "simplified polynomial
832
moduli space" of anticanonical hypersurfaces::
833
834
sage: P2.anticanonical_hypersurface(
835
... monomial_points="simplified")
836
Closed subscheme of 2-d CPR-Fano toric variety
837
covered by 3 affine patches defined by:
838
a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3
839
840
The mirror family to these hypersurfaces lives inside the Fano toric
841
variety obtained using ``simplex`` as ``Delta`` instead of
842
``Delta_polar``::
843
844
sage: FTV = CPRFanoToricVariety(Delta=simplex,
845
... coordinate_points="all")
846
sage: FTV.anticanonical_hypersurface(
847
... monomial_points="simplified")
848
Closed subscheme of 2-d CPR-Fano toric variety
849
covered by 9 affine patches defined by:
850
a2*z2^3*z3^2*z4*z5^2*z8
851
+ a1*z1^3*z3*z4^2*z7^2*z9
852
+ a3*z0*z1*z2*z3*z4*z5*z7*z8*z9
853
+ a0*z0^3*z5*z7*z8^2*z9^2
854
855
Here we have taken the resolved version of the ambient space for the
856
mirror family, but in fact we don't have to resolve singularities
857
corresponding to the interior points of facets - they are singular
858
points which do not lie on a generic anticanonical hypersurface::
859
860
sage: FTV = CPRFanoToricVariety(Delta=simplex,
861
... coordinate_points="all but facets")
862
sage: FTV.anticanonical_hypersurface(
863
... monomial_points="simplified")
864
Closed subscheme of 2-d CPR-Fano toric variety
865
covered by 3 affine patches defined by:
866
a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3
867
868
This looks very similar to our second anticanonical
869
hypersurface of the projective plane, as expected, since all
870
one-dimensional Calabi-Yau manifolds are elliptic curves!
871
872
All anticanonical hypersurfaces constructed above were generic with
873
automatically generated coefficients. If you want, you can specify your
874
own names ::
875
876
sage: FTV.anticanonical_hypersurface(
877
... coefficient_names="a b c d")
878
Closed subscheme of 2-d CPR-Fano toric variety
879
covered by 3 affine patches defined by:
880
a*z0^3 + b*z1^3 + d*z0*z1*z2 + c*z2^3
881
882
or give concrete coefficients ::
883
884
sage: FTV.anticanonical_hypersurface(
885
... coefficients=[1, 2, 3, 4])
886
Closed subscheme of 2-d CPR-Fano toric variety
887
covered by 3 affine patches defined by:
888
z0^3 + 2*z1^3 + 4*z0*z1*z2 + 3*z2^3
889
890
or even mix numerical coefficients with some expressions ::
891
892
sage: H = FTV.anticanonical_hypersurface(
893
... coefficients=[0, "t", "1/t", "psi/(psi^2 + phi)"])
894
sage: H
895
Closed subscheme of 2-d CPR-Fano toric variety
896
covered by 3 affine patches defined by:
897
t*z1^3 + (psi/(psi^2 + phi))*z0*z1*z2 + 1/t*z2^3
898
sage: R = H.ambient_space().base_ring()
899
sage: R
900
Fraction Field of
901
Multivariate Polynomial Ring in phi, psi, t
902
over Rational Field
903
"""
904
# The example above is also copied to the tutorial section in the
905
# main documentation of the module.
906
return AnticanonicalHypersurface(self, **kwds)
907
908
def change_ring(self, F):
909
r"""
910
Return a CPR-Fano toric variety over field ``F``, otherwise the same
911
as ``self``.
912
913
INPUT:
914
915
- ``F`` -- field.
916
917
OUTPUT:
918
919
- :class:`CPR-Fano toric variety <CPRFanoToricVariety_field>` over
920
``F``.
921
922
.. NOTE::
923
924
There is no need to have any relation between ``F`` and the base
925
field of ``self``. If you do want to have such a relation, use
926
:meth:`base_extend` instead.
927
928
EXAMPLES::
929
930
sage: P1xP1 = CPRFanoToricVariety(
931
... Delta_polar=lattice_polytope.octahedron(2))
932
sage: P1xP1.base_ring()
933
Rational Field
934
sage: P1xP1_RR = P1xP1.change_ring(RR)
935
sage: P1xP1_RR.base_ring()
936
Real Field with 53 bits of precision
937
sage: P1xP1_QQ = P1xP1_RR.change_ring(QQ)
938
sage: P1xP1_QQ.base_ring()
939
Rational Field
940
sage: P1xP1_RR.base_extend(QQ)
941
Traceback (most recent call last):
942
...
943
ValueError: no natural map from the base ring
944
(=Real Field with 53 bits of precision)
945
to R (=Rational Field)!
946
"""
947
if self.base_ring() == F:
948
return self
949
else:
950
return CPRFanoToricVariety_field(self._Delta_polar, self._fan,
951
self._coordinate_points, self._point_to_ray,
952
self.variable_names(), None, F)
953
# coordinate_name_indices do not matter, we give explicit
954
# names for all variables
955
956
def coordinate_point_to_coordinate(self, point):
957
r"""
958
Return the variable of the coordinate ring corresponding to ``point``.
959
960
INPUT:
961
962
- ``point`` -- integer from the list of :meth:`coordinate_points`.
963
964
OUTPUT:
965
966
- the corresponding generator of the coordinate ring of ``self``.
967
968
EXAMPLES::
969
970
sage: diamond = lattice_polytope.octahedron(2)
971
sage: FTV = CPRFanoToricVariety(diamond,
972
... coordinate_points=[0,1,2,3,8])
973
sage: FTV.coordinate_points()
974
(0, 1, 2, 3, 8)
975
sage: FTV.gens()
976
(z0, z1, z2, z3, z8)
977
sage: FTV.coordinate_point_to_coordinate(8)
978
z8
979
"""
980
return self.gen(self._point_to_ray[point])
981
982
def coordinate_points(self):
983
r"""
984
Return indices of points of :meth:`Delta_polar` used for coordinates.
985
986
OUTPUT:
987
988
- :class:`tuple` of integers.
989
990
EXAMPLES::
991
992
sage: diamond = lattice_polytope.octahedron(2)
993
sage: square = diamond.polar()
994
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
995
... coordinate_points=[0,1,2,3,8])
996
sage: FTV.coordinate_points()
997
(0, 1, 2, 3, 8)
998
sage: FTV.gens()
999
(z0, z1, z2, z3, z8)
1000
1001
sage: FTV = CPRFanoToricVariety(Delta_polar=square,
1002
... coordinate_points="all")
1003
sage: FTV.coordinate_points()
1004
(0, 1, 2, 3, 4, 5, 7, 8)
1005
sage: FTV.gens()
1006
(z0, z1, z2, z3, z4, z5, z7, z8)
1007
1008
Note that one point is missing, namely ::
1009
1010
sage: square.origin()
1011
6
1012
"""
1013
return self._coordinate_points
1014
1015
def Delta(self):
1016
r"""
1017
Return the reflexive polytope associated to ``self``.
1018
1019
OUTPUT:
1020
1021
- reflexive :class:`lattice polytope
1022
<sage.geometry.lattice_polytope.LatticePolytopeClass>`. The
1023
underlying fan of ``self`` is a coherent subdivision of the
1024
*normal fan* of this polytope.
1025
1026
EXAMPLES::
1027
1028
sage: diamond = lattice_polytope.octahedron(2)
1029
sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond)
1030
sage: P1xP1.Delta()
1031
A polytope polar to An octahedron: 2-dimensional, 4 vertices.
1032
sage: P1xP1.Delta() is diamond.polar()
1033
True
1034
"""
1035
return self._Delta_polar.polar()
1036
1037
def Delta_polar(self):
1038
r"""
1039
Return polar of :meth:`Delta`.
1040
1041
OUTPUT:
1042
1043
- reflexive :class:`lattice polytope
1044
<sage.geometry.lattice_polytope.LatticePolytopeClass>`. The
1045
underlying fan of ``self`` is a coherent subdivision of the
1046
*face fan* of this polytope.
1047
1048
EXAMPLES::
1049
1050
sage: diamond = lattice_polytope.octahedron(2)
1051
sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond)
1052
sage: P1xP1.Delta_polar()
1053
An octahedron: 2-dimensional, 4 vertices.
1054
sage: P1xP1.Delta_polar() is diamond
1055
True
1056
sage: P1xP1.Delta_polar() is P1xP1.Delta().polar()
1057
True
1058
"""
1059
return self._Delta_polar
1060
1061
def nef_complete_intersection(self, nef_partition, **kwds):
1062
r"""
1063
Return a nef complete intersection in ``self``.
1064
1065
.. NOTE::
1066
1067
The returned complete intersection may be actually a subscheme of
1068
**another** CPR-Fano toric variety: if the base field of ``self``
1069
does not include all of the required names for monomial
1070
coefficients, it will be automatically extended.
1071
1072
Below `\Delta` is the reflexive polytope corresponding to ``self``,
1073
i.e. the fan of ``self`` is a refinement of the normal fan of
1074
`\Delta`. Other polytopes are described in the documentation of
1075
:class:`nef-partitions <sage.geometry.lattice_polytope.NefPartition>`
1076
of :class:`reflexive polytopes
1077
<sage.geometry.lattice_polytope.LatticePolytopeClass>`.
1078
1079
Except for the first argument, ``nef_partition``, this method accepts
1080
only keyword parameters.
1081
1082
INPUT:
1083
1084
- ``nef_partition`` -- a `k`-part :class:`nef-partition
1085
<sage.geometry.lattice_polytope.NefPartition>` of `\Delta^\circ`, all
1086
other parameters (if given) must be lists of length `k`;
1087
1088
- ``monomial_points`` -- the `i`-th element of this list is either a
1089
list of integers or a string. A list will be interpreted as indices
1090
of points of `\Delta_i` which should be used for monomials of the
1091
`i`-th polynomial of this complete intersection. A string must be one
1092
of the following descriptions of points of `\Delta_i`:
1093
1094
* "vertices",
1095
* "vertices+origin",
1096
* "all" (default),
1097
1098
when using this description, it is also OK to pass a single string as
1099
``monomial_points`` instead of repeating it `k` times;
1100
1101
- ``coefficient_names`` -- the `i`-th element of this list specifies
1102
names for the monomial coefficients of the `i`-th polynomial, see
1103
:func:`~sage.schemes.toric.variety.normalize_names`
1104
for acceptable formats. If not given, indexed coefficient names will
1105
be created automatically;
1106
1107
- ``coefficient_name_indices`` -- the `i`-th element of this list
1108
specifies indices for indexed coefficients of the `i`-th polynomial.
1109
If not given, the index of each coefficient will coincide with the
1110
index of the corresponding point of `\Delta_i`;
1111
1112
- ``coefficients`` -- as an alternative to specifying coefficient
1113
names and/or indices, you can give the coefficients themselves as
1114
arbitrary expressions and/or strings. Using strings allows you to
1115
easily add "parameters": the base field of ``self`` will be extended
1116
to include all necessary names.
1117
1118
OUTPUT:
1119
1120
- a :class:`nef complete intersection <NefCompleteIntersection>` of
1121
``self`` (with the extended base field, if necessary).
1122
1123
EXAMPLES:
1124
1125
We construct several complete intersections associated to the same
1126
nef-partition of the 3-dimensional reflexive polytope #2254::
1127
1128
sage: p = ReflexivePolytope(3, 2254) # long time (7s on sage.math, 2011)
1129
sage: np = p.nef_partitions()[1] # long time
1130
sage: np # long time
1131
Nef-partition {2, 3, 4, 7, 8} U {0, 1, 5, 6}
1132
sage: X = CPRFanoToricVariety(Delta_polar=p) # long time
1133
sage: X.nef_complete_intersection(np) # long time
1134
Closed subscheme of 3-d CPR-Fano toric variety
1135
covered by 10 affine patches defined by:
1136
a2*z1*z4^2*z5^2*z7^3 + a1*z2*z4*z5*z6*z7^2*z8^2
1137
+ a3*z2*z3*z4*z7*z8 + a0*z0*z2,
1138
b2*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4
1139
+ b5*z1*z3*z4*z5*z6*z7*z8 + b3*z2*z3*z6^2*z8^3
1140
+ b1*z1*z3^2*z4 + b4*z0*z1*z5*z6
1141
1142
Now we include only monomials associated to vertices of `\Delta_i`::
1143
1144
sage: X.nef_complete_intersection(np, monomial_points="vertices") # long time
1145
Closed subscheme of 3-d CPR-Fano toric variety
1146
covered by 10 affine patches defined by:
1147
a2*z1*z4^2*z5^2*z7^3 + a1*z2*z4*z5*z6*z7^2*z8^2
1148
+ a3*z2*z3*z4*z7*z8 + a0*z0*z2,
1149
b2*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4
1150
+ b3*z2*z3*z6^2*z8^3 + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6
1151
1152
(effectively, we set ``b5=0``). Next we provide coefficients explicitly
1153
instead of using default generic names::
1154
1155
sage: X.nef_complete_intersection(np, # long time
1156
... monomial_points="vertices",
1157
... coefficients=[("a", "a^2", "a/e", "c_i"), range(1,6)])
1158
Closed subscheme of 3-d CPR-Fano toric variety
1159
covered by 10 affine patches defined by:
1160
a/e*z1*z4^2*z5^2*z7^3 + a^2*z2*z4*z5*z6*z7^2*z8^2
1161
+ c_i*z2*z3*z4*z7*z8 + a*z0*z2,
1162
3*z1*z4*z5^2*z6^2*z7^2*z8^2 + z2*z5*z6^3*z7*z8^4
1163
+ 4*z2*z3*z6^2*z8^3 + 2*z1*z3^2*z4 + 5*z0*z1*z5*z6
1164
1165
Finally, we take a look at the generic representative of these complete
1166
intersections in a completely resolved ambient toric variety::
1167
1168
sage: X = CPRFanoToricVariety(Delta_polar=p, # long time
1169
... coordinate_points="all")
1170
sage: X.nef_complete_intersection(np) # long time
1171
Closed subscheme of 3-d CPR-Fano toric variety
1172
covered by 22 affine patches defined by:
1173
a1*z2*z4*z5*z6*z7^2*z8^2*z9^2*z10^2*z11*z12*z13
1174
+ a2*z1*z4^2*z5^2*z7^3*z9*z10^2*z12*z13
1175
+ a3*z2*z3*z4*z7*z8*z9*z10*z11*z12 + a0*z0*z2,
1176
b0*z2*z5*z6^3*z7*z8^4*z9^3*z10^2*z11^2*z12*z13^2
1177
+ b2*z1*z4*z5^2*z6^2*z7^2*z8^2*z9^2*z10^2*z11*z12*z13^2
1178
+ b3*z2*z3*z6^2*z8^3*z9^2*z10*z11^2*z12*z13
1179
+ b5*z1*z3*z4*z5*z6*z7*z8*z9*z10*z11*z12*z13
1180
+ b1*z1*z3^2*z4*z11*z12 + b4*z0*z1*z5*z6*z13
1181
"""
1182
return NefCompleteIntersection(self, nef_partition, **kwds)
1183
1184
def cartesian_product(self, other,
1185
coordinate_names=None, coordinate_indices=None):
1186
r"""
1187
Return the Cartesian product of ``self`` with ``other``.
1188
1189
INPUT:
1190
1191
- ``other`` -- a (possibly
1192
:class:`CPR-Fano <CPRFanoToricVariety_field>`) :class:`toric variety
1193
<sage.schemes.toric.variety.ToricVariety_field>`;
1194
1195
- ``coordinate_names`` -- names of variables for the coordinate ring,
1196
see :func:`normalize_names` for acceptable formats. If not given,
1197
indexed variable names will be created automatically;
1198
1199
- ``coordinate_indices`` -- list of integers, indices for indexed
1200
variables. If not given, the index of each variable will coincide
1201
with the index of the corresponding ray of the fan.
1202
1203
OUTPUT:
1204
1205
- a :class:`toric variety
1206
<sage.schemes.toric.variety.ToricVariety_field>`, which is
1207
:class:`CPR-Fano <CPRFanoToricVariety_field>` if ``other`` was.
1208
1209
EXAMPLES::
1210
1211
sage: P1 = toric_varieties.P1()
1212
sage: P2 = toric_varieties.P2()
1213
sage: P1xP2 = P1.cartesian_product(P2); P1xP2
1214
3-d CPR-Fano toric variety covered by 6 affine patches
1215
sage: P1xP2.fan().rays()
1216
N+N( 1, 0, 0),
1217
N+N(-1, 0, 0),
1218
N+N( 0, 1, 0),
1219
N+N( 0, 0, 1),
1220
N+N( 0, -1, -1)
1221
in 3-d lattice N+N
1222
sage: P1xP2.Delta_polar()
1223
A lattice polytope: 3-dimensional, 5 vertices.
1224
"""
1225
if is_CPRFanoToricVariety(other):
1226
fan = self.fan().cartesian_product(other.fan())
1227
Delta_polar = LatticePolytope(fan.rays())
1228
1229
points = Delta_polar.points().columns()
1230
point_to_ray = dict()
1231
coordinate_points = []
1232
for ray_index, ray in enumerate(fan.rays()):
1233
point = points.index(ray)
1234
coordinate_points.append(point)
1235
point_to_ray[point] = ray_index
1236
1237
return CPRFanoToricVariety_field(Delta_polar, fan,
1238
coordinate_points, point_to_ray,
1239
coordinate_names, coordinate_indices,
1240
self.base_ring())
1241
return super(CPRFanoToricVariety_field, self).cartesian_product(other)
1242
1243
def resolve(self, **kwds):
1244
r"""
1245
Construct a toric variety whose fan subdivides the fan of ``self``.
1246
1247
This function accepts only keyword arguments, none of which are
1248
mandatory.
1249
1250
INPUT:
1251
1252
- ``new_points`` -- list of integers, indices of boundary points of
1253
:meth:`Delta_polar`, which should be added as rays to the
1254
subdividing fan;
1255
1256
- all other arguments will be passed to
1257
:meth:`~sage.schemes.toric.variety.ToricVariety_field.resolve`
1258
method of (general) toric varieties, see its documentation for
1259
details.
1260
1261
OUTPUT:
1262
1263
- :class:`CPR-Fano toric variety <CPRFanoToricVariety_field>` if there
1264
was no ``new_rays`` argument and :class:`toric variety
1265
<sage.schemes.toric.variety.ToricVariety_field>` otherwise.
1266
1267
EXAMPLES::
1268
1269
sage: diamond = lattice_polytope.octahedron(2)
1270
sage: FTV = CPRFanoToricVariety(Delta=diamond)
1271
sage: FTV.coordinate_points()
1272
(0, 1, 2, 3)
1273
sage: FTV.gens()
1274
(z0, z1, z2, z3)
1275
sage: FTV_res = FTV.resolve(new_points=[6,8])
1276
Traceback (most recent call last):
1277
...
1278
ValueError: the origin (point #6)
1279
cannot be used for subdivision!
1280
sage: FTV_res = FTV.resolve(new_points=[8,5])
1281
sage: FTV_res
1282
2-d CPR-Fano toric variety covered by 6 affine patches
1283
sage: FTV_res.coordinate_points()
1284
(0, 1, 2, 3, 8, 5)
1285
sage: FTV_res.gens()
1286
(z0, z1, z2, z3, z8, z5)
1287
1288
sage: TV_res = FTV.resolve(new_rays=[(1,2)])
1289
sage: TV_res
1290
2-d toric variety covered by 5 affine patches
1291
sage: TV_res.gens()
1292
(z0, z1, z2, z3, z4)
1293
"""
1294
# Reasons to override the base class:
1295
# - allow using polytope point indices for subdivision
1296
# - handle automatic name creation in a different fashion
1297
# - return CPR-Fano toric variety if the above feature was used and
1298
# just toric variety if subdivision involves rays
1299
if "new_rays" in kwds:
1300
if "new_points" in kwds:
1301
raise ValueError("you cannot give new_points and new_rays at "
1302
"the same time!")
1303
return super(CPRFanoToricVariety_field, self).resolve(**kwds)
1304
# Now we need to construct another Fano variety
1305
new_points = kwds.pop("new_points", ())
1306
coordinate_points = self.coordinate_points()
1307
new_points = tuple(point for point in new_points
1308
if point not in coordinate_points)
1309
Delta_polar = self._Delta_polar
1310
if Delta_polar.origin() in new_points:
1311
raise ValueError("the origin (point #%d) cannot be used for "
1312
"subdivision!" % Delta_polar.origin())
1313
if new_points:
1314
coordinate_points = coordinate_points + new_points
1315
point_to_ray = dict()
1316
for n, point in enumerate(coordinate_points):
1317
point_to_ray[point] = n
1318
else:
1319
point_to_ray = self._point_to_ray
1320
new_rays = [Delta_polar.point(point) for point in new_points]
1321
coordinate_name_indices = kwds.pop("coordinate_name_indices",
1322
coordinate_points)
1323
fan = self.fan()
1324
if "coordinate_names" in kwds:
1325
coordinate_names = kwds.pop("coordinate_names")
1326
else:
1327
coordinate_names = list(self.variable_names())
1328
coordinate_names.extend(normalize_names(ngens=len(new_rays),
1329
indices=coordinate_name_indices[fan.nrays():],
1330
prefix=self._coordinate_prefix))
1331
coordinate_names.append(self._coordinate_prefix + "+")
1332
rfan = fan.subdivide(new_rays=new_rays, **kwds)
1333
resolution = CPRFanoToricVariety_field(Delta_polar, rfan,
1334
coordinate_points, point_to_ray, coordinate_names,
1335
coordinate_name_indices, self.base_ring())
1336
R = self.coordinate_ring()
1337
R_res = resolution.coordinate_ring()
1338
resolution_map = resolution.hom(R.hom(R_res.gens()[:R.ngens()]), self)
1339
resolution._resolution_map = resolution_map
1340
return resolution
1341
1342
1343
class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric):
1344
r"""
1345
Construct an anticanonical hypersurface of a CPR-Fano toric variety.
1346
1347
INPUT:
1348
1349
- ``P_Delta`` -- :class:`CPR-Fano toric variety
1350
<CPRFanoToricVariety_field>` associated to a reflexive polytope
1351
`\Delta`;
1352
1353
- see :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for
1354
documentation on all other acceptable parameters.
1355
1356
OUTPUT:
1357
1358
- :class:`anticanonical hypersurface <AnticanonicalHypersurface>` of
1359
``P_Delta`` (with the extended base field, if necessary).
1360
1361
EXAMPLES::
1362
1363
sage: P1xP1 = CPRFanoToricVariety(
1364
... Delta_polar=lattice_polytope.octahedron(2))
1365
sage: import sage.schemes.toric.fano_variety as ftv
1366
sage: ftv.AnticanonicalHypersurface(P1xP1)
1367
Closed subscheme of 2-d CPR-Fano toric variety
1368
covered by 4 affine patches defined by:
1369
a1*z0^2*z1^2 + a0*z1^2*z2^2 + a6*z0*z1*z2*z3
1370
+ a3*z0^2*z3^2 + a2*z2^2*z3^2
1371
1372
See :meth:`~CPRFanoToricVariety_field.anticanonical_hypersurface()` for a
1373
more elaborate example.
1374
"""
1375
def __init__(self, P_Delta, monomial_points=None, coefficient_names=None,
1376
coefficient_name_indices=None, coefficients=None):
1377
r"""
1378
See :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for
1379
documentation.
1380
1381
TESTS::
1382
1383
sage: P1xP1 = CPRFanoToricVariety(
1384
... Delta_polar=lattice_polytope.octahedron(2))
1385
sage: import sage.schemes.toric.fano_variety as ftv
1386
sage: ftv.AnticanonicalHypersurface(P1xP1)
1387
Closed subscheme of 2-d CPR-Fano toric variety
1388
covered by 4 affine patches defined by:
1389
a1*z0^2*z1^2 + a0*z1^2*z2^2 + a6*z0*z1*z2*z3
1390
+ a3*z0^2*z3^2 + a2*z2^2*z3^2
1391
1392
Check that finite fields are handled correctly :trac:`14899`::
1393
1394
sage: F = GF(5^2, "a")
1395
sage: X = P1xP1.change_ring(F)
1396
sage: X.anticanonical_hypersurface(monomial_points="all",
1397
... coefficients=[1]*X.Delta().npoints())
1398
Closed subscheme of 2-d CPR-Fano toric variety
1399
covered by 4 affine patches defined by:
1400
z0^2*z1^2 + z0*z1^2*z2 + z1^2*z2^2 + z0^2*z1*z3 + z0*z1*z2*z3
1401
+ z1*z2^2*z3 + z0^2*z3^2 + z0*z2*z3^2 + z2^2*z3^2
1402
"""
1403
if not is_CPRFanoToricVariety(P_Delta):
1404
raise TypeError("anticanonical hypersurfaces can only be "
1405
"constructed for CPR-Fano toric varieties!"
1406
"\nGot: %s" % P_Delta)
1407
Delta = P_Delta.Delta()
1408
Delta_polar = Delta.polar()
1409
# Monomial points normalization
1410
if monomial_points == "vertices":
1411
monomial_points = range(Delta.nvertices())
1412
elif monomial_points == "all":
1413
monomial_points = range(Delta.npoints())
1414
elif monomial_points == "vertices+origin":
1415
monomial_points = range(Delta.nvertices())
1416
monomial_points.append(Delta.origin())
1417
elif monomial_points == "simplified" or monomial_points is None:
1418
monomial_points = Delta.skeleton_points(Delta.dim() - 2)
1419
monomial_points.append(Delta.origin())
1420
elif isinstance(monomial_points, str):
1421
raise ValueError("%s is an unsupported description of monomial "
1422
"points!" % monomial_points)
1423
monomial_points = tuple(monomial_points)
1424
self._monomial_points = monomial_points
1425
# Make the necessary ambient space
1426
if coefficients is None:
1427
if coefficient_name_indices is None:
1428
coefficient_name_indices = monomial_points
1429
coefficient_names = normalize_names(
1430
coefficient_names, len(monomial_points),
1431
DEFAULT_COEFFICIENT, coefficient_name_indices)
1432
# We probably don't want it: the analog in else-branch is unclear.
1433
# self._coefficient_names = coefficient_names
1434
F = add_variables(P_Delta.base_ring(), coefficient_names)
1435
coefficients = [F(coef) for coef in coefficient_names]
1436
else:
1437
variables = set()
1438
nonstr = []
1439
regex = re.compile("[_A-Za-z]\w*")
1440
for c in coefficients:
1441
if isinstance(c, str):
1442
variables.update(regex.findall(c))
1443
else:
1444
nonstr.append(c)
1445
F = add_variables(P_Delta.base_ring(), sorted(variables))
1446
F = get_coercion_model().common_parent(F, *nonstr)
1447
coefficients = map(F, coefficients)
1448
P_Delta = P_Delta.base_extend(F)
1449
if len(monomial_points) != len(coefficients):
1450
raise ValueError("cannot construct equation of the anticanonical"
1451
" hypersurface with %d monomials and %d coefficients"
1452
% (len(monomial_points), len(coefficients)))
1453
# Defining polynomial
1454
h = sum(coef * prod(P_Delta.coordinate_point_to_coordinate(n)
1455
** (Delta.point(m) * Delta_polar.point(n) + 1)
1456
for n in P_Delta.coordinate_points())
1457
for m, coef in zip(monomial_points, coefficients))
1458
super(AnticanonicalHypersurface, self).__init__(P_Delta, h)
1459
1460
1461
class NefCompleteIntersection(AlgebraicScheme_subscheme_toric):
1462
r"""
1463
Construct a nef complete intersection in a CPR-Fano toric variety.
1464
1465
INPUT:
1466
1467
- ``P_Delta`` -- a :class:`CPR-Fano toric variety
1468
<CPRFanoToricVariety_field>` associated to a reflexive polytope
1469
`\Delta`;
1470
1471
- see :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for
1472
documentation on all other acceptable parameters.
1473
1474
OUTPUT:
1475
1476
- a :class:`nef complete intersection <NefCompleteIntersection>` of
1477
``P_Delta`` (with the extended base field, if necessary).
1478
1479
EXAMPLES::
1480
1481
sage: o = lattice_polytope.octahedron(3)
1482
sage: np = o.nef_partitions()[0]
1483
sage: np
1484
Nef-partition {0, 1, 3} U {2, 4, 5}
1485
sage: X = CPRFanoToricVariety(Delta_polar=o)
1486
sage: X.nef_complete_intersection(np)
1487
Closed subscheme of 3-d CPR-Fano toric variety
1488
covered by 8 affine patches defined by:
1489
a1*z0^2*z1 + a4*z0*z1*z3 + a3*z1*z3^2
1490
+ a0*z0^2*z4 + a5*z0*z3*z4 + a2*z3^2*z4,
1491
b0*z1*z2^2 + b1*z2^2*z4 + b4*z1*z2*z5
1492
+ b5*z2*z4*z5 + b3*z1*z5^2 + b2*z4*z5^2
1493
1494
See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for a
1495
more elaborate example.
1496
"""
1497
def __init__(self, P_Delta, nef_partition,
1498
monomial_points="all", coefficient_names=None,
1499
coefficient_name_indices=None, coefficients=None):
1500
r"""
1501
See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for
1502
documentation.
1503
1504
TESTS::
1505
1506
sage: o = lattice_polytope.octahedron(3)
1507
sage: np = o.nef_partitions()[0]
1508
sage: np
1509
Nef-partition {0, 1, 3} U {2, 4, 5}
1510
sage: X = CPRFanoToricVariety(Delta_polar=o)
1511
sage: from sage.schemes.toric.fano_variety import *
1512
sage: NefCompleteIntersection(X, np)
1513
Closed subscheme of 3-d CPR-Fano toric variety
1514
covered by 8 affine patches defined by:
1515
a1*z0^2*z1 + a4*z0*z1*z3 + a3*z1*z3^2
1516
+ a0*z0^2*z4 + a5*z0*z3*z4 + a2*z3^2*z4,
1517
b0*z1*z2^2 + b1*z2^2*z4 + b4*z1*z2*z5
1518
+ b5*z2*z4*z5 + b3*z1*z5^2 + b2*z4*z5^2
1519
"""
1520
if not is_CPRFanoToricVariety(P_Delta):
1521
raise TypeError("nef complete intersections can only be "
1522
"constructed for CPR-Fano toric varieties!"
1523
"\nGot: %s" % P_Delta)
1524
if nef_partition.Delta() is not P_Delta.Delta():
1525
raise ValueError("polytopes 'Delta' of the nef-partition and the "
1526
"CPR-Fano toric variety must be the same!")
1527
self._nef_partition = nef_partition
1528
k = nef_partition.nparts()
1529
# Pre-normalize all parameters
1530
if isinstance(monomial_points, str):
1531
monomial_points = [monomial_points] * k
1532
if coefficient_names is None:
1533
coefficient_names = [None] * k
1534
if coefficient_name_indices is None:
1535
coefficient_name_indices = [None] * k
1536
if coefficients is None:
1537
coefficients = [None] * k
1538
1539
polynomials = []
1540
Delta_polar = P_Delta.Delta_polar()
1541
for i in range(k):
1542
Delta_i = nef_partition.Delta(i)
1543
# Monomial points normalization
1544
if monomial_points[i] == "vertices":
1545
monomial_points[i] = range(Delta_i.nvertices())
1546
elif monomial_points[i] == "all":
1547
monomial_points[i] = range(Delta_i.npoints())
1548
elif monomial_points[i] == "vertices+origin":
1549
monomial_points[i] = range(Delta_i.nvertices())
1550
if (Delta_i.origin() is not None
1551
and Delta_i.origin() >= Delta_i.nvertices()):
1552
monomial_points[i].append(Delta_i.origin())
1553
elif isinstance(monomial_points[i], str):
1554
raise ValueError("'%s' is an unsupported description of "
1555
"monomial points!" % monomial_points[i])
1556
monomial_points[i] = tuple(monomial_points[i])
1557
# Extend the base ring of the ambient space if necessary
1558
if coefficients[i] is None:
1559
if coefficient_name_indices[i] is None:
1560
coefficient_name_indices[i] = monomial_points[i]
1561
coefficient_names[i] = normalize_names(
1562
coefficient_names[i], len(monomial_points[i]),
1563
DEFAULT_COEFFICIENTS[i], coefficient_name_indices[i])
1564
F = add_variables(P_Delta.base_ring(), coefficient_names[i])
1565
coefficients[i] = [F(coef) for coef in coefficient_names[i]]
1566
else:
1567
variables = set()
1568
nonstr = []
1569
regex = re.compile("[_A-Za-z]\w*")
1570
for c in coefficients[i]:
1571
if isinstance(c, str):
1572
variables.update(regex.findall(c))
1573
else:
1574
nonstr.append(c)
1575
F = add_variables(P_Delta.base_ring(), sorted(variables))
1576
F = get_coercion_model().common_parent(F, *nonstr)
1577
coefficients[i] = map(F, coefficients[i])
1578
P_Delta = P_Delta.base_extend(F)
1579
if len(monomial_points[i]) != len(coefficients[i]):
1580
raise ValueError("cannot construct equation %d of the complete"
1581
" intersection with %d monomials and %d coefficients"
1582
% (i, len(monomial_points[i]), len(coefficients[i])))
1583
# Defining polynomial
1584
h = sum(coef * prod(P_Delta.coordinate_point_to_coordinate(n)
1585
** (Delta_i.point(m) * Delta_polar.point(n)
1586
+ (nef_partition.part_of_point(n) == i))
1587
for n in P_Delta.coordinate_points())
1588
for m, coef in zip(monomial_points[i], coefficients[i]))
1589
polynomials.append(h)
1590
self._monomial_points = tuple(monomial_points)
1591
super(NefCompleteIntersection, self).__init__(P_Delta, polynomials)
1592
1593
def nef_partition(self):
1594
r"""
1595
Return the nef-partition associated to ``self``.
1596
1597
OUTPUT:
1598
1599
- a :class:`nef-partition
1600
<sage.geometry.lattice_polytope.NefPartition>`.
1601
1602
EXAMPLES::
1603
1604
sage: o = lattice_polytope.octahedron(3)
1605
sage: np = o.nef_partitions()[0]
1606
sage: np
1607
Nef-partition {0, 1, 3} U {2, 4, 5}
1608
sage: X = CPRFanoToricVariety(Delta_polar=o)
1609
sage: CI = X.nef_complete_intersection(np)
1610
sage: CI
1611
Closed subscheme of 3-d CPR-Fano toric variety
1612
covered by 8 affine patches defined by:
1613
a1*z0^2*z1 + a4*z0*z1*z3 + a3*z1*z3^2
1614
+ a0*z0^2*z4 + a5*z0*z3*z4 + a2*z3^2*z4,
1615
b0*z1*z2^2 + b1*z2^2*z4 + b4*z1*z2*z5
1616
+ b5*z2*z4*z5 + b3*z1*z5^2 + b2*z4*z5^2
1617
sage: CI.nef_partition()
1618
Nef-partition {0, 1, 3} U {2, 4, 5}
1619
sage: CI.nef_partition() is np
1620
True
1621
"""
1622
return self._nef_partition
1623
1624
1625
def add_variables(field, variables):
1626
r"""
1627
Extend ``field`` to include all ``variables``.
1628
1629
INPUT:
1630
1631
- ``field`` - a field;
1632
1633
- ``variables`` - a list of strings.
1634
1635
OUTPUT:
1636
1637
- a fraction field extending the original ``field``, which has all
1638
``variables`` among its generators.
1639
1640
EXAMPLES:
1641
1642
We start with the rational field and slowly add more variables::
1643
1644
sage: from sage.schemes.toric.fano_variety import *
1645
sage: F = add_variables(QQ, []); F # No extension
1646
Rational Field
1647
sage: F = add_variables(QQ, ["a"]); F
1648
Fraction Field of Univariate Polynomial Ring
1649
in a over Rational Field
1650
sage: F = add_variables(F, ["a"]); F
1651
Fraction Field of Univariate Polynomial Ring
1652
in a over Rational Field
1653
sage: F = add_variables(F, ["b", "c"]); F
1654
Fraction Field of Multivariate Polynomial Ring
1655
in a, b, c over Rational Field
1656
sage: F = add_variables(F, ["c", "d", "b", "c", "d"]); F
1657
Fraction Field of Multivariate Polynomial Ring
1658
in a, b, c, d over Rational Field
1659
"""
1660
if not variables:
1661
return field
1662
if is_FractionField(field):
1663
# Q(a) ---> Q(a, b) rather than Q(a)(b)
1664
R = field.ring()
1665
if is_PolynomialRing(R) or is_MPolynomialRing(R):
1666
new_variables = list(R.variable_names())
1667
for v in variables:
1668
if v not in new_variables:
1669
new_variables.append(v)
1670
if len(new_variables) > R.ngens():
1671
return PolynomialRing(R.base_ring(),
1672
new_variables).fraction_field()
1673
else:
1674
return field
1675
# "Intelligent extension" didn't work, use the "usual one."
1676
new_variables = []
1677
for v in variables:
1678
if v not in new_variables:
1679
new_variables.append(v)
1680
return PolynomialRing(field, new_variables).fraction_field()
1681
1682
1683