Ticket #11384: trac_11384_fan_complex.patch

File trac_11384_fan_complex.patch, 11.8 KB (added by vbraun, 9 years ago)

Updated patch

  • sage/geometry/fan.py

    # HG changeset patch
    # User Volker Braun <vbraun@stp.dias.ie>
    # Date 1323988871 0
    # Node ID 17886a0b659ed3b2687d5b67c286cac771e56e88
    # Parent  c3c5a2fd5c55d430fee6aa2a0b85887b7cd727d8
    Trac #11384: Construct the complex of a fan
    
    This patch implements new methods fan.oriented_boundary(cone)
    and fan.complex() to return chosen boundary orientations of
    cones and the resulting homology complex.
    
    diff --git a/sage/geometry/fan.py b/sage/geometry/fan.py
    a b  
    25432543                               for i in range(0, self.nrays()) ]) )
    25442544        return ring.ideal(gens)
    25452545
     2546    def oriented_boundary(self, cone):
     2547        r"""
     2548        Return the facets bounding ``cone`` with their induced
     2549        orientation.
     2550
     2551        INPUT:
     2552
     2553        - ``cone`` -- a cone of the fan or the whole fan.
     2554
     2555        OUTPUT:
     2556
     2557        The boundary cones of ``cone`` as a formal linear combination
     2558        of cones with coefficients `\pm 1`. Each summand is a facet of
     2559        ``cone`` and the coefficient indicates whether their (chosen)
     2560        orientation argrees or disagrees with the "outward normal
     2561        first" boundary orientation. Note that the orientation of any
     2562        individial cone is arbitrary. This method once and for all
     2563        picks orientations for all cones and then computes the
     2564        boundaries relative to that chosen orientation.
     2565
     2566        If ``cone`` is the fan itself, the generating cones with their
     2567        orientation relative to the ambient space are returned.
     2568       
     2569        See :meth:`complex` for the associated chain complex. If you
     2570        do not require the orientation, use :meth:`cone.facets()
     2571        <sage.geometry.cone.ConvexRationalPolyhedralCone.facets>`
     2572        instead.
     2573       
     2574        EXAMPLES::
     2575
     2576            sage: fan = toric_varieties.P(3).fan()
     2577            sage: cone = fan(2)[0]
     2578            sage: bdry = fan.oriented_boundary(cone);  bdry
     2579            1-d cone of Rational polyhedral fan in 3-d lattice N
     2580            - 1-d cone of Rational polyhedral fan in 3-d lattice N
     2581            sage: bdry[0]
     2582            (1, 1-d cone of Rational polyhedral fan in 3-d lattice N)
     2583            sage: bdry[1]
     2584            (-1, 1-d cone of Rational polyhedral fan in 3-d lattice N)
     2585            sage: fan.oriented_boundary(bdry[0][1])
     2586            -0-d cone of Rational polyhedral fan in 3-d lattice N
     2587            sage: fan.oriented_boundary(bdry[1][1])
     2588            -0-d cone of Rational polyhedral fan in 3-d lattice N
     2589           
     2590        If you pass the fan itself, this method returns the
     2591        orientation of the generating cones which is determined by the
     2592        order of the rays in :meth:`cone.ray_basis()
     2593        <sage.geometry.cone.IntegralRayCollection.ray_basis>` ::
     2594
     2595            sage: fan.oriented_boundary(fan)
     2596            -3-d cone of Rational polyhedral fan in 3-d lattice N
     2597            + 3-d cone of Rational polyhedral fan in 3-d lattice N
     2598            - 3-d cone of Rational polyhedral fan in 3-d lattice N
     2599            + 3-d cone of Rational polyhedral fan in 3-d lattice N
     2600            sage: [ matrix(cone.ray_basis()).det() for cone in fan.generating_cones() ]
     2601            [-1, 1, -1, 1]
     2602
     2603        A non-full dimensional fan::
     2604       
     2605            sage: cone = Cone([(4,5)])
     2606            sage: fan = Fan([cone])
     2607            sage: fan.oriented_boundary(cone)
     2608            0-d cone of Rational polyhedral fan in 2-d lattice N
     2609            sage: fan.oriented_boundary(fan)
     2610            1-d cone of Rational polyhedral fan in 2-d lattice N
     2611
     2612        TESTS::
     2613
     2614            sage: fan = toric_varieties.P2().fan()
     2615            sage: trivial_cone = fan(0)[0]
     2616            sage: fan.oriented_boundary(trivial_cone)
     2617            0
     2618        """
     2619        if not cone is self:
     2620            cone = self.embed(cone)
     2621        if '_oriented_boundary' in self.__dict__:
     2622            return self._oriented_boundary[cone]
     2623
     2624        # Fix (arbitrary) orientations of the generating cones. Induced
     2625        # by ambient space orientation for full-dimensional cones
     2626        from sage.structure.formal_sum import FormalSum
     2627        def sign(x):
     2628            assert x != 0
     2629            if x>0: return +1
     2630            else: return -1
     2631        N_QQ = self.lattice().base_extend(QQ)
     2632        dim = self.lattice_dim()
     2633        outward_vectors = dict()
     2634        generating_cones = []
     2635        for c in self.generating_cones():
     2636            if c.dim()==dim:
     2637                outward_v = []
     2638            else:
     2639                Q = N_QQ.quotient(c.rays())
     2640                outward_v = [ Q.lift(q) for q in Q.gens() ]
     2641               
     2642            outward_vectors[c] = outward_v
     2643            orientation = sign(matrix(outward_v + list(c.ray_basis())).det())
     2644            generating_cones.append(tuple([orientation, c]))
     2645        boundaries = {self:FormalSum(generating_cones)}
     2646
     2647        # The orientation of each facet is arbitrary, but the
     2648        # partititon of the boundary in positively and negatively
     2649        # oriented facets is not.
     2650        for d in range(dim, -1, -1):
     2651            for c in self(d):
     2652                c_boundary = []
     2653                c_matrix = matrix(outward_vectors[c] + list(c.ray_basis()))
     2654                c_matrix_inv = c_matrix.inverse()
     2655                for facet in c.facets():
     2656                    outward_ray_indices = set(c.ambient_ray_indices()) \
     2657                              .difference(set(facet.ambient_ray_indices()))
     2658                    outward_vector = - sum(self.ray(i) for i in outward_ray_indices)
     2659                    outward_vectors[facet] = [outward_vector] + outward_vectors[c]
     2660                    facet_matrix = matrix(outward_vectors[facet] + list(facet.ray_basis()))
     2661                    orientation = sign((c_matrix_inv * facet_matrix).det())
     2662                    c_boundary.append(tuple([orientation, facet]))
     2663                boundaries[c] = FormalSum(c_boundary)
     2664
     2665        self._oriented_boundary = boundaries
     2666        return boundaries[cone]
     2667
     2668    def complex(self, base_ring=ZZ, extended=False):
     2669        r"""
     2670        Return the chain complex of the fan.
     2671
     2672        To a `d`-dimensional fan `\Sigma`, one can canonically
     2673        associate a chain complex `K^\bullet`
     2674       
     2675        .. math::
     2676
     2677            0 \longrightarrow
     2678            \ZZ^{\Sigma(d)} \longrightarrow
     2679            \ZZ^{\Sigma(d-1)} \longrightarrow
     2680            \cdots \longrightarrow
     2681            \ZZ^{\Sigma(0)} \longrightarrow
     2682            0
     2683           
     2684        where the leftmost non-zero entry is in degree `0` and the
     2685        rightmost entry in degree `d`. See [Klyachko], eq. (3.2). This
     2686        complex computes the homology of `|\Sigma|\subset N_\RR` with
     2687        arbitrary support,
     2688       
     2689        .. math::
     2690
     2691            H_i(K) = H_{d-i}(|\Sigma|, \ZZ)_{\text{non-cpct}}
     2692
     2693        For a complete fan, this is just the non-compactly supported
     2694        homology of `\RR^d`. In this case, `H_0(K)=\ZZ` and `0` in all
     2695        non-zero degrees.
     2696           
     2697        For a complete fan, there is an extended chain complex
     2698
     2699        .. math::
     2700
     2701            0 \longrightarrow
     2702            \ZZ \longrightarrow
     2703            \ZZ^{\Sigma(d)} \longrightarrow
     2704            \ZZ^{\Sigma(d-1)} \longrightarrow
     2705            \cdots \longrightarrow
     2706            \ZZ^{\Sigma(0)} \longrightarrow
     2707            0
     2708
     2709        where we take the first `\ZZ` term to be in degree -1. This
     2710        complex is an exact sequence, that is, all homology groups
     2711        vanish.
     2712
     2713        The orientation of each cone is chosen as in
     2714        :meth:`oriented_boundary`.
     2715
     2716        INPUT:
     2717
     2718        - ``extended`` -- Boolean (default:False). Whether to
     2719          construct the extended complex, that is, including the
     2720          `\ZZ`-term at degree -1 or not.
     2721
     2722        - ``base_ring`` -- A ring (default: ``ZZ``). The ring to use
     2723          instead of `\ZZ`.
     2724
     2725        OUTPUT:
     2726
     2727        The complex associated to the fan as a :class:`ChainComplex
     2728        <sage.homology.chain_complex.ChainComplex>`. Raises a
     2729        ``ValueError`` if the extended complex is requested for a
     2730        non-complete fan.
     2731
     2732        EXAMPLES::
     2733
     2734            sage: fan = toric_varieties.P(3).fan()
     2735            sage: K_normal = fan.complex(); K_normal
     2736            Chain complex with at most 4 nonzero terms over Integer Ring
     2737            sage: K_normal.homology()
     2738            {0: Z, 1: 0, 2: 0, 3: 0}
     2739            sage: K_extended = fan.complex(extended=True); K_extended
     2740            Chain complex with at most 5 nonzero terms over Integer Ring
     2741            sage: K_extended.homology()
     2742            {0: 0, 1: 0, 2: 0, 3: 0, -1: 0}
     2743
     2744        Homology computations are much faster over `\QQ` if you don't
     2745        care about the torsion coefficients::
     2746
     2747            sage: toric_varieties.P2_123().fan().complex(extended=True, base_ring=QQ)
     2748            Chain complex with at most 4 nonzero terms over Rational Field
     2749            sage: _.homology()
     2750            {0: Vector space of dimension 0 over Rational Field,
     2751             1: Vector space of dimension 0 over Rational Field,
     2752             2: Vector space of dimension 0 over Rational Field,
     2753             -1: Vector space of dimension 0 over Rational Field}
     2754
     2755        The extended complex is only defined for complete fans::
     2756
     2757            sage: fan = Fan([ Cone([(1,0)]) ])
     2758            sage: fan.is_complete()
     2759            False
     2760            sage: fan.complex(extended=True)
     2761            Traceback (most recent call last):
     2762            ...
     2763            ValueError: The extended complex is only defined for complete fans!
     2764
     2765        The definition of the complex does not refer to the ambient
     2766        space of the fan, so it does not distinguish a fan from the
     2767        same fan embedded in a subspace::
     2768
     2769            sage: K1 = Fan([Cone([(-1,)]), Cone([(1,)])]).complex()
     2770            sage: K2 = Fan([Cone([(-1,0,0)]), Cone([(1,0,0)])]).complex()
     2771            sage: K1 == K2
     2772            True
     2773
     2774        Things get more complicated for non-complete fans::
     2775
     2776            sage: fan = Fan([Cone([(1,1,1)]),
     2777            ...              Cone([(1,0,0),(0,1,0)]),
     2778            ...              Cone([(-1,0,0),(0,-1,0),(0,0,-1)])])
     2779            sage: fan.complex().homology()
     2780            {0: 0, 1: 0, 2: Z x Z, 3: 0}
     2781            sage: fan = Fan([Cone([(1,0,0),(0,1,0)]),
     2782            ...              Cone([(-1,0,0),(0,-1,0),(0,0,-1)])])
     2783            sage: fan.complex().homology()
     2784            {0: 0, 1: 0, 2: Z, 3: 0}
     2785            sage: fan = Fan([Cone([(-1,0,0),(0,-1,0),(0,0,-1)])])
     2786            sage: fan.complex().homology()
     2787            {0: 0, 1: 0, 2: 0, 3: 0}
     2788
     2789        REFERENCES:
     2790
     2791        ..  [Klyachko]
     2792            A. A. Klyachko,
     2793            Equivariant Bundles on Toral Varieties.
     2794            Mathematics of the USSR - Izvestiya 35 (1990), 337-375.
     2795        """
     2796        dim = self.dim()
     2797        delta = dict()
     2798        for degree in range(1, dim+1):
     2799            m = matrix(base_ring, len(self(degree-1)), len(self(degree)), base_ring.zero())
     2800            for i, cone in enumerate(self(degree)):
     2801                boundary = self.oriented_boundary(cone)
     2802                for orientation, d_cone in boundary:
     2803                    m[self(degree-1).index(d_cone), i] = orientation
     2804            delta[dim-degree] = m
     2805
     2806        from sage.homology.chain_complex import ChainComplex
     2807        if not extended:
     2808            return ChainComplex(delta, base_ring=base_ring)
     2809
     2810        # add the extra entry for the extended complex
     2811        if not self.is_complete():
     2812            raise ValueError('The extended complex is only defined for complete fans!')
     2813        extension = matrix(base_ring, len(self(dim)), 1, base_ring.zero())
     2814        generating_cones = self.oriented_boundary(self)
     2815        for orientation, d_cone in generating_cones:
     2816            extension[self(dim).index(d_cone), 0] = orientation
     2817        delta[-1] = extension
     2818        return ChainComplex(delta, base_ring=base_ring)
     2819
    25462820
    25472821def discard_faces(cones):
    25482822    r"""