From a7fd12e812ad4f14e3d529651b8ff47a36cd0b77 Mon Sep 17 00:00:00 2001 From: local-ring Date: Mon, 14 Jul 2025 22:51:36 -0400 Subject: [PATCH 1/6] initial setup --- src/sage/schemes/toric/cpr_fano_variety.py | 1601 ++++++++++++++++++++ src/sage/schemes/toric/fano_variety.py | 375 +---- src/sage/schemes/toric/library.py | 2 +- 3 files changed, 1652 insertions(+), 326 deletions(-) create mode 100644 src/sage/schemes/toric/cpr_fano_variety.py diff --git a/src/sage/schemes/toric/cpr_fano_variety.py b/src/sage/schemes/toric/cpr_fano_variety.py new file mode 100644 index 00000000000..c4823e6680f --- /dev/null +++ b/src/sage/schemes/toric/cpr_fano_variety.py @@ -0,0 +1,1601 @@ +# sage.doctest: needs sage.geometry.polyhedron sage.graphs +r""" +Fano toric varieties + +This module provides support for (Crepant Partial Resolutions of) Fano toric +varieties, corresponding to crepant subdivisions of face fans of reflexive +:class:`lattice polytopes +`. +The interface is provided via :func:`CPRFanoToricVariety`. + +A careful exposition of different flavours of Fano varieties can be found in +the paper by Benjamin Nill [Nil2005]_. The main goal of this module is to +support work with **Gorenstein weak Fano toric varieties**. Such a variety +corresponds to a **coherent crepant refinement of the normal fan of a +reflexive polytope** `\Delta`, where crepant means that primitive generators +of the refining rays lie on the facets of the polar polytope `\Delta^\circ` +and coherent (a.k.a. regular or projective) means that there exists a strictly +upper convex piecewise linear function whose domains of linearity are +precisely the maximal cones of the subdivision. These varieties are important +for string theory in physics, as they serve as ambient spaces for mirror pairs +of Calabi-Yau manifolds via constructions due to Victor V. Batyrev +[Bat1994]_ and Lev A. Borisov [Bor1993]_. + +From the combinatorial point of view, the "crepant" requirement is much more simple +and natural to work with than "coherent." For this reason, the code in this +module will allow work with arbitrary crepant subdivisions without checking +whether they are coherent or not. We refer to corresponding toric varieties as +**CPR-Fano toric varieties**. + +REFERENCES: + +- [Bat1994]_ +- [Bor1993]_ +- [CD2007]_ +- [Nil2005]_ + +AUTHORS: + +- Andrey Novoseltsev (2010-05-18): initial version. + +EXAMPLES: + +Most of the functions available for Fano toric varieties are the same as +for general toric varieties, so here we will concentrate only on +Calabi-Yau subvarieties, which were the primary goal for creating this +module. + +For our first example we realize the projective plane as a Fano toric +variety:: + + sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) + sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) + +Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau +manifold:: + + sage: P2.anticanonical_hypersurface(monomial_points='all') + Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: + a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 + + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 + +In many cases, it is sufficient to work with the "simplified polynomial +moduli space" of anticanonical hypersurfaces:: + + sage: P2.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 + +The mirror family to these hypersurfaces lives inside the Fano toric +variety obtained using ``simplex`` as ``Delta`` instead of ``Delta_polar``:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points='all') + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety covered by 9 affine patches defined by: + a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 + + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 + +Here we have taken the resolved version of the ambient space for the +mirror family, but in fact we don't have to resolve singularities +corresponding to the interior points of facets - they are singular +points which do not lie on a generic anticanonical hypersurface:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points="all but facets") + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 + +This looks very similar to our second version of the anticanonical +hypersurface of the projective plane, as expected, since all +one-dimensional Calabi-Yau manifolds are elliptic curves! + +Now let's take a look at a toric realization of `M`-polarized K3 surfaces +studied by Adrian Clingher and Charles F. Doran in [CD2007]_:: + + sage: p4318 = ReflexivePolytope(3, 4318) + sage: FTV = CPRFanoToricVariety(Delta_polar=p4318) + sage: FTV.anticanonical_hypersurface() + Closed subscheme of 3-d CPR-Fano toric variety covered by 4 affine patches defined by: + a0*z2^12 + a4*z2^6*z3^6 + a3*z3^12 + a8*z0*z1*z2*z3 + a2*z1^3 + a1*z0^2 + +Below you will find detailed descriptions of available functions. Current +functionality of this module is very basic, but it is under active +development and hopefully will improve in future releases of Sage. If there +are some particular features that you would like to see implemented ASAP, +please consider reporting them to the Sage Development Team or even +implementing them on your own as a patch for inclusion! +""" +# The first example of the tutorial is taken from +# CPRFanoToricVariety_field.anticanonical_hypersurface + +# **************************************************************************** +# Copyright (C) 2010 Andrey Novoseltsev +# Copyright (C) 2010 William Stein +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# https://www.gnu.org/licenses/ +# **************************************************************************** + +import re + +from sage.geometry.cone import Cone +from sage.geometry.fan import FaceFan +from sage.geometry.fan import Fan +from sage.geometry.lattice_polytope import LatticePolytope +from sage.misc.latex import latex +from sage.misc.misc_c import prod +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.rational_field import QQ + +from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_base +from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic +from sage.rings.fraction_field import FractionField_generic + +from sage.schemes.toric.toric_subscheme import AlgebraicScheme_subscheme_toric +from sage.schemes.toric.variety import ( + ToricVariety_field, + normalize_names) +from sage.structure.all import coercion_model +from sage.categories.fields import Fields +_Fields = Fields() + + +# Default coefficient for anticanonical hypersurfaces +DEFAULT_COEFFICIENT = "a" +# Default coefficients for nef complete intersections +DEFAULT_COEFFICIENTS = tuple(chr(i) for i in range(ord("a"), ord("z") + 1)) + + +def is_CPRFanoToricVariety(x): + r""" + Check if ``x`` is a CPR-Fano toric variety. + + INPUT: + + - ``x`` -- anything + + OUTPUT: + + - ``True`` if ``x`` is a :class:`CPR-Fano toric variety + ` and ``False`` otherwise. + + .. NOTE:: + + While projective spaces are Fano toric varieties mathematically, they + are not toric varieties in Sage due to efficiency considerations, so + this function will return ``False``. + + EXAMPLES:: + + sage: from sage.schemes.toric.fano_variety import is_CPRFanoToricVariety + sage: is_CPRFanoToricVariety(1) + doctest:warning... + DeprecationWarning: The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead. + See https://github.com/sagemath/sage/issues/38022 for details. + False + sage: FTV = toric_varieties.P2() + sage: FTV + 2-d CPR-Fano toric variety covered by 3 affine patches + sage: is_CPRFanoToricVariety(FTV) + True + sage: is_CPRFanoToricVariety(ProjectiveSpace(2)) + False + """ + from sage.misc.superseded import deprecation + deprecation(38022, "The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead.") + return isinstance(x, CPRFanoToricVariety_field) + + +def CPRFanoToricVariety(Delta=None, + Delta_polar=None, + coordinate_points=None, + charts=None, + coordinate_names=None, + names=None, + coordinate_name_indices=None, + make_simplicial=False, + base_ring=None, + base_field=None, + check=True): + r""" + Construct a CPR-Fano toric variety. + + .. NOTE:: + + See documentation of the module + :mod:`~sage.schemes.toric.fano_variety` for the used + definitions and supported varieties. + + Due to the large number of available options, it is recommended to always + use keyword parameters. + + INPUT: + + - ``Delta`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be + given, but not both at the same time, since one is completely determined + by another via :meth:`polar + ` method. + + - ``Delta_polar`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must + be given, but not both at the same time, since one is completely + determined by another via :meth:`polar + ` method. + + - ``coordinate_points`` -- list of integers or string. A list will be + interpreted as indices of (boundary) points of ``Delta_polar`` which + should be used as rays of the underlying fan. It must include all + vertices of ``Delta_polar`` and no repetitions are allowed. A string + must be one of the following descriptions of points of ``Delta_polar``: + + * "vertices" (default), + * "all" (will not include the origin), + * "all but facets" (will not include points in the relative interior of + facets); + + - ``charts`` -- list of lists of elements from ``coordinate_points``. Each + of these lists must define a generating cone of a fan subdividing the + normal fan of ``Delta``. Default ``charts`` correspond to the normal fan + of ``Delta`` without subdivision. The fan specified by ``charts`` will + be subdivided to include all of the requested ``coordinate_points``. + + - ``coordinate_names`` -- names of variables for the coordinate ring, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed variable names will be + created automatically. + + - ``names`` -- an alias of ``coordinate_names`` for internal + use. You may specify either ``names`` or ``coordinate_names``, + but not both. + + - ``coordinate_name_indices`` -- list of integers, indices for indexed + variables. If not given, the index of each variable will coincide with + the index of the corresponding point of ``Delta_polar``. + + - ``make_simplicial`` -- if ``True``, the underlying fan will be made + simplicial (default: ``False``) + + - ``base_ring`` -- base field of the CPR-Fano toric variety + (default: `\QQ`) + + - ``base_field`` -- alias for ``base_ring``. Takes precedence if + both are specified. + + - ``check`` -- by default the input data will be checked for correctness + (e.g. that ``charts`` do form a subdivision of the normal fan of + ``Delta``). If you know for sure that the input is valid, you may + significantly decrease construction time using ``check=False`` option. + + OUTPUT: :class:`CPR-Fano toric variety ` + + EXAMPLES: + + We start with the product of two projective lines:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: diamond.vertices() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) + sage: P1xP1 + 2-d CPR-Fano toric variety covered by 4 affine patches + sage: P1xP1.fan() + Rational polyhedral fan in 2-d lattice M + sage: P1xP1.fan().rays() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + + "Unfortunately," this variety is smooth to start with and we cannot + perform any subdivisions of the underlying fan without leaving the + category of CPR-Fano toric varieties. Our next example starts with a + square:: + + sage: square = diamond.polar() + sage: square.vertices() + N( 1, 1), N( 1, -1), + N(-1, -1), N(-1, 1) + in 2-d lattice N + sage: square.points() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N(-1, 0), N( 0, -1), + N( 0, 0), N( 0, 1), N( 1, 0) + in 2-d lattice N + + We will construct several varieties associated to it:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), + N(-1, -1), N(-1, 1) + in 2-d lattice N + sage: FTV.gens() + (z0, z1, z2, z3) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,8]) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N( 1, 0) + in 2-d lattice N + sage: FTV.gens() + (z0, z1, z2, z3, z8) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[8,0,2,1,3], + ....: coordinate_names='x+') + sage: FTV.fan().rays() + N( 1, 0), N( 1, 1), N(-1, -1), + N( 1, -1), N(-1, 1) + in 2-d lattice N + sage: FTV.gens() + (x8, x0, x2, x1, x3) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+") + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), N(-1, 1), + N(-1, 0), N( 0, -1), N( 0, 1), N( 1, 0) + in 2-d lattice N + sage: FTV.gens() + (x, y, Z2, Z3, Z4, Z5, Z7, Z8) + + Note that ``Z6`` is "missing". This is due to the fact that the 6-th point + of ``square`` is the origin, and all automatically created names have the + same indices as corresponding points of + :meth:`~CPRFanoToricVariety_field.Delta_polar`. This is usually very + convenient, especially if you have to work with several partial + resolutions of the same Fano toric variety. However, you can change it, if + you want:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+", + ....: coordinate_name_indices=list(range(8))) + sage: FTV.gens() + (x, y, Z2, Z3, Z4, Z5, Z6, Z7) + + Note that you have to provide indices for *all* variables, including those + that have "completely custom" names. Again, this is usually convenient, + because you can add or remove "custom" variables without disturbing too + much "automatic" ones:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x Z+", + ....: coordinate_name_indices=list(range(8))) + sage: FTV.gens() + (x, Z1, Z2, Z3, Z4, Z5, Z6, Z7) + + If you prefer to always start from zero, you will have to shift indices + accordingly:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x Z+", + ....: coordinate_name_indices=[0] + list(range(7))) + sage: FTV.gens() + (x, Z0, Z1, Z2, Z3, Z4, Z5, Z6) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+", + ....: coordinate_name_indices=[0]*2 + list(range(6))) + sage: FTV.gens() + (x, y, Z0, Z1, Z2, Z3, Z4, Z5) + + So you always can get any names you want, somewhat complicated default + behaviour was designed with the hope that in most cases you will have no + desire to provide different names. + + Now we will use the possibility to specify initial charts:: + + sage: charts = [(0,1), (1,2), (2,3), (3,0)] + + (these charts actually form exactly the face fan of our square) :: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=charts) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N(-1, 0) + in 2-d lattice N + sage: [cone.ambient_ray_indices() for cone in FTV.fan()] + [(0, 1), (1, 2), (2, 4), (3, 4), (0, 3)] + + If charts are wrong, it should be detected:: + + sage: bad_charts = charts + [(3,0)] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: you have provided 5 cones, but only 4 of them are maximal! + Use discard_faces=True if you indeed need to construct a fan from these cones. + + These charts are technically correct, they just happened to list one of + them twice, but it is assumed that such a situation will not happen. It is + especially important when you try to speed up your code:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts, + ....: check=False) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range + + In this case you still get an error message, but it is harder to figure out + what is going on. It may also happen that "everything will still work" in + the sense of not crashing, but work with such an invalid variety may lead to + mathematically wrong results, so use ``check=False`` carefully! + + Here are some other possible mistakes:: + + sage: bad_charts = charts + [(0,2)] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: (0, 2) does not form a chart of a subdivision of + the face fan of 2-d reflexive polytope #14 in 2-d lattice N! + + sage: bad_charts = charts[:-1] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: given charts do not form a complete fan! + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[1,2,3,4]) + Traceback (most recent call last): + ... + ValueError: all 4 vertices of Delta_polar must be used for coordinates! + Got: [1, 2, 3, 4] + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,0,1,2,3,4]) + Traceback (most recent call last): + ... + ValueError: no repetitions are allowed for coordinate points! + Got: [0, 0, 1, 2, 3, 4] + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,6]) + Traceback (most recent call last): + ... + ValueError: the origin (point #6) cannot be used for a coordinate! + Got: [0, 1, 2, 3, 6] + + Here is a shorthand for defining the toric variety and homogeneous + coordinates in one go:: + + sage: P1xP1. = CPRFanoToricVariety(Delta_polar=diamond) + sage: (a^2+b^2) * (c+d) + a^2*c + b^2*c + a^2*d + b^2*d + """ + if names is not None: + if coordinate_names is not None: + raise ValueError('You must not specify both coordinate_names and names!') + coordinate_names = names + # Check/normalize Delta_polar + if Delta is None and Delta_polar is None: + raise ValueError("either Delta or Delta_polar must be given!") + elif Delta is not None and Delta_polar is not None: + raise ValueError("Delta and Delta_polar cannot be given together!") + elif Delta_polar is None: + Delta_polar = Delta.polar() + elif not Delta_polar.is_reflexive(): + raise ValueError("Delta_polar must be reflexive!") + # Check/normalize coordinate_points and construct fan rays + if coordinate_points is None: + coordinate_points = list(range(Delta_polar.nvertices())) + if charts is not None: + for chart in charts: + for point in chart: + if point not in coordinate_points: + coordinate_points.append(point) + elif coordinate_points == "vertices": + coordinate_points = list(range(Delta_polar.nvertices())) + elif coordinate_points == "all": + coordinate_points = list(range(Delta_polar.npoints())) + coordinate_points.remove(Delta_polar.origin()) + elif coordinate_points == "all but facets": + coordinate_points = Delta_polar.skeleton_points(Delta_polar.dim() - 2) + elif isinstance(coordinate_points, str): + raise ValueError("unrecognized description of the coordinate points!" + "\nGot: %s" % coordinate_points) + elif check: + cp_set = set(coordinate_points) + if len(cp_set) != len(coordinate_points): + raise ValueError( + "no repetitions are allowed for coordinate points!\nGot: %s" + % coordinate_points) + if not cp_set.issuperset(list(range(Delta_polar.nvertices()))): + raise ValueError("all %d vertices of Delta_polar must be used " + "for coordinates!\nGot: %s" + % (Delta_polar.nvertices(), coordinate_points)) + if Delta_polar.origin() in cp_set: + raise ValueError("the origin (point #%d) cannot be used for a " + "coordinate!\nGot: %s" + % (Delta_polar.origin(), coordinate_points)) + point_to_ray = {point: n + for n, point in enumerate(coordinate_points)} + # This can be simplified if LatticePolytopeClass is adjusted. + rays = [Delta_polar.point(p) for p in coordinate_points] + # Check/normalize charts and construct the fan based on them. + if charts is None: + # Start with the face fan + fan = FaceFan(Delta_polar) + else: + # First of all, check that each chart is completely contained in a + # single facet of Delta_polar, otherwise they do not form a + # subdivision of the face fan of Delta_polar + if check: + facet_sets = [frozenset(facet.ambient_point_indices()) + for facet in Delta_polar.facets()] + for chart in charts: + is_bad = True + for fset in facet_sets: + if fset.issuperset(chart): + is_bad = False + break + if is_bad: + raise ValueError( + "%s does not form a chart of a subdivision of the " + "face fan of %s!" % (chart, Delta_polar)) + # We will construct the initial fan from Cone objects: since charts + # may not use all of the necessary rays, alternative form is tedious + # With check=False it should not be long anyway. + cones = [Cone((rays[point_to_ray[point]] for point in chart), + check=check) + for chart in charts] + fan = Fan(cones, check=check) + if check and not fan.is_complete(): + raise ValueError("given charts do not form a complete fan!") + # Subdivide this fan to use all required points + fan = fan.subdivide(new_rays=(ray for ray in rays + if ray not in fan.rays().set()), + make_simplicial=make_simplicial) + # Now create yet another fan making sure that the order of the rays is + # the same as requested (it is a bit difficult to get it from the start) + trans = {} + for n, ray in enumerate(fan.rays()): + trans[n] = rays.index(ray) + cones = tuple(tuple(sorted(trans[r] for r in cone.ambient_ray_indices())) + for cone in fan) + fan = Fan(cones, rays, check=False) + # Check/normalize base_field + if base_field is not None: + base_ring = base_field + if base_ring is None: + base_ring = QQ + elif base_ring not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % base_ring) + fan._is_complete = True # At this point it must be for sure + return CPRFanoToricVariety_field( + Delta_polar, fan, coordinate_points, + point_to_ray, coordinate_names, coordinate_name_indices, base_ring) + + +class CPRFanoToricVariety_field(ToricVariety_field): + r""" + Construct a CPR-Fano toric variety associated to a reflexive polytope. + + .. WARNING:: + + This class does not perform any checks of correctness of input and it + does assume that the internal structure of the given parameters is + coordinated in a certain way. Use + :func:`CPRFanoToricVariety` to construct CPR-Fano toric varieties. + + .. NOTE:: + + See documentation of the module + :mod:`~sage.schemes.toric.fano_variety` for the used + definitions and supported varieties. + + INPUT: + + - ``Delta_polar`` -- reflexive polytope + + - ``fan`` -- rational polyhedral fan subdividing the face fan of + ``Delta_polar`` + + - ``coordinate_points`` -- list of indices of points of ``Delta_polar`` + used for rays of ``fan`` + + - ``point_to_ray`` -- dictionary mapping the index of a coordinate point + to the index of the corresponding ray + + - ``coordinate_names`` -- names of the variables of the coordinate ring in + the format accepted by + :func:`~sage.schemes.toric.variety.normalize_names` + + - ``coordinate_name_indices`` -- indices for indexed variables, + if ``None``, will be equal to ``coordinate_points`` + + - ``base_field`` -- base field of the CPR-Fano toric variety + + OUTPUT: :class:`CPR-Fano toric variety ` + + TESTS:: + + sage: P1xP1 = CPRFanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1 + 2-d CPR-Fano toric variety covered by 4 affine patches + """ + + def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray, + coordinate_names, coordinate_name_indices, base_field): + r""" + See :class:`CPRFanoToricVariety_field` for documentation. + + Use ``CPRFanoToricVariety`` to construct CPR-Fano toric varieties. + + TESTS:: + + sage: P1xP1 = CPRFanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1 + 2-d CPR-Fano toric variety covered by 4 affine patches + """ + self._Delta_polar = Delta_polar + self._coordinate_points = tuple(coordinate_points) + self._point_to_ray = point_to_ray + # Check/normalize coordinate_indices + if coordinate_name_indices is None: + coordinate_name_indices = coordinate_points + super().__init__(fan, coordinate_names, + coordinate_name_indices, base_field) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + OUTPUT: string + + TESTS:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: print(P1xP1._latex_()) + \mathbb{P}_{\Delta^{2}_{14}} + """ + return r"\mathbb{P}_{%s}" % latex(self.Delta()) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + OUTPUT: string + + TESTS:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: print(P1xP1._repr_()) + 2-d CPR-Fano toric variety covered by 4 affine patches + """ + return ("%d-d CPR-Fano toric variety covered by %d affine patches" + % (self.dimension_relative(), self.fan().ngenerating_cones())) + + def anticanonical_hypersurface(self, **kwds): + r""" + Return an anticanonical hypersurface of ``self``. + + .. NOTE:: + + The returned hypersurface may be actually a subscheme of + **another** CPR-Fano toric variety: if the base field of ``self`` + does not include all of the required names for generic monomial + coefficients, it will be automatically extended. + + Below `\Delta` is the reflexive polytope corresponding to ``self``, + i.e. the fan of ``self`` is a refinement of the normal fan of + `\Delta`. This function accepts only keyword parameters. + + INPUT: + + - ``monomial_points`` -- list of integers or a string. A list will be + interpreted as indices of points of `\Delta` which should be used + for monomials of this hypersurface. A string must be one of the + following descriptions of points of `\Delta`: + + * "vertices", + * "vertices+origin", + * "all", + * "simplified" (default) -- all points of `\Delta` except for + the interior points of facets, this choice corresponds to working + with the "simplified polynomial moduli space" of anticanonical + hypersurfaces; + + - ``coefficient_names`` -- names for the monomial coefficients, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed coefficient names will + be created automatically. + + - ``coefficient_name_indices`` -- list of integers, indices for + indexed coefficients. If not given, the index of each coefficient + will coincide with the index of the corresponding point of `\Delta`. + + - ``coefficients`` -- as an alternative to specifying coefficient + names and/or indices, you can give the coefficients themselves as + arbitrary expressions and/or strings. Using strings allows you to + easily add "parameters": the base field of ``self`` will be extended + to include all necessary names. + + OUTPUT: + + - an :class:`anticanonical hypersurface ` of + ``self`` (with the extended base field, if necessary). + + EXAMPLES: + + We realize the projective plane as a Fano toric variety:: + + sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) + sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) + + Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau + manifold:: + + sage: P2.anticanonical_hypersurface(monomial_points='all') + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 + + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 + + In many cases it is sufficient to work with the "simplified polynomial + moduli space" of anticanonical hypersurfaces:: + + sage: P2.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 + + The mirror family to these hypersurfaces lives inside the Fano toric + variety obtained using ``simplex`` as ``Delta`` instead of + ``Delta_polar``:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, + ....: coordinate_points='all') + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety + covered by 9 affine patches defined by: + a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 + + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 + + Here we have taken the resolved version of the ambient space for the + mirror family, but in fact we don't have to resolve singularities + corresponding to the interior points of facets - they are singular + points which do not lie on a generic anticanonical hypersurface:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, + ....: coordinate_points="all but facets") + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 + + This looks very similar to our second anticanonical + hypersurface of the projective plane, as expected, since all + one-dimensional Calabi-Yau manifolds are elliptic curves! + + All anticanonical hypersurfaces constructed above were generic with + automatically generated coefficients. If you want, you can specify your + own names :: + + sage: FTV.anticanonical_hypersurface(coefficient_names="a b c d") + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + a*z0^3 + b*z1^3 + d*z0*z1*z2 + c*z2^3 + + or give concrete coefficients :: + + sage: FTV.anticanonical_hypersurface(coefficients=[1, 2, 3, 4]) + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + z0^3 + 2*z1^3 + 4*z0*z1*z2 + 3*z2^3 + + or even mix numerical coefficients with some expressions :: + + sage: H = FTV.anticanonical_hypersurface( + ....: coefficients=[0, "t", "1/t", "psi/(psi^2 + phi)"]) + sage: H + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + t*z1^3 + psi/(phi + psi^2)*z0*z1*z2 + 1/t*z2^3 + sage: R = H.ambient_space().base_ring() + sage: R + Fraction Field of + Multivariate Polynomial Ring in phi, psi, t over Rational Field + """ + # The example above is also copied to the tutorial section in the + # main documentation of the module. + return AnticanonicalHypersurface(self, **kwds) + + def change_ring(self, F): + r""" + Return a CPR-Fano toric variety over field ``F``, otherwise the same + as ``self``. + + INPUT: + + - ``F`` -- field + + OUTPUT: :class:`CPR-Fano toric variety ` over ``F`` + + .. NOTE:: + + There is no need to have any relation between ``F`` and the base + field of ``self``. If you do want to have such a relation, use + :meth:`base_extend` instead. + + EXAMPLES:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: P1xP1.base_ring() + Rational Field + sage: P1xP1_RR = P1xP1.change_ring(RR) + sage: P1xP1_RR.base_ring() + Real Field with 53 bits of precision + sage: P1xP1_QQ = P1xP1_RR.change_ring(QQ) + sage: P1xP1_QQ.base_ring() + Rational Field + sage: P1xP1_RR.base_extend(QQ) + Traceback (most recent call last): + ... + ValueError: no natural map from the base ring + (=Real Field with 53 bits of precision) to R (=Rational Field)! + sage: R = PolynomialRing(QQ, 2, 'a') + sage: P1xP1.change_ring(R) + Traceback (most recent call last): + ... + TypeError: need a field to construct a Fano toric variety! + Got Multivariate Polynomial Ring in a0, a1 over Rational Field + """ + if self.base_ring() == F: + return self + elif F not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % F) + else: + return CPRFanoToricVariety_field(self._Delta_polar, self._fan, + self._coordinate_points, self._point_to_ray, + self.variable_names(), None, F) + # coordinate_name_indices do not matter, we give explicit + # names for all variables + + def coordinate_point_to_coordinate(self, point): + r""" + Return the variable of the coordinate ring corresponding to ``point``. + + INPUT: + + - ``point`` -- integer from the list of :meth:`coordinate_points` + + OUTPUT: the corresponding generator of the coordinate ring of ``self`` + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: FTV = CPRFanoToricVariety(diamond, coordinate_points=[0,1,2,3,8]) + sage: FTV.coordinate_points() + (0, 1, 2, 3, 8) + sage: FTV.gens() + (z0, z1, z2, z3, z8) + sage: FTV.coordinate_point_to_coordinate(8) + z8 + """ + return self.gen(self._point_to_ray[point]) + + def coordinate_points(self): + r""" + Return indices of points of :meth:`Delta_polar` used for coordinates. + + OUTPUT: :class:`tuple` of integers + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: square = diamond.polar() + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,8]) + sage: FTV.coordinate_points() + (0, 1, 2, 3, 8) + sage: FTV.gens() + (z0, z1, z2, z3, z8) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all') + sage: FTV.coordinate_points() + (0, 1, 2, 3, 4, 5, 7, 8) + sage: FTV.gens() + (z0, z1, z2, z3, z4, z5, z7, z8) + + Note that one point is missing, namely :: + + sage: square.origin() + 6 + """ + return self._coordinate_points + + def Delta(self): + r""" + Return the reflexive polytope associated to ``self``. + + OUTPUT: + + - reflexive :class:`lattice polytope + `. The + underlying fan of ``self`` is a coherent subdivision of the + *normal fan* of this polytope. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) + sage: P1xP1.Delta() + 2-d reflexive polytope #14 in 2-d lattice N + sage: P1xP1.Delta() is diamond.polar() + True + """ + return self._Delta_polar.polar() + + def Delta_polar(self): + r""" + Return polar of :meth:`Delta`. + + OUTPUT: + + - reflexive :class:`lattice polytope + `. The + underlying fan of ``self`` is a coherent subdivision of the + *face fan* of this polytope. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) + sage: P1xP1.Delta_polar() + 2-d reflexive polytope #3 in 2-d lattice M + sage: P1xP1.Delta_polar() is diamond + True + sage: P1xP1.Delta_polar() is P1xP1.Delta().polar() + True + """ + return self._Delta_polar + + def nef_complete_intersection(self, nef_partition, **kwds): + r""" + Return a nef complete intersection in ``self``. + + .. NOTE:: + + The returned complete intersection may be actually a subscheme of + **another** CPR-Fano toric variety: if the base field of ``self`` + does not include all of the required names for monomial + coefficients, it will be automatically extended. + + Below `\Delta` is the reflexive polytope corresponding to ``self``, + i.e. the fan of ``self`` is a refinement of the normal fan of + `\Delta`. Other polytopes are described in the documentation of + :class:`nef-partitions ` + of :class:`reflexive polytopes + `. + + Except for the first argument, ``nef_partition``, this method accepts + only keyword parameters. + + INPUT: + + - ``nef_partition`` -- a `k`-part :class:`nef-partition + ` of `\Delta^\circ`, all + other parameters (if given) must be lists of length `k` + + - ``monomial_points`` -- the `i`-th element of this list is either a + list of integers or a string. A list will be interpreted as indices + of points of `\Delta_i` which should be used for monomials of the + `i`-th polynomial of this complete intersection. A string must be one + of the following descriptions of points of `\Delta_i`: + + * "vertices", + * "vertices+origin", + * "all" (default), + + when using this description, it is also OK to pass a single string as + ``monomial_points`` instead of repeating it `k` times. + + - ``coefficient_names`` -- the `i`-th element of this list specifies + names for the monomial coefficients of the `i`-th polynomial, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed coefficient names will + be created automatically. + + - ``coefficient_name_indices`` -- the `i`-th element of this list + specifies indices for indexed coefficients of the `i`-th polynomial. + If not given, the index of each coefficient will coincide with the + index of the corresponding point of `\Delta_i`. + + - ``coefficients`` -- as an alternative to specifying coefficient + names and/or indices, you can give the coefficients themselves as + arbitrary expressions and/or strings. Using strings allows you to + easily add "parameters": the base field of ``self`` will be extended + to include all necessary names. + + OUTPUT: + + - a :class:`nef complete intersection ` of + ``self`` (with the extended base field, if necessary). + + EXAMPLES: + + We construct several complete intersections associated to the same + nef-partition of the 3-dimensional reflexive polytope #2254:: + + sage: p = ReflexivePolytope(3, 2254) + sage: np = p.nef_partitions()[1]; np + Nef-partition {2, 3, 4, 7, 8} ⊔ {0, 1, 5, 6} + sage: X = CPRFanoToricVariety(Delta_polar=p) + sage: X.nef_complete_intersection(np) + Closed subscheme of 3-d CPR-Fano toric variety + covered by 10 affine patches defined by: + a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + + a3*z2*z3*z4*z7*z8 + a1*z0*z2, + b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 + + b5*z1*z3*z4*z5*z6*z7*z8 + b2*z2*z3*z6^2*z8^3 + + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 + + Now we include only monomials associated to vertices of `\Delta_i`:: + + sage: X.nef_complete_intersection(np, monomial_points='vertices') + Closed subscheme of 3-d CPR-Fano toric variety + covered by 10 affine patches defined by: + a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + + a3*z2*z3*z4*z7*z8 + a1*z0*z2, + b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 + + b2*z2*z3*z6^2*z8^3 + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 + + (effectively, we set ``b5=0``). Next we provide coefficients explicitly + instead of using default generic names:: + + sage: X.nef_complete_intersection(np, + ....: monomial_points='vertices', + ....: coefficients=[("a", "a^2", "a/e", "c_i"), list(range(1,6))]) + Closed subscheme of 3-d CPR-Fano toric variety + covered by 10 affine patches defined by: + a*z1*z4^2*z5^2*z7^3 + a/e*z2*z4*z5*z6*z7^2*z8^2 + + (c_i)*z2*z3*z4*z7*z8 + (a^2)*z0*z2, + 4*z1*z4*z5^2*z6^2*z7^2*z8^2 + z2*z5*z6^3*z7*z8^4 + + 3*z2*z3*z6^2*z8^3 + 2*z1*z3^2*z4 + 5*z0*z1*z5*z6 + + Finally, we take a look at the generic representative of these complete + intersections in a completely resolved ambient toric variety:: + + sage: X = CPRFanoToricVariety(Delta_polar=p, + ....: coordinate_points='all') + sage: X.nef_complete_intersection(np) + Closed subscheme of 3-d CPR-Fano toric variety + covered by 22 affine patches defined by: + a2*z2*z4*z5*z6*z7^2*z8^2*z9^2*z10^2*z11*z12*z13 + + a0*z1*z4^2*z5^2*z7^3*z9*z10^2*z12*z13 + + a3*z2*z3*z4*z7*z8*z9*z10*z11*z12 + a1*z0*z2, + b0*z2*z5*z6^3*z7*z8^4*z9^3*z10^2*z11^2*z12*z13^2 + + b3*z1*z4*z5^2*z6^2*z7^2*z8^2*z9^2*z10^2*z11*z12*z13^2 + + b2*z2*z3*z6^2*z8^3*z9^2*z10*z11^2*z12*z13 + + b5*z1*z3*z4*z5*z6*z7*z8*z9*z10*z11*z12*z13 + + b1*z1*z3^2*z4*z11*z12 + b4*z0*z1*z5*z6*z13 + """ + return NefCompleteIntersection(self, nef_partition, **kwds) + + def cartesian_product(self, other, + coordinate_names=None, coordinate_indices=None): + r""" + Return the Cartesian product of ``self`` with ``other``. + + INPUT: + + - ``other`` -- a (possibly + :class:`CPR-Fano `) :class:`toric variety + ` + + - ``coordinate_names`` -- names of variables for the coordinate ring, + see :func:`normalize_names` for acceptable formats. If not given, + indexed variable names will be created automatically. + + - ``coordinate_indices`` -- list of integers, indices for indexed + variables. If not given, the index of each variable will coincide + with the index of the corresponding ray of the fan. + + OUTPUT: + + - a :class:`toric variety + `, which is + :class:`CPR-Fano ` if ``other`` was. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: P2 = toric_varieties.P2() + sage: P1xP2 = P1.cartesian_product(P2); P1xP2 + 3-d CPR-Fano toric variety covered by 6 affine patches + sage: P1xP2.fan().rays() + N+N( 1, 0, 0), N+N(-1, 0, 0), N+N( 0, 1, 0), + N+N( 0, 0, 1), N+N( 0, -1, -1) + in 3-d lattice N+N + sage: P1xP2.Delta_polar() + 3-d reflexive polytope in 3-d lattice N+N + """ + if isinstance(other, CPRFanoToricVariety_field): + fan = self.fan().cartesian_product(other.fan()) + Delta_polar = LatticePolytope(fan.rays()) + + points = Delta_polar.points() + point_to_ray = {} + coordinate_points = [] + for ray_index, ray in enumerate(fan.rays()): + point = points.index(ray) + coordinate_points.append(point) + point_to_ray[point] = ray_index + + return CPRFanoToricVariety_field(Delta_polar, fan, + coordinate_points, point_to_ray, + coordinate_names, coordinate_indices, + self.base_ring()) + return super().cartesian_product(other) + + def resolve(self, **kwds): + r""" + Construct a toric variety whose fan subdivides the fan of ``self``. + + This function accepts only keyword arguments, none of which are + mandatory. + + INPUT: + + - ``new_points`` -- list of integers, indices of boundary points of + :meth:`Delta_polar`, which should be added as rays to the + subdividing fan + + - all other arguments will be passed to + :meth:`~sage.schemes.toric.variety.ToricVariety_field.resolve` + method of (general) toric varieties; see its documentation for + details + + OUTPUT: + + - :class:`CPR-Fano toric variety ` if there + was no ``new_rays`` argument and :class:`toric variety + ` otherwise. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: FTV = CPRFanoToricVariety(Delta=diamond) + sage: FTV.coordinate_points() + (0, 1, 2, 3) + sage: FTV.gens() + (z0, z1, z2, z3) + sage: FTV_res = FTV.resolve(new_points=[6,8]) + Traceback (most recent call last): + ... + ValueError: the origin (point #6) + cannot be used for subdivision! + sage: FTV_res = FTV.resolve(new_points=[8,5]); FTV_res + 2-d CPR-Fano toric variety covered by 6 affine patches + sage: FTV_res.coordinate_points() + (0, 1, 2, 3, 8, 5) + sage: FTV_res.gens() + (z0, z1, z2, z3, z8, z5) + + sage: TV_res = FTV.resolve(new_rays=[(1,2)]); TV_res + 2-d toric variety covered by 5 affine patches + sage: TV_res.gens() + (z0, z1, z2, z3, z4) + """ + # Reasons to override the base class: + # - allow using polytope point indices for subdivision + # - handle automatic name creation in a different fashion + # - return CPR-Fano toric variety if the above feature was used and + # just toric variety if subdivision involves rays + if "new_rays" in kwds: + if "new_points" in kwds: + raise ValueError("you cannot give new_points and new_rays at " + "the same time!") + return super().resolve(**kwds) + # Now we need to construct another Fano variety + new_points = kwds.pop("new_points", ()) + coordinate_points = self.coordinate_points() + new_points = tuple(point for point in new_points + if point not in coordinate_points) + Delta_polar = self._Delta_polar + if Delta_polar.origin() in new_points: + raise ValueError("the origin (point #%d) cannot be used for " + "subdivision!" % Delta_polar.origin()) + if new_points: + coordinate_points = coordinate_points + new_points + point_to_ray = {point: n + for n, point in enumerate(coordinate_points)} + else: + point_to_ray = self._point_to_ray + new_rays = [Delta_polar.point(point) for point in new_points] + coordinate_name_indices = kwds.pop("coordinate_name_indices", + coordinate_points) + fan = self.fan() + if "coordinate_names" in kwds: + coordinate_names = kwds.pop("coordinate_names") + else: + coordinate_names = list(self.variable_names()) + coordinate_names.extend(normalize_names(ngens=len(new_rays), + indices=coordinate_name_indices[fan.nrays():], + prefix=self._coordinate_prefix)) + coordinate_names.append(self._coordinate_prefix + "+") + rfan = fan.subdivide(new_rays=new_rays, **kwds) + resolution = CPRFanoToricVariety_field(Delta_polar, rfan, + coordinate_points, point_to_ray, coordinate_names, + coordinate_name_indices, self.base_ring()) + R = self.coordinate_ring() + R_res = resolution.coordinate_ring() + resolution_map = resolution.hom(R.hom(R_res.gens()[:R.ngens()]), self) + resolution._resolution_map = resolution_map + return resolution + + +class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric): + r""" + Construct an anticanonical hypersurface of a CPR-Fano toric variety. + + INPUT: + + - ``P_Delta`` -- :class:`CPR-Fano toric variety + ` associated to a reflexive polytope `\Delta` + + - see :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for + documentation on all other acceptable parameters + + OUTPUT: + + :class:`anticanonical hypersurface ` of + ``P_Delta`` (with the extended base field, if necessary). + + EXAMPLES:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: import sage.schemes.toric.fano_variety as ftv + sage: ftv.AnticanonicalHypersurface(P1xP1) + Closed subscheme of 2-d CPR-Fano toric variety + covered by 4 affine patches defined by: + a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 + + See :meth:`~CPRFanoToricVariety_field.anticanonical_hypersurface()` for a + more elaborate example. + """ + def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, + coefficient_name_indices=None, coefficients=None): + r""" + See :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for + documentation. + + TESTS:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: import sage.schemes.toric.fano_variety as ftv + sage: ftv.AnticanonicalHypersurface(P1xP1) + Closed subscheme of 2-d CPR-Fano toric variety + covered by 4 affine patches defined by: + a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 + + Check that finite fields are handled correctly :issue:`14899`:: + + sage: F = GF(5^2, "a") # needs sage.rings.finite_rings + sage: X = P1xP1.change_ring(F) # needs sage.rings.finite_rings + sage: X.anticanonical_hypersurface(monomial_points='all', # needs sage.rings.finite_rings + ....: coefficients=[1]*X.Delta().npoints()) + Closed subscheme of 2-d CPR-Fano toric variety + covered by 4 affine patches defined by: + s^2*x^2 + s*t*x^2 + t^2*x^2 + s^2*x*y + s*t*x*y + + t^2*x*y + s^2*y^2 + s*t*y^2 + t^2*y^2 + """ + if not isinstance(P_Delta, CPRFanoToricVariety_field): + raise TypeError("anticanonical hypersurfaces can only be " + "constructed for CPR-Fano toric varieties!" + "\nGot: %s" % P_Delta) + Delta = P_Delta.Delta() + Delta_polar = Delta.polar() + # Monomial points normalization + if monomial_points == "vertices": + monomial_points = list(range(Delta.nvertices())) + elif monomial_points == "all": + monomial_points = list(range(Delta.npoints())) + elif monomial_points == "vertices+origin": + monomial_points = list(range(Delta.nvertices())) + monomial_points.append(Delta.origin()) + elif monomial_points == "simplified" or monomial_points is None: + monomial_points = Delta.skeleton_points(Delta.dim() - 2) + monomial_points.append(Delta.origin()) + elif isinstance(monomial_points, str): + raise ValueError("%s is an unsupported description of monomial " + "points!" % monomial_points) + monomial_points = tuple(monomial_points) + self._monomial_points = monomial_points + # Make the necessary ambient space + if coefficients is None: + if coefficient_name_indices is None: + coefficient_name_indices = monomial_points + coefficient_names = normalize_names( + coefficient_names, len(monomial_points), + DEFAULT_COEFFICIENT, coefficient_name_indices) + # We probably don't want it: the analog in else-branch is unclear. + # self._coefficient_names = coefficient_names + F = add_variables(P_Delta.base_ring(), coefficient_names) + coefficients = [F(coef) for coef in coefficient_names] + else: + variables = set() + nonstr = [] + regex = re.compile(r"[_A-Za-z]\w*") + for c in coefficients: + if isinstance(c, str): + variables.update(regex.findall(c)) + else: + nonstr.append(c) + F = add_variables(P_Delta.base_ring(), sorted(variables)) + F = coercion_model.common_parent(F, *nonstr) + coefficients = [F(_) for _ in coefficients] + P_Delta = P_Delta.base_extend(F) + if len(monomial_points) != len(coefficients): + raise ValueError("cannot construct equation of the anticanonical" + " hypersurface with %d monomials and %d coefficients" + % (len(monomial_points), len(coefficients))) + # Defining polynomial + h = sum(coef * prod(P_Delta.coordinate_point_to_coordinate(n) + ** (Delta.point(m) * Delta_polar.point(n) + 1) + for n in P_Delta.coordinate_points()) + for m, coef in zip(monomial_points, coefficients)) + super().__init__(P_Delta, h) + + +class NefCompleteIntersection(AlgebraicScheme_subscheme_toric): + r""" + Construct a nef complete intersection in a CPR-Fano toric variety. + + INPUT: + + - ``P_Delta`` -- a :class:`CPR-Fano toric variety + ` associated to a reflexive polytope `\Delta` + + - see :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for + documentation on all other acceptable parameters + + OUTPUT: + + - a :class:`nef complete intersection ` of + ``P_Delta`` (with the extended base field, if necessary). + + EXAMPLES:: + + sage: o = lattice_polytope.cross_polytope(3) + sage: np = o.nef_partitions()[0]; np + Nef-partition {0, 1, 3} ⊔ {2, 4, 5} + sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: X.nef_complete_intersection(np) + Closed subscheme of 3-d CPR-Fano toric variety + covered by 8 affine patches defined by: + a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, + b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 + + See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for a + more elaborate example. + """ + def __init__(self, P_Delta, nef_partition, + monomial_points='all', coefficient_names=None, + coefficient_name_indices=None, coefficients=None): + r""" + See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for + documentation. + + TESTS:: + + sage: o = lattice_polytope.cross_polytope(3) + sage: np = o.nef_partitions()[0] + sage: np + Nef-partition {0, 1, 3} ⊔ {2, 4, 5} + sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: from sage.schemes.toric.fano_variety import * + sage: NefCompleteIntersection(X, np) + Closed subscheme of 3-d CPR-Fano toric variety + covered by 8 affine patches defined by: + a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, + b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 + """ + if not isinstance(P_Delta, CPRFanoToricVariety_field): + raise TypeError("nef complete intersections can only be " + "constructed for CPR-Fano toric varieties!" + "\nGot: %s" % P_Delta) + if nef_partition.Delta() is not P_Delta.Delta(): + raise ValueError("polytopes 'Delta' of the nef-partition and the " + "CPR-Fano toric variety must be the same!") + self._nef_partition = nef_partition + k = nef_partition.nparts() + # Pre-normalize all parameters + if isinstance(monomial_points, str): + monomial_points = [monomial_points] * k + if coefficient_names is None: + coefficient_names = [None] * k + if coefficient_name_indices is None: + coefficient_name_indices = [None] * k + if coefficients is None: + coefficients = [None] * k + + polynomials = [] + Delta_polar = P_Delta.Delta_polar() + for i in range(k): + Delta_i = nef_partition.Delta(i) + # Monomial points normalization + if monomial_points[i] == "vertices": + monomial_points[i] = list(range(Delta_i.nvertices())) + elif monomial_points[i] == "all": + monomial_points[i] = list(range(Delta_i.npoints())) + elif monomial_points[i] == "vertices+origin": + monomial_points[i] = list(range(Delta_i.nvertices())) + if (Delta_i.origin() is not None + and Delta_i.origin() >= Delta_i.nvertices()): + monomial_points[i].append(Delta_i.origin()) + elif isinstance(monomial_points[i], str): + raise ValueError("'%s' is an unsupported description of " + "monomial points!" % monomial_points[i]) + monomial_points[i] = tuple(monomial_points[i]) + # Extend the base ring of the ambient space if necessary + if coefficients[i] is None: + if coefficient_name_indices[i] is None: + coefficient_name_indices[i] = monomial_points[i] + coefficient_names[i] = normalize_names( + coefficient_names[i], len(monomial_points[i]), + DEFAULT_COEFFICIENTS[i], coefficient_name_indices[i]) + F = add_variables(P_Delta.base_ring(), coefficient_names[i]) + coefficients[i] = [F(coef) for coef in coefficient_names[i]] + else: + variables = set() + nonstr = [] + regex = re.compile(r"[_A-Za-z]\w*") + for c in coefficients[i]: + if isinstance(c, str): + variables.update(regex.findall(c)) + else: + nonstr.append(c) + F = add_variables(P_Delta.base_ring(), sorted(variables)) + F = coercion_model.common_parent(F, *nonstr) + coefficients[i] = [F(_) for _ in coefficients[i]] + P_Delta = P_Delta.base_extend(F) + if len(monomial_points[i]) != len(coefficients[i]): + raise ValueError("cannot construct equation %d of the complete" + " intersection with %d monomials and %d coefficients" + % (i, len(monomial_points[i]), len(coefficients[i]))) + # Defining polynomial + h = sum(coef * prod(P_Delta.coordinate_point_to_coordinate(n) + ** (Delta_i.point(m) * Delta_polar.point(n) + + (nef_partition.part_of_point(n) == i)) + for n in P_Delta.coordinate_points()) + for m, coef in zip(monomial_points[i], coefficients[i])) + polynomials.append(h) + self._monomial_points = tuple(monomial_points) + super().__init__(P_Delta, polynomials) + + def cohomology_class(self): + r""" + Return the class of ``self`` in the ambient space cohomology ring. + + OUTPUT: a :class:`cohomology class ` + + EXAMPLES:: + + sage: o = lattice_polytope.cross_polytope(3) + sage: np = o.nef_partitions()[0]; np + Nef-partition {0, 1, 3} ⊔ {2, 4, 5} + sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: CI = X.nef_complete_intersection(np); CI + Closed subscheme of 3-d CPR-Fano toric variety + covered by 8 affine patches defined by: + a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, + b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 + sage: CI.cohomology_class() # needs sage.libs.singular + [2*z3*z4 + 4*z3*z5 + 2*z4*z5] + """ + X = self.ambient_space() + H = X.cohomology_ring() + return prod(sum(H.gen(X._point_to_ray[point]) + for point in part if point in X._coordinate_points) + for part in self.nef_partition().parts(all_points=True)) + + def nef_partition(self): + r""" + Return the nef-partition associated to ``self``. + + OUTPUT: a :class:`nef-partition ` + + EXAMPLES:: + + sage: o = lattice_polytope.cross_polytope(3) + sage: np = o.nef_partitions()[0]; np + Nef-partition {0, 1, 3} ⊔ {2, 4, 5} + sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: CI = X.nef_complete_intersection(np); CI + Closed subscheme of 3-d CPR-Fano toric variety + covered by 8 affine patches defined by: + a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, + b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 + sage: CI.nef_partition() + Nef-partition {0, 1, 3} ⊔ {2, 4, 5} + sage: CI.nef_partition() is np + True + """ + return self._nef_partition + + +def add_variables(field, variables): + r""" + Extend ``field`` to include all ``variables``. + + INPUT: + + - ``field`` -- a field + + - ``variables`` -- list of strings + + OUTPUT: + + - a fraction field extending the original ``field``, which has all + ``variables`` among its generators. + + EXAMPLES: + + We start with the rational field and slowly add more variables:: + + sage: from sage.schemes.toric.fano_variety import * + sage: F = add_variables(QQ, []); F # No extension + Rational Field + sage: F = add_variables(QQ, ["a"]); F + Fraction Field of Univariate Polynomial Ring in a over Rational Field + sage: F = add_variables(F, ["a"]); F + Fraction Field of Univariate Polynomial Ring in a over Rational Field + sage: F = add_variables(F, ["b", "c"]); F + Fraction Field of Multivariate Polynomial Ring in a, b, c over Rational Field + sage: F = add_variables(F, ["c", "d", "b", "c", "d"]); F + Fraction Field of Multivariate Polynomial Ring in a, b, c, d over Rational Field + """ + if not variables: + return field + if isinstance(field, FractionField_generic): + # Q(a) ---> Q(a, b) rather than Q(a)(b) + R = field.ring() + if isinstance(R, (PolynomialRing_generic, MPolynomialRing_base)): + new_variables = list(R.variable_names()) + for v in variables: + if v not in new_variables: + new_variables.append(v) + if len(new_variables) > R.ngens(): + return PolynomialRing(R.base_ring(), + new_variables).fraction_field() + else: + return field + # "Intelligent extension" didn't work, use the "usual one." + new_variables = [] + for v in variables: + if v not in new_variables: + new_variables.append(v) + return PolynomialRing(field, new_variables).fraction_field() diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index c4823e6680f..467833bc2b9 100644 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -2,120 +2,10 @@ r""" Fano toric varieties -This module provides support for (Crepant Partial Resolutions of) Fano toric -varieties, corresponding to crepant subdivisions of face fans of reflexive -:class:`lattice polytopes -`. -The interface is provided via :func:`CPRFanoToricVariety`. - -A careful exposition of different flavours of Fano varieties can be found in -the paper by Benjamin Nill [Nil2005]_. The main goal of this module is to -support work with **Gorenstein weak Fano toric varieties**. Such a variety -corresponds to a **coherent crepant refinement of the normal fan of a -reflexive polytope** `\Delta`, where crepant means that primitive generators -of the refining rays lie on the facets of the polar polytope `\Delta^\circ` -and coherent (a.k.a. regular or projective) means that there exists a strictly -upper convex piecewise linear function whose domains of linearity are -precisely the maximal cones of the subdivision. These varieties are important -for string theory in physics, as they serve as ambient spaces for mirror pairs -of Calabi-Yau manifolds via constructions due to Victor V. Batyrev -[Bat1994]_ and Lev A. Borisov [Bor1993]_. - -From the combinatorial point of view, the "crepant" requirement is much more simple -and natural to work with than "coherent." For this reason, the code in this -module will allow work with arbitrary crepant subdivisions without checking -whether they are coherent or not. We refer to corresponding toric varieties as -**CPR-Fano toric varieties**. - -REFERENCES: - -- [Bat1994]_ -- [Bor1993]_ -- [CD2007]_ -- [Nil2005]_ - -AUTHORS: - -- Andrey Novoseltsev (2010-05-18): initial version. - -EXAMPLES: - -Most of the functions available for Fano toric varieties are the same as -for general toric varieties, so here we will concentrate only on -Calabi-Yau subvarieties, which were the primary goal for creating this -module. - -For our first example we realize the projective plane as a Fano toric -variety:: - - sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) - sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) - -Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau -manifold:: - - sage: P2.anticanonical_hypersurface(monomial_points='all') - Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: - a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 - + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 - -In many cases, it is sufficient to work with the "simplified polynomial -moduli space" of anticanonical hypersurfaces:: - - sage: P2.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 - -The mirror family to these hypersurfaces lives inside the Fano toric -variety obtained using ``simplex`` as ``Delta`` instead of ``Delta_polar``:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points='all') - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety covered by 9 affine patches defined by: - a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 - + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 - -Here we have taken the resolved version of the ambient space for the -mirror family, but in fact we don't have to resolve singularities -corresponding to the interior points of facets - they are singular -points which do not lie on a generic anticanonical hypersurface:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points="all but facets") - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 - -This looks very similar to our second version of the anticanonical -hypersurface of the projective plane, as expected, since all -one-dimensional Calabi-Yau manifolds are elliptic curves! - -Now let's take a look at a toric realization of `M`-polarized K3 surfaces -studied by Adrian Clingher and Charles F. Doran in [CD2007]_:: - - sage: p4318 = ReflexivePolytope(3, 4318) - sage: FTV = CPRFanoToricVariety(Delta_polar=p4318) - sage: FTV.anticanonical_hypersurface() - Closed subscheme of 3-d CPR-Fano toric variety covered by 4 affine patches defined by: - a0*z2^12 + a4*z2^6*z3^6 + a3*z3^12 + a8*z0*z1*z2*z3 + a2*z1^3 + a1*z0^2 - -Below you will find detailed descriptions of available functions. Current -functionality of this module is very basic, but it is under active -development and hopefully will improve in future releases of Sage. If there -are some particular features that you would like to see implemented ASAP, -please consider reporting them to the Sage Development Team or even -implementing them on your own as a patch for inclusion! +This module provides base class of (Gorenstein) Fano varieties, so we can naturally +introduce the subclass of CPR-Fano toric variety, as well as a factory +for smooth Fano toric varieties. """ -# The first example of the tutorial is taken from -# CPRFanoToricVariety_field.anticanonical_hypersurface - -# **************************************************************************** -# Copyright (C) 2010 Andrey Novoseltsev -# Copyright (C) 2010 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# https://www.gnu.org/licenses/ -# **************************************************************************** import re @@ -147,59 +37,20 @@ DEFAULT_COEFFICIENTS = tuple(chr(i) for i in range(ord("a"), ord("z") + 1)) -def is_CPRFanoToricVariety(x): - r""" - Check if ``x`` is a CPR-Fano toric variety. - - INPUT: - - - ``x`` -- anything - - OUTPUT: - - - ``True`` if ``x`` is a :class:`CPR-Fano toric variety - ` and ``False`` otherwise. - - .. NOTE:: - While projective spaces are Fano toric varieties mathematically, they - are not toric varieties in Sage due to efficiency considerations, so - this function will return ``False``. - - EXAMPLES:: - - sage: from sage.schemes.toric.fano_variety import is_CPRFanoToricVariety - sage: is_CPRFanoToricVariety(1) - doctest:warning... - DeprecationWarning: The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead. - See https://github.com/sagemath/sage/issues/38022 for details. - False - sage: FTV = toric_varieties.P2() - sage: FTV - 2-d CPR-Fano toric variety covered by 3 affine patches - sage: is_CPRFanoToricVariety(FTV) - True - sage: is_CPRFanoToricVariety(ProjectiveSpace(2)) - False - """ - from sage.misc.superseded import deprecation - deprecation(38022, "The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead.") - return isinstance(x, CPRFanoToricVariety_field) - - -def CPRFanoToricVariety(Delta=None, - Delta_polar=None, - coordinate_points=None, - charts=None, - coordinate_names=None, - names=None, - coordinate_name_indices=None, - make_simplicial=False, - base_ring=None, - base_field=None, - check=True): +def FanoToricVariety(Delta=None, + Delta_polar=None, + coordinate_points=None, + charts=None, + coordinate_names=None, + names=None, + coordinate_name_indices=None, + make_simplicial=False, + base_ring=None, + base_field=None, + check=True): r""" - Construct a CPR-Fano toric variety. + Construct a Fano toric variety. .. NOTE:: @@ -592,16 +443,17 @@ def CPRFanoToricVariety(Delta=None, point_to_ray, coordinate_names, coordinate_name_indices, base_ring) -class CPRFanoToricVariety_field(ToricVariety_field): +class FanoToricVariety_field(ToricVariety_field): r""" - Construct a CPR-Fano toric variety associated to a reflexive polytope. + Base class for Gorenstein Fano toric varieties over a field. + Construct a Fano toric variety associated to a reflexive polytope. .. WARNING:: This class does not perform any checks of correctness of input and it does assume that the internal structure of the given parameters is coordinated in a certain way. Use - :func:`CPRFanoToricVariety` to construct CPR-Fano toric varieties. + :func:`FanoToricVariety` to construct Fano toric varieties. .. NOTE:: @@ -613,15 +465,9 @@ class CPRFanoToricVariety_field(ToricVariety_field): - ``Delta_polar`` -- reflexive polytope - - ``fan`` -- rational polyhedral fan subdividing the face fan of + - ``fan`` -- rational polyhedral fan which is the face fan of ``Delta_polar`` - - ``coordinate_points`` -- list of indices of points of ``Delta_polar`` - used for rays of ``fan`` - - - ``point_to_ray`` -- dictionary mapping the index of a coordinate point - to the index of the corresponding ray - - ``coordinate_names`` -- names of the variables of the coordinate ring in the format accepted by :func:`~sage.schemes.toric.variety.normalize_names` @@ -629,40 +475,43 @@ class CPRFanoToricVariety_field(ToricVariety_field): - ``coordinate_name_indices`` -- indices for indexed variables, if ``None``, will be equal to ``coordinate_points`` - - ``base_field`` -- base field of the CPR-Fano toric variety + - ``base_field`` -- base field of the Fano toric variety - OUTPUT: :class:`CPR-Fano toric variety ` + OUTPUT: :class:`Fano toric variety ` TESTS:: - sage: P1xP1 = CPRFanoToricVariety( + sage: P1xP1 = FanoToricVariety( ....: Delta_polar=lattice_polytope.cross_polytope(2)) sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches + 2-d Fano toric variety covered by 4 affine patches """ - def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray, - coordinate_names, coordinate_name_indices, base_field): + def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field): r""" - See :class:`CPRFanoToricVariety_field` for documentation. + See :class:`FanoToricVariety_field` for documentation. - Use ``CPRFanoToricVariety`` to construct CPR-Fano toric varieties. + Use ``FanoToricVariety`` to construct Fano toric varieties. TESTS:: - sage: P1xP1 = CPRFanoToricVariety( + sage: P1xP1 = FanoToricVariety( ....: Delta_polar=lattice_polytope.cross_polytope(2)) sage: P1xP1 2-d CPR-Fano toric variety covered by 4 affine patches """ self._Delta_polar = Delta_polar - self._coordinate_points = tuple(coordinate_points) - self._point_to_ray = point_to_ray - # Check/normalize coordinate_indices - if coordinate_name_indices is None: - coordinate_name_indices = coordinate_points super().__init__(fan, coordinate_names, coordinate_name_indices, base_field) + + # AL: we want to use the anticanonical surface and nef complete intersection functions + @property + def _coordinate_points(self): + return list(range(self.fan().rays())) + + @property + def _point_to_ray(self): + return {i: i for i in self._coordinate_points} def _latex_(self): r""" @@ -690,7 +539,7 @@ def _repr_(self): sage: print(P1xP1._repr_()) 2-d CPR-Fano toric variety covered by 4 affine patches """ - return ("%d-d CPR-Fano toric variety covered by %d affine patches" + return ("%d-d Fano toric variety covered by %d affine patches" % (self.dimension_relative(), self.fan().ngenerating_cones())) def anticanonical_hypersurface(self, **kwds): @@ -700,7 +549,7 @@ def anticanonical_hypersurface(self, **kwds): .. NOTE:: The returned hypersurface may be actually a subscheme of - **another** CPR-Fano toric variety: if the base field of ``self`` + **another** Fano toric variety: if the base field of ``self`` does not include all of the required names for generic monomial coefficients, it will be automatically extended. @@ -748,96 +597,30 @@ def anticanonical_hypersurface(self, **kwds): We realize the projective plane as a Fano toric variety:: sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) - sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) + sage: P2 = FanoToricVariety(Delta_polar=simplex) Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau manifold:: sage: P2.anticanonical_hypersurface(monomial_points='all') - Closed subscheme of 2-d CPR-Fano toric variety + Closed subscheme of 2-d Fano toric variety covered by 3 affine patches defined by: a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 - - In many cases it is sufficient to work with the "simplified polynomial - moduli space" of anticanonical hypersurfaces:: - - sage: P2.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 - - The mirror family to these hypersurfaces lives inside the Fano toric - variety obtained using ``simplex`` as ``Delta`` instead of - ``Delta_polar``:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, - ....: coordinate_points='all') - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety - covered by 9 affine patches defined by: - a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 - + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 - - Here we have taken the resolved version of the ambient space for the - mirror family, but in fact we don't have to resolve singularities - corresponding to the interior points of facets - they are singular - points which do not lie on a generic anticanonical hypersurface:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, - ....: coordinate_points="all but facets") - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 - - This looks very similar to our second anticanonical - hypersurface of the projective plane, as expected, since all - one-dimensional Calabi-Yau manifolds are elliptic curves! - - All anticanonical hypersurfaces constructed above were generic with - automatically generated coefficients. If you want, you can specify your - own names :: - - sage: FTV.anticanonical_hypersurface(coefficient_names="a b c d") - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - a*z0^3 + b*z1^3 + d*z0*z1*z2 + c*z2^3 - - or give concrete coefficients :: - - sage: FTV.anticanonical_hypersurface(coefficients=[1, 2, 3, 4]) - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - z0^3 + 2*z1^3 + 4*z0*z1*z2 + 3*z2^3 - - or even mix numerical coefficients with some expressions :: - - sage: H = FTV.anticanonical_hypersurface( - ....: coefficients=[0, "t", "1/t", "psi/(psi^2 + phi)"]) - sage: H - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - t*z1^3 + psi/(phi + psi^2)*z0*z1*z2 + 1/t*z2^3 - sage: R = H.ambient_space().base_ring() - sage: R - Fraction Field of - Multivariate Polynomial Ring in phi, psi, t over Rational Field """ - # The example above is also copied to the tutorial section in the - # main documentation of the module. + # AL: we include this function because the construction only rely on the Delta and cox variables return AnticanonicalHypersurface(self, **kwds) def change_ring(self, F): r""" - Return a CPR-Fano toric variety over field ``F``, otherwise the same + Return a Fano toric variety over field ``F``, otherwise the same as ``self``. INPUT: - ``F`` -- field - OUTPUT: :class:`CPR-Fano toric variety ` over ``F`` + OUTPUT: :class:`Fano toric variety ` over ``F`` .. NOTE:: @@ -874,66 +657,9 @@ def change_ring(self, F): raise TypeError("need a field to construct a Fano toric variety!" "\n Got %s" % F) else: - return CPRFanoToricVariety_field(self._Delta_polar, self._fan, - self._coordinate_points, self._point_to_ray, + return FanoToricVariety_field(self._Delta_polar, self._fan, self.variable_names(), None, F) - # coordinate_name_indices do not matter, we give explicit - # names for all variables - - def coordinate_point_to_coordinate(self, point): - r""" - Return the variable of the coordinate ring corresponding to ``point``. - - INPUT: - - - ``point`` -- integer from the list of :meth:`coordinate_points` - - OUTPUT: the corresponding generator of the coordinate ring of ``self`` - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: FTV = CPRFanoToricVariety(diamond, coordinate_points=[0,1,2,3,8]) - sage: FTV.coordinate_points() - (0, 1, 2, 3, 8) - sage: FTV.gens() - (z0, z1, z2, z3, z8) - sage: FTV.coordinate_point_to_coordinate(8) - z8 - """ - return self.gen(self._point_to_ray[point]) - - def coordinate_points(self): - r""" - Return indices of points of :meth:`Delta_polar` used for coordinates. - - OUTPUT: :class:`tuple` of integers - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: square = diamond.polar() - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,8]) - sage: FTV.coordinate_points() - (0, 1, 2, 3, 8) - sage: FTV.gens() - (z0, z1, z2, z3, z8) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all') - sage: FTV.coordinate_points() - (0, 1, 2, 3, 4, 5, 7, 8) - sage: FTV.gens() - (z0, z1, z2, z3, z4, z5, z7, z8) - - Note that one point is missing, namely :: - - sage: square.origin() - 6 - """ - return self._coordinate_points - + def Delta(self): r""" Return the reflexive polytope associated to ``self``. @@ -942,13 +668,13 @@ def Delta(self): - reflexive :class:`lattice polytope `. The - underlying fan of ``self`` is a coherent subdivision of the + underlying fan of ``self`` is the *normal fan* of this polytope. EXAMPLES:: sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) sage: P1xP1.Delta() 2-d reflexive polytope #14 in 2-d lattice N sage: P1xP1.Delta() is diamond.polar() @@ -964,13 +690,12 @@ def Delta_polar(self): - reflexive :class:`lattice polytope `. The - underlying fan of ``self`` is a coherent subdivision of the - *face fan* of this polytope. + underlying fan of ``self`` is the *face fan* of this polytope. EXAMPLES:: sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) sage: P1xP1.Delta_polar() 2-d reflexive polytope #3 in 2-d lattice M sage: P1xP1.Delta_polar() is diamond diff --git a/src/sage/schemes/toric/library.py b/src/sage/schemes/toric/library.py index 6f833c5c5d2..e892a135d0b 100644 --- a/src/sage/schemes/toric/library.py +++ b/src/sage/schemes/toric/library.py @@ -52,7 +52,7 @@ from sage.schemes.toric.variety import (DEFAULT_PREFIX, ToricVariety, normalize_names) -from sage.schemes.toric.fano_variety import CPRFanoToricVariety +from sage.schemes.toric.cpr_fano_variety import CPRFanoToricVariety # The combinatorial data of the toric varieties is stored separately here From d68fc482a165de57ce01a4bdc96e4ab0ad42d07b Mon Sep 17 00:00:00 2001 From: local-ring Date: Tue, 15 Jul 2025 15:53:33 -0400 Subject: [PATCH 2/6] reorganize the file structure --- src/sage/schemes/toric/cpr_fano_variety.py | 840 +++++++++++++++++++++ src/sage/schemes/toric/fano_variety.py | 162 +--- 2 files changed, 868 insertions(+), 134 deletions(-) diff --git a/src/sage/schemes/toric/cpr_fano_variety.py b/src/sage/schemes/toric/cpr_fano_variety.py index c4823e6680f..7808bbf3260 100644 --- a/src/sage/schemes/toric/cpr_fano_variety.py +++ b/src/sage/schemes/toric/cpr_fano_variety.py @@ -1599,3 +1599,843 @@ def add_variables(field, variables): if v not in new_variables: new_variables.append(v) return PolynomialRing(field, new_variables).fraction_field() + + +r""" +AL: add base class for both of (Gorenstein) Fano varieties, which serve as the superclass for both CPR-Fano and smooth Fano varieties. +""" +def FanoToricVariety(Delta=None, + Delta_polar=None, + coordinate_points=None, + charts=None, + coordinate_names=None, + names=None, + coordinate_name_indices=None, + make_simplicial=False, + base_ring=None, + base_field=None, + check=True): + r""" + Construct a Fano toric variety. + + .. NOTE:: + + See documentation of the module + :mod:`~sage.schemes.toric.fano_variety` for the used + definitions and supported varieties. + + Due to the large number of available options, it is recommended to always + use keyword parameters. + + INPUT: + + - ``Delta`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be + given, but not both at the same time, since one is completely determined + by another via :meth:`polar + ` method. + + - ``Delta_polar`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must + be given, but not both at the same time, since one is completely + determined by another via :meth:`polar + ` method. + + - ``coordinate_points`` -- list of integers or string. A list will be + interpreted as indices of (boundary) points of ``Delta_polar`` which + should be used as rays of the underlying fan. It must include all + vertices of ``Delta_polar`` and no repetitions are allowed. A string + must be one of the following descriptions of points of ``Delta_polar``: + + * "vertices" (default), + * "all" (will not include the origin), + * "all but facets" (will not include points in the relative interior of + facets); + + - ``charts`` -- list of lists of elements from ``coordinate_points``. Each + of these lists must define a generating cone of a fan subdividing the + normal fan of ``Delta``. Default ``charts`` correspond to the normal fan + of ``Delta`` without subdivision. The fan specified by ``charts`` will + be subdivided to include all of the requested ``coordinate_points``. + + - ``coordinate_names`` -- names of variables for the coordinate ring, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed variable names will be + created automatically. + + - ``names`` -- an alias of ``coordinate_names`` for internal + use. You may specify either ``names`` or ``coordinate_names``, + but not both. + + - ``coordinate_name_indices`` -- list of integers, indices for indexed + variables. If not given, the index of each variable will coincide with + the index of the corresponding point of ``Delta_polar``. + + - ``make_simplicial`` -- if ``True``, the underlying fan will be made + simplicial (default: ``False``) + + - ``base_ring`` -- base field of the CPR-Fano toric variety + (default: `\QQ`) + + - ``base_field`` -- alias for ``base_ring``. Takes precedence if + both are specified. + + - ``check`` -- by default the input data will be checked for correctness + (e.g. that ``charts`` do form a subdivision of the normal fan of + ``Delta``). If you know for sure that the input is valid, you may + significantly decrease construction time using ``check=False`` option. + + OUTPUT: :class:`CPR-Fano toric variety ` + + EXAMPLES: + + We start with the product of two projective lines:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: diamond.vertices() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) + sage: P1xP1 + 2-d CPR-Fano toric variety covered by 4 affine patches + sage: P1xP1.fan() + Rational polyhedral fan in 2-d lattice M + sage: P1xP1.fan().rays() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + + "Unfortunately," this variety is smooth to start with and we cannot + perform any subdivisions of the underlying fan without leaving the + category of CPR-Fano toric varieties. Our next example starts with a + square:: + + sage: square = diamond.polar() + sage: square.vertices() + N( 1, 1), N( 1, -1), + N(-1, -1), N(-1, 1) + in 2-d lattice N + sage: square.points() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N(-1, 0), N( 0, -1), + N( 0, 0), N( 0, 1), N( 1, 0) + in 2-d lattice N + + We will construct several varieties associated to it:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), + N(-1, -1), N(-1, 1) + in 2-d lattice N + sage: FTV.gens() + (z0, z1, z2, z3) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,8]) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N( 1, 0) + in 2-d lattice N + sage: FTV.gens() + (z0, z1, z2, z3, z8) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[8,0,2,1,3], + ....: coordinate_names='x+') + sage: FTV.fan().rays() + N( 1, 0), N( 1, 1), N(-1, -1), + N( 1, -1), N(-1, 1) + in 2-d lattice N + sage: FTV.gens() + (x8, x0, x2, x1, x3) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+") + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), N(-1, 1), + N(-1, 0), N( 0, -1), N( 0, 1), N( 1, 0) + in 2-d lattice N + sage: FTV.gens() + (x, y, Z2, Z3, Z4, Z5, Z7, Z8) + + Note that ``Z6`` is "missing". This is due to the fact that the 6-th point + of ``square`` is the origin, and all automatically created names have the + same indices as corresponding points of + :meth:`~CPRFanoToricVariety_field.Delta_polar`. This is usually very + convenient, especially if you have to work with several partial + resolutions of the same Fano toric variety. However, you can change it, if + you want:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+", + ....: coordinate_name_indices=list(range(8))) + sage: FTV.gens() + (x, y, Z2, Z3, Z4, Z5, Z6, Z7) + + Note that you have to provide indices for *all* variables, including those + that have "completely custom" names. Again, this is usually convenient, + because you can add or remove "custom" variables without disturbing too + much "automatic" ones:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x Z+", + ....: coordinate_name_indices=list(range(8))) + sage: FTV.gens() + (x, Z1, Z2, Z3, Z4, Z5, Z6, Z7) + + If you prefer to always start from zero, you will have to shift indices + accordingly:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x Z+", + ....: coordinate_name_indices=[0] + list(range(7))) + sage: FTV.gens() + (x, Z0, Z1, Z2, Z3, Z4, Z5, Z6) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+", + ....: coordinate_name_indices=[0]*2 + list(range(6))) + sage: FTV.gens() + (x, y, Z0, Z1, Z2, Z3, Z4, Z5) + + So you always can get any names you want, somewhat complicated default + behaviour was designed with the hope that in most cases you will have no + desire to provide different names. + + Now we will use the possibility to specify initial charts:: + + sage: charts = [(0,1), (1,2), (2,3), (3,0)] + + (these charts actually form exactly the face fan of our square) :: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=charts) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N(-1, 0) + in 2-d lattice N + sage: [cone.ambient_ray_indices() for cone in FTV.fan()] + [(0, 1), (1, 2), (2, 4), (3, 4), (0, 3)] + + If charts are wrong, it should be detected:: + + sage: bad_charts = charts + [(3,0)] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: you have provided 5 cones, but only 4 of them are maximal! + Use discard_faces=True if you indeed need to construct a fan from these cones. + + These charts are technically correct, they just happened to list one of + them twice, but it is assumed that such a situation will not happen. It is + especially important when you try to speed up your code:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts, + ....: check=False) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range + + In this case you still get an error message, but it is harder to figure out + what is going on. It may also happen that "everything will still work" in + the sense of not crashing, but work with such an invalid variety may lead to + mathematically wrong results, so use ``check=False`` carefully! + + Here are some other possible mistakes:: + + sage: bad_charts = charts + [(0,2)] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: (0, 2) does not form a chart of a subdivision of + the face fan of 2-d reflexive polytope #14 in 2-d lattice N! + + sage: bad_charts = charts[:-1] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: given charts do not form a complete fan! + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[1,2,3,4]) + Traceback (most recent call last): + ... + ValueError: all 4 vertices of Delta_polar must be used for coordinates! + Got: [1, 2, 3, 4] + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,0,1,2,3,4]) + Traceback (most recent call last): + ... + ValueError: no repetitions are allowed for coordinate points! + Got: [0, 0, 1, 2, 3, 4] + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,6]) + Traceback (most recent call last): + ... + ValueError: the origin (point #6) cannot be used for a coordinate! + Got: [0, 1, 2, 3, 6] + + Here is a shorthand for defining the toric variety and homogeneous + coordinates in one go:: + + sage: P1xP1. = CPRFanoToricVariety(Delta_polar=diamond) + sage: (a^2+b^2) * (c+d) + a^2*c + b^2*c + a^2*d + b^2*d + """ + if names is not None: + if coordinate_names is not None: + raise ValueError('You must not specify both coordinate_names and names!') + coordinate_names = names + # Check/normalize Delta_polar + if Delta is None and Delta_polar is None: + raise ValueError("either Delta or Delta_polar must be given!") + elif Delta is not None and Delta_polar is not None: + raise ValueError("Delta and Delta_polar cannot be given together!") + elif Delta_polar is None: + Delta_polar = Delta.polar() + elif not Delta_polar.is_reflexive(): + raise ValueError("Delta_polar must be reflexive!") + # Check/normalize coordinate_points and construct fan rays + if coordinate_points is None: + coordinate_points = list(range(Delta_polar.nvertices())) + if charts is not None: + for chart in charts: + for point in chart: + if point not in coordinate_points: + coordinate_points.append(point) + elif coordinate_points == "vertices": + coordinate_points = list(range(Delta_polar.nvertices())) + elif coordinate_points == "all": + coordinate_points = list(range(Delta_polar.npoints())) + coordinate_points.remove(Delta_polar.origin()) + elif coordinate_points == "all but facets": + coordinate_points = Delta_polar.skeleton_points(Delta_polar.dim() - 2) + elif isinstance(coordinate_points, str): + raise ValueError("unrecognized description of the coordinate points!" + "\nGot: %s" % coordinate_points) + elif check: + cp_set = set(coordinate_points) + if len(cp_set) != len(coordinate_points): + raise ValueError( + "no repetitions are allowed for coordinate points!\nGot: %s" + % coordinate_points) + if not cp_set.issuperset(list(range(Delta_polar.nvertices()))): + raise ValueError("all %d vertices of Delta_polar must be used " + "for coordinates!\nGot: %s" + % (Delta_polar.nvertices(), coordinate_points)) + if Delta_polar.origin() in cp_set: + raise ValueError("the origin (point #%d) cannot be used for a " + "coordinate!\nGot: %s" + % (Delta_polar.origin(), coordinate_points)) + point_to_ray = {point: n + for n, point in enumerate(coordinate_points)} + # This can be simplified if LatticePolytopeClass is adjusted. + rays = [Delta_polar.point(p) for p in coordinate_points] + # Check/normalize charts and construct the fan based on them. + if charts is None: + # Start with the face fan + fan = FaceFan(Delta_polar) + else: + # First of all, check that each chart is completely contained in a + # single facet of Delta_polar, otherwise they do not form a + # subdivision of the face fan of Delta_polar + if check: + facet_sets = [frozenset(facet.ambient_point_indices()) + for facet in Delta_polar.facets()] + for chart in charts: + is_bad = True + for fset in facet_sets: + if fset.issuperset(chart): + is_bad = False + break + if is_bad: + raise ValueError( + "%s does not form a chart of a subdivision of the " + "face fan of %s!" % (chart, Delta_polar)) + # We will construct the initial fan from Cone objects: since charts + # may not use all of the necessary rays, alternative form is tedious + # With check=False it should not be long anyway. + cones = [Cone((rays[point_to_ray[point]] for point in chart), + check=check) + for chart in charts] + fan = Fan(cones, check=check) + if check and not fan.is_complete(): + raise ValueError("given charts do not form a complete fan!") + # Subdivide this fan to use all required points + fan = fan.subdivide(new_rays=(ray for ray in rays + if ray not in fan.rays().set()), + make_simplicial=make_simplicial) + # Now create yet another fan making sure that the order of the rays is + # the same as requested (it is a bit difficult to get it from the start) + trans = {} + for n, ray in enumerate(fan.rays()): + trans[n] = rays.index(ray) + cones = tuple(tuple(sorted(trans[r] for r in cone.ambient_ray_indices())) + for cone in fan) + fan = Fan(cones, rays, check=False) + # Check/normalize base_field + if base_field is not None: + base_ring = base_field + if base_ring is None: + base_ring = QQ + elif base_ring not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % base_ring) + fan._is_complete = True # At this point it must be for sure + return CPRFanoToricVariety_field( + Delta_polar, fan, coordinate_points, + point_to_ray, coordinate_names, coordinate_name_indices, base_ring) + + +class FanoToricVariety_field(ToricVariety_field): + r""" + Base class for Gorenstein Fano toric varieties over a field. + Construct a Fano toric variety associated to a reflexive polytope. + + .. WARNING:: + + This class does not perform any checks of correctness of input and it + does assume that the internal structure of the given parameters is + coordinated in a certain way. Use + :func:`FanoToricVariety` to construct Fano toric varieties. + + .. NOTE:: + + See documentation of the module + :mod:`~sage.schemes.toric.fano_variety` for the used + definitions and supported varieties. + + INPUT: + + - ``Delta_polar`` -- reflexive polytope + + - ``fan`` -- rational polyhedral fan which is the face fan of + ``Delta_polar`` + + - ``coordinate_names`` -- names of the variables of the coordinate ring in + the format accepted by + :func:`~sage.schemes.toric.variety.normalize_names` + + - ``coordinate_name_indices`` -- indices for indexed variables, + if ``None``, will be equal to ``coordinate_points`` + + - ``base_field`` -- base field of the Fano toric variety + + OUTPUT: :class:`Fano toric variety ` + + TESTS:: + + sage: P1xP1 = FanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1 + 2-d Fano toric variety covered by 4 affine patches + """ + + def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field): + r""" + See :class:`FanoToricVariety_field` for documentation. + + Use ``FanoToricVariety`` to construct Fano toric varieties. + + TESTS:: + + sage: P1xP1 = FanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1 + 2-d CPR-Fano toric variety covered by 4 affine patches + """ + self._Delta_polar = Delta_polar + super().__init__(fan, coordinate_names, + coordinate_name_indices, base_field) + + # AL: we want to use the anticanonical surface and nef complete intersection functions + @property + def _coordinate_points(self): + return list(range(self.fan().rays())) + + @property + def _point_to_ray(self): + return {i: i for i in self._coordinate_points} + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + OUTPUT: string + + TESTS:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: print(P1xP1._latex_()) + \mathbb{P}_{\Delta^{2}_{14}} + """ + return r"\mathbb{P}_{%s}" % latex(self.Delta()) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + OUTPUT: string + + TESTS:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: print(P1xP1._repr_()) + 2-d CPR-Fano toric variety covered by 4 affine patches + """ + return ("%d-d Fano toric variety covered by %d affine patches" + % (self.dimension_relative(), self.fan().ngenerating_cones())) + + def anticanonical_hypersurface(self, **kwds): + r""" + Return an anticanonical hypersurface of ``self``. + + .. NOTE:: + + The returned hypersurface may be actually a subscheme of + **another** Fano toric variety: if the base field of ``self`` + does not include all of the required names for generic monomial + coefficients, it will be automatically extended. + + Below `\Delta` is the reflexive polytope corresponding to ``self``, + i.e. the fan of ``self`` is the normal fan of + `\Delta`. This function accepts only keyword parameters. + + INPUT: + + - ``monomial_points`` -- list of integers or a string. A list will be + interpreted as indices of points of `\Delta` which should be used + for monomials of this hypersurface. A string must be one of the + following descriptions of points of `\Delta`: + + * "vertices", + * "vertices+origin", + * "all", + * "simplified" (default) -- all points of `\Delta` except for + the interior points of facets, this choice corresponds to working + with the "simplified polynomial moduli space" of anticanonical + hypersurfaces; + + - ``coefficient_names`` -- names for the monomial coefficients, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed coefficient names will + be created automatically. + + - ``coefficient_name_indices`` -- list of integers, indices for + indexed coefficients. If not given, the index of each coefficient + will coincide with the index of the corresponding point of `\Delta`. + + - ``coefficients`` -- as an alternative to specifying coefficient + names and/or indices, you can give the coefficients themselves as + arbitrary expressions and/or strings. Using strings allows you to + easily add "parameters": the base field of ``self`` will be extended + to include all necessary names. + + OUTPUT: + + - an :class:`anticanonical hypersurface ` of + ``self`` (with the extended base field, if necessary). + + EXAMPLES: + + We realize the projective plane as a Fano toric variety:: + + sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) + sage: P2 = FanoToricVariety(Delta_polar=simplex) + + Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau + manifold:: + + sage: P2.anticanonical_hypersurface(monomial_points='all') + Closed subscheme of 2-d Fano toric variety + covered by 3 affine patches defined by: + a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 + + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 + """ + # AL: we include this function because the construction only rely on the Delta and cox variables + return AnticanonicalHypersurface(self, **kwds) + + def change_ring(self, F): + r""" + Return a Fano toric variety over field ``F``, otherwise the same + as ``self``. + + INPUT: + + - ``F`` -- field + + OUTPUT: :class:`Fano toric variety ` over ``F`` + + .. NOTE:: + + There is no need to have any relation between ``F`` and the base + field of ``self``. If you do want to have such a relation, use + :meth:`base_extend` instead. + + EXAMPLES:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: P1xP1.base_ring() + Rational Field + sage: P1xP1_RR = P1xP1.change_ring(RR) + sage: P1xP1_RR.base_ring() + Real Field with 53 bits of precision + sage: P1xP1_QQ = P1xP1_RR.change_ring(QQ) + sage: P1xP1_QQ.base_ring() + Rational Field + sage: P1xP1_RR.base_extend(QQ) + Traceback (most recent call last): + ... + ValueError: no natural map from the base ring + (=Real Field with 53 bits of precision) to R (=Rational Field)! + sage: R = PolynomialRing(QQ, 2, 'a') + sage: P1xP1.change_ring(R) + Traceback (most recent call last): + ... + TypeError: need a field to construct a Fano toric variety! + Got Multivariate Polynomial Ring in a0, a1 over Rational Field + """ + if self.base_ring() == F: + return self + elif F not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % F) + else: + return FanoToricVariety_field(self._Delta_polar, self._fan, + self.variable_names(), None, F) + + def Delta(self): + r""" + Return the reflexive polytope associated to ``self``. + + OUTPUT: + + - reflexive :class:`lattice polytope + `. The + underlying fan of ``self`` is the + *normal fan* of this polytope. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1.Delta() + 2-d reflexive polytope #14 in 2-d lattice N + sage: P1xP1.Delta() is diamond.polar() + True + """ + return self._Delta_polar.polar() + + def Delta_polar(self): + r""" + Return polar of :meth:`Delta`. + + OUTPUT: + + - reflexive :class:`lattice polytope + `. The + underlying fan of ``self`` is the *face fan* of this polytope. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1.Delta_polar() + 2-d reflexive polytope #3 in 2-d lattice M + sage: P1xP1.Delta_polar() is diamond + True + sage: P1xP1.Delta_polar() is P1xP1.Delta().polar() + True + """ + return self._Delta_polar + + def nef_complete_intersection(self, nef_partition, **kwds): + r""" + Return a nef complete intersection in ``self``. + + .. NOTE:: + + The returned complete intersection may be actually a subscheme of + **another** Fano toric variety: if the base field of ``self`` + does not include all of the required names for monomial + coefficients, it will be automatically extended. + + Below `\Delta` is the reflexive polytope corresponding to ``self``, + i.e. the fan of ``self`` is the normal fan of + `\Delta`. Other polytopes are described in the documentation of + :class:`nef-partitions ` + of :class:`reflexive polytopes + `. + + Except for the first argument, ``nef_partition``, this method accepts + only keyword parameters. + + INPUT: + + - ``nef_partition`` -- a `k`-part :class:`nef-partition + ` of `\Delta^\circ`, all + other parameters (if given) must be lists of length `k` + + - ``monomial_points`` -- the `i`-th element of this list is either a + list of integers or a string. A list will be interpreted as indices + of points of `\Delta_i` which should be used for monomials of the + `i`-th polynomial of this complete intersection. A string must be one + of the following descriptions of points of `\Delta_i`: + + * "vertices", + * "vertices+origin", + * "all" (default), + + when using this description, it is also OK to pass a single string as + ``monomial_points`` instead of repeating it `k` times. + + - ``coefficient_names`` -- the `i`-th element of this list specifies + names for the monomial coefficients of the `i`-th polynomial, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed coefficient names will + be created automatically. + + - ``coefficient_name_indices`` -- the `i`-th element of this list + specifies indices for indexed coefficients of the `i`-th polynomial. + If not given, the index of each coefficient will coincide with the + index of the corresponding point of `\Delta_i`. + + - ``coefficients`` -- as an alternative to specifying coefficient + names and/or indices, you can give the coefficients themselves as + arbitrary expressions and/or strings. Using strings allows you to + easily add "parameters": the base field of ``self`` will be extended + to include all necessary names. + + OUTPUT: + + - a :class:`nef complete intersection ` of + ``self`` (with the extended base field, if necessary). + + EXAMPLES: + + We construct several complete intersections associated to the same + nef-partition of the 3-dimensional reflexive polytope #2254:: + + sage: p = ReflexivePolytope(3, 2254) + sage: np = p.nef_partitions()[1]; np + Nef-partition {2, 3, 4, 7, 8} ⊔ {0, 1, 5, 6} + sage: X = FanoToricVariety(Delta_polar=p) + sage: X.nef_complete_intersection(np) + Closed subscheme of 3-d Fano toric variety + covered by 10 affine patches defined by: + a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + + a3*z2*z3*z4*z7*z8 + a1*z0*z2, + b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 + + b5*z1*z3*z4*z5*z6*z7*z8 + b2*z2*z3*z6^2*z8^3 + + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 + + Now we include only monomials associated to vertices of `\Delta_i`:: + + sage: X.nef_complete_intersection(np, monomial_points='vertices') + Closed subscheme of 3-d Fano toric variety + covered by 10 affine patches defined by: + a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + + a3*z2*z3*z4*z7*z8 + a1*z0*z2, + b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 + + b2*z2*z3*z6^2*z8^3 + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 + + (effectively, we set ``b5=0``). Next we provide coefficients explicitly + instead of using default generic names:: + + sage: X.nef_complete_intersection(np, + ....: monomial_points='vertices', + ....: coefficients=[("a", "a^2", "a/e", "c_i"), list(range(1,6))]) + Closed subscheme of 3-d Fano toric variety + covered by 10 affine patches defined by: + a*z1*z4^2*z5^2*z7^3 + a/e*z2*z4*z5*z6*z7^2*z8^2 + + (c_i)*z2*z3*z4*z7*z8 + (a^2)*z0*z2, + 4*z1*z4*z5^2*z6^2*z7^2*z8^2 + z2*z5*z6^3*z7*z8^4 + + 3*z2*z3*z6^2*z8^3 + 2*z1*z3^2*z4 + 5*z0*z1*z5*z6 + + Finally, we take a look at the generic representative of these complete + intersections in a completely resolved ambient toric variety:: + + sage: X = FanoToricVariety(Delta_polar=p, + ....: coordinate_points='all') + sage: X.nef_complete_intersection(np) + Closed subscheme of 3-d Fano toric variety + covered by 22 affine patches defined by: + a2*z2*z4*z5*z6*z7^2*z8^2*z9^2*z10^2*z11*z12*z13 + + a0*z1*z4^2*z5^2*z7^3*z9*z10^2*z12*z13 + + a3*z2*z3*z4*z7*z8*z9*z10*z11*z12 + a1*z0*z2, + b0*z2*z5*z6^3*z7*z8^4*z9^3*z10^2*z11^2*z12*z13^2 + + b3*z1*z4*z5^2*z6^2*z7^2*z8^2*z9^2*z10^2*z11*z12*z13^2 + + b2*z2*z3*z6^2*z8^3*z9^2*z10*z11^2*z12*z13 + + b5*z1*z3*z4*z5*z6*z7*z8*z9*z10*z11*z12*z13 + + b1*z1*z3^2*z4*z11*z12 + b4*z0*z1*z5*z6*z13 + """ + return NefCompleteIntersection(self, nef_partition, **kwds) + + def cartesian_product(self, other, + coordinate_names=None, coordinate_indices=None): + r""" + Return the Cartesian product of ``self`` with ``other``. + + INPUT: + + - ``other`` -- a (possibly + :class:`CPR-Fano `) :class:`toric variety + ` + + - ``coordinate_names`` -- names of variables for the coordinate ring, + see :func:`normalize_names` for acceptable formats. If not given, + indexed variable names will be created automatically. + + - ``coordinate_indices`` -- list of integers, indices for indexed + variables. If not given, the index of each variable will coincide + with the index of the corresponding ray of the fan. + + OUTPUT: + + - a :class:`toric variety + `, which is + :class:`CPR-Fano ` if ``other`` was. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: P2 = toric_varieties.P2() + sage: P1xP2 = P1.cartesian_product(P2); P1xP2 + 3-d CPR-Fano toric variety covered by 6 affine patches + sage: P1xP2.fan().rays() + N+N( 1, 0, 0), N+N(-1, 0, 0), N+N( 0, 1, 0), + N+N( 0, 0, 1), N+N( 0, -1, -1) + in 3-d lattice N+N + sage: P1xP2.Delta_polar() + 3-d reflexive polytope in 3-d lattice N+N + """ + if isinstance(other, FanoToricVariety_field) and not isinstance(other, CPRFanoToricVariety_field) \ + and not isinstance(self, SmoothFanoToricVariety_field): + fan = self.fan().cartesian_product(other.fan()) + Delta_polar = LatticePolytope(fan.rays()) + return FanoToricVariety_field(Delta_polar, fan, + coordinate_names, coordinate_indices, + self.base_ring()) + return super().cartesian_product(other) diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index 467833bc2b9..f3c58c7d15b 100644 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -554,7 +554,7 @@ def anticanonical_hypersurface(self, **kwds): coefficients, it will be automatically extended. Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is a refinement of the normal fan of + i.e. the fan of ``self`` is the normal fan of `\Delta`. This function accepts only keyword parameters. INPUT: @@ -712,12 +712,12 @@ def nef_complete_intersection(self, nef_partition, **kwds): .. NOTE:: The returned complete intersection may be actually a subscheme of - **another** CPR-Fano toric variety: if the base field of ``self`` + **another** Fano toric variety: if the base field of ``self`` does not include all of the required names for monomial coefficients, it will be automatically extended. Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is a refinement of the normal fan of + i.e. the fan of ``self`` is the normal fan of `\Delta`. Other polytopes are described in the documentation of :class:`nef-partitions ` of :class:`reflexive polytopes @@ -775,9 +775,9 @@ def nef_complete_intersection(self, nef_partition, **kwds): sage: p = ReflexivePolytope(3, 2254) sage: np = p.nef_partitions()[1]; np Nef-partition {2, 3, 4, 7, 8} ⊔ {0, 1, 5, 6} - sage: X = CPRFanoToricVariety(Delta_polar=p) + sage: X = FanoToricVariety(Delta_polar=p) sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 10 affine patches defined by: a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + a3*z2*z3*z4*z7*z8 + a1*z0*z2, @@ -788,7 +788,7 @@ def nef_complete_intersection(self, nef_partition, **kwds): Now we include only monomials associated to vertices of `\Delta_i`:: sage: X.nef_complete_intersection(np, monomial_points='vertices') - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 10 affine patches defined by: a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + a3*z2*z3*z4*z7*z8 + a1*z0*z2, @@ -801,7 +801,7 @@ def nef_complete_intersection(self, nef_partition, **kwds): sage: X.nef_complete_intersection(np, ....: monomial_points='vertices', ....: coefficients=[("a", "a^2", "a/e", "c_i"), list(range(1,6))]) - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 10 affine patches defined by: a*z1*z4^2*z5^2*z7^3 + a/e*z2*z4*z5*z6*z7^2*z8^2 + (c_i)*z2*z3*z4*z7*z8 + (a^2)*z0*z2, @@ -811,10 +811,10 @@ def nef_complete_intersection(self, nef_partition, **kwds): Finally, we take a look at the generic representative of these complete intersections in a completely resolved ambient toric variety:: - sage: X = CPRFanoToricVariety(Delta_polar=p, + sage: X = FanoToricVariety(Delta_polar=p, ....: coordinate_points='all') sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 22 affine patches defined by: a2*z2*z4*z5*z6*z7^2*z8^2*z9^2*z10^2*z11*z12*z13 + a0*z1*z4^2*z5^2*z7^3*z9*z10^2*z12*z13 @@ -865,121 +865,15 @@ def cartesian_product(self, other, sage: P1xP2.Delta_polar() 3-d reflexive polytope in 3-d lattice N+N """ - if isinstance(other, CPRFanoToricVariety_field): + if isinstance(other, FanoToricVariety_field) and not isinstance(other, CPRFanoToricVariety_field) \ + and not isinstance(self, SmoothFanoToricVariety_field): fan = self.fan().cartesian_product(other.fan()) Delta_polar = LatticePolytope(fan.rays()) - - points = Delta_polar.points() - point_to_ray = {} - coordinate_points = [] - for ray_index, ray in enumerate(fan.rays()): - point = points.index(ray) - coordinate_points.append(point) - point_to_ray[point] = ray_index - - return CPRFanoToricVariety_field(Delta_polar, fan, - coordinate_points, point_to_ray, + return FanoToricVariety_field(Delta_polar, fan, coordinate_names, coordinate_indices, self.base_ring()) return super().cartesian_product(other) - def resolve(self, **kwds): - r""" - Construct a toric variety whose fan subdivides the fan of ``self``. - - This function accepts only keyword arguments, none of which are - mandatory. - - INPUT: - - - ``new_points`` -- list of integers, indices of boundary points of - :meth:`Delta_polar`, which should be added as rays to the - subdividing fan - - - all other arguments will be passed to - :meth:`~sage.schemes.toric.variety.ToricVariety_field.resolve` - method of (general) toric varieties; see its documentation for - details - - OUTPUT: - - - :class:`CPR-Fano toric variety ` if there - was no ``new_rays`` argument and :class:`toric variety - ` otherwise. - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: FTV = CPRFanoToricVariety(Delta=diamond) - sage: FTV.coordinate_points() - (0, 1, 2, 3) - sage: FTV.gens() - (z0, z1, z2, z3) - sage: FTV_res = FTV.resolve(new_points=[6,8]) - Traceback (most recent call last): - ... - ValueError: the origin (point #6) - cannot be used for subdivision! - sage: FTV_res = FTV.resolve(new_points=[8,5]); FTV_res - 2-d CPR-Fano toric variety covered by 6 affine patches - sage: FTV_res.coordinate_points() - (0, 1, 2, 3, 8, 5) - sage: FTV_res.gens() - (z0, z1, z2, z3, z8, z5) - - sage: TV_res = FTV.resolve(new_rays=[(1,2)]); TV_res - 2-d toric variety covered by 5 affine patches - sage: TV_res.gens() - (z0, z1, z2, z3, z4) - """ - # Reasons to override the base class: - # - allow using polytope point indices for subdivision - # - handle automatic name creation in a different fashion - # - return CPR-Fano toric variety if the above feature was used and - # just toric variety if subdivision involves rays - if "new_rays" in kwds: - if "new_points" in kwds: - raise ValueError("you cannot give new_points and new_rays at " - "the same time!") - return super().resolve(**kwds) - # Now we need to construct another Fano variety - new_points = kwds.pop("new_points", ()) - coordinate_points = self.coordinate_points() - new_points = tuple(point for point in new_points - if point not in coordinate_points) - Delta_polar = self._Delta_polar - if Delta_polar.origin() in new_points: - raise ValueError("the origin (point #%d) cannot be used for " - "subdivision!" % Delta_polar.origin()) - if new_points: - coordinate_points = coordinate_points + new_points - point_to_ray = {point: n - for n, point in enumerate(coordinate_points)} - else: - point_to_ray = self._point_to_ray - new_rays = [Delta_polar.point(point) for point in new_points] - coordinate_name_indices = kwds.pop("coordinate_name_indices", - coordinate_points) - fan = self.fan() - if "coordinate_names" in kwds: - coordinate_names = kwds.pop("coordinate_names") - else: - coordinate_names = list(self.variable_names()) - coordinate_names.extend(normalize_names(ngens=len(new_rays), - indices=coordinate_name_indices[fan.nrays():], - prefix=self._coordinate_prefix)) - coordinate_names.append(self._coordinate_prefix + "+") - rfan = fan.subdivide(new_rays=new_rays, **kwds) - resolution = CPRFanoToricVariety_field(Delta_polar, rfan, - coordinate_points, point_to_ray, coordinate_names, - coordinate_name_indices, self.base_ring()) - R = self.coordinate_ring() - R_res = resolution.coordinate_ring() - resolution_map = resolution.hom(R.hom(R_res.gens()[:R.ngens()]), self) - resolution._resolution_map = resolution_map - return resolution - - class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric): r""" Construct an anticanonical hypersurface of a CPR-Fano toric variety. @@ -1095,14 +989,14 @@ def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, class NefCompleteIntersection(AlgebraicScheme_subscheme_toric): r""" - Construct a nef complete intersection in a CPR-Fano toric variety. + Construct a nef complete intersection in a Fano toric variety. INPUT: - - ``P_Delta`` -- a :class:`CPR-Fano toric variety - ` associated to a reflexive polytope `\Delta` + - ``P_Delta`` -- a :class:`Fano toric variety + ` associated to a reflexive polytope `\Delta` - - see :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for + - see :meth:`FanoToricVariety_field.nef_complete_intersection` for documentation on all other acceptable parameters OUTPUT: @@ -1117,19 +1011,19 @@ class NefCompleteIntersection(AlgebraicScheme_subscheme_toric): Nef-partition {0, 1, 3} ⊔ {2, 4, 5} sage: X = CPRFanoToricVariety(Delta_polar=o) sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 8 affine patches defined by: a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 - See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for a + See :meth:`FanoToricVariety_field.nef_complete_intersection` for a more elaborate example. """ def __init__(self, P_Delta, nef_partition, monomial_points='all', coefficient_names=None, coefficient_name_indices=None, coefficients=None): r""" - See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for + See :meth:`FanoToricVariety_field.nef_complete_intersection` for documentation. TESTS:: @@ -1138,23 +1032,23 @@ def __init__(self, P_Delta, nef_partition, sage: np = o.nef_partitions()[0] sage: np Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: X = FanoToricVariety(Delta_polar=o) sage: from sage.schemes.toric.fano_variety import * sage: NefCompleteIntersection(X, np) - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 8 affine patches defined by: a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 """ - if not isinstance(P_Delta, CPRFanoToricVariety_field): + if not isinstance(P_Delta, FanoToricVariety_field): raise TypeError("nef complete intersections can only be " "constructed for CPR-Fano toric varieties!" "\nGot: %s" % P_Delta) if nef_partition.Delta() is not P_Delta.Delta(): raise ValueError("polytopes 'Delta' of the nef-partition and the " - "CPR-Fano toric variety must be the same!") + "Fano toric variety must be the same!") self._nef_partition = nef_partition k = nef_partition.nparts() # Pre-normalize all parameters @@ -1232,9 +1126,9 @@ def cohomology_class(self): sage: o = lattice_polytope.cross_polytope(3) sage: np = o.nef_partitions()[0]; np Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: X = FanoToricVariety(Delta_polar=o) sage: CI = X.nef_complete_intersection(np); CI - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 8 affine patches defined by: a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 @@ -1258,9 +1152,9 @@ def nef_partition(self): sage: o = lattice_polytope.cross_polytope(3) sage: np = o.nef_partitions()[0]; np Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: X = FanoToricVariety(Delta_polar=o) sage: CI = X.nef_complete_intersection(np); CI - Closed subscheme of 3-d CPR-Fano toric variety + Closed subscheme of 3-d Fano toric variety covered by 8 affine patches defined by: a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 @@ -1323,4 +1217,4 @@ def add_variables(field, variables): for v in variables: if v not in new_variables: new_variables.append(v) - return PolynomialRing(field, new_variables).fraction_field() + return PolynomialRing(field, new_variables).fraction_field() \ No newline at end of file From ceb468b7cb1e493ec17d4e822d1bacd54b922539 Mon Sep 17 00:00:00 2001 From: local-ring Date: Tue, 15 Jul 2025 16:57:45 -0400 Subject: [PATCH 3/6] clean superclass doctest --- src/sage/schemes/toric/all.py | 2 +- src/sage/schemes/toric/cpr_fano_variety.py | 2441 -------------------- src/sage/schemes/toric/fano_variety.py | 1036 ++++++++- src/sage/schemes/toric/library.py | 2 +- 4 files changed, 958 insertions(+), 2523 deletions(-) delete mode 100644 src/sage/schemes/toric/cpr_fano_variety.py diff --git a/src/sage/schemes/toric/all.py b/src/sage/schemes/toric/all.py index 17965d2899c..4612e898ef9 100644 --- a/src/sage/schemes/toric/all.py +++ b/src/sage/schemes/toric/all.py @@ -4,6 +4,6 @@ lazy_import('sage.schemes.toric.weierstrass', 'WeierstrassForm') lazy_import('sage.schemes.toric.variety', ['AffineToricVariety', 'ToricVariety']) lazy_import('sage.schemes.toric.library', 'toric_varieties') -lazy_import('sage.schemes.toric.fano_variety', 'CPRFanoToricVariety') +lazy_import('sage.schemes.toric.fano_variety', ['FanoToricVariety', 'CPRFanoToricVariety']) lazy_import('sage.schemes.toric.ideal', 'ToricIdeal') del lazy_import diff --git a/src/sage/schemes/toric/cpr_fano_variety.py b/src/sage/schemes/toric/cpr_fano_variety.py deleted file mode 100644 index 7808bbf3260..00000000000 --- a/src/sage/schemes/toric/cpr_fano_variety.py +++ /dev/null @@ -1,2441 +0,0 @@ -# sage.doctest: needs sage.geometry.polyhedron sage.graphs -r""" -Fano toric varieties - -This module provides support for (Crepant Partial Resolutions of) Fano toric -varieties, corresponding to crepant subdivisions of face fans of reflexive -:class:`lattice polytopes -`. -The interface is provided via :func:`CPRFanoToricVariety`. - -A careful exposition of different flavours of Fano varieties can be found in -the paper by Benjamin Nill [Nil2005]_. The main goal of this module is to -support work with **Gorenstein weak Fano toric varieties**. Such a variety -corresponds to a **coherent crepant refinement of the normal fan of a -reflexive polytope** `\Delta`, where crepant means that primitive generators -of the refining rays lie on the facets of the polar polytope `\Delta^\circ` -and coherent (a.k.a. regular or projective) means that there exists a strictly -upper convex piecewise linear function whose domains of linearity are -precisely the maximal cones of the subdivision. These varieties are important -for string theory in physics, as they serve as ambient spaces for mirror pairs -of Calabi-Yau manifolds via constructions due to Victor V. Batyrev -[Bat1994]_ and Lev A. Borisov [Bor1993]_. - -From the combinatorial point of view, the "crepant" requirement is much more simple -and natural to work with than "coherent." For this reason, the code in this -module will allow work with arbitrary crepant subdivisions without checking -whether they are coherent or not. We refer to corresponding toric varieties as -**CPR-Fano toric varieties**. - -REFERENCES: - -- [Bat1994]_ -- [Bor1993]_ -- [CD2007]_ -- [Nil2005]_ - -AUTHORS: - -- Andrey Novoseltsev (2010-05-18): initial version. - -EXAMPLES: - -Most of the functions available for Fano toric varieties are the same as -for general toric varieties, so here we will concentrate only on -Calabi-Yau subvarieties, which were the primary goal for creating this -module. - -For our first example we realize the projective plane as a Fano toric -variety:: - - sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) - sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) - -Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau -manifold:: - - sage: P2.anticanonical_hypersurface(monomial_points='all') - Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: - a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 - + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 - -In many cases, it is sufficient to work with the "simplified polynomial -moduli space" of anticanonical hypersurfaces:: - - sage: P2.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 - -The mirror family to these hypersurfaces lives inside the Fano toric -variety obtained using ``simplex`` as ``Delta`` instead of ``Delta_polar``:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points='all') - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety covered by 9 affine patches defined by: - a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 - + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 - -Here we have taken the resolved version of the ambient space for the -mirror family, but in fact we don't have to resolve singularities -corresponding to the interior points of facets - they are singular -points which do not lie on a generic anticanonical hypersurface:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points="all but facets") - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 - -This looks very similar to our second version of the anticanonical -hypersurface of the projective plane, as expected, since all -one-dimensional Calabi-Yau manifolds are elliptic curves! - -Now let's take a look at a toric realization of `M`-polarized K3 surfaces -studied by Adrian Clingher and Charles F. Doran in [CD2007]_:: - - sage: p4318 = ReflexivePolytope(3, 4318) - sage: FTV = CPRFanoToricVariety(Delta_polar=p4318) - sage: FTV.anticanonical_hypersurface() - Closed subscheme of 3-d CPR-Fano toric variety covered by 4 affine patches defined by: - a0*z2^12 + a4*z2^6*z3^6 + a3*z3^12 + a8*z0*z1*z2*z3 + a2*z1^3 + a1*z0^2 - -Below you will find detailed descriptions of available functions. Current -functionality of this module is very basic, but it is under active -development and hopefully will improve in future releases of Sage. If there -are some particular features that you would like to see implemented ASAP, -please consider reporting them to the Sage Development Team or even -implementing them on your own as a patch for inclusion! -""" -# The first example of the tutorial is taken from -# CPRFanoToricVariety_field.anticanonical_hypersurface - -# **************************************************************************** -# Copyright (C) 2010 Andrey Novoseltsev -# Copyright (C) 2010 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# https://www.gnu.org/licenses/ -# **************************************************************************** - -import re - -from sage.geometry.cone import Cone -from sage.geometry.fan import FaceFan -from sage.geometry.fan import Fan -from sage.geometry.lattice_polytope import LatticePolytope -from sage.misc.latex import latex -from sage.misc.misc_c import prod -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.rational_field import QQ - -from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_base -from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic -from sage.rings.fraction_field import FractionField_generic - -from sage.schemes.toric.toric_subscheme import AlgebraicScheme_subscheme_toric -from sage.schemes.toric.variety import ( - ToricVariety_field, - normalize_names) -from sage.structure.all import coercion_model -from sage.categories.fields import Fields -_Fields = Fields() - - -# Default coefficient for anticanonical hypersurfaces -DEFAULT_COEFFICIENT = "a" -# Default coefficients for nef complete intersections -DEFAULT_COEFFICIENTS = tuple(chr(i) for i in range(ord("a"), ord("z") + 1)) - - -def is_CPRFanoToricVariety(x): - r""" - Check if ``x`` is a CPR-Fano toric variety. - - INPUT: - - - ``x`` -- anything - - OUTPUT: - - - ``True`` if ``x`` is a :class:`CPR-Fano toric variety - ` and ``False`` otherwise. - - .. NOTE:: - - While projective spaces are Fano toric varieties mathematically, they - are not toric varieties in Sage due to efficiency considerations, so - this function will return ``False``. - - EXAMPLES:: - - sage: from sage.schemes.toric.fano_variety import is_CPRFanoToricVariety - sage: is_CPRFanoToricVariety(1) - doctest:warning... - DeprecationWarning: The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead. - See https://github.com/sagemath/sage/issues/38022 for details. - False - sage: FTV = toric_varieties.P2() - sage: FTV - 2-d CPR-Fano toric variety covered by 3 affine patches - sage: is_CPRFanoToricVariety(FTV) - True - sage: is_CPRFanoToricVariety(ProjectiveSpace(2)) - False - """ - from sage.misc.superseded import deprecation - deprecation(38022, "The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead.") - return isinstance(x, CPRFanoToricVariety_field) - - -def CPRFanoToricVariety(Delta=None, - Delta_polar=None, - coordinate_points=None, - charts=None, - coordinate_names=None, - names=None, - coordinate_name_indices=None, - make_simplicial=False, - base_ring=None, - base_field=None, - check=True): - r""" - Construct a CPR-Fano toric variety. - - .. NOTE:: - - See documentation of the module - :mod:`~sage.schemes.toric.fano_variety` for the used - definitions and supported varieties. - - Due to the large number of available options, it is recommended to always - use keyword parameters. - - INPUT: - - - ``Delta`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be - given, but not both at the same time, since one is completely determined - by another via :meth:`polar - ` method. - - - ``Delta_polar`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must - be given, but not both at the same time, since one is completely - determined by another via :meth:`polar - ` method. - - - ``coordinate_points`` -- list of integers or string. A list will be - interpreted as indices of (boundary) points of ``Delta_polar`` which - should be used as rays of the underlying fan. It must include all - vertices of ``Delta_polar`` and no repetitions are allowed. A string - must be one of the following descriptions of points of ``Delta_polar``: - - * "vertices" (default), - * "all" (will not include the origin), - * "all but facets" (will not include points in the relative interior of - facets); - - - ``charts`` -- list of lists of elements from ``coordinate_points``. Each - of these lists must define a generating cone of a fan subdividing the - normal fan of ``Delta``. Default ``charts`` correspond to the normal fan - of ``Delta`` without subdivision. The fan specified by ``charts`` will - be subdivided to include all of the requested ``coordinate_points``. - - - ``coordinate_names`` -- names of variables for the coordinate ring, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed variable names will be - created automatically. - - - ``names`` -- an alias of ``coordinate_names`` for internal - use. You may specify either ``names`` or ``coordinate_names``, - but not both. - - - ``coordinate_name_indices`` -- list of integers, indices for indexed - variables. If not given, the index of each variable will coincide with - the index of the corresponding point of ``Delta_polar``. - - - ``make_simplicial`` -- if ``True``, the underlying fan will be made - simplicial (default: ``False``) - - - ``base_ring`` -- base field of the CPR-Fano toric variety - (default: `\QQ`) - - - ``base_field`` -- alias for ``base_ring``. Takes precedence if - both are specified. - - - ``check`` -- by default the input data will be checked for correctness - (e.g. that ``charts`` do form a subdivision of the normal fan of - ``Delta``). If you know for sure that the input is valid, you may - significantly decrease construction time using ``check=False`` option. - - OUTPUT: :class:`CPR-Fano toric variety ` - - EXAMPLES: - - We start with the product of two projective lines:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: diamond.vertices() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) - sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches - sage: P1xP1.fan() - Rational polyhedral fan in 2-d lattice M - sage: P1xP1.fan().rays() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - - "Unfortunately," this variety is smooth to start with and we cannot - perform any subdivisions of the underlying fan without leaving the - category of CPR-Fano toric varieties. Our next example starts with a - square:: - - sage: square = diamond.polar() - sage: square.vertices() - N( 1, 1), N( 1, -1), - N(-1, -1), N(-1, 1) - in 2-d lattice N - sage: square.points() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N(-1, 0), N( 0, -1), - N( 0, 0), N( 0, 1), N( 1, 0) - in 2-d lattice N - - We will construct several varieties associated to it:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), - N(-1, -1), N(-1, 1) - in 2-d lattice N - sage: FTV.gens() - (z0, z1, z2, z3) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,8]) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N( 1, 0) - in 2-d lattice N - sage: FTV.gens() - (z0, z1, z2, z3, z8) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[8,0,2,1,3], - ....: coordinate_names='x+') - sage: FTV.fan().rays() - N( 1, 0), N( 1, 1), N(-1, -1), - N( 1, -1), N(-1, 1) - in 2-d lattice N - sage: FTV.gens() - (x8, x0, x2, x1, x3) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+") - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), N(-1, 1), - N(-1, 0), N( 0, -1), N( 0, 1), N( 1, 0) - in 2-d lattice N - sage: FTV.gens() - (x, y, Z2, Z3, Z4, Z5, Z7, Z8) - - Note that ``Z6`` is "missing". This is due to the fact that the 6-th point - of ``square`` is the origin, and all automatically created names have the - same indices as corresponding points of - :meth:`~CPRFanoToricVariety_field.Delta_polar`. This is usually very - convenient, especially if you have to work with several partial - resolutions of the same Fano toric variety. However, you can change it, if - you want:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+", - ....: coordinate_name_indices=list(range(8))) - sage: FTV.gens() - (x, y, Z2, Z3, Z4, Z5, Z6, Z7) - - Note that you have to provide indices for *all* variables, including those - that have "completely custom" names. Again, this is usually convenient, - because you can add or remove "custom" variables without disturbing too - much "automatic" ones:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x Z+", - ....: coordinate_name_indices=list(range(8))) - sage: FTV.gens() - (x, Z1, Z2, Z3, Z4, Z5, Z6, Z7) - - If you prefer to always start from zero, you will have to shift indices - accordingly:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x Z+", - ....: coordinate_name_indices=[0] + list(range(7))) - sage: FTV.gens() - (x, Z0, Z1, Z2, Z3, Z4, Z5, Z6) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+", - ....: coordinate_name_indices=[0]*2 + list(range(6))) - sage: FTV.gens() - (x, y, Z0, Z1, Z2, Z3, Z4, Z5) - - So you always can get any names you want, somewhat complicated default - behaviour was designed with the hope that in most cases you will have no - desire to provide different names. - - Now we will use the possibility to specify initial charts:: - - sage: charts = [(0,1), (1,2), (2,3), (3,0)] - - (these charts actually form exactly the face fan of our square) :: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=charts) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N(-1, 0) - in 2-d lattice N - sage: [cone.ambient_ray_indices() for cone in FTV.fan()] - [(0, 1), (1, 2), (2, 4), (3, 4), (0, 3)] - - If charts are wrong, it should be detected:: - - sage: bad_charts = charts + [(3,0)] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: you have provided 5 cones, but only 4 of them are maximal! - Use discard_faces=True if you indeed need to construct a fan from these cones. - - These charts are technically correct, they just happened to list one of - them twice, but it is assumed that such a situation will not happen. It is - especially important when you try to speed up your code:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts, - ....: check=False) - Traceback (most recent call last): - ... - IndexError: list assignment index out of range - - In this case you still get an error message, but it is harder to figure out - what is going on. It may also happen that "everything will still work" in - the sense of not crashing, but work with such an invalid variety may lead to - mathematically wrong results, so use ``check=False`` carefully! - - Here are some other possible mistakes:: - - sage: bad_charts = charts + [(0,2)] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: (0, 2) does not form a chart of a subdivision of - the face fan of 2-d reflexive polytope #14 in 2-d lattice N! - - sage: bad_charts = charts[:-1] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: given charts do not form a complete fan! - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[1,2,3,4]) - Traceback (most recent call last): - ... - ValueError: all 4 vertices of Delta_polar must be used for coordinates! - Got: [1, 2, 3, 4] - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,0,1,2,3,4]) - Traceback (most recent call last): - ... - ValueError: no repetitions are allowed for coordinate points! - Got: [0, 0, 1, 2, 3, 4] - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,6]) - Traceback (most recent call last): - ... - ValueError: the origin (point #6) cannot be used for a coordinate! - Got: [0, 1, 2, 3, 6] - - Here is a shorthand for defining the toric variety and homogeneous - coordinates in one go:: - - sage: P1xP1. = CPRFanoToricVariety(Delta_polar=diamond) - sage: (a^2+b^2) * (c+d) - a^2*c + b^2*c + a^2*d + b^2*d - """ - if names is not None: - if coordinate_names is not None: - raise ValueError('You must not specify both coordinate_names and names!') - coordinate_names = names - # Check/normalize Delta_polar - if Delta is None and Delta_polar is None: - raise ValueError("either Delta or Delta_polar must be given!") - elif Delta is not None and Delta_polar is not None: - raise ValueError("Delta and Delta_polar cannot be given together!") - elif Delta_polar is None: - Delta_polar = Delta.polar() - elif not Delta_polar.is_reflexive(): - raise ValueError("Delta_polar must be reflexive!") - # Check/normalize coordinate_points and construct fan rays - if coordinate_points is None: - coordinate_points = list(range(Delta_polar.nvertices())) - if charts is not None: - for chart in charts: - for point in chart: - if point not in coordinate_points: - coordinate_points.append(point) - elif coordinate_points == "vertices": - coordinate_points = list(range(Delta_polar.nvertices())) - elif coordinate_points == "all": - coordinate_points = list(range(Delta_polar.npoints())) - coordinate_points.remove(Delta_polar.origin()) - elif coordinate_points == "all but facets": - coordinate_points = Delta_polar.skeleton_points(Delta_polar.dim() - 2) - elif isinstance(coordinate_points, str): - raise ValueError("unrecognized description of the coordinate points!" - "\nGot: %s" % coordinate_points) - elif check: - cp_set = set(coordinate_points) - if len(cp_set) != len(coordinate_points): - raise ValueError( - "no repetitions are allowed for coordinate points!\nGot: %s" - % coordinate_points) - if not cp_set.issuperset(list(range(Delta_polar.nvertices()))): - raise ValueError("all %d vertices of Delta_polar must be used " - "for coordinates!\nGot: %s" - % (Delta_polar.nvertices(), coordinate_points)) - if Delta_polar.origin() in cp_set: - raise ValueError("the origin (point #%d) cannot be used for a " - "coordinate!\nGot: %s" - % (Delta_polar.origin(), coordinate_points)) - point_to_ray = {point: n - for n, point in enumerate(coordinate_points)} - # This can be simplified if LatticePolytopeClass is adjusted. - rays = [Delta_polar.point(p) for p in coordinate_points] - # Check/normalize charts and construct the fan based on them. - if charts is None: - # Start with the face fan - fan = FaceFan(Delta_polar) - else: - # First of all, check that each chart is completely contained in a - # single facet of Delta_polar, otherwise they do not form a - # subdivision of the face fan of Delta_polar - if check: - facet_sets = [frozenset(facet.ambient_point_indices()) - for facet in Delta_polar.facets()] - for chart in charts: - is_bad = True - for fset in facet_sets: - if fset.issuperset(chart): - is_bad = False - break - if is_bad: - raise ValueError( - "%s does not form a chart of a subdivision of the " - "face fan of %s!" % (chart, Delta_polar)) - # We will construct the initial fan from Cone objects: since charts - # may not use all of the necessary rays, alternative form is tedious - # With check=False it should not be long anyway. - cones = [Cone((rays[point_to_ray[point]] for point in chart), - check=check) - for chart in charts] - fan = Fan(cones, check=check) - if check and not fan.is_complete(): - raise ValueError("given charts do not form a complete fan!") - # Subdivide this fan to use all required points - fan = fan.subdivide(new_rays=(ray for ray in rays - if ray not in fan.rays().set()), - make_simplicial=make_simplicial) - # Now create yet another fan making sure that the order of the rays is - # the same as requested (it is a bit difficult to get it from the start) - trans = {} - for n, ray in enumerate(fan.rays()): - trans[n] = rays.index(ray) - cones = tuple(tuple(sorted(trans[r] for r in cone.ambient_ray_indices())) - for cone in fan) - fan = Fan(cones, rays, check=False) - # Check/normalize base_field - if base_field is not None: - base_ring = base_field - if base_ring is None: - base_ring = QQ - elif base_ring not in _Fields: - raise TypeError("need a field to construct a Fano toric variety!" - "\n Got %s" % base_ring) - fan._is_complete = True # At this point it must be for sure - return CPRFanoToricVariety_field( - Delta_polar, fan, coordinate_points, - point_to_ray, coordinate_names, coordinate_name_indices, base_ring) - - -class CPRFanoToricVariety_field(ToricVariety_field): - r""" - Construct a CPR-Fano toric variety associated to a reflexive polytope. - - .. WARNING:: - - This class does not perform any checks of correctness of input and it - does assume that the internal structure of the given parameters is - coordinated in a certain way. Use - :func:`CPRFanoToricVariety` to construct CPR-Fano toric varieties. - - .. NOTE:: - - See documentation of the module - :mod:`~sage.schemes.toric.fano_variety` for the used - definitions and supported varieties. - - INPUT: - - - ``Delta_polar`` -- reflexive polytope - - - ``fan`` -- rational polyhedral fan subdividing the face fan of - ``Delta_polar`` - - - ``coordinate_points`` -- list of indices of points of ``Delta_polar`` - used for rays of ``fan`` - - - ``point_to_ray`` -- dictionary mapping the index of a coordinate point - to the index of the corresponding ray - - - ``coordinate_names`` -- names of the variables of the coordinate ring in - the format accepted by - :func:`~sage.schemes.toric.variety.normalize_names` - - - ``coordinate_name_indices`` -- indices for indexed variables, - if ``None``, will be equal to ``coordinate_points`` - - - ``base_field`` -- base field of the CPR-Fano toric variety - - OUTPUT: :class:`CPR-Fano toric variety ` - - TESTS:: - - sage: P1xP1 = CPRFanoToricVariety( - ....: Delta_polar=lattice_polytope.cross_polytope(2)) - sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches - """ - - def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray, - coordinate_names, coordinate_name_indices, base_field): - r""" - See :class:`CPRFanoToricVariety_field` for documentation. - - Use ``CPRFanoToricVariety`` to construct CPR-Fano toric varieties. - - TESTS:: - - sage: P1xP1 = CPRFanoToricVariety( - ....: Delta_polar=lattice_polytope.cross_polytope(2)) - sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches - """ - self._Delta_polar = Delta_polar - self._coordinate_points = tuple(coordinate_points) - self._point_to_ray = point_to_ray - # Check/normalize coordinate_indices - if coordinate_name_indices is None: - coordinate_name_indices = coordinate_points - super().__init__(fan, coordinate_names, - coordinate_name_indices, base_field) - - def _latex_(self): - r""" - Return a LaTeX representation of ``self``. - - OUTPUT: string - - TESTS:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: print(P1xP1._latex_()) - \mathbb{P}_{\Delta^{2}_{14}} - """ - return r"\mathbb{P}_{%s}" % latex(self.Delta()) - - def _repr_(self): - r""" - Return a string representation of ``self``. - - OUTPUT: string - - TESTS:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: print(P1xP1._repr_()) - 2-d CPR-Fano toric variety covered by 4 affine patches - """ - return ("%d-d CPR-Fano toric variety covered by %d affine patches" - % (self.dimension_relative(), self.fan().ngenerating_cones())) - - def anticanonical_hypersurface(self, **kwds): - r""" - Return an anticanonical hypersurface of ``self``. - - .. NOTE:: - - The returned hypersurface may be actually a subscheme of - **another** CPR-Fano toric variety: if the base field of ``self`` - does not include all of the required names for generic monomial - coefficients, it will be automatically extended. - - Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is a refinement of the normal fan of - `\Delta`. This function accepts only keyword parameters. - - INPUT: - - - ``monomial_points`` -- list of integers or a string. A list will be - interpreted as indices of points of `\Delta` which should be used - for monomials of this hypersurface. A string must be one of the - following descriptions of points of `\Delta`: - - * "vertices", - * "vertices+origin", - * "all", - * "simplified" (default) -- all points of `\Delta` except for - the interior points of facets, this choice corresponds to working - with the "simplified polynomial moduli space" of anticanonical - hypersurfaces; - - - ``coefficient_names`` -- names for the monomial coefficients, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed coefficient names will - be created automatically. - - - ``coefficient_name_indices`` -- list of integers, indices for - indexed coefficients. If not given, the index of each coefficient - will coincide with the index of the corresponding point of `\Delta`. - - - ``coefficients`` -- as an alternative to specifying coefficient - names and/or indices, you can give the coefficients themselves as - arbitrary expressions and/or strings. Using strings allows you to - easily add "parameters": the base field of ``self`` will be extended - to include all necessary names. - - OUTPUT: - - - an :class:`anticanonical hypersurface ` of - ``self`` (with the extended base field, if necessary). - - EXAMPLES: - - We realize the projective plane as a Fano toric variety:: - - sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) - sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) - - Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau - manifold:: - - sage: P2.anticanonical_hypersurface(monomial_points='all') - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 - + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 - - In many cases it is sufficient to work with the "simplified polynomial - moduli space" of anticanonical hypersurfaces:: - - sage: P2.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 - - The mirror family to these hypersurfaces lives inside the Fano toric - variety obtained using ``simplex`` as ``Delta`` instead of - ``Delta_polar``:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, - ....: coordinate_points='all') - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety - covered by 9 affine patches defined by: - a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 - + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 - - Here we have taken the resolved version of the ambient space for the - mirror family, but in fact we don't have to resolve singularities - corresponding to the interior points of facets - they are singular - points which do not lie on a generic anticanonical hypersurface:: - - sage: FTV = CPRFanoToricVariety(Delta=simplex, - ....: coordinate_points="all but facets") - sage: FTV.anticanonical_hypersurface(monomial_points='simplified') - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 - - This looks very similar to our second anticanonical - hypersurface of the projective plane, as expected, since all - one-dimensional Calabi-Yau manifolds are elliptic curves! - - All anticanonical hypersurfaces constructed above were generic with - automatically generated coefficients. If you want, you can specify your - own names :: - - sage: FTV.anticanonical_hypersurface(coefficient_names="a b c d") - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - a*z0^3 + b*z1^3 + d*z0*z1*z2 + c*z2^3 - - or give concrete coefficients :: - - sage: FTV.anticanonical_hypersurface(coefficients=[1, 2, 3, 4]) - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - z0^3 + 2*z1^3 + 4*z0*z1*z2 + 3*z2^3 - - or even mix numerical coefficients with some expressions :: - - sage: H = FTV.anticanonical_hypersurface( - ....: coefficients=[0, "t", "1/t", "psi/(psi^2 + phi)"]) - sage: H - Closed subscheme of 2-d CPR-Fano toric variety - covered by 3 affine patches defined by: - t*z1^3 + psi/(phi + psi^2)*z0*z1*z2 + 1/t*z2^3 - sage: R = H.ambient_space().base_ring() - sage: R - Fraction Field of - Multivariate Polynomial Ring in phi, psi, t over Rational Field - """ - # The example above is also copied to the tutorial section in the - # main documentation of the module. - return AnticanonicalHypersurface(self, **kwds) - - def change_ring(self, F): - r""" - Return a CPR-Fano toric variety over field ``F``, otherwise the same - as ``self``. - - INPUT: - - - ``F`` -- field - - OUTPUT: :class:`CPR-Fano toric variety ` over ``F`` - - .. NOTE:: - - There is no need to have any relation between ``F`` and the base - field of ``self``. If you do want to have such a relation, use - :meth:`base_extend` instead. - - EXAMPLES:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: P1xP1.base_ring() - Rational Field - sage: P1xP1_RR = P1xP1.change_ring(RR) - sage: P1xP1_RR.base_ring() - Real Field with 53 bits of precision - sage: P1xP1_QQ = P1xP1_RR.change_ring(QQ) - sage: P1xP1_QQ.base_ring() - Rational Field - sage: P1xP1_RR.base_extend(QQ) - Traceback (most recent call last): - ... - ValueError: no natural map from the base ring - (=Real Field with 53 bits of precision) to R (=Rational Field)! - sage: R = PolynomialRing(QQ, 2, 'a') - sage: P1xP1.change_ring(R) - Traceback (most recent call last): - ... - TypeError: need a field to construct a Fano toric variety! - Got Multivariate Polynomial Ring in a0, a1 over Rational Field - """ - if self.base_ring() == F: - return self - elif F not in _Fields: - raise TypeError("need a field to construct a Fano toric variety!" - "\n Got %s" % F) - else: - return CPRFanoToricVariety_field(self._Delta_polar, self._fan, - self._coordinate_points, self._point_to_ray, - self.variable_names(), None, F) - # coordinate_name_indices do not matter, we give explicit - # names for all variables - - def coordinate_point_to_coordinate(self, point): - r""" - Return the variable of the coordinate ring corresponding to ``point``. - - INPUT: - - - ``point`` -- integer from the list of :meth:`coordinate_points` - - OUTPUT: the corresponding generator of the coordinate ring of ``self`` - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: FTV = CPRFanoToricVariety(diamond, coordinate_points=[0,1,2,3,8]) - sage: FTV.coordinate_points() - (0, 1, 2, 3, 8) - sage: FTV.gens() - (z0, z1, z2, z3, z8) - sage: FTV.coordinate_point_to_coordinate(8) - z8 - """ - return self.gen(self._point_to_ray[point]) - - def coordinate_points(self): - r""" - Return indices of points of :meth:`Delta_polar` used for coordinates. - - OUTPUT: :class:`tuple` of integers - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: square = diamond.polar() - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,8]) - sage: FTV.coordinate_points() - (0, 1, 2, 3, 8) - sage: FTV.gens() - (z0, z1, z2, z3, z8) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all') - sage: FTV.coordinate_points() - (0, 1, 2, 3, 4, 5, 7, 8) - sage: FTV.gens() - (z0, z1, z2, z3, z4, z5, z7, z8) - - Note that one point is missing, namely :: - - sage: square.origin() - 6 - """ - return self._coordinate_points - - def Delta(self): - r""" - Return the reflexive polytope associated to ``self``. - - OUTPUT: - - - reflexive :class:`lattice polytope - `. The - underlying fan of ``self`` is a coherent subdivision of the - *normal fan* of this polytope. - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) - sage: P1xP1.Delta() - 2-d reflexive polytope #14 in 2-d lattice N - sage: P1xP1.Delta() is diamond.polar() - True - """ - return self._Delta_polar.polar() - - def Delta_polar(self): - r""" - Return polar of :meth:`Delta`. - - OUTPUT: - - - reflexive :class:`lattice polytope - `. The - underlying fan of ``self`` is a coherent subdivision of the - *face fan* of this polytope. - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) - sage: P1xP1.Delta_polar() - 2-d reflexive polytope #3 in 2-d lattice M - sage: P1xP1.Delta_polar() is diamond - True - sage: P1xP1.Delta_polar() is P1xP1.Delta().polar() - True - """ - return self._Delta_polar - - def nef_complete_intersection(self, nef_partition, **kwds): - r""" - Return a nef complete intersection in ``self``. - - .. NOTE:: - - The returned complete intersection may be actually a subscheme of - **another** CPR-Fano toric variety: if the base field of ``self`` - does not include all of the required names for monomial - coefficients, it will be automatically extended. - - Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is a refinement of the normal fan of - `\Delta`. Other polytopes are described in the documentation of - :class:`nef-partitions ` - of :class:`reflexive polytopes - `. - - Except for the first argument, ``nef_partition``, this method accepts - only keyword parameters. - - INPUT: - - - ``nef_partition`` -- a `k`-part :class:`nef-partition - ` of `\Delta^\circ`, all - other parameters (if given) must be lists of length `k` - - - ``monomial_points`` -- the `i`-th element of this list is either a - list of integers or a string. A list will be interpreted as indices - of points of `\Delta_i` which should be used for monomials of the - `i`-th polynomial of this complete intersection. A string must be one - of the following descriptions of points of `\Delta_i`: - - * "vertices", - * "vertices+origin", - * "all" (default), - - when using this description, it is also OK to pass a single string as - ``monomial_points`` instead of repeating it `k` times. - - - ``coefficient_names`` -- the `i`-th element of this list specifies - names for the monomial coefficients of the `i`-th polynomial, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed coefficient names will - be created automatically. - - - ``coefficient_name_indices`` -- the `i`-th element of this list - specifies indices for indexed coefficients of the `i`-th polynomial. - If not given, the index of each coefficient will coincide with the - index of the corresponding point of `\Delta_i`. - - - ``coefficients`` -- as an alternative to specifying coefficient - names and/or indices, you can give the coefficients themselves as - arbitrary expressions and/or strings. Using strings allows you to - easily add "parameters": the base field of ``self`` will be extended - to include all necessary names. - - OUTPUT: - - - a :class:`nef complete intersection ` of - ``self`` (with the extended base field, if necessary). - - EXAMPLES: - - We construct several complete intersections associated to the same - nef-partition of the 3-dimensional reflexive polytope #2254:: - - sage: p = ReflexivePolytope(3, 2254) - sage: np = p.nef_partitions()[1]; np - Nef-partition {2, 3, 4, 7, 8} ⊔ {0, 1, 5, 6} - sage: X = CPRFanoToricVariety(Delta_polar=p) - sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d CPR-Fano toric variety - covered by 10 affine patches defined by: - a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 - + a3*z2*z3*z4*z7*z8 + a1*z0*z2, - b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 - + b5*z1*z3*z4*z5*z6*z7*z8 + b2*z2*z3*z6^2*z8^3 - + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 - - Now we include only monomials associated to vertices of `\Delta_i`:: - - sage: X.nef_complete_intersection(np, monomial_points='vertices') - Closed subscheme of 3-d CPR-Fano toric variety - covered by 10 affine patches defined by: - a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 - + a3*z2*z3*z4*z7*z8 + a1*z0*z2, - b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 - + b2*z2*z3*z6^2*z8^3 + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 - - (effectively, we set ``b5=0``). Next we provide coefficients explicitly - instead of using default generic names:: - - sage: X.nef_complete_intersection(np, - ....: monomial_points='vertices', - ....: coefficients=[("a", "a^2", "a/e", "c_i"), list(range(1,6))]) - Closed subscheme of 3-d CPR-Fano toric variety - covered by 10 affine patches defined by: - a*z1*z4^2*z5^2*z7^3 + a/e*z2*z4*z5*z6*z7^2*z8^2 - + (c_i)*z2*z3*z4*z7*z8 + (a^2)*z0*z2, - 4*z1*z4*z5^2*z6^2*z7^2*z8^2 + z2*z5*z6^3*z7*z8^4 - + 3*z2*z3*z6^2*z8^3 + 2*z1*z3^2*z4 + 5*z0*z1*z5*z6 - - Finally, we take a look at the generic representative of these complete - intersections in a completely resolved ambient toric variety:: - - sage: X = CPRFanoToricVariety(Delta_polar=p, - ....: coordinate_points='all') - sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d CPR-Fano toric variety - covered by 22 affine patches defined by: - a2*z2*z4*z5*z6*z7^2*z8^2*z9^2*z10^2*z11*z12*z13 - + a0*z1*z4^2*z5^2*z7^3*z9*z10^2*z12*z13 - + a3*z2*z3*z4*z7*z8*z9*z10*z11*z12 + a1*z0*z2, - b0*z2*z5*z6^3*z7*z8^4*z9^3*z10^2*z11^2*z12*z13^2 - + b3*z1*z4*z5^2*z6^2*z7^2*z8^2*z9^2*z10^2*z11*z12*z13^2 - + b2*z2*z3*z6^2*z8^3*z9^2*z10*z11^2*z12*z13 - + b5*z1*z3*z4*z5*z6*z7*z8*z9*z10*z11*z12*z13 - + b1*z1*z3^2*z4*z11*z12 + b4*z0*z1*z5*z6*z13 - """ - return NefCompleteIntersection(self, nef_partition, **kwds) - - def cartesian_product(self, other, - coordinate_names=None, coordinate_indices=None): - r""" - Return the Cartesian product of ``self`` with ``other``. - - INPUT: - - - ``other`` -- a (possibly - :class:`CPR-Fano `) :class:`toric variety - ` - - - ``coordinate_names`` -- names of variables for the coordinate ring, - see :func:`normalize_names` for acceptable formats. If not given, - indexed variable names will be created automatically. - - - ``coordinate_indices`` -- list of integers, indices for indexed - variables. If not given, the index of each variable will coincide - with the index of the corresponding ray of the fan. - - OUTPUT: - - - a :class:`toric variety - `, which is - :class:`CPR-Fano ` if ``other`` was. - - EXAMPLES:: - - sage: P1 = toric_varieties.P1() - sage: P2 = toric_varieties.P2() - sage: P1xP2 = P1.cartesian_product(P2); P1xP2 - 3-d CPR-Fano toric variety covered by 6 affine patches - sage: P1xP2.fan().rays() - N+N( 1, 0, 0), N+N(-1, 0, 0), N+N( 0, 1, 0), - N+N( 0, 0, 1), N+N( 0, -1, -1) - in 3-d lattice N+N - sage: P1xP2.Delta_polar() - 3-d reflexive polytope in 3-d lattice N+N - """ - if isinstance(other, CPRFanoToricVariety_field): - fan = self.fan().cartesian_product(other.fan()) - Delta_polar = LatticePolytope(fan.rays()) - - points = Delta_polar.points() - point_to_ray = {} - coordinate_points = [] - for ray_index, ray in enumerate(fan.rays()): - point = points.index(ray) - coordinate_points.append(point) - point_to_ray[point] = ray_index - - return CPRFanoToricVariety_field(Delta_polar, fan, - coordinate_points, point_to_ray, - coordinate_names, coordinate_indices, - self.base_ring()) - return super().cartesian_product(other) - - def resolve(self, **kwds): - r""" - Construct a toric variety whose fan subdivides the fan of ``self``. - - This function accepts only keyword arguments, none of which are - mandatory. - - INPUT: - - - ``new_points`` -- list of integers, indices of boundary points of - :meth:`Delta_polar`, which should be added as rays to the - subdividing fan - - - all other arguments will be passed to - :meth:`~sage.schemes.toric.variety.ToricVariety_field.resolve` - method of (general) toric varieties; see its documentation for - details - - OUTPUT: - - - :class:`CPR-Fano toric variety ` if there - was no ``new_rays`` argument and :class:`toric variety - ` otherwise. - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: FTV = CPRFanoToricVariety(Delta=diamond) - sage: FTV.coordinate_points() - (0, 1, 2, 3) - sage: FTV.gens() - (z0, z1, z2, z3) - sage: FTV_res = FTV.resolve(new_points=[6,8]) - Traceback (most recent call last): - ... - ValueError: the origin (point #6) - cannot be used for subdivision! - sage: FTV_res = FTV.resolve(new_points=[8,5]); FTV_res - 2-d CPR-Fano toric variety covered by 6 affine patches - sage: FTV_res.coordinate_points() - (0, 1, 2, 3, 8, 5) - sage: FTV_res.gens() - (z0, z1, z2, z3, z8, z5) - - sage: TV_res = FTV.resolve(new_rays=[(1,2)]); TV_res - 2-d toric variety covered by 5 affine patches - sage: TV_res.gens() - (z0, z1, z2, z3, z4) - """ - # Reasons to override the base class: - # - allow using polytope point indices for subdivision - # - handle automatic name creation in a different fashion - # - return CPR-Fano toric variety if the above feature was used and - # just toric variety if subdivision involves rays - if "new_rays" in kwds: - if "new_points" in kwds: - raise ValueError("you cannot give new_points and new_rays at " - "the same time!") - return super().resolve(**kwds) - # Now we need to construct another Fano variety - new_points = kwds.pop("new_points", ()) - coordinate_points = self.coordinate_points() - new_points = tuple(point for point in new_points - if point not in coordinate_points) - Delta_polar = self._Delta_polar - if Delta_polar.origin() in new_points: - raise ValueError("the origin (point #%d) cannot be used for " - "subdivision!" % Delta_polar.origin()) - if new_points: - coordinate_points = coordinate_points + new_points - point_to_ray = {point: n - for n, point in enumerate(coordinate_points)} - else: - point_to_ray = self._point_to_ray - new_rays = [Delta_polar.point(point) for point in new_points] - coordinate_name_indices = kwds.pop("coordinate_name_indices", - coordinate_points) - fan = self.fan() - if "coordinate_names" in kwds: - coordinate_names = kwds.pop("coordinate_names") - else: - coordinate_names = list(self.variable_names()) - coordinate_names.extend(normalize_names(ngens=len(new_rays), - indices=coordinate_name_indices[fan.nrays():], - prefix=self._coordinate_prefix)) - coordinate_names.append(self._coordinate_prefix + "+") - rfan = fan.subdivide(new_rays=new_rays, **kwds) - resolution = CPRFanoToricVariety_field(Delta_polar, rfan, - coordinate_points, point_to_ray, coordinate_names, - coordinate_name_indices, self.base_ring()) - R = self.coordinate_ring() - R_res = resolution.coordinate_ring() - resolution_map = resolution.hom(R.hom(R_res.gens()[:R.ngens()]), self) - resolution._resolution_map = resolution_map - return resolution - - -class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric): - r""" - Construct an anticanonical hypersurface of a CPR-Fano toric variety. - - INPUT: - - - ``P_Delta`` -- :class:`CPR-Fano toric variety - ` associated to a reflexive polytope `\Delta` - - - see :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for - documentation on all other acceptable parameters - - OUTPUT: - - :class:`anticanonical hypersurface ` of - ``P_Delta`` (with the extended base field, if necessary). - - EXAMPLES:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: import sage.schemes.toric.fano_variety as ftv - sage: ftv.AnticanonicalHypersurface(P1xP1) - Closed subscheme of 2-d CPR-Fano toric variety - covered by 4 affine patches defined by: - a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 - - See :meth:`~CPRFanoToricVariety_field.anticanonical_hypersurface()` for a - more elaborate example. - """ - def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, - coefficient_name_indices=None, coefficients=None): - r""" - See :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for - documentation. - - TESTS:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: import sage.schemes.toric.fano_variety as ftv - sage: ftv.AnticanonicalHypersurface(P1xP1) - Closed subscheme of 2-d CPR-Fano toric variety - covered by 4 affine patches defined by: - a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 - - Check that finite fields are handled correctly :issue:`14899`:: - - sage: F = GF(5^2, "a") # needs sage.rings.finite_rings - sage: X = P1xP1.change_ring(F) # needs sage.rings.finite_rings - sage: X.anticanonical_hypersurface(monomial_points='all', # needs sage.rings.finite_rings - ....: coefficients=[1]*X.Delta().npoints()) - Closed subscheme of 2-d CPR-Fano toric variety - covered by 4 affine patches defined by: - s^2*x^2 + s*t*x^2 + t^2*x^2 + s^2*x*y + s*t*x*y - + t^2*x*y + s^2*y^2 + s*t*y^2 + t^2*y^2 - """ - if not isinstance(P_Delta, CPRFanoToricVariety_field): - raise TypeError("anticanonical hypersurfaces can only be " - "constructed for CPR-Fano toric varieties!" - "\nGot: %s" % P_Delta) - Delta = P_Delta.Delta() - Delta_polar = Delta.polar() - # Monomial points normalization - if monomial_points == "vertices": - monomial_points = list(range(Delta.nvertices())) - elif monomial_points == "all": - monomial_points = list(range(Delta.npoints())) - elif monomial_points == "vertices+origin": - monomial_points = list(range(Delta.nvertices())) - monomial_points.append(Delta.origin()) - elif monomial_points == "simplified" or monomial_points is None: - monomial_points = Delta.skeleton_points(Delta.dim() - 2) - monomial_points.append(Delta.origin()) - elif isinstance(monomial_points, str): - raise ValueError("%s is an unsupported description of monomial " - "points!" % monomial_points) - monomial_points = tuple(monomial_points) - self._monomial_points = monomial_points - # Make the necessary ambient space - if coefficients is None: - if coefficient_name_indices is None: - coefficient_name_indices = monomial_points - coefficient_names = normalize_names( - coefficient_names, len(monomial_points), - DEFAULT_COEFFICIENT, coefficient_name_indices) - # We probably don't want it: the analog in else-branch is unclear. - # self._coefficient_names = coefficient_names - F = add_variables(P_Delta.base_ring(), coefficient_names) - coefficients = [F(coef) for coef in coefficient_names] - else: - variables = set() - nonstr = [] - regex = re.compile(r"[_A-Za-z]\w*") - for c in coefficients: - if isinstance(c, str): - variables.update(regex.findall(c)) - else: - nonstr.append(c) - F = add_variables(P_Delta.base_ring(), sorted(variables)) - F = coercion_model.common_parent(F, *nonstr) - coefficients = [F(_) for _ in coefficients] - P_Delta = P_Delta.base_extend(F) - if len(monomial_points) != len(coefficients): - raise ValueError("cannot construct equation of the anticanonical" - " hypersurface with %d monomials and %d coefficients" - % (len(monomial_points), len(coefficients))) - # Defining polynomial - h = sum(coef * prod(P_Delta.coordinate_point_to_coordinate(n) - ** (Delta.point(m) * Delta_polar.point(n) + 1) - for n in P_Delta.coordinate_points()) - for m, coef in zip(monomial_points, coefficients)) - super().__init__(P_Delta, h) - - -class NefCompleteIntersection(AlgebraicScheme_subscheme_toric): - r""" - Construct a nef complete intersection in a CPR-Fano toric variety. - - INPUT: - - - ``P_Delta`` -- a :class:`CPR-Fano toric variety - ` associated to a reflexive polytope `\Delta` - - - see :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for - documentation on all other acceptable parameters - - OUTPUT: - - - a :class:`nef complete intersection ` of - ``P_Delta`` (with the extended base field, if necessary). - - EXAMPLES:: - - sage: o = lattice_polytope.cross_polytope(3) - sage: np = o.nef_partitions()[0]; np - Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) - sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d CPR-Fano toric variety - covered by 8 affine patches defined by: - a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, - b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 - - See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for a - more elaborate example. - """ - def __init__(self, P_Delta, nef_partition, - monomial_points='all', coefficient_names=None, - coefficient_name_indices=None, coefficients=None): - r""" - See :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for - documentation. - - TESTS:: - - sage: o = lattice_polytope.cross_polytope(3) - sage: np = o.nef_partitions()[0] - sage: np - Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) - sage: from sage.schemes.toric.fano_variety import * - sage: NefCompleteIntersection(X, np) - Closed subscheme of 3-d CPR-Fano toric variety - covered by 8 affine patches defined by: - a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 - + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, - b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 - + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 - """ - if not isinstance(P_Delta, CPRFanoToricVariety_field): - raise TypeError("nef complete intersections can only be " - "constructed for CPR-Fano toric varieties!" - "\nGot: %s" % P_Delta) - if nef_partition.Delta() is not P_Delta.Delta(): - raise ValueError("polytopes 'Delta' of the nef-partition and the " - "CPR-Fano toric variety must be the same!") - self._nef_partition = nef_partition - k = nef_partition.nparts() - # Pre-normalize all parameters - if isinstance(monomial_points, str): - monomial_points = [monomial_points] * k - if coefficient_names is None: - coefficient_names = [None] * k - if coefficient_name_indices is None: - coefficient_name_indices = [None] * k - if coefficients is None: - coefficients = [None] * k - - polynomials = [] - Delta_polar = P_Delta.Delta_polar() - for i in range(k): - Delta_i = nef_partition.Delta(i) - # Monomial points normalization - if monomial_points[i] == "vertices": - monomial_points[i] = list(range(Delta_i.nvertices())) - elif monomial_points[i] == "all": - monomial_points[i] = list(range(Delta_i.npoints())) - elif monomial_points[i] == "vertices+origin": - monomial_points[i] = list(range(Delta_i.nvertices())) - if (Delta_i.origin() is not None - and Delta_i.origin() >= Delta_i.nvertices()): - monomial_points[i].append(Delta_i.origin()) - elif isinstance(monomial_points[i], str): - raise ValueError("'%s' is an unsupported description of " - "monomial points!" % monomial_points[i]) - monomial_points[i] = tuple(monomial_points[i]) - # Extend the base ring of the ambient space if necessary - if coefficients[i] is None: - if coefficient_name_indices[i] is None: - coefficient_name_indices[i] = monomial_points[i] - coefficient_names[i] = normalize_names( - coefficient_names[i], len(monomial_points[i]), - DEFAULT_COEFFICIENTS[i], coefficient_name_indices[i]) - F = add_variables(P_Delta.base_ring(), coefficient_names[i]) - coefficients[i] = [F(coef) for coef in coefficient_names[i]] - else: - variables = set() - nonstr = [] - regex = re.compile(r"[_A-Za-z]\w*") - for c in coefficients[i]: - if isinstance(c, str): - variables.update(regex.findall(c)) - else: - nonstr.append(c) - F = add_variables(P_Delta.base_ring(), sorted(variables)) - F = coercion_model.common_parent(F, *nonstr) - coefficients[i] = [F(_) for _ in coefficients[i]] - P_Delta = P_Delta.base_extend(F) - if len(monomial_points[i]) != len(coefficients[i]): - raise ValueError("cannot construct equation %d of the complete" - " intersection with %d monomials and %d coefficients" - % (i, len(monomial_points[i]), len(coefficients[i]))) - # Defining polynomial - h = sum(coef * prod(P_Delta.coordinate_point_to_coordinate(n) - ** (Delta_i.point(m) * Delta_polar.point(n) - + (nef_partition.part_of_point(n) == i)) - for n in P_Delta.coordinate_points()) - for m, coef in zip(monomial_points[i], coefficients[i])) - polynomials.append(h) - self._monomial_points = tuple(monomial_points) - super().__init__(P_Delta, polynomials) - - def cohomology_class(self): - r""" - Return the class of ``self`` in the ambient space cohomology ring. - - OUTPUT: a :class:`cohomology class ` - - EXAMPLES:: - - sage: o = lattice_polytope.cross_polytope(3) - sage: np = o.nef_partitions()[0]; np - Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) - sage: CI = X.nef_complete_intersection(np); CI - Closed subscheme of 3-d CPR-Fano toric variety - covered by 8 affine patches defined by: - a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, - b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 - sage: CI.cohomology_class() # needs sage.libs.singular - [2*z3*z4 + 4*z3*z5 + 2*z4*z5] - """ - X = self.ambient_space() - H = X.cohomology_ring() - return prod(sum(H.gen(X._point_to_ray[point]) - for point in part if point in X._coordinate_points) - for part in self.nef_partition().parts(all_points=True)) - - def nef_partition(self): - r""" - Return the nef-partition associated to ``self``. - - OUTPUT: a :class:`nef-partition ` - - EXAMPLES:: - - sage: o = lattice_polytope.cross_polytope(3) - sage: np = o.nef_partitions()[0]; np - Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) - sage: CI = X.nef_complete_intersection(np); CI - Closed subscheme of 3-d CPR-Fano toric variety - covered by 8 affine patches defined by: - a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, - b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 - sage: CI.nef_partition() - Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: CI.nef_partition() is np - True - """ - return self._nef_partition - - -def add_variables(field, variables): - r""" - Extend ``field`` to include all ``variables``. - - INPUT: - - - ``field`` -- a field - - - ``variables`` -- list of strings - - OUTPUT: - - - a fraction field extending the original ``field``, which has all - ``variables`` among its generators. - - EXAMPLES: - - We start with the rational field and slowly add more variables:: - - sage: from sage.schemes.toric.fano_variety import * - sage: F = add_variables(QQ, []); F # No extension - Rational Field - sage: F = add_variables(QQ, ["a"]); F - Fraction Field of Univariate Polynomial Ring in a over Rational Field - sage: F = add_variables(F, ["a"]); F - Fraction Field of Univariate Polynomial Ring in a over Rational Field - sage: F = add_variables(F, ["b", "c"]); F - Fraction Field of Multivariate Polynomial Ring in a, b, c over Rational Field - sage: F = add_variables(F, ["c", "d", "b", "c", "d"]); F - Fraction Field of Multivariate Polynomial Ring in a, b, c, d over Rational Field - """ - if not variables: - return field - if isinstance(field, FractionField_generic): - # Q(a) ---> Q(a, b) rather than Q(a)(b) - R = field.ring() - if isinstance(R, (PolynomialRing_generic, MPolynomialRing_base)): - new_variables = list(R.variable_names()) - for v in variables: - if v not in new_variables: - new_variables.append(v) - if len(new_variables) > R.ngens(): - return PolynomialRing(R.base_ring(), - new_variables).fraction_field() - else: - return field - # "Intelligent extension" didn't work, use the "usual one." - new_variables = [] - for v in variables: - if v not in new_variables: - new_variables.append(v) - return PolynomialRing(field, new_variables).fraction_field() - - -r""" -AL: add base class for both of (Gorenstein) Fano varieties, which serve as the superclass for both CPR-Fano and smooth Fano varieties. -""" -def FanoToricVariety(Delta=None, - Delta_polar=None, - coordinate_points=None, - charts=None, - coordinate_names=None, - names=None, - coordinate_name_indices=None, - make_simplicial=False, - base_ring=None, - base_field=None, - check=True): - r""" - Construct a Fano toric variety. - - .. NOTE:: - - See documentation of the module - :mod:`~sage.schemes.toric.fano_variety` for the used - definitions and supported varieties. - - Due to the large number of available options, it is recommended to always - use keyword parameters. - - INPUT: - - - ``Delta`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be - given, but not both at the same time, since one is completely determined - by another via :meth:`polar - ` method. - - - ``Delta_polar`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must - be given, but not both at the same time, since one is completely - determined by another via :meth:`polar - ` method. - - - ``coordinate_points`` -- list of integers or string. A list will be - interpreted as indices of (boundary) points of ``Delta_polar`` which - should be used as rays of the underlying fan. It must include all - vertices of ``Delta_polar`` and no repetitions are allowed. A string - must be one of the following descriptions of points of ``Delta_polar``: - - * "vertices" (default), - * "all" (will not include the origin), - * "all but facets" (will not include points in the relative interior of - facets); - - - ``charts`` -- list of lists of elements from ``coordinate_points``. Each - of these lists must define a generating cone of a fan subdividing the - normal fan of ``Delta``. Default ``charts`` correspond to the normal fan - of ``Delta`` without subdivision. The fan specified by ``charts`` will - be subdivided to include all of the requested ``coordinate_points``. - - - ``coordinate_names`` -- names of variables for the coordinate ring, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed variable names will be - created automatically. - - - ``names`` -- an alias of ``coordinate_names`` for internal - use. You may specify either ``names`` or ``coordinate_names``, - but not both. - - - ``coordinate_name_indices`` -- list of integers, indices for indexed - variables. If not given, the index of each variable will coincide with - the index of the corresponding point of ``Delta_polar``. - - - ``make_simplicial`` -- if ``True``, the underlying fan will be made - simplicial (default: ``False``) - - - ``base_ring`` -- base field of the CPR-Fano toric variety - (default: `\QQ`) - - - ``base_field`` -- alias for ``base_ring``. Takes precedence if - both are specified. - - - ``check`` -- by default the input data will be checked for correctness - (e.g. that ``charts`` do form a subdivision of the normal fan of - ``Delta``). If you know for sure that the input is valid, you may - significantly decrease construction time using ``check=False`` option. - - OUTPUT: :class:`CPR-Fano toric variety ` - - EXAMPLES: - - We start with the product of two projective lines:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: diamond.vertices() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) - sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches - sage: P1xP1.fan() - Rational polyhedral fan in 2-d lattice M - sage: P1xP1.fan().rays() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - - "Unfortunately," this variety is smooth to start with and we cannot - perform any subdivisions of the underlying fan without leaving the - category of CPR-Fano toric varieties. Our next example starts with a - square:: - - sage: square = diamond.polar() - sage: square.vertices() - N( 1, 1), N( 1, -1), - N(-1, -1), N(-1, 1) - in 2-d lattice N - sage: square.points() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N(-1, 0), N( 0, -1), - N( 0, 0), N( 0, 1), N( 1, 0) - in 2-d lattice N - - We will construct several varieties associated to it:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), - N(-1, -1), N(-1, 1) - in 2-d lattice N - sage: FTV.gens() - (z0, z1, z2, z3) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,8]) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N( 1, 0) - in 2-d lattice N - sage: FTV.gens() - (z0, z1, z2, z3, z8) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[8,0,2,1,3], - ....: coordinate_names='x+') - sage: FTV.fan().rays() - N( 1, 0), N( 1, 1), N(-1, -1), - N( 1, -1), N(-1, 1) - in 2-d lattice N - sage: FTV.gens() - (x8, x0, x2, x1, x3) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+") - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), N(-1, 1), - N(-1, 0), N( 0, -1), N( 0, 1), N( 1, 0) - in 2-d lattice N - sage: FTV.gens() - (x, y, Z2, Z3, Z4, Z5, Z7, Z8) - - Note that ``Z6`` is "missing". This is due to the fact that the 6-th point - of ``square`` is the origin, and all automatically created names have the - same indices as corresponding points of - :meth:`~CPRFanoToricVariety_field.Delta_polar`. This is usually very - convenient, especially if you have to work with several partial - resolutions of the same Fano toric variety. However, you can change it, if - you want:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+", - ....: coordinate_name_indices=list(range(8))) - sage: FTV.gens() - (x, y, Z2, Z3, Z4, Z5, Z6, Z7) - - Note that you have to provide indices for *all* variables, including those - that have "completely custom" names. Again, this is usually convenient, - because you can add or remove "custom" variables without disturbing too - much "automatic" ones:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x Z+", - ....: coordinate_name_indices=list(range(8))) - sage: FTV.gens() - (x, Z1, Z2, Z3, Z4, Z5, Z6, Z7) - - If you prefer to always start from zero, you will have to shift indices - accordingly:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x Z+", - ....: coordinate_name_indices=[0] + list(range(7))) - sage: FTV.gens() - (x, Z0, Z1, Z2, Z3, Z4, Z5, Z6) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+", - ....: coordinate_name_indices=[0]*2 + list(range(6))) - sage: FTV.gens() - (x, y, Z0, Z1, Z2, Z3, Z4, Z5) - - So you always can get any names you want, somewhat complicated default - behaviour was designed with the hope that in most cases you will have no - desire to provide different names. - - Now we will use the possibility to specify initial charts:: - - sage: charts = [(0,1), (1,2), (2,3), (3,0)] - - (these charts actually form exactly the face fan of our square) :: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=charts) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N(-1, 0) - in 2-d lattice N - sage: [cone.ambient_ray_indices() for cone in FTV.fan()] - [(0, 1), (1, 2), (2, 4), (3, 4), (0, 3)] - - If charts are wrong, it should be detected:: - - sage: bad_charts = charts + [(3,0)] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: you have provided 5 cones, but only 4 of them are maximal! - Use discard_faces=True if you indeed need to construct a fan from these cones. - - These charts are technically correct, they just happened to list one of - them twice, but it is assumed that such a situation will not happen. It is - especially important when you try to speed up your code:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts, - ....: check=False) - Traceback (most recent call last): - ... - IndexError: list assignment index out of range - - In this case you still get an error message, but it is harder to figure out - what is going on. It may also happen that "everything will still work" in - the sense of not crashing, but work with such an invalid variety may lead to - mathematically wrong results, so use ``check=False`` carefully! - - Here are some other possible mistakes:: - - sage: bad_charts = charts + [(0,2)] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: (0, 2) does not form a chart of a subdivision of - the face fan of 2-d reflexive polytope #14 in 2-d lattice N! - - sage: bad_charts = charts[:-1] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: given charts do not form a complete fan! - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[1,2,3,4]) - Traceback (most recent call last): - ... - ValueError: all 4 vertices of Delta_polar must be used for coordinates! - Got: [1, 2, 3, 4] - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,0,1,2,3,4]) - Traceback (most recent call last): - ... - ValueError: no repetitions are allowed for coordinate points! - Got: [0, 0, 1, 2, 3, 4] - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,6]) - Traceback (most recent call last): - ... - ValueError: the origin (point #6) cannot be used for a coordinate! - Got: [0, 1, 2, 3, 6] - - Here is a shorthand for defining the toric variety and homogeneous - coordinates in one go:: - - sage: P1xP1. = CPRFanoToricVariety(Delta_polar=diamond) - sage: (a^2+b^2) * (c+d) - a^2*c + b^2*c + a^2*d + b^2*d - """ - if names is not None: - if coordinate_names is not None: - raise ValueError('You must not specify both coordinate_names and names!') - coordinate_names = names - # Check/normalize Delta_polar - if Delta is None and Delta_polar is None: - raise ValueError("either Delta or Delta_polar must be given!") - elif Delta is not None and Delta_polar is not None: - raise ValueError("Delta and Delta_polar cannot be given together!") - elif Delta_polar is None: - Delta_polar = Delta.polar() - elif not Delta_polar.is_reflexive(): - raise ValueError("Delta_polar must be reflexive!") - # Check/normalize coordinate_points and construct fan rays - if coordinate_points is None: - coordinate_points = list(range(Delta_polar.nvertices())) - if charts is not None: - for chart in charts: - for point in chart: - if point not in coordinate_points: - coordinate_points.append(point) - elif coordinate_points == "vertices": - coordinate_points = list(range(Delta_polar.nvertices())) - elif coordinate_points == "all": - coordinate_points = list(range(Delta_polar.npoints())) - coordinate_points.remove(Delta_polar.origin()) - elif coordinate_points == "all but facets": - coordinate_points = Delta_polar.skeleton_points(Delta_polar.dim() - 2) - elif isinstance(coordinate_points, str): - raise ValueError("unrecognized description of the coordinate points!" - "\nGot: %s" % coordinate_points) - elif check: - cp_set = set(coordinate_points) - if len(cp_set) != len(coordinate_points): - raise ValueError( - "no repetitions are allowed for coordinate points!\nGot: %s" - % coordinate_points) - if not cp_set.issuperset(list(range(Delta_polar.nvertices()))): - raise ValueError("all %d vertices of Delta_polar must be used " - "for coordinates!\nGot: %s" - % (Delta_polar.nvertices(), coordinate_points)) - if Delta_polar.origin() in cp_set: - raise ValueError("the origin (point #%d) cannot be used for a " - "coordinate!\nGot: %s" - % (Delta_polar.origin(), coordinate_points)) - point_to_ray = {point: n - for n, point in enumerate(coordinate_points)} - # This can be simplified if LatticePolytopeClass is adjusted. - rays = [Delta_polar.point(p) for p in coordinate_points] - # Check/normalize charts and construct the fan based on them. - if charts is None: - # Start with the face fan - fan = FaceFan(Delta_polar) - else: - # First of all, check that each chart is completely contained in a - # single facet of Delta_polar, otherwise they do not form a - # subdivision of the face fan of Delta_polar - if check: - facet_sets = [frozenset(facet.ambient_point_indices()) - for facet in Delta_polar.facets()] - for chart in charts: - is_bad = True - for fset in facet_sets: - if fset.issuperset(chart): - is_bad = False - break - if is_bad: - raise ValueError( - "%s does not form a chart of a subdivision of the " - "face fan of %s!" % (chart, Delta_polar)) - # We will construct the initial fan from Cone objects: since charts - # may not use all of the necessary rays, alternative form is tedious - # With check=False it should not be long anyway. - cones = [Cone((rays[point_to_ray[point]] for point in chart), - check=check) - for chart in charts] - fan = Fan(cones, check=check) - if check and not fan.is_complete(): - raise ValueError("given charts do not form a complete fan!") - # Subdivide this fan to use all required points - fan = fan.subdivide(new_rays=(ray for ray in rays - if ray not in fan.rays().set()), - make_simplicial=make_simplicial) - # Now create yet another fan making sure that the order of the rays is - # the same as requested (it is a bit difficult to get it from the start) - trans = {} - for n, ray in enumerate(fan.rays()): - trans[n] = rays.index(ray) - cones = tuple(tuple(sorted(trans[r] for r in cone.ambient_ray_indices())) - for cone in fan) - fan = Fan(cones, rays, check=False) - # Check/normalize base_field - if base_field is not None: - base_ring = base_field - if base_ring is None: - base_ring = QQ - elif base_ring not in _Fields: - raise TypeError("need a field to construct a Fano toric variety!" - "\n Got %s" % base_ring) - fan._is_complete = True # At this point it must be for sure - return CPRFanoToricVariety_field( - Delta_polar, fan, coordinate_points, - point_to_ray, coordinate_names, coordinate_name_indices, base_ring) - - -class FanoToricVariety_field(ToricVariety_field): - r""" - Base class for Gorenstein Fano toric varieties over a field. - Construct a Fano toric variety associated to a reflexive polytope. - - .. WARNING:: - - This class does not perform any checks of correctness of input and it - does assume that the internal structure of the given parameters is - coordinated in a certain way. Use - :func:`FanoToricVariety` to construct Fano toric varieties. - - .. NOTE:: - - See documentation of the module - :mod:`~sage.schemes.toric.fano_variety` for the used - definitions and supported varieties. - - INPUT: - - - ``Delta_polar`` -- reflexive polytope - - - ``fan`` -- rational polyhedral fan which is the face fan of - ``Delta_polar`` - - - ``coordinate_names`` -- names of the variables of the coordinate ring in - the format accepted by - :func:`~sage.schemes.toric.variety.normalize_names` - - - ``coordinate_name_indices`` -- indices for indexed variables, - if ``None``, will be equal to ``coordinate_points`` - - - ``base_field`` -- base field of the Fano toric variety - - OUTPUT: :class:`Fano toric variety ` - - TESTS:: - - sage: P1xP1 = FanoToricVariety( - ....: Delta_polar=lattice_polytope.cross_polytope(2)) - sage: P1xP1 - 2-d Fano toric variety covered by 4 affine patches - """ - - def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field): - r""" - See :class:`FanoToricVariety_field` for documentation. - - Use ``FanoToricVariety`` to construct Fano toric varieties. - - TESTS:: - - sage: P1xP1 = FanoToricVariety( - ....: Delta_polar=lattice_polytope.cross_polytope(2)) - sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches - """ - self._Delta_polar = Delta_polar - super().__init__(fan, coordinate_names, - coordinate_name_indices, base_field) - - # AL: we want to use the anticanonical surface and nef complete intersection functions - @property - def _coordinate_points(self): - return list(range(self.fan().rays())) - - @property - def _point_to_ray(self): - return {i: i for i in self._coordinate_points} - - def _latex_(self): - r""" - Return a LaTeX representation of ``self``. - - OUTPUT: string - - TESTS:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: print(P1xP1._latex_()) - \mathbb{P}_{\Delta^{2}_{14}} - """ - return r"\mathbb{P}_{%s}" % latex(self.Delta()) - - def _repr_(self): - r""" - Return a string representation of ``self``. - - OUTPUT: string - - TESTS:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: print(P1xP1._repr_()) - 2-d CPR-Fano toric variety covered by 4 affine patches - """ - return ("%d-d Fano toric variety covered by %d affine patches" - % (self.dimension_relative(), self.fan().ngenerating_cones())) - - def anticanonical_hypersurface(self, **kwds): - r""" - Return an anticanonical hypersurface of ``self``. - - .. NOTE:: - - The returned hypersurface may be actually a subscheme of - **another** Fano toric variety: if the base field of ``self`` - does not include all of the required names for generic monomial - coefficients, it will be automatically extended. - - Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is the normal fan of - `\Delta`. This function accepts only keyword parameters. - - INPUT: - - - ``monomial_points`` -- list of integers or a string. A list will be - interpreted as indices of points of `\Delta` which should be used - for monomials of this hypersurface. A string must be one of the - following descriptions of points of `\Delta`: - - * "vertices", - * "vertices+origin", - * "all", - * "simplified" (default) -- all points of `\Delta` except for - the interior points of facets, this choice corresponds to working - with the "simplified polynomial moduli space" of anticanonical - hypersurfaces; - - - ``coefficient_names`` -- names for the monomial coefficients, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed coefficient names will - be created automatically. - - - ``coefficient_name_indices`` -- list of integers, indices for - indexed coefficients. If not given, the index of each coefficient - will coincide with the index of the corresponding point of `\Delta`. - - - ``coefficients`` -- as an alternative to specifying coefficient - names and/or indices, you can give the coefficients themselves as - arbitrary expressions and/or strings. Using strings allows you to - easily add "parameters": the base field of ``self`` will be extended - to include all necessary names. - - OUTPUT: - - - an :class:`anticanonical hypersurface ` of - ``self`` (with the extended base field, if necessary). - - EXAMPLES: - - We realize the projective plane as a Fano toric variety:: - - sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) - sage: P2 = FanoToricVariety(Delta_polar=simplex) - - Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau - manifold:: - - sage: P2.anticanonical_hypersurface(monomial_points='all') - Closed subscheme of 2-d Fano toric variety - covered by 3 affine patches defined by: - a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 - + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 - """ - # AL: we include this function because the construction only rely on the Delta and cox variables - return AnticanonicalHypersurface(self, **kwds) - - def change_ring(self, F): - r""" - Return a Fano toric variety over field ``F``, otherwise the same - as ``self``. - - INPUT: - - - ``F`` -- field - - OUTPUT: :class:`Fano toric variety ` over ``F`` - - .. NOTE:: - - There is no need to have any relation between ``F`` and the base - field of ``self``. If you do want to have such a relation, use - :meth:`base_extend` instead. - - EXAMPLES:: - - sage: P1xP1 = toric_varieties.P1xP1() - sage: P1xP1.base_ring() - Rational Field - sage: P1xP1_RR = P1xP1.change_ring(RR) - sage: P1xP1_RR.base_ring() - Real Field with 53 bits of precision - sage: P1xP1_QQ = P1xP1_RR.change_ring(QQ) - sage: P1xP1_QQ.base_ring() - Rational Field - sage: P1xP1_RR.base_extend(QQ) - Traceback (most recent call last): - ... - ValueError: no natural map from the base ring - (=Real Field with 53 bits of precision) to R (=Rational Field)! - sage: R = PolynomialRing(QQ, 2, 'a') - sage: P1xP1.change_ring(R) - Traceback (most recent call last): - ... - TypeError: need a field to construct a Fano toric variety! - Got Multivariate Polynomial Ring in a0, a1 over Rational Field - """ - if self.base_ring() == F: - return self - elif F not in _Fields: - raise TypeError("need a field to construct a Fano toric variety!" - "\n Got %s" % F) - else: - return FanoToricVariety_field(self._Delta_polar, self._fan, - self.variable_names(), None, F) - - def Delta(self): - r""" - Return the reflexive polytope associated to ``self``. - - OUTPUT: - - - reflexive :class:`lattice polytope - `. The - underlying fan of ``self`` is the - *normal fan* of this polytope. - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) - sage: P1xP1.Delta() - 2-d reflexive polytope #14 in 2-d lattice N - sage: P1xP1.Delta() is diamond.polar() - True - """ - return self._Delta_polar.polar() - - def Delta_polar(self): - r""" - Return polar of :meth:`Delta`. - - OUTPUT: - - - reflexive :class:`lattice polytope - `. The - underlying fan of ``self`` is the *face fan* of this polytope. - - EXAMPLES:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) - sage: P1xP1.Delta_polar() - 2-d reflexive polytope #3 in 2-d lattice M - sage: P1xP1.Delta_polar() is diamond - True - sage: P1xP1.Delta_polar() is P1xP1.Delta().polar() - True - """ - return self._Delta_polar - - def nef_complete_intersection(self, nef_partition, **kwds): - r""" - Return a nef complete intersection in ``self``. - - .. NOTE:: - - The returned complete intersection may be actually a subscheme of - **another** Fano toric variety: if the base field of ``self`` - does not include all of the required names for monomial - coefficients, it will be automatically extended. - - Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is the normal fan of - `\Delta`. Other polytopes are described in the documentation of - :class:`nef-partitions ` - of :class:`reflexive polytopes - `. - - Except for the first argument, ``nef_partition``, this method accepts - only keyword parameters. - - INPUT: - - - ``nef_partition`` -- a `k`-part :class:`nef-partition - ` of `\Delta^\circ`, all - other parameters (if given) must be lists of length `k` - - - ``monomial_points`` -- the `i`-th element of this list is either a - list of integers or a string. A list will be interpreted as indices - of points of `\Delta_i` which should be used for monomials of the - `i`-th polynomial of this complete intersection. A string must be one - of the following descriptions of points of `\Delta_i`: - - * "vertices", - * "vertices+origin", - * "all" (default), - - when using this description, it is also OK to pass a single string as - ``monomial_points`` instead of repeating it `k` times. - - - ``coefficient_names`` -- the `i`-th element of this list specifies - names for the monomial coefficients of the `i`-th polynomial, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed coefficient names will - be created automatically. - - - ``coefficient_name_indices`` -- the `i`-th element of this list - specifies indices for indexed coefficients of the `i`-th polynomial. - If not given, the index of each coefficient will coincide with the - index of the corresponding point of `\Delta_i`. - - - ``coefficients`` -- as an alternative to specifying coefficient - names and/or indices, you can give the coefficients themselves as - arbitrary expressions and/or strings. Using strings allows you to - easily add "parameters": the base field of ``self`` will be extended - to include all necessary names. - - OUTPUT: - - - a :class:`nef complete intersection ` of - ``self`` (with the extended base field, if necessary). - - EXAMPLES: - - We construct several complete intersections associated to the same - nef-partition of the 3-dimensional reflexive polytope #2254:: - - sage: p = ReflexivePolytope(3, 2254) - sage: np = p.nef_partitions()[1]; np - Nef-partition {2, 3, 4, 7, 8} ⊔ {0, 1, 5, 6} - sage: X = FanoToricVariety(Delta_polar=p) - sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d Fano toric variety - covered by 10 affine patches defined by: - a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 - + a3*z2*z3*z4*z7*z8 + a1*z0*z2, - b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 - + b5*z1*z3*z4*z5*z6*z7*z8 + b2*z2*z3*z6^2*z8^3 - + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 - - Now we include only monomials associated to vertices of `\Delta_i`:: - - sage: X.nef_complete_intersection(np, monomial_points='vertices') - Closed subscheme of 3-d Fano toric variety - covered by 10 affine patches defined by: - a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 - + a3*z2*z3*z4*z7*z8 + a1*z0*z2, - b3*z1*z4*z5^2*z6^2*z7^2*z8^2 + b0*z2*z5*z6^3*z7*z8^4 - + b2*z2*z3*z6^2*z8^3 + b1*z1*z3^2*z4 + b4*z0*z1*z5*z6 - - (effectively, we set ``b5=0``). Next we provide coefficients explicitly - instead of using default generic names:: - - sage: X.nef_complete_intersection(np, - ....: monomial_points='vertices', - ....: coefficients=[("a", "a^2", "a/e", "c_i"), list(range(1,6))]) - Closed subscheme of 3-d Fano toric variety - covered by 10 affine patches defined by: - a*z1*z4^2*z5^2*z7^3 + a/e*z2*z4*z5*z6*z7^2*z8^2 - + (c_i)*z2*z3*z4*z7*z8 + (a^2)*z0*z2, - 4*z1*z4*z5^2*z6^2*z7^2*z8^2 + z2*z5*z6^3*z7*z8^4 - + 3*z2*z3*z6^2*z8^3 + 2*z1*z3^2*z4 + 5*z0*z1*z5*z6 - - Finally, we take a look at the generic representative of these complete - intersections in a completely resolved ambient toric variety:: - - sage: X = FanoToricVariety(Delta_polar=p, - ....: coordinate_points='all') - sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d Fano toric variety - covered by 22 affine patches defined by: - a2*z2*z4*z5*z6*z7^2*z8^2*z9^2*z10^2*z11*z12*z13 - + a0*z1*z4^2*z5^2*z7^3*z9*z10^2*z12*z13 - + a3*z2*z3*z4*z7*z8*z9*z10*z11*z12 + a1*z0*z2, - b0*z2*z5*z6^3*z7*z8^4*z9^3*z10^2*z11^2*z12*z13^2 - + b3*z1*z4*z5^2*z6^2*z7^2*z8^2*z9^2*z10^2*z11*z12*z13^2 - + b2*z2*z3*z6^2*z8^3*z9^2*z10*z11^2*z12*z13 - + b5*z1*z3*z4*z5*z6*z7*z8*z9*z10*z11*z12*z13 - + b1*z1*z3^2*z4*z11*z12 + b4*z0*z1*z5*z6*z13 - """ - return NefCompleteIntersection(self, nef_partition, **kwds) - - def cartesian_product(self, other, - coordinate_names=None, coordinate_indices=None): - r""" - Return the Cartesian product of ``self`` with ``other``. - - INPUT: - - - ``other`` -- a (possibly - :class:`CPR-Fano `) :class:`toric variety - ` - - - ``coordinate_names`` -- names of variables for the coordinate ring, - see :func:`normalize_names` for acceptable formats. If not given, - indexed variable names will be created automatically. - - - ``coordinate_indices`` -- list of integers, indices for indexed - variables. If not given, the index of each variable will coincide - with the index of the corresponding ray of the fan. - - OUTPUT: - - - a :class:`toric variety - `, which is - :class:`CPR-Fano ` if ``other`` was. - - EXAMPLES:: - - sage: P1 = toric_varieties.P1() - sage: P2 = toric_varieties.P2() - sage: P1xP2 = P1.cartesian_product(P2); P1xP2 - 3-d CPR-Fano toric variety covered by 6 affine patches - sage: P1xP2.fan().rays() - N+N( 1, 0, 0), N+N(-1, 0, 0), N+N( 0, 1, 0), - N+N( 0, 0, 1), N+N( 0, -1, -1) - in 3-d lattice N+N - sage: P1xP2.Delta_polar() - 3-d reflexive polytope in 3-d lattice N+N - """ - if isinstance(other, FanoToricVariety_field) and not isinstance(other, CPRFanoToricVariety_field) \ - and not isinstance(self, SmoothFanoToricVariety_field): - fan = self.fan().cartesian_product(other.fan()) - Delta_polar = LatticePolytope(fan.rays()) - return FanoToricVariety_field(Delta_polar, fan, - coordinate_names, coordinate_indices, - self.base_ring()) - return super().cartesian_product(other) diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index f3c58c7d15b..e31f25c669d 100644 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -2,10 +2,120 @@ r""" Fano toric varieties -This module provides base class of (Gorenstein) Fano varieties, so we can naturally -introduce the subclass of CPR-Fano toric variety, as well as a factory -for smooth Fano toric varieties. +This module provides support for (Crepant Partial Resolutions of) Fano toric +varieties, corresponding to crepant subdivisions of face fans of reflexive +:class:`lattice polytopes +`. +The interface is provided via :func:`CPRFanoToricVariety`. + +A careful exposition of different flavours of Fano varieties can be found in +the paper by Benjamin Nill [Nil2005]_. The main goal of this module is to +support work with **Gorenstein weak Fano toric varieties**. Such a variety +corresponds to a **coherent crepant refinement of the normal fan of a +reflexive polytope** `\Delta`, where crepant means that primitive generators +of the refining rays lie on the facets of the polar polytope `\Delta^\circ` +and coherent (a.k.a. regular or projective) means that there exists a strictly +upper convex piecewise linear function whose domains of linearity are +precisely the maximal cones of the subdivision. These varieties are important +for string theory in physics, as they serve as ambient spaces for mirror pairs +of Calabi-Yau manifolds via constructions due to Victor V. Batyrev +[Bat1994]_ and Lev A. Borisov [Bor1993]_. + +From the combinatorial point of view, the "crepant" requirement is much more simple +and natural to work with than "coherent." For this reason, the code in this +module will allow work with arbitrary crepant subdivisions without checking +whether they are coherent or not. We refer to corresponding toric varieties as +**CPR-Fano toric varieties**. + +REFERENCES: + +- [Bat1994]_ +- [Bor1993]_ +- [CD2007]_ +- [Nil2005]_ + +AUTHORS: + +- Andrey Novoseltsev (2010-05-18): initial version. + +EXAMPLES: + +Most of the functions available for Fano toric varieties are the same as +for general toric varieties, so here we will concentrate only on +Calabi-Yau subvarieties, which were the primary goal for creating this +module. + +For our first example we realize the projective plane as a Fano toric +variety:: + + sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) + sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) + +Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau +manifold:: + + sage: P2.anticanonical_hypersurface(monomial_points='all') + Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: + a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 + + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 + +In many cases, it is sufficient to work with the "simplified polynomial +moduli space" of anticanonical hypersurfaces:: + + sage: P2.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 + +The mirror family to these hypersurfaces lives inside the Fano toric +variety obtained using ``simplex`` as ``Delta`` instead of ``Delta_polar``:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points='all') + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety covered by 9 affine patches defined by: + a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 + + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 + +Here we have taken the resolved version of the ambient space for the +mirror family, but in fact we don't have to resolve singularities +corresponding to the interior points of facets - they are singular +points which do not lie on a generic anticanonical hypersurface:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, coordinate_points="all but facets") + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 + +This looks very similar to our second version of the anticanonical +hypersurface of the projective plane, as expected, since all +one-dimensional Calabi-Yau manifolds are elliptic curves! + +Now let's take a look at a toric realization of `M`-polarized K3 surfaces +studied by Adrian Clingher and Charles F. Doran in [CD2007]_:: + + sage: p4318 = ReflexivePolytope(3, 4318) + sage: FTV = CPRFanoToricVariety(Delta_polar=p4318) + sage: FTV.anticanonical_hypersurface() + Closed subscheme of 3-d CPR-Fano toric variety covered by 4 affine patches defined by: + a0*z2^12 + a4*z2^6*z3^6 + a3*z3^12 + a8*z0*z1*z2*z3 + a2*z1^3 + a1*z0^2 + +Below you will find detailed descriptions of available functions. Current +functionality of this module is very basic, but it is under active +development and hopefully will improve in future releases of Sage. If there +are some particular features that you would like to see implemented ASAP, +please consider reporting them to the Sage Development Team or even +implementing them on your own as a patch for inclusion! """ +# The first example of the tutorial is taken from +# CPRFanoToricVariety_field.anticanonical_hypersurface + +# **************************************************************************** +# Copyright (C) 2010 Andrey Novoseltsev +# Copyright (C) 2010 William Stein +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# https://www.gnu.org/licenses/ +# **************************************************************************** import re @@ -37,20 +147,552 @@ DEFAULT_COEFFICIENTS = tuple(chr(i) for i in range(ord("a"), ord("z") + 1)) +r""" +AL: add base class of (Gorenstein) Fano varieties, which serve as the superclass for both CPR-Fano and smooth Fano varieties. +""" + +def FanoToricVariety(Delta=None, + Delta_polar=None, + coordinate_names=None, + names=None, + coordinate_name_indices=None, + base_ring=None, + base_field=None, + check=True): + r""" + Construct a Fano toric variety. + + .. NOTE:: + + See documentation of the module + :mod:`~sage.schemes.toric.fano_variety` for the used + definitions and supported varieties. + + Due to the large number of available options, it is recommended to always + use keyword parameters. + + INPUT: + + - ``Delta`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be + given, but not both at the same time, since one is completely determined + by another via :meth:`polar + ` method. + + - ``Delta_polar`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must + be given, but not both at the same time, since one is completely + determined by another via :meth:`polar + ` method. + + - ``coordinate_names`` -- names of variables for the coordinate ring, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed variable names will be + created automatically. + + - ``names`` -- an alias of ``coordinate_names`` for internal + use. You may specify either ``names`` or ``coordinate_names``, + but not both. + + - ``coordinate_name_indices`` -- list of integers, indices for indexed + variables. If not given, the index of each variable will coincide with + the index of the corresponding point of ``Delta_polar``. + + - ``base_ring`` -- base field of the Fano toric variety + (default: `\QQ`) + + - ``base_field`` -- alias for ``base_ring``. Takes precedence if + both are specified. + + - ``check`` -- by default the input data will be checked for correctness + (e.g. that ``charts`` do form a subdivision of the normal fan of + ``Delta``). If you know for sure that the input is valid, you may + significantly decrease construction time using ``check=False`` option. + + OUTPUT: :class:`Fano toric variety ` + + EXAMPLES: + + We start with the product of two projective lines:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: diamond.vertices() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1 + 2-d Fano toric variety covered by 4 affine patches + sage: P1xP1.fan() + Rational polyhedral fan in 2-d lattice M + sage: P1xP1.fan().rays() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + + Here is a shorthand for defining the toric variety and homogeneous + coordinates in one go:: + + sage: P1xP1. = FanoToricVariety(Delta_polar=diamond) + sage: (a^2+b^2) * (c+d) + a^2*c + b^2*c + a^2*d + b^2*d + """ + if names is not None: + if coordinate_names is not None: + raise ValueError('You must not specify both coordinate_names and names!') + coordinate_names = names + # Check/normalize Delta_polar + if (Delta is None) == (Delta_polar is None): + raise ValueError("specify exactly one of Delta or Delta_polar") + if Delta_polar is None: + Delta_polar = Delta.polar() + if check and not Delta_polar.is_reflexive(): + raise ValueError("Delta_polar must be reflexive") + + # AL: construct the fan fromt the original polytope without worrying about the refinement + fan = FaceFan(Delta_polar) + + # Check/normalize base_field + if base_field is not None: + base_ring = base_field + if base_ring is None: + base_ring = QQ + elif base_ring not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % base_ring) + + return FanoToricVariety_field( + Delta_polar, fan, coordinate_names, coordinate_name_indices, base_ring) + + +class FanoToricVariety_field(ToricVariety_field): + r""" + Base class for Gorenstein Fano toric varieties over a field. + Construct a Fano toric variety associated to a reflexive polytope. + + .. WARNING:: + + This class does not perform any checks of correctness of input and it + does assume that the internal structure of the given parameters is + coordinated in a certain way. Use + :func:`FanoToricVariety` to construct Fano toric varieties. + + .. NOTE:: + + See documentation of the module + :mod:`~sage.schemes.toric.fano_variety` for the used + definitions and supported varieties. + + INPUT: + + - ``Delta_polar`` -- reflexive polytope + + - ``fan`` -- rational polyhedral fan which is the face fan of + ``Delta_polar`` + + - ``coordinate_names`` -- names of the variables of the coordinate ring in + the format accepted by + :func:`~sage.schemes.toric.variety.normalize_names` + + - ``coordinate_name_indices`` -- indices for indexed variables, + if ``None``, will be equal to ``coordinate_points`` + + - ``base_field`` -- base field of the Fano toric variety + + OUTPUT: :class:`Fano toric variety ` + + TESTS:: + + sage: P1xP1 = FanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1 + 2-d Fano toric variety covered by 4 affine patches + """ + + def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field): + r""" + See :class:`FanoToricVariety_field` for documentation. + + Use ``FanoToricVariety`` to construct Fano toric varieties. + + TESTS:: + + sage: P1xP1 = FanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1 + 2-d CPR-Fano toric variety covered by 4 affine patches + """ + self._Delta_polar = Delta_polar + # AL: we want to use the anticanonical surface and nef complete intersection functions for general Fano toric varieties + self._coordinate_points = tuple(range(fan.rays())) + self._point_to_ray = {i: i for i in self._coordinate_points} + super().__init__(fan, coordinate_names, + coordinate_name_indices, base_field) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + OUTPUT: string + + TESTS:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: print(P1xP1._latex_()) + \mathbb{P}_{\Delta^{2}_{14}} + """ + return r"\mathbb{P}_{%s}" % latex(self.Delta()) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + OUTPUT: string + + TESTS:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: print(P1xP1._repr_()) + 2-d Fano toric variety covered by 4 affine patches + """ + return ("%d-d Fano toric variety covered by %d affine patches" + % (self.dimension_relative(), self.fan().ngenerating_cones())) + + def anticanonical_hypersurface(self, **kwds): + r""" + Return an anticanonical hypersurface of ``self``. + + .. NOTE:: + + The returned hypersurface may be actually a subscheme of + **another** Fano toric variety: if the base field of ``self`` + does not include all of the required names for generic monomial + coefficients, it will be automatically extended. + + Below `\Delta` is the reflexive polytope corresponding to ``self``, + i.e. the fan of ``self`` is the normal fan of + `\Delta`. This function accepts only keyword parameters. + + INPUT: + + - ``monomial_points`` -- list of integers or a string. A list will be + interpreted as indices of points of `\Delta` which should be used + for monomials of this hypersurface. A string must be one of the + following descriptions of points of `\Delta`: + + * "vertices", + * "vertices+origin", + * "all", + * "simplified" (default) -- all points of `\Delta` except for + the interior points of facets, this choice corresponds to working + with the "simplified polynomial moduli space" of anticanonical + hypersurfaces; + + - ``coefficient_names`` -- names for the monomial coefficients, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed coefficient names will + be created automatically. + + - ``coefficient_name_indices`` -- list of integers, indices for + indexed coefficients. If not given, the index of each coefficient + will coincide with the index of the corresponding point of `\Delta`. + + - ``coefficients`` -- as an alternative to specifying coefficient + names and/or indices, you can give the coefficients themselves as + arbitrary expressions and/or strings. Using strings allows you to + easily add "parameters": the base field of ``self`` will be extended + to include all necessary names. + + OUTPUT: + + - an :class:`anticanonical hypersurface ` of + ``self`` (with the extended base field, if necessary). + + EXAMPLES: + + We realize the projective plane as a Fano toric variety:: + + sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) + sage: P2 = FanoToricVariety(Delta_polar=simplex) + + Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau + manifold:: + + sage: P2.anticanonical_hypersurface(monomial_points='all') + Closed subscheme of 2-d Fano toric variety + covered by 3 affine patches defined by: + a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 + + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 + """ + # AL: we include this function because the construction only rely on the Delta and cox variables + # and the anticanonical hypersurface is important for Fano varieties + return AnticanonicalHypersurface(self, **kwds) + + def change_ring(self, F): + r""" + Return a Fano toric variety over field ``F``, otherwise the same + as ``self``. + + INPUT: + + - ``F`` -- field + + OUTPUT: :class:`Fano toric variety ` over ``F`` + + .. NOTE:: + + There is no need to have any relation between ``F`` and the base + field of ``self``. If you do want to have such a relation, use + :meth:`base_extend` instead. + + EXAMPLES:: + + sage: P1xP1 = toric_varieties.P1xP1() + sage: P1xP1.base_ring() + Rational Field + sage: P1xP1_RR = P1xP1.change_ring(RR) + sage: P1xP1_RR.base_ring() + Real Field with 53 bits of precision + sage: P1xP1_QQ = P1xP1_RR.change_ring(QQ) + sage: P1xP1_QQ.base_ring() + Rational Field + sage: P1xP1_RR.base_extend(QQ) + Traceback (most recent call last): + ... + ValueError: no natural map from the base ring + (=Real Field with 53 bits of precision) to R (=Rational Field)! + sage: R = PolynomialRing(QQ, 2, 'a') + sage: P1xP1.change_ring(R) + Traceback (most recent call last): + ... + TypeError: need a field to construct a Fano toric variety! + Got Multivariate Polynomial Ring in a0, a1 over Rational Field + """ + if self.base_ring() == F: + return self + elif F not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % F) + else: + return FanoToricVariety_field(self._Delta_polar, self._fan, + self.variable_names(), None, F) + + def Delta(self): + r""" + Return the reflexive polytope associated to ``self``. + + OUTPUT: + + - reflexive :class:`lattice polytope + `. The + underlying fan of ``self`` is the + *normal fan* of this polytope. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1.Delta() + 2-d reflexive polytope #14 in 2-d lattice N + sage: P1xP1.Delta() is diamond.polar() + True + """ + return self._Delta_polar.polar() + + def Delta_polar(self): + r""" + Return polar of :meth:`Delta`. + + OUTPUT: + + - reflexive :class:`lattice polytope + `. The + underlying fan of ``self`` is the *face fan* of this polytope. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1.Delta_polar() + 2-d reflexive polytope #3 in 2-d lattice M + sage: P1xP1.Delta_polar() is diamond + True + sage: P1xP1.Delta_polar() is P1xP1.Delta().polar() + True + """ + return self._Delta_polar -def FanoToricVariety(Delta=None, - Delta_polar=None, - coordinate_points=None, - charts=None, - coordinate_names=None, - names=None, - coordinate_name_indices=None, - make_simplicial=False, - base_ring=None, - base_field=None, - check=True): + def nef_complete_intersection(self, nef_partition, **kwds): + r""" + Return a nef complete intersection in ``self``. + + .. NOTE:: + + The returned complete intersection may be actually a subscheme of + **another** Fano toric variety: if the base field of ``self`` + does not include all of the required names for monomial + coefficients, it will be automatically extended. + + Below `\Delta` is the reflexive polytope corresponding to ``self``, + i.e. the fan of ``self`` is the normal fan of + `\Delta`. Other polytopes are described in the documentation of + :class:`nef-partitions ` + of :class:`reflexive polytopes + `. + + Except for the first argument, ``nef_partition``, this method accepts + only keyword parameters. + + INPUT: + + - ``nef_partition`` -- a `k`-part :class:`nef-partition + ` of `\Delta^\circ`, all + other parameters (if given) must be lists of length `k` + + - ``monomial_points`` -- the `i`-th element of this list is either a + list of integers or a string. A list will be interpreted as indices + of points of `\Delta_i` which should be used for monomials of the + `i`-th polynomial of this complete intersection. A string must be one + of the following descriptions of points of `\Delta_i`: + + * "vertices", + * "vertices+origin", + * "all" (default), + + when using this description, it is also OK to pass a single string as + ``monomial_points`` instead of repeating it `k` times. + + - ``coefficient_names`` -- the `i`-th element of this list specifies + names for the monomial coefficients of the `i`-th polynomial, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed coefficient names will + be created automatically. + + - ``coefficient_name_indices`` -- the `i`-th element of this list + specifies indices for indexed coefficients of the `i`-th polynomial. + If not given, the index of each coefficient will coincide with the + index of the corresponding point of `\Delta_i`. + + - ``coefficients`` -- as an alternative to specifying coefficient + names and/or indices, you can give the coefficients themselves as + arbitrary expressions and/or strings. Using strings allows you to + easily add "parameters": the base field of ``self`` will be extended + to include all necessary names. + + OUTPUT: + + - a :class:`nef complete intersection ` of + ``self`` (with the extended base field, if necessary). + """ + return NefCompleteIntersection(self, nef_partition, **kwds) + + + def cartesian_product(self, other, + coordinate_names=None, coordinate_indices=None): + r""" + Return the Cartesian product of ``self`` with ``other``. + + INPUT: + + - ``other`` -- a (possibly + :class:`Fano `) :class:`toric variety + ` + + - ``coordinate_names`` -- names of variables for the coordinate ring, + see :func:`normalize_names` for acceptable formats. If not given, + indexed variable names will be created automatically. + + - ``coordinate_indices`` -- list of integers, indices for indexed + variables. If not given, the index of each variable will coincide + with the index of the corresponding ray of the fan. + + OUTPUT: + + - a :class:`toric variety + `, which is + :class:`Fano ` if ``other`` was. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: P2 = toric_varieties.P2() + sage: P1xP2 = P1.cartesian_product(P2); P1xP2 + 3-d Fano toric variety covered by 6 affine patches + sage: P1xP2.fan().rays() + N+N( 1, 0, 0), N+N(-1, 0, 0), N+N( 0, 1, 0), + N+N( 0, 0, 1), N+N( 0, -1, -1) + in 3-d lattice N+N + sage: P1xP2.Delta_polar() + 3-d reflexive polytope in 3-d lattice N+N + """ + if isinstance(other, FanoToricVariety_field) and not isinstance(other, CPRFanoToricVariety_field): + # and not isinstance(other, SmoothFanoToricVariety_field): we will include this in the future if necessary + fan = self.fan().cartesian_product(other.fan()) + Delta_polar = LatticePolytope(fan.rays()) + return FanoToricVariety_field(Delta_polar, fan, + coordinate_names, coordinate_indices, + self.base_ring()) + return super().cartesian_product(other) + + + +def is_CPRFanoToricVariety(x): r""" - Construct a Fano toric variety. + Check if ``x`` is a CPR-Fano toric variety. + + INPUT: + + - ``x`` -- anything + + OUTPUT: + + - ``True`` if ``x`` is a :class:`CPR-Fano toric variety + ` and ``False`` otherwise. + + .. NOTE:: + + While projective spaces are Fano toric varieties mathematically, they + are not toric varieties in Sage due to efficiency considerations, so + this function will return ``False``. + + EXAMPLES:: + + sage: from sage.schemes.toric.fano_variety import is_CPRFanoToricVariety + sage: is_CPRFanoToricVariety(1) + doctest:warning... + DeprecationWarning: The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead. + See https://github.com/sagemath/sage/issues/38022 for details. + False + sage: FTV = toric_varieties.P2() + sage: FTV + 2-d CPR-Fano toric variety covered by 3 affine patches + sage: is_CPRFanoToricVariety(FTV) + True + sage: is_CPRFanoToricVariety(ProjectiveSpace(2)) + False + """ + from sage.misc.superseded import deprecation + deprecation(38022, "The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead.") + return isinstance(x, CPRFanoToricVariety_field) + + +def CPRFanoToricVariety(Delta=None, + Delta_polar=None, + coordinate_points=None, + charts=None, + coordinate_names=None, + names=None, + coordinate_name_indices=None, + make_simplicial=False, + base_ring=None, + base_field=None, + check=True): + r""" + Construct a CPR-Fano toric variety. .. NOTE:: @@ -443,17 +1085,16 @@ def FanoToricVariety(Delta=None, point_to_ray, coordinate_names, coordinate_name_indices, base_ring) -class FanoToricVariety_field(ToricVariety_field): +class CPRFanoToricVariety_field(FanoToricVariety_field): r""" - Base class for Gorenstein Fano toric varieties over a field. - Construct a Fano toric variety associated to a reflexive polytope. + Construct a CPR-Fano toric variety associated to a reflexive polytope. .. WARNING:: This class does not perform any checks of correctness of input and it does assume that the internal structure of the given parameters is coordinated in a certain way. Use - :func:`FanoToricVariety` to construct Fano toric varieties. + :func:`CPRFanoToricVariety` to construct CPR-Fano toric varieties. .. NOTE:: @@ -465,9 +1106,15 @@ class FanoToricVariety_field(ToricVariety_field): - ``Delta_polar`` -- reflexive polytope - - ``fan`` -- rational polyhedral fan which is the face fan of + - ``fan`` -- rational polyhedral fan subdividing the face fan of ``Delta_polar`` + - ``coordinate_points`` -- list of indices of points of ``Delta_polar`` + used for rays of ``fan`` + + - ``point_to_ray`` -- dictionary mapping the index of a coordinate point + to the index of the corresponding ray + - ``coordinate_names`` -- names of the variables of the coordinate ring in the format accepted by :func:`~sage.schemes.toric.variety.normalize_names` @@ -475,43 +1122,40 @@ class FanoToricVariety_field(ToricVariety_field): - ``coordinate_name_indices`` -- indices for indexed variables, if ``None``, will be equal to ``coordinate_points`` - - ``base_field`` -- base field of the Fano toric variety + - ``base_field`` -- base field of the CPR-Fano toric variety - OUTPUT: :class:`Fano toric variety ` + OUTPUT: :class:`CPR-Fano toric variety ` TESTS:: - sage: P1xP1 = FanoToricVariety( + sage: P1xP1 = CPRFanoToricVariety( ....: Delta_polar=lattice_polytope.cross_polytope(2)) sage: P1xP1 - 2-d Fano toric variety covered by 4 affine patches + 2-d CPR-Fano toric variety covered by 4 affine patches """ - def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field): + def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray, + coordinate_names, coordinate_name_indices, base_field): r""" - See :class:`FanoToricVariety_field` for documentation. + See :class:`CPRFanoToricVariety_field` for documentation. - Use ``FanoToricVariety`` to construct Fano toric varieties. + Use ``CPRFanoToricVariety`` to construct CPR-Fano toric varieties. TESTS:: - sage: P1xP1 = FanoToricVariety( + sage: P1xP1 = CPRFanoToricVariety( ....: Delta_polar=lattice_polytope.cross_polytope(2)) sage: P1xP1 2-d CPR-Fano toric variety covered by 4 affine patches """ self._Delta_polar = Delta_polar - super().__init__(fan, coordinate_names, + self._coordinate_points = tuple(coordinate_points) + self._point_to_ray = point_to_ray + # Check/normalize coordinate_indices + if coordinate_name_indices is None: + coordinate_name_indices = coordinate_points + super().__init__(Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field) - - # AL: we want to use the anticanonical surface and nef complete intersection functions - @property - def _coordinate_points(self): - return list(range(self.fan().rays())) - - @property - def _point_to_ray(self): - return {i: i for i in self._coordinate_points} def _latex_(self): r""" @@ -539,7 +1183,7 @@ def _repr_(self): sage: print(P1xP1._repr_()) 2-d CPR-Fano toric variety covered by 4 affine patches """ - return ("%d-d Fano toric variety covered by %d affine patches" + return ("%d-d CPR-Fano toric variety covered by %d affine patches" % (self.dimension_relative(), self.fan().ngenerating_cones())) def anticanonical_hypersurface(self, **kwds): @@ -549,12 +1193,12 @@ def anticanonical_hypersurface(self, **kwds): .. NOTE:: The returned hypersurface may be actually a subscheme of - **another** Fano toric variety: if the base field of ``self`` + **another** CPR-Fano toric variety: if the base field of ``self`` does not include all of the required names for generic monomial coefficients, it will be automatically extended. Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is the normal fan of + i.e. the fan of ``self`` is a refinement of the normal fan of `\Delta`. This function accepts only keyword parameters. INPUT: @@ -597,30 +1241,96 @@ def anticanonical_hypersurface(self, **kwds): We realize the projective plane as a Fano toric variety:: sage: simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) - sage: P2 = FanoToricVariety(Delta_polar=simplex) + sage: P2 = CPRFanoToricVariety(Delta_polar=simplex) Its anticanonical "hypersurface" is a one-dimensional Calabi-Yau manifold:: sage: P2.anticanonical_hypersurface(monomial_points='all') - Closed subscheme of 2-d Fano toric variety + Closed subscheme of 2-d CPR-Fano toric variety covered by 3 affine patches defined by: a0*z0^3 + a9*z0^2*z1 + a7*z0*z1^2 + a1*z1^3 + a8*z0^2*z2 + a6*z0*z1*z2 + a4*z1^2*z2 + a5*z0*z2^2 + a3*z1*z2^2 + a2*z2^3 + + In many cases it is sufficient to work with the "simplified polynomial + moduli space" of anticanonical hypersurfaces:: + + sage: P2.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a6*z0*z1*z2 + a2*z2^3 + + The mirror family to these hypersurfaces lives inside the Fano toric + variety obtained using ``simplex`` as ``Delta`` instead of + ``Delta_polar``:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, + ....: coordinate_points='all') + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety + covered by 9 affine patches defined by: + a2*z2^3*z3^2*z4*z5^2*z8 + a1*z1^3*z3*z4^2*z7^2*z9 + + a3*z0*z1*z2*z3*z4*z5*z7*z8*z9 + a0*z0^3*z5*z7*z8^2*z9^2 + + Here we have taken the resolved version of the ambient space for the + mirror family, but in fact we don't have to resolve singularities + corresponding to the interior points of facets - they are singular + points which do not lie on a generic anticanonical hypersurface:: + + sage: FTV = CPRFanoToricVariety(Delta=simplex, + ....: coordinate_points="all but facets") + sage: FTV.anticanonical_hypersurface(monomial_points='simplified') + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + a0*z0^3 + a1*z1^3 + a3*z0*z1*z2 + a2*z2^3 + + This looks very similar to our second anticanonical + hypersurface of the projective plane, as expected, since all + one-dimensional Calabi-Yau manifolds are elliptic curves! + + All anticanonical hypersurfaces constructed above were generic with + automatically generated coefficients. If you want, you can specify your + own names :: + + sage: FTV.anticanonical_hypersurface(coefficient_names="a b c d") + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + a*z0^3 + b*z1^3 + d*z0*z1*z2 + c*z2^3 + + or give concrete coefficients :: + + sage: FTV.anticanonical_hypersurface(coefficients=[1, 2, 3, 4]) + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + z0^3 + 2*z1^3 + 4*z0*z1*z2 + 3*z2^3 + + or even mix numerical coefficients with some expressions :: + + sage: H = FTV.anticanonical_hypersurface( + ....: coefficients=[0, "t", "1/t", "psi/(psi^2 + phi)"]) + sage: H + Closed subscheme of 2-d CPR-Fano toric variety + covered by 3 affine patches defined by: + t*z1^3 + psi/(phi + psi^2)*z0*z1*z2 + 1/t*z2^3 + sage: R = H.ambient_space().base_ring() + sage: R + Fraction Field of + Multivariate Polynomial Ring in phi, psi, t over Rational Field """ - # AL: we include this function because the construction only rely on the Delta and cox variables + # The example above is also copied to the tutorial section in the + # main documentation of the module. return AnticanonicalHypersurface(self, **kwds) def change_ring(self, F): r""" - Return a Fano toric variety over field ``F``, otherwise the same + Return a CPR-Fano toric variety over field ``F``, otherwise the same as ``self``. INPUT: - ``F`` -- field - OUTPUT: :class:`Fano toric variety ` over ``F`` + OUTPUT: :class:`CPR-Fano toric variety ` over ``F`` .. NOTE:: @@ -657,9 +1367,66 @@ def change_ring(self, F): raise TypeError("need a field to construct a Fano toric variety!" "\n Got %s" % F) else: - return FanoToricVariety_field(self._Delta_polar, self._fan, + return CPRFanoToricVariety_field(self._Delta_polar, self._fan, + self._coordinate_points, self._point_to_ray, self.variable_names(), None, F) - + # coordinate_name_indices do not matter, we give explicit + # names for all variables + + def coordinate_point_to_coordinate(self, point): + r""" + Return the variable of the coordinate ring corresponding to ``point``. + + INPUT: + + - ``point`` -- integer from the list of :meth:`coordinate_points` + + OUTPUT: the corresponding generator of the coordinate ring of ``self`` + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: FTV = CPRFanoToricVariety(diamond, coordinate_points=[0,1,2,3,8]) + sage: FTV.coordinate_points() + (0, 1, 2, 3, 8) + sage: FTV.gens() + (z0, z1, z2, z3, z8) + sage: FTV.coordinate_point_to_coordinate(8) + z8 + """ + return self.gen(self._point_to_ray[point]) + + def coordinate_points(self): + r""" + Return indices of points of :meth:`Delta_polar` used for coordinates. + + OUTPUT: :class:`tuple` of integers + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: square = diamond.polar() + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,8]) + sage: FTV.coordinate_points() + (0, 1, 2, 3, 8) + sage: FTV.gens() + (z0, z1, z2, z3, z8) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all') + sage: FTV.coordinate_points() + (0, 1, 2, 3, 4, 5, 7, 8) + sage: FTV.gens() + (z0, z1, z2, z3, z4, z5, z7, z8) + + Note that one point is missing, namely :: + + sage: square.origin() + 6 + """ + return self._coordinate_points + def Delta(self): r""" Return the reflexive polytope associated to ``self``. @@ -668,13 +1435,13 @@ def Delta(self): - reflexive :class:`lattice polytope `. The - underlying fan of ``self`` is the + underlying fan of ``self`` is a coherent subdivision of the *normal fan* of this polytope. EXAMPLES:: sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) sage: P1xP1.Delta() 2-d reflexive polytope #14 in 2-d lattice N sage: P1xP1.Delta() is diamond.polar() @@ -690,12 +1457,13 @@ def Delta_polar(self): - reflexive :class:`lattice polytope `. The - underlying fan of ``self`` is the *face fan* of this polytope. + underlying fan of ``self`` is a coherent subdivision of the + *face fan* of this polytope. EXAMPLES:: sage: diamond = lattice_polytope.cross_polytope(2) - sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) sage: P1xP1.Delta_polar() 2-d reflexive polytope #3 in 2-d lattice M sage: P1xP1.Delta_polar() is diamond @@ -712,12 +1480,12 @@ def nef_complete_intersection(self, nef_partition, **kwds): .. NOTE:: The returned complete intersection may be actually a subscheme of - **another** Fano toric variety: if the base field of ``self`` + **another** CPR-Fano toric variety: if the base field of ``self`` does not include all of the required names for monomial coefficients, it will be automatically extended. Below `\Delta` is the reflexive polytope corresponding to ``self``, - i.e. the fan of ``self`` is the normal fan of + i.e. the fan of ``self`` is a refinement of the normal fan of `\Delta`. Other polytopes are described in the documentation of :class:`nef-partitions ` of :class:`reflexive polytopes @@ -775,9 +1543,9 @@ def nef_complete_intersection(self, nef_partition, **kwds): sage: p = ReflexivePolytope(3, 2254) sage: np = p.nef_partitions()[1]; np Nef-partition {2, 3, 4, 7, 8} ⊔ {0, 1, 5, 6} - sage: X = FanoToricVariety(Delta_polar=p) + sage: X = CPRFanoToricVariety(Delta_polar=p) sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d Fano toric variety + Closed subscheme of 3-d CPR-Fano toric variety covered by 10 affine patches defined by: a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + a3*z2*z3*z4*z7*z8 + a1*z0*z2, @@ -788,7 +1556,7 @@ def nef_complete_intersection(self, nef_partition, **kwds): Now we include only monomials associated to vertices of `\Delta_i`:: sage: X.nef_complete_intersection(np, monomial_points='vertices') - Closed subscheme of 3-d Fano toric variety + Closed subscheme of 3-d CPR-Fano toric variety covered by 10 affine patches defined by: a0*z1*z4^2*z5^2*z7^3 + a2*z2*z4*z5*z6*z7^2*z8^2 + a3*z2*z3*z4*z7*z8 + a1*z0*z2, @@ -801,7 +1569,7 @@ def nef_complete_intersection(self, nef_partition, **kwds): sage: X.nef_complete_intersection(np, ....: monomial_points='vertices', ....: coefficients=[("a", "a^2", "a/e", "c_i"), list(range(1,6))]) - Closed subscheme of 3-d Fano toric variety + Closed subscheme of 3-d CPR-Fano toric variety covered by 10 affine patches defined by: a*z1*z4^2*z5^2*z7^3 + a/e*z2*z4*z5*z6*z7^2*z8^2 + (c_i)*z2*z3*z4*z7*z8 + (a^2)*z0*z2, @@ -811,10 +1579,10 @@ def nef_complete_intersection(self, nef_partition, **kwds): Finally, we take a look at the generic representative of these complete intersections in a completely resolved ambient toric variety:: - sage: X = FanoToricVariety(Delta_polar=p, + sage: X = CPRFanoToricVariety(Delta_polar=p, ....: coordinate_points='all') sage: X.nef_complete_intersection(np) - Closed subscheme of 3-d Fano toric variety + Closed subscheme of 3-d CPR-Fano toric variety covered by 22 affine patches defined by: a2*z2*z4*z5*z6*z7^2*z8^2*z9^2*z10^2*z11*z12*z13 + a0*z1*z4^2*z5^2*z7^3*z9*z10^2*z12*z13 @@ -865,25 +1633,131 @@ def cartesian_product(self, other, sage: P1xP2.Delta_polar() 3-d reflexive polytope in 3-d lattice N+N """ - if isinstance(other, FanoToricVariety_field) and not isinstance(other, CPRFanoToricVariety_field) \ - and not isinstance(self, SmoothFanoToricVariety_field): + if isinstance(other, CPRFanoToricVariety_field): fan = self.fan().cartesian_product(other.fan()) Delta_polar = LatticePolytope(fan.rays()) - return FanoToricVariety_field(Delta_polar, fan, + + points = Delta_polar.points() + point_to_ray = {} + coordinate_points = [] + for ray_index, ray in enumerate(fan.rays()): + point = points.index(ray) + coordinate_points.append(point) + point_to_ray[point] = ray_index + + return CPRFanoToricVariety_field(Delta_polar, fan, + coordinate_points, point_to_ray, coordinate_names, coordinate_indices, self.base_ring()) return super().cartesian_product(other) + def resolve(self, **kwds): + r""" + Construct a toric variety whose fan subdivides the fan of ``self``. + + This function accepts only keyword arguments, none of which are + mandatory. + + INPUT: + + - ``new_points`` -- list of integers, indices of boundary points of + :meth:`Delta_polar`, which should be added as rays to the + subdividing fan + + - all other arguments will be passed to + :meth:`~sage.schemes.toric.variety.ToricVariety_field.resolve` + method of (general) toric varieties; see its documentation for + details + + OUTPUT: + + - :class:`CPR-Fano toric variety ` if there + was no ``new_rays`` argument and :class:`toric variety + ` otherwise. + + EXAMPLES:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: FTV = CPRFanoToricVariety(Delta=diamond) + sage: FTV.coordinate_points() + (0, 1, 2, 3) + sage: FTV.gens() + (z0, z1, z2, z3) + sage: FTV_res = FTV.resolve(new_points=[6,8]) + Traceback (most recent call last): + ... + ValueError: the origin (point #6) + cannot be used for subdivision! + sage: FTV_res = FTV.resolve(new_points=[8,5]); FTV_res + 2-d CPR-Fano toric variety covered by 6 affine patches + sage: FTV_res.coordinate_points() + (0, 1, 2, 3, 8, 5) + sage: FTV_res.gens() + (z0, z1, z2, z3, z8, z5) + + sage: TV_res = FTV.resolve(new_rays=[(1,2)]); TV_res + 2-d toric variety covered by 5 affine patches + sage: TV_res.gens() + (z0, z1, z2, z3, z4) + """ + # Reasons to override the base class: + # - allow using polytope point indices for subdivision + # - handle automatic name creation in a different fashion + # - return CPR-Fano toric variety if the above feature was used and + # just toric variety if subdivision involves rays + if "new_rays" in kwds: + if "new_points" in kwds: + raise ValueError("you cannot give new_points and new_rays at " + "the same time!") + return super().resolve(**kwds) + # Now we need to construct another Fano variety + new_points = kwds.pop("new_points", ()) + coordinate_points = self.coordinate_points() + new_points = tuple(point for point in new_points + if point not in coordinate_points) + Delta_polar = self._Delta_polar + if Delta_polar.origin() in new_points: + raise ValueError("the origin (point #%d) cannot be used for " + "subdivision!" % Delta_polar.origin()) + if new_points: + coordinate_points = coordinate_points + new_points + point_to_ray = {point: n + for n, point in enumerate(coordinate_points)} + else: + point_to_ray = self._point_to_ray + new_rays = [Delta_polar.point(point) for point in new_points] + coordinate_name_indices = kwds.pop("coordinate_name_indices", + coordinate_points) + fan = self.fan() + if "coordinate_names" in kwds: + coordinate_names = kwds.pop("coordinate_names") + else: + coordinate_names = list(self.variable_names()) + coordinate_names.extend(normalize_names(ngens=len(new_rays), + indices=coordinate_name_indices[fan.nrays():], + prefix=self._coordinate_prefix)) + coordinate_names.append(self._coordinate_prefix + "+") + rfan = fan.subdivide(new_rays=new_rays, **kwds) + resolution = CPRFanoToricVariety_field(Delta_polar, rfan, + coordinate_points, point_to_ray, coordinate_names, + coordinate_name_indices, self.base_ring()) + R = self.coordinate_ring() + R_res = resolution.coordinate_ring() + resolution_map = resolution.hom(R.hom(R_res.gens()[:R.ngens()]), self) + resolution._resolution_map = resolution_map + return resolution + + class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric): r""" - Construct an anticanonical hypersurface of a CPR-Fano toric variety. + Construct an anticanonical hypersurface of a Fano toric variety. INPUT: - - ``P_Delta`` -- :class:`CPR-Fano toric variety - ` associated to a reflexive polytope `\Delta` + - ``P_Delta`` -- :class:`Fano toric variety + ` associated to a reflexive polytope `\Delta` - - see :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for + - see :meth:`FanoToricVariety_field.anticanonical_hypersurface` for documentation on all other acceptable parameters OUTPUT: @@ -896,17 +1770,17 @@ class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric): sage: P1xP1 = toric_varieties.P1xP1() sage: import sage.schemes.toric.fano_variety as ftv sage: ftv.AnticanonicalHypersurface(P1xP1) - Closed subscheme of 2-d CPR-Fano toric variety + Closed subscheme of 2-d Fano toric variety covered by 4 affine patches defined by: a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 - See :meth:`~CPRFanoToricVariety_field.anticanonical_hypersurface()` for a + See :meth:`~FanoToricVariety_field.anticanonical_hypersurface()` for a more elaborate example. """ def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, coefficient_name_indices=None, coefficients=None): r""" - See :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for + See :meth:`FanoToricVariety_field.anticanonical_hypersurface` for documentation. TESTS:: @@ -914,7 +1788,7 @@ def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, sage: P1xP1 = toric_varieties.P1xP1() sage: import sage.schemes.toric.fano_variety as ftv sage: ftv.AnticanonicalHypersurface(P1xP1) - Closed subscheme of 2-d CPR-Fano toric variety + Closed subscheme of 2-d Fano toric variety covered by 4 affine patches defined by: a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 @@ -924,12 +1798,12 @@ def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, sage: X = P1xP1.change_ring(F) # needs sage.rings.finite_rings sage: X.anticanonical_hypersurface(monomial_points='all', # needs sage.rings.finite_rings ....: coefficients=[1]*X.Delta().npoints()) - Closed subscheme of 2-d CPR-Fano toric variety + Closed subscheme of 2-d Fano toric variety covered by 4 affine patches defined by: s^2*x^2 + s*t*x^2 + t^2*x^2 + s^2*x*y + s*t*x*y + t^2*x*y + s^2*y^2 + s*t*y^2 + t^2*y^2 """ - if not isinstance(P_Delta, CPRFanoToricVariety_field): + if not isinstance(P_Delta, FanoToricVariety_field): raise TypeError("anticanonical hypersurfaces can only be " "constructed for CPR-Fano toric varieties!" "\nGot: %s" % P_Delta) @@ -1009,7 +1883,7 @@ class NefCompleteIntersection(AlgebraicScheme_subscheme_toric): sage: o = lattice_polytope.cross_polytope(3) sage: np = o.nef_partitions()[0]; np Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = CPRFanoToricVariety(Delta_polar=o) + sage: X = FanoToricVariety(Delta_polar=o) sage: X.nef_complete_intersection(np) Closed subscheme of 3-d Fano toric variety covered by 8 affine patches defined by: @@ -1044,7 +1918,7 @@ def __init__(self, P_Delta, nef_partition, """ if not isinstance(P_Delta, FanoToricVariety_field): raise TypeError("nef complete intersections can only be " - "constructed for CPR-Fano toric varieties!" + "constructed for Fano toric varieties!" "\nGot: %s" % P_Delta) if nef_partition.Delta() is not P_Delta.Delta(): raise ValueError("polytopes 'Delta' of the nef-partition and the " @@ -1126,9 +2000,9 @@ def cohomology_class(self): sage: o = lattice_polytope.cross_polytope(3) sage: np = o.nef_partitions()[0]; np Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = FanoToricVariety(Delta_polar=o) + sage: X = CPRFanoToricVariety(Delta_polar=o) sage: CI = X.nef_complete_intersection(np); CI - Closed subscheme of 3-d Fano toric variety + Closed subscheme of 3-d CPR-Fano toric variety covered by 8 affine patches defined by: a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 @@ -1152,9 +2026,9 @@ def nef_partition(self): sage: o = lattice_polytope.cross_polytope(3) sage: np = o.nef_partitions()[0]; np Nef-partition {0, 1, 3} ⊔ {2, 4, 5} - sage: X = FanoToricVariety(Delta_polar=o) + sage: X = CPRFanoToricVariety(Delta_polar=o) sage: CI = X.nef_complete_intersection(np); CI - Closed subscheme of 3-d Fano toric variety + Closed subscheme of 3-d CPR-Fano toric variety covered by 8 affine patches defined by: a2*z0^2*z1 + a5*z0*z1*z3 + a1*z1*z3^2 + a3*z0^2*z4 + a4*z0*z3*z4 + a0*z3^2*z4, b1*z1*z2^2 + b2*z2^2*z4 + b5*z1*z2*z5 + b4*z2*z4*z5 + b3*z1*z5^2 + b0*z4*z5^2 @@ -1217,4 +2091,6 @@ def add_variables(field, variables): for v in variables: if v not in new_variables: new_variables.append(v) - return PolynomialRing(field, new_variables).fraction_field() \ No newline at end of file + return PolynomialRing(field, new_variables).fraction_field() + + diff --git a/src/sage/schemes/toric/library.py b/src/sage/schemes/toric/library.py index e892a135d0b..6f833c5c5d2 100644 --- a/src/sage/schemes/toric/library.py +++ b/src/sage/schemes/toric/library.py @@ -52,7 +52,7 @@ from sage.schemes.toric.variety import (DEFAULT_PREFIX, ToricVariety, normalize_names) -from sage.schemes.toric.cpr_fano_variety import CPRFanoToricVariety +from sage.schemes.toric.fano_variety import CPRFanoToricVariety # The combinatorial data of the toric varieties is stored separately here From f03bdda72de19d8d08414a5cfe39254643b9abcd Mon Sep 17 00:00:00 2001 From: local-ring Date: Tue, 15 Jul 2025 17:45:07 -0400 Subject: [PATCH 4/6] wrap up the work introducing the superclass of general fano toric --- src/sage/schemes/toric/fano_variety.py | 88 +++++++++++++++++++------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index e31f25c669d..79aad736012 100644 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -324,11 +324,11 @@ def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, sage: P1xP1 = FanoToricVariety( ....: Delta_polar=lattice_polytope.cross_polytope(2)) sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches + 2-d Fano toric variety covered by 4 affine patches """ self._Delta_polar = Delta_polar # AL: we want to use the anticanonical surface and nef complete intersection functions for general Fano toric varieties - self._coordinate_points = tuple(range(fan.rays())) + self._coordinate_points = tuple(range(len(fan.rays()))) self._point_to_ray = {i: i for i in self._coordinate_points} super().__init__(fan, coordinate_names, coordinate_name_indices, base_field) @@ -355,7 +355,8 @@ def _repr_(self): TESTS:: - sage: P1xP1 = toric_varieties.P1xP1() + sage: P1xP1 = FanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) sage: print(P1xP1._repr_()) 2-d Fano toric variety covered by 4 affine patches """ @@ -481,6 +482,41 @@ def change_ring(self, F): return FanoToricVariety_field(self._Delta_polar, self._fan, self.variable_names(), None, F) + def coordinate_point_to_coordinate(self, point): + r""" + Return the coordinate of the point in the coordinate ring of ``self``. + + INPUT: + + - ``point`` -- integer, index of a coordinate point of ``self`` + (see :meth:`coordinate_points`) + + OUTPUT: :class:`sage.rings.polynomial.polynomial_element.Polynomial_generic` + + EXAMPLES:: + + sage: P1xP1 = FanoToricVariety(Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1.coordinate_point_to_coordinate(0) + z0 + sage: P1xP1.coordinate_point_to_coordinate(3) + z3 + """ + return self.gen(self._point_to_ray[point]) + + def coordinate_points(self): + r""" + Return the list of indices of coordinate points of ``self``. + + OUTPUT: list of integers + + EXAMPLES:: + + sage: P1xP1 = FanoToricVariety(Delta_polar=lattice_polytope.cross_polytope(2)) + sage: P1xP1.coordinate_points() + (0, 1, 2, 3) + """ + return self._coordinate_points + def Delta(self): r""" Return the reflexive polytope associated to ``self``. @@ -618,16 +654,21 @@ def cartesian_product(self, other, EXAMPLES:: - sage: P1 = toric_varieties.P1() - sage: P2 = toric_varieties.P2() + sage: P1_poly = lattice_polytope.cross_polytope(1) + sage: P2_simplex = LatticePolytope([(1,0), (0,1), (-1,-1)]) + sage: P1 = FanoToricVariety(Delta=P1_poly) + sage: P2 = FanoToricVariety(Delta_polar=P2_simplex) sage: P1xP2 = P1.cartesian_product(P2); P1xP2 3-d Fano toric variety covered by 6 affine patches sage: P1xP2.fan().rays() - N+N( 1, 0, 0), N+N(-1, 0, 0), N+N( 0, 1, 0), - N+N( 0, 0, 1), N+N( 0, -1, -1) - in 3-d lattice N+N + N+M(-1, 0, 0), + N+M( 1, 0, 0), + N+M( 0, 1, 0), + N+M( 0, 0, 1), + N+M( 0, -1, -1) + in 3-d lattice N+M sage: P1xP2.Delta_polar() - 3-d reflexive polytope in 3-d lattice N+N + 3-d reflexive polytope in 3-d lattice N+M """ if isinstance(other, FanoToricVariety_field) and not isinstance(other, CPRFanoToricVariety_field): # and not isinstance(other, SmoothFanoToricVariety_field): we will include this in the future if necessary @@ -1149,13 +1190,14 @@ def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray, 2-d CPR-Fano toric variety covered by 4 affine patches """ self._Delta_polar = Delta_polar - self._coordinate_points = tuple(coordinate_points) - self._point_to_ray = point_to_ray # Check/normalize coordinate_indices if coordinate_name_indices is None: coordinate_name_indices = coordinate_points super().__init__(Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field) + # AL: we add the trivial attributes for the general case, so we need to update after super().__init__ + self._coordinate_points = tuple(coordinate_points) + self._point_to_ray = point_to_ray def _latex_(self): r""" @@ -1767,12 +1809,13 @@ class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric): EXAMPLES:: - sage: P1xP1 = toric_varieties.P1xP1() + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) sage: import sage.schemes.toric.fano_variety as ftv sage: ftv.AnticanonicalHypersurface(P1xP1) - Closed subscheme of 2-d Fano toric variety - covered by 4 affine patches defined by: - a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 + Closed subscheme of 2-d Fano toric variety covered by 4 affine patches defined by: + a0*z0^2*z1^2 + a3*z1^2*z2^2 + a6*z0*z1*z2*z3 + a1*z0^2*z3^2 + a2*z2^2*z3^2 + See :meth:`~FanoToricVariety_field.anticanonical_hypersurface()` for a more elaborate example. @@ -1785,12 +1828,12 @@ def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, TESTS:: - sage: P1xP1 = toric_varieties.P1xP1() + sage: diamond = lattice_polytope.cross_polytope(2) + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) sage: import sage.schemes.toric.fano_variety as ftv sage: ftv.AnticanonicalHypersurface(P1xP1) - Closed subscheme of 2-d Fano toric variety - covered by 4 affine patches defined by: - a0*s^2*x^2 + a3*t^2*x^2 + a6*s*t*x*y + a1*s^2*y^2 + a2*t^2*y^2 + Closed subscheme of 2-d Fano toric variety covered by 4 affine patches defined by: + a0*z0^2*z1^2 + a3*z1^2*z2^2 + a6*z0*z1*z2*z3 + a1*z0^2*z3^2 + a2*z2^2*z3^2 Check that finite fields are handled correctly :issue:`14899`:: @@ -1798,10 +1841,9 @@ def __init__(self, P_Delta, monomial_points=None, coefficient_names=None, sage: X = P1xP1.change_ring(F) # needs sage.rings.finite_rings sage: X.anticanonical_hypersurface(monomial_points='all', # needs sage.rings.finite_rings ....: coefficients=[1]*X.Delta().npoints()) - Closed subscheme of 2-d Fano toric variety - covered by 4 affine patches defined by: - s^2*x^2 + s*t*x^2 + t^2*x^2 + s^2*x*y + s*t*x*y - + t^2*x*y + s^2*y^2 + s*t*y^2 + t^2*y^2 + Closed subscheme of 2-d Fano toric variety covered by 4 affine patches defined by: + z0^2*z1^2 + z0*z1^2*z2 + z1^2*z2^2 + z0^2*z1*z3 + z0*z1*z2*z3 + z1*z2^2*z3 + z0^2*z3^2 + z0*z2*z3^2 + z2^2*z3^2 + """ if not isinstance(P_Delta, FanoToricVariety_field): raise TypeError("anticanonical hypersurfaces can only be " From 7031598c32546599121d6954a0130c07e07ab049 Mon Sep 17 00:00:00 2001 From: local-ring Date: Tue, 29 Jul 2025 19:15:54 -0400 Subject: [PATCH 5/6] introduce the classcall_private --- src/sage/schemes/toric/fano_variety.py | 1065 ++++++++++++------------ 1 file changed, 515 insertions(+), 550 deletions(-) diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index 79aad736012..eae51b76615 100644 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -148,176 +148,126 @@ r""" -AL: add base class of (Gorenstein) Fano varieties, which serve as the superclass for both CPR-Fano and smooth Fano varieties. +AL: add class of (Gorenstein) Fano varieties, served as the superclass for both CPR-Fano and smooth Fano varieties. """ - -def FanoToricVariety(Delta=None, - Delta_polar=None, - coordinate_names=None, - names=None, - coordinate_name_indices=None, - base_ring=None, - base_field=None, - check=True): +class FanoToricVariety_field(ToricVariety_field): r""" - Construct a Fano toric variety. + A (Gorenstein) Fano toric variety over a field. - .. NOTE:: + Let `k` be a field, and let `N` be a lattice of rank `n`. A *Fano toric variety* over `k` + is a normal projective toric variety `X` defined over `k` such that its anticanonical + divisor `-K_X` is ample. When `X` is Gorenstein, `-K_X` is also Cartier, and the associated + polytope `\Delta` is a *reflexive lattice polytope* in `N_\RR`. - See documentation of the module - :mod:`~sage.schemes.toric.fano_variety` for the used - definitions and supported varieties. + There is a one-to-one correspondence between Gorenstein Fano toric varieties and reflexive + lattice polytopes: given a reflexive polytope `\Delta \subseteq N_\RR`, the *face fan* + `\Sigma(\Delta)` defines a complete fan whose associated toric variety `X(\Sigma(\Delta))` + is a Gorenstein Fano toric variety. - Due to the large number of available options, it is recommended to always - use keyword parameters. + This class constructs such a variety over a field `k`, from either a reflexive polytope + or its polar. - INPUT: + In the current implementation, the Fano toric variety is constructed as + ``X = X(\Sigma(\Delta^\circ))`` for a reflexive polytope `\Delta`, where `\Delta^\circ` + denotes its polar in the dual lattice `M`. - - ``Delta`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be - given, but not both at the same time, since one is completely determined - by another via :meth:`polar - ` method. - - - ``Delta_polar`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must - be given, but not both at the same time, since one is completely - determined by another via :meth:`polar - ` method. - - - ``coordinate_names`` -- names of variables for the coordinate ring, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed variable names will be - created automatically. - - - ``names`` -- an alias of ``coordinate_names`` for internal - use. You may specify either ``names`` or ``coordinate_names``, - but not both. - - - ``coordinate_name_indices`` -- list of integers, indices for indexed - variables. If not given, the index of each variable will coincide with - the index of the corresponding point of ``Delta_polar``. + .. SEEALSO:: - - ``base_ring`` -- base field of the Fano toric variety - (default: `\QQ`) + See the module documentation + :mod:`~sage.schemes.toric.fano_variety` for further background and references. - - ``base_field`` -- alias for ``base_ring``. Takes precedence if - both are specified. - - - ``check`` -- by default the input data will be checked for correctness - (e.g. that ``charts`` do form a subdivision of the normal fan of - ``Delta``). If you know for sure that the input is valid, you may - significantly decrease construction time using ``check=False`` option. + INPUT: - OUTPUT: :class:`Fano toric variety ` + - ``Delta_polar`` -- reflexive polytope (i.e., polar of a reflexive polytope) + - ``fan`` -- rational polyhedral fan (constructed from `Delta_polar` if not given) + - ``coordinate_names`` -- names for the homogeneous coordinate ring + - ``coordinate_name_indices`` -- indices for named variables (optional) + - ``base_field`` -- the field `k` over which the variety is defined - EXAMPLES: + OUTPUT: A :class:`Fano toric variety ` over the field `k` - We start with the product of two projective lines:: + TESTS:: - sage: diamond = lattice_polytope.cross_polytope(2) - sage: diamond.vertices() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1 = FanoToricVariety( + ....: Delta_polar=lattice_polytope.cross_polytope(2)) sage: P1xP1 2-d Fano toric variety covered by 4 affine patches - sage: P1xP1.fan() - Rational polyhedral fan in 2-d lattice M - sage: P1xP1.fan().rays() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - - Here is a shorthand for defining the toric variety and homogeneous - coordinates in one go:: - - sage: P1xP1. = FanoToricVariety(Delta_polar=diamond) - sage: (a^2+b^2) * (c+d) - a^2*c + b^2*c + a^2*d + b^2*d """ - if names is not None: - if coordinate_names is not None: - raise ValueError('You must not specify both coordinate_names and names!') - coordinate_names = names - # Check/normalize Delta_polar - if (Delta is None) == (Delta_polar is None): - raise ValueError("specify exactly one of Delta or Delta_polar") - if Delta_polar is None: - Delta_polar = Delta.polar() - if check and not Delta_polar.is_reflexive(): - raise ValueError("Delta_polar must be reflexive") - - # AL: construct the fan fromt the original polytope without worrying about the refinement - fan = FaceFan(Delta_polar) - - # Check/normalize base_field - if base_field is not None: - base_ring = base_field - if base_ring is None: - base_ring = QQ - elif base_ring not in _Fields: - raise TypeError("need a field to construct a Fano toric variety!" - "\n Got %s" % base_ring) - - return FanoToricVariety_field( - Delta_polar, fan, coordinate_names, coordinate_name_indices, base_ring) - - -class FanoToricVariety_field(ToricVariety_field): - r""" - Base class for Gorenstein Fano toric varieties over a field. - Construct a Fano toric variety associated to a reflexive polytope. - - .. WARNING:: - - This class does not perform any checks of correctness of input and it - does assume that the internal structure of the given parameters is - coordinated in a certain way. Use - :func:`FanoToricVariety` to construct Fano toric varieties. + @staticmethod + def __classcall_private__(cls, Delta=None, Delta_polar=None, coordinate_names=None, + names=None, coordinate_name_indices=None, base_ring=None, + base_field=None, check=True): + """ + Normalize input to ensure a unique representation. - .. NOTE:: + EXAMPLES:: - See documentation of the module - :mod:`~sage.schemes.toric.fano_variety` for the used - definitions and supported varieties. + sage: diamond = lattice_polytope.cross_polytope(2) + sage: diamond_polar = diamond.polar() + sage: FTV1 = FanoToricVariety(Delta_polar=diamond) + sage: FTV2 = FanoToricVariety(Delta=diamond_polar) + sage: FTV1 is FTV2 + True + """ + if names is not None: + if coordinate_names is not None: + raise ValueError('You must not specify both coordinate_names and names!') + coordinate_names = names + # Check/normalize Delta_polar + if (Delta is None) == (Delta_polar is None): + raise ValueError("specify exactly one of Delta or Delta_polar") + if Delta_polar is None: + Delta_polar = Delta.polar() + if check and not Delta_polar.is_reflexive(): + raise ValueError("Delta_polar must be reflexive") + + # AL: construct the fan fromt the original polytope without worrying about the refinement + fan = FaceFan(Delta_polar) - INPUT: + # Check/normalize base_field + if base_field is not None: + base_ring = base_field + if base_ring is None: + base_ring = QQ + elif base_ring not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % base_ring) - - ``Delta_polar`` -- reflexive polytope + return super().__classcall__(cls, Delta_polar, fan, coordinate_names, + coordinate_name_indices, base_ring) - - ``fan`` -- rational polyhedral fan which is the face fan of - ``Delta_polar`` - - ``coordinate_names`` -- names of the variables of the coordinate ring in - the format accepted by - :func:`~sage.schemes.toric.variety.normalize_names` - - - ``coordinate_name_indices`` -- indices for indexed variables, - if ``None``, will be equal to ``coordinate_points`` + def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field): + r""" + See :class:`FanoToricVariety_field` for documentation. - - ``base_field`` -- base field of the Fano toric variety + Use ``FanoToricVariety`` to construct Fano toric varieties. - OUTPUT: :class:`Fano toric variety ` + EXAMPLES: - TESTS:: + We start with the product of two projective lines:: - sage: P1xP1 = FanoToricVariety( - ....: Delta_polar=lattice_polytope.cross_polytope(2)) - sage: P1xP1 - 2-d Fano toric variety covered by 4 affine patches - """ + sage: diamond = lattice_polytope.cross_polytope(2) + sage: diamond.vertices() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + sage: P1xP1 = FanoToricVariety(Delta_polar=diamond) + sage: P1xP1 + 2-d Fano toric variety covered by 4 affine patches + sage: P1xP1.fan() + Rational polyhedral fan in 2-d lattice M + sage: P1xP1.fan().rays() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M - def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field): - r""" - See :class:`FanoToricVariety_field` for documentation. + Here is a shorthand for defining the toric variety and homogeneous + coordinates in one go:: - Use ``FanoToricVariety`` to construct Fano toric varieties. + sage: P1xP1. = FanoToricVariety(Delta_polar=diamond) + sage: (a^2+b^2) * (c+d) + a^2*c + b^2*c + a^2*d + b^2*d TESTS:: @@ -330,7 +280,7 @@ def __init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, # AL: we want to use the anticanonical surface and nef complete intersection functions for general Fano toric varieties self._coordinate_points = tuple(range(len(fan.rays()))) self._point_to_ray = {i: i for i in self._coordinate_points} - super().__init__(fan, coordinate_names, + ToricVariety_field.__init__(self, fan, coordinate_names, coordinate_name_indices, base_field) def _latex_(self): @@ -720,412 +670,6 @@ def is_CPRFanoToricVariety(x): deprecation(38022, "The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead.") return isinstance(x, CPRFanoToricVariety_field) - -def CPRFanoToricVariety(Delta=None, - Delta_polar=None, - coordinate_points=None, - charts=None, - coordinate_names=None, - names=None, - coordinate_name_indices=None, - make_simplicial=False, - base_ring=None, - base_field=None, - check=True): - r""" - Construct a CPR-Fano toric variety. - - .. NOTE:: - - See documentation of the module - :mod:`~sage.schemes.toric.fano_variety` for the used - definitions and supported varieties. - - Due to the large number of available options, it is recommended to always - use keyword parameters. - - INPUT: - - - ``Delta`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be - given, but not both at the same time, since one is completely determined - by another via :meth:`polar - ` method. - - - ``Delta_polar`` -- reflexive :class:`lattice polytope - `. The fan of the - constructed CPR-Fano toric variety will be a crepant subdivision of the - *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must - be given, but not both at the same time, since one is completely - determined by another via :meth:`polar - ` method. - - - ``coordinate_points`` -- list of integers or string. A list will be - interpreted as indices of (boundary) points of ``Delta_polar`` which - should be used as rays of the underlying fan. It must include all - vertices of ``Delta_polar`` and no repetitions are allowed. A string - must be one of the following descriptions of points of ``Delta_polar``: - - * "vertices" (default), - * "all" (will not include the origin), - * "all but facets" (will not include points in the relative interior of - facets); - - - ``charts`` -- list of lists of elements from ``coordinate_points``. Each - of these lists must define a generating cone of a fan subdividing the - normal fan of ``Delta``. Default ``charts`` correspond to the normal fan - of ``Delta`` without subdivision. The fan specified by ``charts`` will - be subdivided to include all of the requested ``coordinate_points``. - - - ``coordinate_names`` -- names of variables for the coordinate ring, see - :func:`~sage.schemes.toric.variety.normalize_names` - for acceptable formats. If not given, indexed variable names will be - created automatically. - - - ``names`` -- an alias of ``coordinate_names`` for internal - use. You may specify either ``names`` or ``coordinate_names``, - but not both. - - - ``coordinate_name_indices`` -- list of integers, indices for indexed - variables. If not given, the index of each variable will coincide with - the index of the corresponding point of ``Delta_polar``. - - - ``make_simplicial`` -- if ``True``, the underlying fan will be made - simplicial (default: ``False``) - - - ``base_ring`` -- base field of the CPR-Fano toric variety - (default: `\QQ`) - - - ``base_field`` -- alias for ``base_ring``. Takes precedence if - both are specified. - - - ``check`` -- by default the input data will be checked for correctness - (e.g. that ``charts`` do form a subdivision of the normal fan of - ``Delta``). If you know for sure that the input is valid, you may - significantly decrease construction time using ``check=False`` option. - - OUTPUT: :class:`CPR-Fano toric variety ` - - EXAMPLES: - - We start with the product of two projective lines:: - - sage: diamond = lattice_polytope.cross_polytope(2) - sage: diamond.vertices() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) - sage: P1xP1 - 2-d CPR-Fano toric variety covered by 4 affine patches - sage: P1xP1.fan() - Rational polyhedral fan in 2-d lattice M - sage: P1xP1.fan().rays() - M( 1, 0), M( 0, 1), - M(-1, 0), M( 0, -1) - in 2-d lattice M - - "Unfortunately," this variety is smooth to start with and we cannot - perform any subdivisions of the underlying fan without leaving the - category of CPR-Fano toric varieties. Our next example starts with a - square:: - - sage: square = diamond.polar() - sage: square.vertices() - N( 1, 1), N( 1, -1), - N(-1, -1), N(-1, 1) - in 2-d lattice N - sage: square.points() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N(-1, 0), N( 0, -1), - N( 0, 0), N( 0, 1), N( 1, 0) - in 2-d lattice N - - We will construct several varieties associated to it:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), - N(-1, -1), N(-1, 1) - in 2-d lattice N - sage: FTV.gens() - (z0, z1, z2, z3) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,8]) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N( 1, 0) - in 2-d lattice N - sage: FTV.gens() - (z0, z1, z2, z3, z8) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[8,0,2,1,3], - ....: coordinate_names='x+') - sage: FTV.fan().rays() - N( 1, 0), N( 1, 1), N(-1, -1), - N( 1, -1), N(-1, 1) - in 2-d lattice N - sage: FTV.gens() - (x8, x0, x2, x1, x3) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+") - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), N(-1, 1), - N(-1, 0), N( 0, -1), N( 0, 1), N( 1, 0) - in 2-d lattice N - sage: FTV.gens() - (x, y, Z2, Z3, Z4, Z5, Z7, Z8) - - Note that ``Z6`` is "missing". This is due to the fact that the 6-th point - of ``square`` is the origin, and all automatically created names have the - same indices as corresponding points of - :meth:`~CPRFanoToricVariety_field.Delta_polar`. This is usually very - convenient, especially if you have to work with several partial - resolutions of the same Fano toric variety. However, you can change it, if - you want:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+", - ....: coordinate_name_indices=list(range(8))) - sage: FTV.gens() - (x, y, Z2, Z3, Z4, Z5, Z6, Z7) - - Note that you have to provide indices for *all* variables, including those - that have "completely custom" names. Again, this is usually convenient, - because you can add or remove "custom" variables without disturbing too - much "automatic" ones:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x Z+", - ....: coordinate_name_indices=list(range(8))) - sage: FTV.gens() - (x, Z1, Z2, Z3, Z4, Z5, Z6, Z7) - - If you prefer to always start from zero, you will have to shift indices - accordingly:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x Z+", - ....: coordinate_name_indices=[0] + list(range(7))) - sage: FTV.gens() - (x, Z0, Z1, Z2, Z3, Z4, Z5, Z6) - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points='all', - ....: coordinate_names="x y Z+", - ....: coordinate_name_indices=[0]*2 + list(range(6))) - sage: FTV.gens() - (x, y, Z0, Z1, Z2, Z3, Z4, Z5) - - So you always can get any names you want, somewhat complicated default - behaviour was designed with the hope that in most cases you will have no - desire to provide different names. - - Now we will use the possibility to specify initial charts:: - - sage: charts = [(0,1), (1,2), (2,3), (3,0)] - - (these charts actually form exactly the face fan of our square) :: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=charts) - sage: FTV.fan().rays() - N( 1, 1), N( 1, -1), N(-1, -1), - N(-1, 1), N(-1, 0) - in 2-d lattice N - sage: [cone.ambient_ray_indices() for cone in FTV.fan()] - [(0, 1), (1, 2), (2, 4), (3, 4), (0, 3)] - - If charts are wrong, it should be detected:: - - sage: bad_charts = charts + [(3,0)] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: you have provided 5 cones, but only 4 of them are maximal! - Use discard_faces=True if you indeed need to construct a fan from these cones. - - These charts are technically correct, they just happened to list one of - them twice, but it is assumed that such a situation will not happen. It is - especially important when you try to speed up your code:: - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts, - ....: check=False) - Traceback (most recent call last): - ... - IndexError: list assignment index out of range - - In this case you still get an error message, but it is harder to figure out - what is going on. It may also happen that "everything will still work" in - the sense of not crashing, but work with such an invalid variety may lead to - mathematically wrong results, so use ``check=False`` carefully! - - Here are some other possible mistakes:: - - sage: bad_charts = charts + [(0,2)] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: (0, 2) does not form a chart of a subdivision of - the face fan of 2-d reflexive polytope #14 in 2-d lattice N! - - sage: bad_charts = charts[:-1] - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,4], - ....: charts=bad_charts) - Traceback (most recent call last): - ... - ValueError: given charts do not form a complete fan! - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[1,2,3,4]) - Traceback (most recent call last): - ... - ValueError: all 4 vertices of Delta_polar must be used for coordinates! - Got: [1, 2, 3, 4] - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,0,1,2,3,4]) - Traceback (most recent call last): - ... - ValueError: no repetitions are allowed for coordinate points! - Got: [0, 0, 1, 2, 3, 4] - - sage: FTV = CPRFanoToricVariety(Delta_polar=square, - ....: coordinate_points=[0,1,2,3,6]) - Traceback (most recent call last): - ... - ValueError: the origin (point #6) cannot be used for a coordinate! - Got: [0, 1, 2, 3, 6] - - Here is a shorthand for defining the toric variety and homogeneous - coordinates in one go:: - - sage: P1xP1. = CPRFanoToricVariety(Delta_polar=diamond) - sage: (a^2+b^2) * (c+d) - a^2*c + b^2*c + a^2*d + b^2*d - """ - if names is not None: - if coordinate_names is not None: - raise ValueError('You must not specify both coordinate_names and names!') - coordinate_names = names - # Check/normalize Delta_polar - if Delta is None and Delta_polar is None: - raise ValueError("either Delta or Delta_polar must be given!") - elif Delta is not None and Delta_polar is not None: - raise ValueError("Delta and Delta_polar cannot be given together!") - elif Delta_polar is None: - Delta_polar = Delta.polar() - elif not Delta_polar.is_reflexive(): - raise ValueError("Delta_polar must be reflexive!") - # Check/normalize coordinate_points and construct fan rays - if coordinate_points is None: - coordinate_points = list(range(Delta_polar.nvertices())) - if charts is not None: - for chart in charts: - for point in chart: - if point not in coordinate_points: - coordinate_points.append(point) - elif coordinate_points == "vertices": - coordinate_points = list(range(Delta_polar.nvertices())) - elif coordinate_points == "all": - coordinate_points = list(range(Delta_polar.npoints())) - coordinate_points.remove(Delta_polar.origin()) - elif coordinate_points == "all but facets": - coordinate_points = Delta_polar.skeleton_points(Delta_polar.dim() - 2) - elif isinstance(coordinate_points, str): - raise ValueError("unrecognized description of the coordinate points!" - "\nGot: %s" % coordinate_points) - elif check: - cp_set = set(coordinate_points) - if len(cp_set) != len(coordinate_points): - raise ValueError( - "no repetitions are allowed for coordinate points!\nGot: %s" - % coordinate_points) - if not cp_set.issuperset(list(range(Delta_polar.nvertices()))): - raise ValueError("all %d vertices of Delta_polar must be used " - "for coordinates!\nGot: %s" - % (Delta_polar.nvertices(), coordinate_points)) - if Delta_polar.origin() in cp_set: - raise ValueError("the origin (point #%d) cannot be used for a " - "coordinate!\nGot: %s" - % (Delta_polar.origin(), coordinate_points)) - point_to_ray = {point: n - for n, point in enumerate(coordinate_points)} - # This can be simplified if LatticePolytopeClass is adjusted. - rays = [Delta_polar.point(p) for p in coordinate_points] - # Check/normalize charts and construct the fan based on them. - if charts is None: - # Start with the face fan - fan = FaceFan(Delta_polar) - else: - # First of all, check that each chart is completely contained in a - # single facet of Delta_polar, otherwise they do not form a - # subdivision of the face fan of Delta_polar - if check: - facet_sets = [frozenset(facet.ambient_point_indices()) - for facet in Delta_polar.facets()] - for chart in charts: - is_bad = True - for fset in facet_sets: - if fset.issuperset(chart): - is_bad = False - break - if is_bad: - raise ValueError( - "%s does not form a chart of a subdivision of the " - "face fan of %s!" % (chart, Delta_polar)) - # We will construct the initial fan from Cone objects: since charts - # may not use all of the necessary rays, alternative form is tedious - # With check=False it should not be long anyway. - cones = [Cone((rays[point_to_ray[point]] for point in chart), - check=check) - for chart in charts] - fan = Fan(cones, check=check) - if check and not fan.is_complete(): - raise ValueError("given charts do not form a complete fan!") - # Subdivide this fan to use all required points - fan = fan.subdivide(new_rays=(ray for ray in rays - if ray not in fan.rays().set()), - make_simplicial=make_simplicial) - # Now create yet another fan making sure that the order of the rays is - # the same as requested (it is a bit difficult to get it from the start) - trans = {} - for n, ray in enumerate(fan.rays()): - trans[n] = rays.index(ray) - cones = tuple(tuple(sorted(trans[r] for r in cone.ambient_ray_indices())) - for cone in fan) - fan = Fan(cones, rays, check=False) - # Check/normalize base_field - if base_field is not None: - base_ring = base_field - if base_ring is None: - base_ring = QQ - elif base_ring not in _Fields: - raise TypeError("need a field to construct a Fano toric variety!" - "\n Got %s" % base_ring) - fan._is_complete = True # At this point it must be for sure - return CPRFanoToricVariety_field( - Delta_polar, fan, coordinate_points, - point_to_ray, coordinate_names, coordinate_name_indices, base_ring) - - class CPRFanoToricVariety_field(FanoToricVariety_field): r""" Construct a CPR-Fano toric variety associated to a reflexive polytope. @@ -1174,6 +718,411 @@ class CPRFanoToricVariety_field(FanoToricVariety_field): sage: P1xP1 2-d CPR-Fano toric variety covered by 4 affine patches """ + @staticmethod + def __classcall_private__(cls, Delta=None, + Delta_polar=None, + coordinate_points=None, + charts=None, + coordinate_names=None, + names=None, + coordinate_name_indices=None, + make_simplicial=False, + base_ring=None, + base_field=None, + check=True): + r""" + Normalize the input to construct a CPR-Fano toric variety, to ensure a unique representation. + + .. NOTE:: + + See documentation of the module + :mod:`~sage.schemes.toric.fano_variety` for the used + definitions and supported varieties. + + Due to the large number of available options, it is recommended to always + use keyword parameters. + + INPUT: + + - ``Delta`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *normal fan* of ``Delta``. Either ``Delta`` or ``Delta_polar`` must be + given, but not both at the same time, since one is completely determined + by another via :meth:`polar + ` method. + + - ``Delta_polar`` -- reflexive :class:`lattice polytope + `. The fan of the + constructed CPR-Fano toric variety will be a crepant subdivision of the + *face fan* of ``Delta_polar``. Either ``Delta`` or ``Delta_polar`` must + be given, but not both at the same time, since one is completely + determined by another via :meth:`polar + ` method. + + - ``coordinate_points`` -- list of integers or string. A list will be + interpreted as indices of (boundary) points of ``Delta_polar`` which + should be used as rays of the underlying fan. It must include all + vertices of ``Delta_polar`` and no repetitions are allowed. A string + must be one of the following descriptions of points of ``Delta_polar``: + + * "vertices" (default), + * "all" (will not include the origin), + * "all but facets" (will not include points in the relative interior of + facets); + + - ``charts`` -- list of lists of elements from ``coordinate_points``. Each + of these lists must define a generating cone of a fan subdividing the + normal fan of ``Delta``. Default ``charts`` correspond to the normal fan + of ``Delta`` without subdivision. The fan specified by ``charts`` will + be subdivided to include all of the requested ``coordinate_points``. + + - ``coordinate_names`` -- names of variables for the coordinate ring, see + :func:`~sage.schemes.toric.variety.normalize_names` + for acceptable formats. If not given, indexed variable names will be + created automatically. + + - ``names`` -- an alias of ``coordinate_names`` for internal + use. You may specify either ``names`` or ``coordinate_names``, + but not both. + + - ``coordinate_name_indices`` -- list of integers, indices for indexed + variables. If not given, the index of each variable will coincide with + the index of the corresponding point of ``Delta_polar``. + + - ``make_simplicial`` -- if ``True``, the underlying fan will be made + simplicial (default: ``False``) + + - ``base_ring`` -- base field of the CPR-Fano toric variety + (default: `\QQ`) + + - ``base_field`` -- alias for ``base_ring``. Takes precedence if + both are specified. + + - ``check`` -- by default the input data will be checked for correctness + (e.g. that ``charts`` do form a subdivision of the normal fan of + ``Delta``). If you know for sure that the input is valid, you may + significantly decrease construction time using ``check=False`` option. + + OUTPUT: :class:`CPR-Fano toric variety ` + + EXAMPLES: + + We start with the product of two projective lines:: + + sage: diamond = lattice_polytope.cross_polytope(2) + sage: diamond.vertices() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + sage: P1xP1 = CPRFanoToricVariety(Delta_polar=diamond) + sage: P1xP1 + 2-d CPR-Fano toric variety covered by 4 affine patches + sage: P1xP1.fan() + Rational polyhedral fan in 2-d lattice M + sage: P1xP1.fan().rays() + M( 1, 0), M( 0, 1), + M(-1, 0), M( 0, -1) + in 2-d lattice M + + "Unfortunately," this variety is smooth to start with and we cannot + perform any subdivisions of the underlying fan without leaving the + category of CPR-Fano toric varieties. Our next example starts with a + square:: + + sage: square = diamond.polar() + sage: square.vertices() + N( 1, 1), N( 1, -1), + N(-1, -1), N(-1, 1) + in 2-d lattice N + sage: square.points() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N(-1, 0), N( 0, -1), + N( 0, 0), N( 0, 1), N( 1, 0) + in 2-d lattice N + + We will construct several varieties associated to it:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), + N(-1, -1), N(-1, 1) + in 2-d lattice N + sage: FTV.gens() + (z0, z1, z2, z3) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,8]) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N( 1, 0) + in 2-d lattice N + sage: FTV.gens() + (z0, z1, z2, z3, z8) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[8,0,2,1,3], + ....: coordinate_names='x+') + sage: FTV.fan().rays() + N( 1, 0), N( 1, 1), N(-1, -1), + N( 1, -1), N(-1, 1) + in 2-d lattice N + sage: FTV.gens() + (x8, x0, x2, x1, x3) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+") + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), N(-1, 1), + N(-1, 0), N( 0, -1), N( 0, 1), N( 1, 0) + in 2-d lattice N + sage: FTV.gens() + (x, y, Z2, Z3, Z4, Z5, Z7, Z8) + + Note that ``Z6`` is "missing". This is due to the fact that the 6-th point + of ``square`` is the origin, and all automatically created names have the + same indices as corresponding points of + :meth:`~CPRFanoToricVariety_field.Delta_polar`. This is usually very + convenient, especially if you have to work with several partial + resolutions of the same Fano toric variety. However, you can change it, if + you want:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+", + ....: coordinate_name_indices=list(range(8))) + sage: FTV.gens() + (x, y, Z2, Z3, Z4, Z5, Z6, Z7) + + Note that you have to provide indices for *all* variables, including those + that have "completely custom" names. Again, this is usually convenient, + because you can add or remove "custom" variables without disturbing too + much "automatic" ones:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x Z+", + ....: coordinate_name_indices=list(range(8))) + sage: FTV.gens() + (x, Z1, Z2, Z3, Z4, Z5, Z6, Z7) + + If you prefer to always start from zero, you will have to shift indices + accordingly:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x Z+", + ....: coordinate_name_indices=[0] + list(range(7))) + sage: FTV.gens() + (x, Z0, Z1, Z2, Z3, Z4, Z5, Z6) + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points='all', + ....: coordinate_names="x y Z+", + ....: coordinate_name_indices=[0]*2 + list(range(6))) + sage: FTV.gens() + (x, y, Z0, Z1, Z2, Z3, Z4, Z5) + + So you always can get any names you want, somewhat complicated default + behaviour was designed with the hope that in most cases you will have no + desire to provide different names. + + Now we will use the possibility to specify initial charts:: + + sage: charts = [(0,1), (1,2), (2,3), (3,0)] + + (these charts actually form exactly the face fan of our square) :: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=charts) + sage: FTV.fan().rays() + N( 1, 1), N( 1, -1), N(-1, -1), + N(-1, 1), N(-1, 0) + in 2-d lattice N + sage: [cone.ambient_ray_indices() for cone in FTV.fan()] + [(0, 1), (1, 2), (2, 4), (3, 4), (0, 3)] + + If charts are wrong, it should be detected:: + + sage: bad_charts = charts + [(3,0)] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: you have provided 5 cones, but only 4 of them are maximal! + Use discard_faces=True if you indeed need to construct a fan from these cones. + + These charts are technically correct, they just happened to list one of + them twice, but it is assumed that such a situation will not happen. It is + especially important when you try to speed up your code:: + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts, + ....: check=False) + Traceback (most recent call last): + ... + IndexError: list assignment index out of range + + In this case you still get an error message, but it is harder to figure out + what is going on. It may also happen that "everything will still work" in + the sense of not crashing, but work with such an invalid variety may lead to + mathematically wrong results, so use ``check=False`` carefully! + + Here are some other possible mistakes:: + + sage: bad_charts = charts + [(0,2)] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: (0, 2) does not form a chart of a subdivision of + the face fan of 2-d reflexive polytope #14 in 2-d lattice N! + + sage: bad_charts = charts[:-1] + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,4], + ....: charts=bad_charts) + Traceback (most recent call last): + ... + ValueError: given charts do not form a complete fan! + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[1,2,3,4]) + Traceback (most recent call last): + ... + ValueError: all 4 vertices of Delta_polar must be used for coordinates! + Got: [1, 2, 3, 4] + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,0,1,2,3,4]) + Traceback (most recent call last): + ... + ValueError: no repetitions are allowed for coordinate points! + Got: [0, 0, 1, 2, 3, 4] + + sage: FTV = CPRFanoToricVariety(Delta_polar=square, + ....: coordinate_points=[0,1,2,3,6]) + Traceback (most recent call last): + ... + ValueError: the origin (point #6) cannot be used for a coordinate! + Got: [0, 1, 2, 3, 6] + + Here is a shorthand for defining the toric variety and homogeneous + coordinates in one go:: + + sage: P1xP1. = CPRFanoToricVariety(Delta_polar=diamond) + sage: (a^2+b^2) * (c+d) + a^2*c + b^2*c + a^2*d + b^2*d + """ + if names is not None: + if coordinate_names is not None: + raise ValueError('You must not specify both coordinate_names and names!') + coordinate_names = names + # Check/normalize Delta_polar + if Delta is None and Delta_polar is None: + raise ValueError("either Delta or Delta_polar must be given!") + elif Delta is not None and Delta_polar is not None: + raise ValueError("Delta and Delta_polar cannot be given together!") + elif Delta_polar is None: + Delta_polar = Delta.polar() + elif not Delta_polar.is_reflexive(): + raise ValueError("Delta_polar must be reflexive!") + # Check/normalize coordinate_points and construct fan rays + if coordinate_points is None: + coordinate_points = list(range(Delta_polar.nvertices())) + if charts is not None: + for chart in charts: + for point in chart: + if point not in coordinate_points: + coordinate_points.append(point) + elif coordinate_points == "vertices": + coordinate_points = list(range(Delta_polar.nvertices())) + elif coordinate_points == "all": + coordinate_points = list(range(Delta_polar.npoints())) + coordinate_points.remove(Delta_polar.origin()) + elif coordinate_points == "all but facets": + coordinate_points = Delta_polar.skeleton_points(Delta_polar.dim() - 2) + elif isinstance(coordinate_points, str): + raise ValueError("unrecognized description of the coordinate points!" + "\nGot: %s" % coordinate_points) + elif check: + cp_set = set(coordinate_points) + if len(cp_set) != len(coordinate_points): + raise ValueError( + "no repetitions are allowed for coordinate points!\nGot: %s" + % coordinate_points) + if not cp_set.issuperset(list(range(Delta_polar.nvertices()))): + raise ValueError("all %d vertices of Delta_polar must be used " + "for coordinates!\nGot: %s" + % (Delta_polar.nvertices(), coordinate_points)) + if Delta_polar.origin() in cp_set: + raise ValueError("the origin (point #%d) cannot be used for a " + "coordinate!\nGot: %s" + % (Delta_polar.origin(), coordinate_points)) + point_to_ray = {point: n + for n, point in enumerate(coordinate_points)} + # This can be simplified if LatticePolytopeClass is adjusted. + rays = [Delta_polar.point(p) for p in coordinate_points] + # Check/normalize charts and construct the fan based on them. + if charts is None: + # Start with the face fan + fan = FaceFan(Delta_polar) + else: + # First of all, check that each chart is completely contained in a + # single facet of Delta_polar, otherwise they do not form a + # subdivision of the face fan of Delta_polar + if check: + facet_sets = [frozenset(facet.ambient_point_indices()) + for facet in Delta_polar.facets()] + for chart in charts: + is_bad = True + for fset in facet_sets: + if fset.issuperset(chart): + is_bad = False + break + if is_bad: + raise ValueError( + "%s does not form a chart of a subdivision of the " + "face fan of %s!" % (chart, Delta_polar)) + # We will construct the initial fan from Cone objects: since charts + # may not use all of the necessary rays, alternative form is tedious + # With check=False it should not be long anyway. + cones = [Cone((rays[point_to_ray[point]] for point in chart), + check=check) + for chart in charts] + fan = Fan(cones, check=check) + if check and not fan.is_complete(): + raise ValueError("given charts do not form a complete fan!") + # Subdivide this fan to use all required points + fan = fan.subdivide(new_rays=(ray for ray in rays + if ray not in fan.rays().set()), + make_simplicial=make_simplicial) + # Now create yet another fan making sure that the order of the rays is + # the same as requested (it is a bit difficult to get it from the start) + trans = {} + for n, ray in enumerate(fan.rays()): + trans[n] = rays.index(ray) + cones = tuple(tuple(sorted(trans[r] for r in cone.ambient_ray_indices())) + for cone in fan) + fan = Fan(cones, rays, check=False) + # Check/normalize base_field + if base_field is not None: + base_ring = base_field + if base_ring is None: + base_ring = QQ + elif base_ring not in _Fields: + raise TypeError("need a field to construct a Fano toric variety!" + "\n Got %s" % base_ring) + fan._is_complete = True # At this point it must be for sure + return super().__classcall__(cls, + Delta_polar, fan, coordinate_points, point_to_ray, + coordinate_names, coordinate_name_indices, base_ring) + def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray, coordinate_names, coordinate_name_indices, base_field): @@ -1193,7 +1142,7 @@ def __init__(self, Delta_polar, fan, coordinate_points, point_to_ray, # Check/normalize coordinate_indices if coordinate_name_indices is None: coordinate_name_indices = coordinate_points - super().__init__(Delta_polar, fan, coordinate_names, + FanoToricVariety_field.__init__(self, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_field) # AL: we add the trivial attributes for the general case, so we need to update after super().__init__ self._coordinate_points = tuple(coordinate_points) @@ -2136,3 +2085,19 @@ def add_variables(field, variables): return PolynomialRing(field, new_variables).fraction_field() + +# AL: adhoc solution for not failing the existing test cases +# where used the old constructor function +# def CPRFanoToricVariety(*args, **kwargs): +# return CPRFanoToricVariety_field.__classcall__(CPRFanoToricVariety_field, +# *args, **kwargs) + +# def FanoToricVariety(*args, **kwargs): +# return FanoToricVariety_field.__classcall__(FanoToricVariety_field, +# *args, **kwargs) + +def CPRFanoToricVariety(*args, **kwargs): + return CPRFanoToricVariety_field(*args, **kwargs) +def FanoToricVariety(*args, **kwargs): + return FanoToricVariety_field(*args, **kwargs) + From 222899c039562cd4fe7304a0368b9893e3ccf9a7 Mon Sep 17 00:00:00 2001 From: local-ring Date: Fri, 1 Aug 2025 22:35:41 -0400 Subject: [PATCH 6/6] a workable version except failing the unique representation test --- src/sage/schemes/toric/fano_variety.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index eae51b76615..d1312fba718 100644 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -138,6 +138,8 @@ normalize_names) from sage.structure.all import coercion_model from sage.categories.fields import Fields +from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall +from sage.structure.unique_representation import UniqueRepresentation _Fields = Fields() @@ -150,7 +152,7 @@ r""" AL: add class of (Gorenstein) Fano varieties, served as the superclass for both CPR-Fano and smooth Fano varieties. """ -class FanoToricVariety_field(ToricVariety_field): +class FanoToricVariety_field(ToricVariety_field, metaclass=ClasscallMetaclass): r""" A (Gorenstein) Fano toric variety over a field. @@ -203,9 +205,8 @@ def __classcall_private__(cls, Delta=None, Delta_polar=None, coordinate_names=No EXAMPLES:: sage: diamond = lattice_polytope.cross_polytope(2) - sage: diamond_polar = diamond.polar() sage: FTV1 = FanoToricVariety(Delta_polar=diamond) - sage: FTV2 = FanoToricVariety(Delta=diamond_polar) + sage: FTV2 = FanoToricVariety(Delta_polar=diamond) sage: FTV1 is FTV2 True """ @@ -233,7 +234,7 @@ def __classcall_private__(cls, Delta=None, Delta_polar=None, coordinate_names=No raise TypeError("need a field to construct a Fano toric variety!" "\n Got %s" % base_ring) - return super().__classcall__(cls, Delta_polar, fan, coordinate_names, + return typecall(cls, Delta_polar, fan, coordinate_names, coordinate_name_indices, base_ring) @@ -429,7 +430,7 @@ def change_ring(self, F): raise TypeError("need a field to construct a Fano toric variety!" "\n Got %s" % F) else: - return FanoToricVariety_field(self._Delta_polar, self._fan, + return type(self)(self._Delta_polar, self._fan, self.variable_names(), None, F) def coordinate_point_to_coordinate(self, point): @@ -624,7 +625,7 @@ def cartesian_product(self, other, # and not isinstance(other, SmoothFanoToricVariety_field): we will include this in the future if necessary fan = self.fan().cartesian_product(other.fan()) Delta_polar = LatticePolytope(fan.rays()) - return FanoToricVariety_field(Delta_polar, fan, + return type(self)(Delta_polar, fan, coordinate_names, coordinate_indices, self.base_ring()) return super().cartesian_product(other) @@ -670,7 +671,7 @@ def is_CPRFanoToricVariety(x): deprecation(38022, "The function is_CPRFanoToricVariety is deprecated; use 'isinstance(..., CPRFanoToricVariety_field)' instead.") return isinstance(x, CPRFanoToricVariety_field) -class CPRFanoToricVariety_field(FanoToricVariety_field): +class CPRFanoToricVariety_field(FanoToricVariety_field, metaclass=ClasscallMetaclass): r""" Construct a CPR-Fano toric variety associated to a reflexive polytope. @@ -1119,7 +1120,7 @@ def __classcall_private__(cls, Delta=None, raise TypeError("need a field to construct a Fano toric variety!" "\n Got %s" % base_ring) fan._is_complete = True # At this point it must be for sure - return super().__classcall__(cls, + return typecall(cls, Delta_polar, fan, coordinate_points, point_to_ray, coordinate_names, coordinate_name_indices, base_ring) @@ -1358,7 +1359,7 @@ def change_ring(self, F): raise TypeError("need a field to construct a Fano toric variety!" "\n Got %s" % F) else: - return CPRFanoToricVariety_field(self._Delta_polar, self._fan, + return type(self)(self._Delta_polar, self._fan, self._coordinate_points, self._point_to_ray, self.variable_names(), None, F) # coordinate_name_indices do not matter, we give explicit @@ -1636,7 +1637,7 @@ def cartesian_product(self, other, coordinate_points.append(point) point_to_ray[point] = ray_index - return CPRFanoToricVariety_field(Delta_polar, fan, + return type(self)(Delta_polar, fan, coordinate_points, point_to_ray, coordinate_names, coordinate_indices, self.base_ring()) @@ -1729,7 +1730,7 @@ def resolve(self, **kwds): prefix=self._coordinate_prefix)) coordinate_names.append(self._coordinate_prefix + "+") rfan = fan.subdivide(new_rays=new_rays, **kwds) - resolution = CPRFanoToricVariety_field(Delta_polar, rfan, + resolution = type(self)(Delta_polar, rfan, coordinate_points, point_to_ray, coordinate_names, coordinate_name_indices, self.base_ring()) R = self.coordinate_ring()