Ticket #14353: trac_14353_fan_morphism_factoring.patch

File trac_14353_fan_morphism_factoring.patch, 14.6 KB (added by novoselt, 9 years ago)
  • sage/geometry/fan_morphism.py

    # HG changeset patch
    # User Andrey Novoseltsev <novoselt@gmail.com>
    # Date 1365551602 21600
    # Node ID 4d3fba45384c0feb0b2d7944abd0c60a4d35741d
    # Parent  c74ca2a4c97bca63e2e1c63f653d6418f56f37ca
    Factor method for FanMorphism.
    
    diff --git a/sage/geometry/fan_morphism.py b/sage/geometry/fan_morphism.py
    a b  
    8181from sage.categories.all import Hom
    8282from sage.geometry.cone import Cone
    8383from sage.geometry.fan import Fan, is_Fan, discard_faces
    84 from sage.matrix.all import matrix, is_Matrix
     84from sage.matrix.all import identity_matrix, matrix, is_Matrix
    8585from sage.misc.all import cached_method, latex, prod, walltime
    8686from sage.modules.free_module_morphism import (FreeModuleMorphism,
    8787                                               is_FreeModuleMorphism)
     
    287287        self._preimage_fans = dict()
    288288        self._primitive_preimage_cones = dict()
    289289        if codomain_fan is None:
    290             self._construct_codomain_fan()
     290            self._construct_codomain_fan(check)
    291291        else:
    292292            self._codomain_fan = codomain_fan
    293293            if subdivide:
    294294                self._subdivide_domain_fan(check, verbose)
    295295            elif check:
    296296                self._validate()
    297                
     297
     298    def __mul__(self, right):
     299        """
     300        Return the composition of ``self`` and ``right``.
     301       
     302        INPUT:
     303       
     304        - ``right`` -- a :class:`FanMorphism` whose :meth:`codomain_fan` can be
     305          mapped to :meth:`domain_fan` of ``self`` via identity map of lattices.
     306       
     307        OUTPUT:
     308       
     309        - a :class:`FanMorphism`.
     310       
     311        EXAMPLES::
     312
     313            sage: A2 = toric_varieties.A2()
     314            sage: P3 = toric_varieties.P(3)
     315            sage: m = matrix([(2,0,0), (1,1,0)])
     316            sage: phi = A2.hom(m, P3).fan_morphism()
     317            sage: phi
     318            Fan morphism defined by the matrix
     319            [2 0 0]
     320            [1 1 0]
     321            Domain fan: Rational polyhedral fan in 2-d lattice N
     322            Codomain fan: Rational polyhedral fan in 3-d lattice N
     323            sage: prod(phi.factor()) # indirect test
     324            Fan morphism defined by the matrix
     325            [2 0 0]
     326            [1 1 0]
     327            Domain fan: Rational polyhedral fan in 2-d lattice N
     328            Codomain fan: Rational polyhedral fan in 3-d lattice N
     329        """
     330        if not isinstance(right, FanMorphism):
     331            raise TypeError(
     332                "fan morphisms should be composed with fan morphisms")       
     333        # We don't need it, we just check compatibility of fans:
     334        FanMorphism(identity_matrix(self.domain().dimension()),
     335                    right.codomain_fan(), self.domain_fan())
     336        m = right.matrix() * self.matrix()
     337        return FanMorphism(m, right.domain_fan(), self.codomain_fan())
     338
    298339    def _RISGIS(self):
    299340        r"""
    300341        Return Ray Images Star Generator Indices Sets.
     
    385426                chambers.append(chamber)
    386427        return (chambers, cone_to_chamber)
    387428       
    388     def _construct_codomain_fan(self):
     429    def _construct_codomain_fan(self, check):
    389430        r"""
    390431        Construct the codomain fan as the image of the domain one.
    391432       
     
    393434       
    394435            This method should be called only during initialization.
    395436           
     437        INPUT:
     438       
     439        - ``check`` -- passed on to the fan constructor.
     440           
    396441        OUTPUT:
    397442       
    398         - none, but the codomain fan of ``self`` is set to the constucted fan.
     443        - none, but the codomain fan of ``self`` is set to the constructed fan.
    399444       
    400445        TESTS::
    401446       
     
    422467                                        for domain_cone in domain_fan),
    423468                                 rays=(self(ray) for ray in domain_fan.rays()),
    424469                                 lattice=self.codomain(),
    425                                  discard_faces=True)
     470                                 discard_faces=True, check=check)
    426471   
    427472    def _latex_(self):
    428473        r"""
     
    942987                                                    self(ray) for ray in cone)
    943988        return self._image_cone[cone]
    944989       
    945     @cached_method
    946990    def index(self, cone=None):
    947991        r"""
    948992        Return the index of ``self`` as a map between lattices.
     
    9971041            N(-1, -1),
    9981042            N( 1,  0)
    9991043            in 2-d lattice N
    1000             sage: xi.restrict_to_image().codomain_fan().rays()
     1044            sage: xi.factor()[0].domain_fan().rays()
    10011045            N( 1, 0),
    10021046            N(-1, 0)
    10031047            in Sublattice <N(1, 0)>
     
    10271071            sage: [zeta.index(cone) for cone in flatten(Sigma_p.cones())]
    10281072            [+Infinity, None, None, None, None, None, None, None, None, None,
    10291073             4, 4, None, 4, None, None, 2, None, 4, None, 4, 1, 1, 1, 1, 1, 1]
    1030             sage: zeta = zeta.restrict_to_image()
     1074            sage: zeta = prod(zeta.factor()[1:])
    10311075            sage: Sigma_p = zeta.codomain_fan()
    10321076            sage: [zeta.index(cone) for cone in flatten(Sigma_p.cones())]
    10331077            [4, 4, 1, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1]
     
    10491093        i = prod((Q/S).invariants())
    10501094        return i if i > 0 else Infinity
    10511095
     1096    def is_birational(self):
     1097        r"""
     1098        Check if ``self`` is birational.
     1099       
     1100        OUTPUT:
     1101       
     1102        - ``True`` if ``self`` is birational, ``False`` otherwise.
     1103
     1104        For fan morphisms this check is equivalent to ``self.index() == 1`` and
     1105        means that the corresponding map between toric varieties is birational.
     1106       
     1107        EXAMPLES::
     1108       
     1109            sage: Sigma = toric_varieties.dP8().fan()
     1110            sage: Sigma_p = toric_varieties.P1().fan()
     1111            sage: phi = FanMorphism(matrix([[1], [-1]]), Sigma, Sigma_p)
     1112            sage: psi = FanMorphism(matrix([[2], [-2]]), Sigma, Sigma_p)
     1113            sage: xi = FanMorphism(matrix([[1, 0]]), Sigma_p, Sigma)
     1114            sage: phi.index(), psi.index(), xi.index()
     1115            (1, 2, +Infinity)
     1116            sage: phi.is_birational(), psi.is_birational(), xi.is_birational()
     1117            (True, False, False)
     1118        """       
     1119        return self.index() == 1
     1120
    10521121    @cached_method
    10531122    def is_bundle(self):
    10541123        r"""
     
    13131382        if self.matrix().index_in_saturation() != 1:
    13141383            return False
    13151384        if self.image().dimension() < self.codomain().dimension():
    1316             return self.restrict_to_image().is_injective()
     1385            return prod(self.factor()[1:]).is_injective()
    13171386        # Now we know that underlying lattice morphism is bijective.
    13181387        Sigma = self.domain_fan()
    13191388        return all(all(self.image_cone(sigma).dim() == d for sigma in Sigma(d))
     
    16171686            self._primitive_preimage_cones[sigma_p] = tuple(primitive_cones)
    16181687        return self._primitive_preimage_cones[sigma_p]
    16191688
    1620     @cached_method
    1621     def restrict_to_image(self):
     1689    def factor(self):
    16221690        r"""
    1623         Return the fan morphism from the domain fan to the image fan.
    1624        
     1691        Factor ``self`` into injective * birational * surjective morphisms.
     1692
    16251693        OUTPUT:
    16261694       
    1627         - a :class:`fan morphism <FanMorphism>`.
     1695        - a triple of :class:`FanMorphism` `(\phi_i, \phi_b, \phi_s)`, such that
     1696          `\phi_s` is surjective, `\phi_b` is birational, `\phi_i` is injective,
     1697          and ``self`` is equal to `\phi_i \circ \phi_b \circ \phi_s`.
     1698
     1699        Intermediate fans live in the saturation of the image of ``self``
     1700        as a map between lattices and are the image of the :meth:`domain_fan`
     1701        and the restriction of the :meth:`codomain_fan`, i.e. if ``self`` maps
     1702        `\Sigma \to \Sigma'`, then we have factorization into
    16281703       
     1704        .. math::
     1705       
     1706            \Sigma
     1707            \twoheadrightarrow
     1708            \Sigma_s
     1709            \to
     1710            \Sigma_i
     1711            \hookrightarrow
     1712            \Sigma.
     1713           
    16291714        .. note::
    16301715       
    1631             By the *image fan* of a fan morphism we mean the fan generated by
    1632             the intersections of cones of the codomain fan with the image
    1633             vector space of ``self``. The lattice of this fan is the saturation
    1634             of the image of ``self``.
    1635        
     1716            * `\Sigma_s` is the finest fan with the smallest support that is
     1717              compatible with ``self``: any fan morphism from `\Sigma` given by
     1718              the same map of lattices as ``self`` factors through `\Sigma_s`.
     1719             
     1720            * `\Sigma_i` is the coarsest fan of the largest support that is
     1721              compatible with ``self``: any fan morphism into `\Sigma'` given by
     1722              the same map of lattices as ``self`` factors though `\Sigma_i`.
     1723
    16361724        EXAMPLES:
    16371725       
    1638         We embed a projective line "diagonally" into the product of two lines::
     1726        We map an affine plane into a projective 3-space in such a way, that it
     1727        becomes "a double cover of a chart of the blow up of one of the
     1728        coordinate planes"::
     1729
     1730            sage: A2 = toric_varieties.A2()
     1731            sage: P3 = toric_varieties.P(3)
     1732            sage: m = matrix([(2,0,0), (1,1,0)])
     1733            sage: phi = A2.hom(m, P3)
     1734            sage: phi.as_polynomial_map()
     1735            Scheme morphism:
     1736              From: 2-d affine toric variety
     1737              To:   3-d CPR-Fano toric variety covered by 4 affine patches
     1738              Defn: Defined on coordinates by sending [x : y] to
     1739                    [x^2*y : y : 1 : 1]
    16391740       
    1640             sage: Sigma = toric_varieties.P1().fan()
    1641             sage: Sigmap = toric_varieties.P1xP1().fan()
    1642             sage: m = matrix(1, 2, [1,1])
    1643             sage: phi = FanMorphism(m, Sigma, Sigmap)
     1741        Now we will work with the underlying fan morphism::
     1742       
     1743            sage: phi = phi.fan_morphism()
    16441744            sage: phi
    16451745            Fan morphism defined by the matrix
     1746            [2 0 0]
     1747            [1 1 0]
     1748            Domain fan: Rational polyhedral fan in 2-d lattice N
     1749            Codomain fan: Rational polyhedral fan in 3-d lattice N
     1750            sage: phi.is_surjective(), phi.is_birational(), phi.is_injective()
     1751            (False, False, False)
     1752            sage: phi_i, phi_b, phi_s = phi.factor()
     1753            sage: phi_s.is_surjective(), phi_b.is_birational(), phi_i.is_injective()
     1754            (True, True, True)
     1755            sage: prod(phi.factor()) == phi
     1756            True
     1757           
     1758        Double cover (surjective)::
     1759       
     1760            sage: A2.fan().rays()
     1761            N(1, 0),
     1762            N(0, 1)
     1763            in 2-d lattice N
     1764            sage: phi_s
     1765            Fan morphism defined by the matrix
     1766            [2 0]
    16461767            [1 1]
    1647             Domain fan: Rational polyhedral fan in 1-d lattice N
    1648             Codomain fan: Rational polyhedral fan in 2-d lattice N
     1768            Domain fan: Rational polyhedral fan in 2-d lattice N
     1769            Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
     1770            sage: phi_s.codomain_fan().rays()
     1771            N(1, 0, 0),
     1772            N(1, 1, 0)
     1773            in Sublattice <N(1, 0, 0), N(0, 1, 0)>
     1774                                     
     1775        Blowup chart (birational)::
     1776       
     1777            sage: phi_b
     1778            Fan morphism defined by the matrix
     1779            [1 0]
     1780            [0 1]
     1781            Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
     1782            Codomain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
     1783            sage: phi_b.codomain_fan().rays()
     1784            N( 1,  0, 0),
     1785            N( 0,  1, 0),
     1786            N(-1, -1, 0)
     1787            in Sublattice <N(1, 0, 0), N(0, 1, 0)>
    16491788           
    1650         Now we restrict this morphism to its image::
     1789        Coordinate plane inclusion (injective)::
    16511790       
    1652             sage: psi = phi.restrict_to_image()
    1653             sage: psi
     1791            sage: phi_i
    16541792            Fan morphism defined by the matrix
    1655             [1]
    1656             Domain fan: Rational polyhedral fan in 1-d lattice N
    1657             Codomain fan: Rational polyhedral fan in Sublattice <N(1, 1)>
     1793            [1 0 0]
     1794            [0 1 0]
     1795            Domain fan: Rational polyhedral fan in Sublattice <N(1, 0, 0), N(0, 1, 0)>
     1796            Codomain fan: Rational polyhedral fan in 3-d lattice N
     1797            sage: phi.codomain_fan().rays()
     1798            N( 1,  0,  0),
     1799            N( 0,  1,  0),
     1800            N( 0,  0,  1),
     1801            N(-1, -1, -1)
     1802            in 3-d lattice N           
    16581803           
    1659         Note that the matrix defining a morphism to a fan in a sublattice
    1660         operates with generators of this sublattice, so ``[1]`` in the above
    1661         example means that `\psi` sends the only generator of the domain
    1662         lattice to the generator of the image sublattice::
     1804        TESTS::
    16631805       
    1664             sage: psi.codomain().gens()
    1665             (N(1, 1),)
    1666             sage: psi.codomain_fan().rays()
    1667             N( 1,  1),
    1668             N(-1, -1)
    1669             in Sublattice <N(1, 1)>
    1670            
    1671         Restriction to image returns exactly the same map if the corresponding
    1672         map of vector spaces is surjective, e.g. in the case of double
    1673         restriction::
    1674        
    1675             sage: psi.restrict_to_image() is psi
     1806            sage: phi_s.matrix() * phi_b.matrix() * phi_i.matrix() == m
     1807            True
     1808
     1809            sage: phi.domain_fan() is phi_s.domain_fan()
     1810            True
     1811            sage: phi_s.codomain_fan() is phi_b.domain_fan()
     1812            True
     1813            sage: phi_b.codomain_fan() is phi_i.domain_fan()
     1814            True
     1815            sage: phi_i.codomain_fan() is phi.codomain_fan()
    16761816            True
    16771817        """
    16781818        L = self.image().saturation()
    16791819        d = L.dimension()
    1680         if self.codomain().dimension() == d:
    1681             return self
    16821820        m = self.matrix()
    16831821        m = matrix(ZZ, m.nrows(), d, (L.coordinates(c) for c in m.rows()))
     1822        phi_s = FanMorphism(m, self.domain_fan(), L, check=False)
     1823        Sigma_prime = self.codomain_fan()
    16841824        L_cone = Cone(sum(([g, -g] for g in L.gens()), []))
    1685         Sigma = Fan(cones=discard_faces(L_cone.intersection(cone)
    1686                                         for cone in self.codomain_fan()),
    1687                     lattice=L, check=False)
    1688         return FanMorphism(m, self.domain_fan(), Sigma)
     1825        Sigma_i = Fan(cones=(L_cone.intersection(cone) for cone in Sigma_prime),
     1826                      lattice=L, discard_faces=True, check=False)
     1827        phi_b = FanMorphism(identity_matrix(d), phi_s.codomain_fan(), Sigma_i,
     1828                            check=False)
     1829        phi_i = FanMorphism(L.basis_matrix(), Sigma_i, Sigma_prime, check=False)
     1830        return (phi_i, phi_b, phi_s)