• ## doc/en/reference/geometry.rst

```# HG changeset patch
# User Andrey Novoseltsev <novoselt@gmail.com>
# Date 1285207580 21600
# Node ID ae67d4d01eca3f3f6da37e31f4cb38fcb02d55ea
# Parent  91fbc1c694603c16d3776ae0dc753588a7d35d10
Trac 9972: Add toric lattice morphisms.

diff -r 91fbc1c69460 -r ae67d4d01eca doc/en/reference/geometry.rst```
 a sage/geometry/fan sage/geometry/toric_lattice sage/geometry/toric_lattice_morphism
• ## sage/geometry/toric_lattice.py

`diff -r 91fbc1c69460 -r ae67d4d01eca sage/geometry/toric_lattice.py`
 a """ try: self(point) return True except TypeError: return False return True def _Hom_(self, Y, category): r""" Return the Hom-space from ``self`` to ``Y``. INPUT: - ``Y`` -- codomain of the Hom-space; - ``category`` -- category in which the Hom-space should live. OUTPUT: - :class:`toric lattce Hom-space `. TESTS:: sage: N3 = ToricLattice(3) sage: N2 = ToricLattice(2) sage: N3._Hom_(N2, None) Set of Morphisms from 3-d lattice N to 2-d lattice N in Category of modules with basis over Integer Ring sage: N3._Hom_(N2, None) == Hom(N3, N2) True """ from sage.geometry.toric_lattice_morphism import ToricLatticeHomspace return ToricLatticeHomspace(self, Y, category) # We need to override this function, otherwise e.g. the sum of elements of # different lattices of the same dimension will live in ZZ^n. N[0, 1, 0] """ return str(self.lift()).replace("(", "[", 1).replace(")", "]", 1)
• ## new file sage/geometry/toric_lattice_morphism.py

`diff -r 91fbc1c69460 -r ae67d4d01eca sage/geometry/toric_lattice_morphism.py`
 - r""" Morphisms between toric lattices This module was designed as a part of the framework for toric varieties (:mod:`~sage.schemes.generic.toric_variety`, :mod:`~sage.schemes.generic.fano_toric_variety`). Its main purpose is to provide support for working with morphisms compatible with fans, see :meth:`~ToricLatticeMorphism.is_compatible_with` and :meth:`~ToricLatticeMorphism.make_compatible_with`. AUTHORS: - Andrey Novoseltsev (2010-09-22): initial version. EXAMPLES: Let's consider the face and normal fans of the "diamond" and the projection to the `x`-axis:: sage: diamond = lattice_polytope.octahedron(2) sage: face = FaceFan(diamond) sage: normal = NormalFan(diamond) sage: N = face.lattice() sage: H = End(N) sage: phi = H([N.0, 0]) sage: phi Free module morphism defined by the matrix [1 0] [0 0] Domain: 2-d lattice N Codomain: 2-d lattice N sage: phi.is_compatible_with(normal, face) False Some of the cones of the normal fan fail to be mapped to a single cone of the face fan. We can rectify the situation in the following way:: sage: subdivision = phi.make_compatible_with(normal, face) sage: subdivision.ray_matrix() [-1  1 -1  1  0  0] [ 1  1 -1 -1 -1  1] sage: normal.ray_matrix() [-1  1 -1  1] [ 1  1 -1 -1] As you see, it was necessary to insert two new rays (to prevent "upper" and "lower" cones of the normal fan from being mapped to the whole `x`-axis). """ #***************************************************************************** #       Copyright (C) 2010 Andrey Novoseltsev #       Copyright (C) 2010 William Stein # #  Distributed under the terms of the GNU General Public License (GPL) # #                  http://www.gnu.org/licenses/ #***************************************************************************** import operator from sage.geometry.cone import Cone, is_Cone from sage.geometry.fan import Fan, is_Fan from sage.matrix.all import is_Matrix, matrix from sage.misc.all import walltime from sage.modules.free_module_homspace import FreeModuleHomspace from sage.modules.free_module_morphism import FreeModuleMorphism from sage.rings.all import ZZ class ToricLatticeHomspace(FreeModuleHomspace): r""" Create a space of homomorphisms between toric lattices. INPUT: - same as for :class:`~sage.modules.free_module_homspace.FreeModuleHomspace`. OUTPUT: - a space of homomorphisms between toric lattices. EXAMPLES:: sage: N = ToricLattice(3) sage: M = N.dual() sage: H = Hom(N, M)  # indirect doctest sage: H Set of Morphisms from 3-d lattice N to 3-d lattice M in Category of modules with basis over Integer Ring """ # sage: TestSuite(H).run() is not tested since the base class fails it # We could be happy with the base class call, but it return objects of the # wrong class def __call__(self, A, check=True): """ Construct a morphism corresponding to ``A``. INPUT: - ``A`` -- a matrix or a list of images of generators of the domain of ``self``; - ``check`` -- boolean (default: ``True``). If ``A`` is a matrix, then it is the matrix of this linear transformation, with respect to the bases for the domain and codomain of ``self``. EXAMPLES:: sage: N3 = ToricLattice(3, "N3") sage: N2 = ToricLattice(2, "N2") sage: H = Hom(N3, N2) sage: H Set of Morphisms from 3-d lattice N3 to 2-d lattice N2 in Category of modules with basis over Integer Ring sage: phi = H([N2.0, N2.1, N2.0])   # indirect doctest sage: phi Free module morphism defined by the matrix [1 0] [0 1] [1 0] Domain: 3-d lattice N3 Codomain: 2-d lattice N2 sage: phi(N3(1,2,3)) N2(4, 2) """ if not is_Matrix(A): # Compute the matrix of the morphism that sends the # generators of the domain to the elements of A. codomain = self.codomain() try: A = matrix(ZZ, [codomain.coordinates(codomain(a)) for a in A]) except TypeError: pass return ToricLatticeMorphism(self, A) class ToricLatticeMorphism(FreeModuleMorphism): r""" Create a morphism between toric lattices. INPUT: - ``parent`` -- a :class:`space of homomorphisms between toric lattices `; - ``A`` -- an integral matrix defining the morphism. OUTPUT: - a morphism between toric lattices. EXAMPLES:: sage: N3 = ToricLattice(3, "N3") sage: N2 = ToricLattice(2, "N2") sage: H = Hom(N3, N2) sage: phi = H([N2.0, N2.1, N2.0])  # indirect doctest sage: phi Free module morphism defined by the matrix [1 0] [0 1] [1 0] Domain: 3-d lattice N3 Codomain: 2-d lattice N2 """ def _chambers(self, fan): r""" Compute chambers in the domain of ``self`` corresponding to ``fan``. This function is useful for automatic refinement of fans to make them compatible with ``self``, see :meth:`make_compatible_with`. INPUT: - ``fan`` -- a :class:`fan ` in the codomain of ``self``. OUTPUT: - a :class:`tuple` ``(chambers, cone_to_chamber)``, where - ``chambers`` is a :class:`list` of :class:`cones ` in the domain of ``self``; - ``cone_to_chamber`` is a :class:`list` of integers, if its `i`-th element is `j`, then the `j`-th element of ``chambers`` is the inverse image of the `i`-th generating cone of ``fan``. TESTS:: sage: F = NormalFan(lattice_polytope.octahedron(2)) sage: N = F.lattice() sage: H = End(N) sage: phi = H([N.0, 0]) sage: phi._chambers(F) ([2-d cone in 2-d lattice N, 1-d cone in 2-d lattice N, 2-d cone in 2-d lattice N], [0, 1, 2, 1]) """ # It probably does not make much sense to cache chambers, since their # computation for fan subdivision is likely to be much shorter than # the subdivision itself. kernel_rays = [] for ray in self.kernel().basis(): kernel_rays.append(ray) kernel_rays.append(-ray) image_rays = [] for ray in self.image().basis(): image_rays.append(ray) image_rays.append(-ray) image = Cone(image_rays) chambers = [] cone_to_chamber = [] for cone in fan: chamber = Cone([self.lift(ray) for ray in cone.intersection(image)] + kernel_rays, lattice=self.domain()) cone_to_chamber.append(len(chambers)) for i, old_chamber in enumerate(chambers): if old_chamber.is_equivalent(chamber): cone_to_chamber[-1] = i break if cone_to_chamber[-1] == len(chambers): chambers.append(chamber) return (chambers, cone_to_chamber) def is_compatible_with(self, x, y): r""" Check if ``self`` is compatible with given cones or fans. INPUT: - ``x`` -- a :class:`cone = dim) parts = (domain_cone.intersection(chamber) for chamber in containing_chambers) # We cannot leave parts as a generator since we use them twice. parts = [part for part in parts if part.dim() == dim] if check: # Check if the subdivision is complete, i.e. there are no # missing pieces of domain_cone. To do this, we construct a # fan from the obtained parts and check that interior points # of boundary cones of this fan are in the interior of the # original cone. In any case we know that we are constructing # a valid fan, so passing check=False to Fan(...) is OK. if verbose: print "%.3f ms" % walltime(start) start = walltime() print "Checking for missing pieces... ", cone_subdivision = Fan(parts, check=False) for cone in cone_subdivision(dim - 1): if len(cone.star_generators()) == 1: if domain_cone.relative_interior_contains( sum(cone.rays())): raise ValueError("%s\ndoes not map\n%s\ninto the " "support of\n%s!" % (self, domain_fan, codomain_fan)) new_cones.extend(parts) if verbose: print "%.3f ms" % walltime(start) if len(new_cones) == domain_fan.ngenerating_cones(): return domain_fan # Construct a new fan keeping old rays in the same order new_rays = list(domain_fan.rays()) for cone in new_cones: for ray in cone: if ray not in new_rays: new_rays.append(ray) return Fan(new_cones, new_rays, domain_cone.lattice(), check=False)