Exemplo n.º 1
0
    def get_homology_kernel(self, hecke_data = None):
        verbose('Entering get_homology_kernel...')
        verb = get_verbose()
        set_verbose(0)
        if hecke_data is None:
            hecke_data = []
        wp = self.wp()
        Gn = self.large_group()
        B = ArithHomology(self, ZZ**1, trivial_action = True)
        C = HomologyGroup(Gn, ZZ**1, trivial_action = True)
        group = B.group()
        Bsp = B.space()
        def phif(x):
            ans = C(0)
            for g, v in zip(group.gens(), x.values()):
                if not self.use_shapiro():
                    ans += C((Gn(g), v))
                else:
                    for a, ti in zip(v.values(), self.coset_reps()):
                        # We are considering a * (g tns t_i)
                        g0, _ = self.get_coset_ti( set_immutable(ti * g.quaternion_rep ))
                        ans += C((Gn(g0), a))
            return ans
        f = Bsp.hom([vector(C(phif(o))) for o in B.gens()])
        def phig(x):
            ans = C(0)
            for g, v in zip(group.gens(), x.values()):
                if not self.use_shapiro():
                    ans += C((Gn(wp**-1 * g.quaternion_rep * wp), v))
                else:
                    for a, ti in zip(v.values(), self.coset_reps()):
                        # We are considering a * (g tns t_i)
                        g0, _ = self.get_coset_ti( set_immutable(ti * g.quaternion_rep ))
                        ans += C((Gn(wp**-1 * g0 * wp), a))
            return ans
        g = Bsp.hom([vector(C(phig(o))) for o in B.gens()])
        maplist = [f, g]

        for ell, T in hecke_data:
            Aq = B.hecke_matrix(ell, with_torsion = True)
            tmap = Bsp.hom([sum([ZZ(a) * o for a, o in zip(col, Bsp.gens())]) for col in T.charpoly()(Aq).columns()])
            maplist.append(tmap)
        fg = direct_sum_of_maps(maplist)
        ker = fg.kernel()
        try:
            kerV = ker.V()
            good_ker = [o.lift() for o,inv in zip(ker.gens(), ker.invariants()) if inv == 0]
        except AttributeError:
            kerV = ker
            try:
                good_ker = [kerV.lift(o) for o in ker.gens()]
            except AttributeError:
                good_ker = ker.gens()
        kerVZ_amb = ZZ**(kerV.ambient_module().dimension())
        kerVZ = kerVZ_amb.submodule([kerVZ_amb(o.denominator() * o) for o in kerV.basis()])
        good_ker = kerVZ.span_of_basis([kerVZ((o.denominator() * o).change_ring(ZZ)) for o in good_ker])
        good_ker = [B(o.denominator() * o) for o in good_ker.LLL().rows()]
        set_verbose(verb)
        verbose('Done with get_homology_kernel')
        return good_ker
Exemplo n.º 2
0
    def plot(self):
        """
        Plot the lattice polygon.

        OUTPUT:

        A graphics object.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: P = LatticePolytope_PPL((1,0), (0,1), (0,0), (2,2))
            sage: P.plot()
            Graphics object consisting of 6 graphics primitives
            sage: LatticePolytope_PPL([0], [1]).plot()
            Graphics object consisting of 3 graphics primitives
            sage: LatticePolytope_PPL([0]).plot()
            Graphics object consisting of 2 graphics primitives
        """
        from sage.plot.point import point2d
        from sage.plot.polygon import polygon2d
        vertices = self.ordered_vertices()
        points = self.integral_points()
        if self.space_dimension() == 1:
            vertices = [vector(ZZ, (v[0], 0)) for v in vertices]
            points = [vector(ZZ, (p[0], 0)) for p in points]
        point_plot = sum(point2d(p, pointsize=100, color='red')
                         for p in points)
        polygon_plot = polygon2d(vertices, alpha=0.2, color='green',
                                 zorder=-1, thickness=2)
        return polygon_plot + point_plot
Exemplo n.º 3
0
        def _split_hyperbolic(L) :
            cur_cor = 2
            Lcor = L + cur_cor * identity_matrix(L.nrows())
            while not is_positive_definite(Lcor) :
                cur_cor += 2
                Lcor = L + cur_cor * identity_matrix(L.nrows())

            a = FreeModule(ZZ, L.nrows()).gen(0)
            if a * L * a >= 0 :
                Lcor_length_inc = max(3, a * L * a)
                cur_Lcor_length = Lcor_length_inc
                while True :
                    short_vectors = flatten(QuadraticForm(Lcor).short_vector_list_up_to_length( a * Lcor * a )[cur_Lcor_length - Lcor_length_inc: cur_Lcor_length], max_level = 1)
                    for a in short_vectors :
                        if a * L * a < 0 :
                            break
                    else :
                        continue
                    break
            n = -a * L * a // 2

            short_vectors = E8.short_vector_list_up_to_length(n + 1)[-1]

            for v in short_vectors :
                for w in short_vectors :
                    if v * E8_gram * w == 2 * n - 1 :
                        LE8_mat = L.block_sum(E8_gram)
                        v_form = vector( list(a) + list(v) ) * LE8_mat
                        w_form = vector( list(a) + list(w) ) * LE8_mat
                        Lred_basis = matrix(ZZ, [v_form, w_form]).right_kernel().basis_matrix().transpose()
                        Lred_basis = matrix(ZZ, Lred_basis)

                        return Lred_basis.transpose() * LE8_mat * Lred_basis
Exemplo n.º 4
0
def _eval_restriction_vector(R, s, r, reduction_function) :
    r"""
    For each list rs in R compute the multiplicity of s r' = r, r' in rs.
    
    INPUT:
    
    - `R` -- A list of lists of vectors.
    
    - `s` -- A vector of the same length.
    
    - `r` -- An integer.
    
    - ``reduction_function`` -- A function that takes a tuple representing an element in `L^\#`
                                and returs a pair of a reduced element in `L^\#` and a sign.
    
    OUTPUT:
    
    - A vector with integer entries that correspond to the elements
      of R in the given order.
    """
    if reduction_function is None :
        return vector([ len([ rp for rp in rs if s.dot_product(rp) == r ])
                        for rs in R ])
    else :
        return vector([ sum( reduction_function(rp)[1] for rp in rs if s.dot_product(rp) == r )
                        for rs in R ])
Exemplo n.º 5
0
def sector(ray1, ray2, **extra_options):
    r"""
    Plot a sector between ``ray1`` and ``ray2`` centered at the origin.

    .. NOTE::

        This function was intended for plotting strictly convex cones, so it
        plots the smaller sector between ``ray1`` and ``ray2`` and, therefore,
        they cannot be opposite. If you do want to use this function for bigger
        regions, split them into several parts.

    .. NOTE::

        As of version 4.6 Sage does not have a graphic primitive for sectors in
        3-dimensional space, so this function will actually approximate them
        using polygons (the number of vertices used depends on the angle
        between rays).

    INPUT:

    - ``ray1``, ``ray2`` -- rays in 2- or 3-dimensional space of the same
      length;

    - ``extra_options`` -- a dictionary of options that should be passed to
      lower level plotting functions.

    OUTPUT:

    - a plot.

    EXAMPLES::

        sage: from sage.geometry.toric_plotter import sector
        sage: sector((1,0), (0,1))
        Graphics object consisting of 1 graphics primitive
        sage: sector((3,2,1), (1,2,3))
        Graphics3d Object
    """
    ray1 = vector(RDF, ray1)
    ray2 = vector(RDF, ray2)
    r = ray1.norm()
    if len(ray1) == 2:
        # Plot an honest sector
        phi1 = arctan2(ray1[1], ray1[0])
        phi2 = arctan2(ray2[1], ray2[0])
        if phi1 > phi2:
            phi1, phi2 = phi2, phi1
        if phi2 - phi1 > pi:
            phi1, phi2 = phi2, phi1 + 2 * pi
        return disk((0,0), r, (phi1, phi2), **extra_options)
    else:
        # Plot a polygon, 30 vertices per radian.
        vertices_per_radian = 30
        n = ceil(arccos(ray1 * ray2 / r**2) * vertices_per_radian)
        dr = (ray2 - ray1) / n
        points = (ray1 + i * dr for i in range(n + 1))
        points = [r / pt.norm() * pt for pt in points]
        points.append(vector(RDF, 3))
        return polygon(points, **extra_options)
Exemplo n.º 6
0
 def homogenize(inhomog, degree):
     e = tuple(hom._A * vector(ZZ,[inhomog[x], inhomog[y]]) + degree * hom._b)
     result = list(inhomog)
     for i, var in enumerate(variables):
         result[R.gens().index(var)] = e[i]
     result = vector(ZZ, result)
     result.set_immutable()
     return result
Exemplo n.º 7
0
Arquivo: element.py Projeto: rwst/sage
    def fan(self, origin=None):
        r"""
        Construct the fan of cones over the simplices of the triangulation.

        INPUT:

        - ``origin`` -- ``None`` (default) or coordinates of a
          point. The common apex of all cones of the fan. If ``None``,
          the triangulation must be a star triangulation and the
          distinguished central point is used as the origin.

        OUTPUT:

        A :class:`~sage.geometry.fan.RationalPolyhedralFan`. The
        coordinates of the points are shifted so that the apex of the
        fan is the origin of the coordinate system.

        .. note:: If the set of cones over the simplices is not a fan, a
            suitable exception is raised.

        EXAMPLES::

            sage: pc = PointConfiguration([(0,0), (1,0), (0,1), (-1,-1)], star=0, fine=True)
            sage: triangulation = pc.triangulate()
            sage: fan = triangulation.fan(); fan
            Rational polyhedral fan in 2-d lattice N
            sage: fan.is_equivalent( toric_varieties.P2().fan() )
            True

        Toric diagrams (the `\ZZ_5` hyperconifold)::

            sage: vertices=[(0, 1, 0), (0, 3, 1), (0, 2, 3), (0, 0, 2)]
            sage: interior=[(0, 1, 1), (0, 1, 2), (0, 2, 1), (0, 2, 2)]
            sage: points = vertices+interior
            sage: pc = PointConfiguration(points, fine=True)
            sage: triangulation = pc.triangulate()
            sage: fan = triangulation.fan( (-1,0,0) )
            sage: fan
            Rational polyhedral fan in 3-d lattice N
            sage: fan.rays()
            N(1, 1, 0),
            N(1, 3, 1),
            N(1, 2, 3),
            N(1, 0, 2),
            N(1, 1, 1),
            N(1, 1, 2),
            N(1, 2, 1),
            N(1, 2, 2)
            in 3-d lattice N
        """
        from sage.geometry.fan import Fan

        if origin is None:
            origin = self.point_configuration().star_center()
        R = self.base_ring()
        origin = vector(R, origin)
        points = self.point_configuration().points()
        return Fan(self, (vector(R, p) - origin for p in points))
Exemplo n.º 8
0
    def set_rays(self, generators):
        r"""
        Set up rays and their ``generators`` to be used by plotting functions.

        As an alternative to using this method, you can pass ``generators`` to
        :class:`ToricPlotter` constructor.

        INPUT:

        - ``generators`` - a list of primitive non-zero ray generators.

        OUTPUT:

        - none.

        EXAMPLES::

            sage: from sage.geometry.toric_plotter import ToricPlotter
            sage: tp = ToricPlotter(dict(), 2)
            sage: tp.adjust_options()
            sage: tp.plot_rays()
            Traceback (most recent call last):
            ...
            AttributeError: 'ToricPlotter' object has no attribute 'rays'
            sage: tp.set_rays([(0,1)])
            sage: tp.plot_rays()
            Graphics object consisting of 2 graphics primitives
        """
        d = self.dimension
        if d == 1:
            generators = [vector(RDF, 2, (gen[0], 0)) for gen in generators]
        else:
            generators = [vector(RDF, d, gen) for gen in generators]
        self.generators = generators
        if self.mode == "box":
            rays = []
            bounds = [self.__dict__[bound]
                for bound in ["xmin", "xmax", "ymin", "ymax", "zmin", "zmax"]]
            bounds = bounds[:2 * d]
            for gen in generators:
                factors = []
                for i, gen_i in enumerate(gen):
                    factors.append(gen_i / bounds[2 * i])
                    factors.append(gen_i / bounds[2 * i + 1])
                rays.append(gen / max(factors))
        elif self.mode == "generators":
            rays = generators
        elif self.mode == "round":
            r = self.radius
            rays = [r  * gen / gen.norm() for gen in generators]
        self.rays = rays
    def reduce_r(self, r) :
        for rred in map(lambda rs: rs[0], self._r_representatives) :
            r_rred = vector(r) - vector(rred)
            if r_rred in self.__L_span :
                break
        else :
            raise RuntimeError( "Could not find reduced r" )
        
        if rred in self._r_reduced_representatives :
            s = 1
        else :
            rred = tuple(map(operator.neg, rred))
            s = -1

        return (rred, s)
Exemplo n.º 10
0
    def __call__(self, v):
        """
        Apply the affine transformation to ``v``.

        INPUT:

        - ``v`` -- a multivariate polynomial, a vector, or anything
          that can be converted into a vector.

        OUTPUT:

        The image of ``v`` under the affine group element.

        EXAMPLES::

            sage: G = AffineGroup(2, QQ)
            sage: g = G([0,1,-1,0],[2,3]);  g
                  [ 0  1]     [2]
            x |-> [-1  0] x + [3]
            sage: v = vector([4,5])
            sage: g(v)
            (7, -1)

            sage: R.<x,y> = QQ[]
            sage: g(x), g(y)
            (y + 2, -x + 3)
            sage: p = x^2 + 2*x*y + y + 1
            sage: g(p)
            -2*x*y + y^2 - 5*x + 10*y + 20

        The action on polynomials is such that it intertwines with
        evaluation. That is::

            sage: p(*g(v)) == g(p)(*v)
            True

        Test that the univariate polynomial ring is covered::

            sage: H = AffineGroup(1, QQ)
            sage: h = H([2],[3]);  h
            x |-> [2] x + [3]
            sage: R.<z> = QQ[]
            sage: h(z+1)
            3*z + 2
        """
        from sage.rings.polynomial.polynomial_element import is_Polynomial
        from sage.rings.polynomial.multi_polynomial import is_MPolynomial

        parent = self.parent()
        if is_Polynomial(v) and parent.degree() == 1:
            ring = v.parent()
            return ring([self._A[0, 0], self._b[0]])
        if is_MPolynomial(v) and parent.degree() == v.parent().ngens():
            ring = v.parent()
            from sage.modules.all import vector

            image_coords = self._A * vector(ring, ring.gens()) + self._b
            return v(*image_coords)
        v = parent.vector_space()(v)
        return self._A * v + self._b
Exemplo n.º 11
0
    def vertices(self):
        r"""
        Return the vertices as a tuple of `\ZZ`-vectors.

        OUTPUT:

        A tuple of `\ZZ`-vectors. Each entry is the coordinate vector
        of an integral points of the lattice polytope.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: p = LatticePolytope_PPL((-9,-6,-1,-1),(0,0,0,1),(0,0,1,0),(0,1,0,0),(1,0,0,0))
            sage: p.vertices()
            ((-9, -6, -1, -1), (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0), (1, 0, 0, 0))
            sage: p.minimized_generators()
            Generator_System {point(-9/1, -6/1, -1/1, -1/1), point(0/1, 0/1, 0/1, 1/1),
            point(0/1, 0/1, 1/1, 0/1), point(0/1, 1/1, 0/1, 0/1), point(1/1, 0/1, 0/1, 0/1)}
        """
        d = self.space_dimension()
        v = vector(ZZ, d)
        points = []
        for g in self.minimized_generators():
            for i in range(0,d):
                v[i] = g.coefficient(Variable(i))
            v_copy = copy.copy(v)
            v_copy.set_immutable()
            points.append(v_copy)
        return tuple(points)
Exemplo n.º 12
0
    def weil_representation(self) :
        r"""
        OUTPUT:
        
        - A pair of matrices corresponding to T and S.
        """
        disc_bilinear = lambda a, b: (self._dual_basis * vector(QQ, a.lift())) * self._L * (self._dual_basis * vector(QQ, b.lift()))
        disc_quadratic = lambda a: disc_bilinear(a, a) / ZZ(2)
        
        zeta_order = ZZ(lcm([8, 12, prod(self.invariants())] + map(lambda ed: 2 * ed, self.invariants())))
        K = CyclotomicField(zeta_order); zeta = K.gen()

        R = PolynomialRing(K, 'x'); x = R.gen()
#        sqrt2s = (x**2 - 2).factor()
#        if sqrt2s[0][0][0].complex_embedding().real() > 0 :        
#            sqrt2  = sqrt2s[0][0][0]
#        else : 
#            sqrt2  = sqrt2s[0][1]
        Ldet_rts = (x**2 - prod(self.invariants())).factor()
        if Ldet_rts[0][0][0].complex_embedding().real() > 0 :
            Ldet_rt  = Ldet_rts[0][0][0] 
        else :
            Ldet_rt  = Ldet_rts[0][0][0]
                
        Tmat  = diagonal_matrix( K, [zeta**(zeta_order*disc_quadratic(a)) for a in self] )
        Smat = zeta**(zeta_order / 8 * self._L.nrows()) / Ldet_rt  \
               * matrix( K,  [ [ zeta**ZZ(-zeta_order * disc_bilinear(gamma,delta))
                                 for delta in self ]
                               for gamma in self ])
        
        return (Tmat, Smat)
Exemplo n.º 13
0
Arquivo: element.py Projeto: rwst/sage
    def gkz_phi(self):
        r"""
        Calculate the GKZ phi vector of the triangulation.

        The phi vector is a vector of length equals to the number of
        points in the point configuration. For a fixed triangulation
        `T`, the entry corresponding to the `i`-th point `p_i` is

        .. math::

            \phi_T(p_i) = \sum_{t\in T, t\owns p_i} Vol(t)

        that is, the total volume of all simplices containing `p_i`.
        See also [GKZ]_ page 220 equation 1.4.

        OUTPUT:

        The phi vector of self.

        EXAMPLES::

            sage: p = PointConfiguration([[0,0],[1,0],[2,1],[1,2],[0,1]])
            sage: p.triangulate().gkz_phi()
            (3, 1, 5, 2, 4)
            sage: p.lexicographic_triangulation().gkz_phi()
            (1, 3, 4, 2, 5)
        """
        vec = vector(ZZ, self.point_configuration().n_points())
        for simplex in self:
            vol = self.point_configuration().volume(simplex)
            for i in simplex:
                vec[i] = vec[i] + vol
        return vec
    def _unpivot_ray(self, ray):
        """
        Undo the pivoting to go back to the original inequalities
        containing a linear subspace.

        INPUT:

        - ``ray`` -- ray in the pivoted coordinates.

        OUTPUT:

        Ray in the original coordinates.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.double_description_inhomogeneous \
            ....:     import PivotedInequalities
            sage: piv = PivotedInequalities(QQ, 2)
            sage: piv._pivot_inequalities(matrix([(1,1,3), (5,5,7)]))
            [1 3]
            [5 7]
            sage: piv._unpivot_ray([1, 3])
            (1, 0, 3)
        """
        result = [self.base_ring.zero()] * (self.dim + 1)
        for r, i in zip(ray, self._pivots):
            result[i] = r
        return vector(self.base_ring, result)
Exemplo n.º 15
0
    def coleman_integral_S_to_Q(self, w, S, Q):
        r"""
        Computes the Coleman integral $\int_S^Q w$

        **one should be able to feed $S,Q$ into coleman_integral,
        but currently that segfaults**

        INPUT:

        - w: a differential
        - S: a point with coordinates in an extension of $\Q_p$
        - Q: a non-Weierstrass point defined over $\Q_p$

        OUTPUT:

        the Coleman integral $\int_S^Q w$

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^3-10*x+9)
            sage: K = Qp(5,6)
            sage: HK = H.change_ring(K)
            sage: J.<a> = K.extension(x^20-5)
            sage: HJ  = H.change_ring(J)
            sage: x,y = HK.monsky_washnitzer_gens()
            sage: P = HK(1,0)
            sage: Q = HK(0,3)
            sage: S = HK.get_boundary_point(HJ,P)
            sage: P_to_S = HK.coleman_integral_P_to_S(y.diff(),P,S)
            sage: S_to_Q = HJ.coleman_integral_S_to_Q(y.diff(),S,Q)
            sage: P_to_S  + S_to_Q
            3 + O(a^119)
            sage: HK.coleman_integral(y.diff(),P,Q)
            3 + O(5^6)

        AUTHOR:

        - Jennifer Balakrishnan

        """
        import sage.schemes.hyperelliptic_curves.monsky_washnitzer as monsky_washnitzer

        K = self.base_ring()
        R = monsky_washnitzer.SpecialHyperellipticQuotientRing(self, K)
        MW = monsky_washnitzer.MonskyWashnitzerDifferentialRing(R)
        w = MW(w)
        f, vec = w.reduce_fast()
        g = self.genus()
        const = f(Q[0], Q[1]) - f(S[0], S[1])
        if vec == vector(2 * g * [0]):
            return const
        else:
            basis_values = self.S_to_Q(S, Q)
            dim = len(basis_values)
            dot = sum([vec[i] * basis_values[i] for i in range(dim)])
            return const + dot
Exemplo n.º 16
0
    def include_points(self, points, force=False):
        r"""
        Try to include ``points`` into the bounding box of ``self``.

        INPUT:

        - ``points`` -- a list of points;

        - ``force`` -- boolean (default: ``False``). by default, only bounds
          that were not set before will be chosen to include ``points``. Use
          ``force=True`` if you don't mind increasing existing bounding box.

        OUTPUT:

        - none.

        EXAMPLES::

            sage: from sage.geometry.toric_plotter import ToricPlotter
            sage: tp = ToricPlotter(dict(), 2)
            sage: print(tp.radius)
            None
            sage: tp.include_points([(3, 4)])
            sage: print(tp.radius)
            5.5...
            sage: tp.include_points([(5, 12)])
            sage: print(tp.radius)
            5.5...
            sage: tp.include_points([(5, 12)], force=True)
            sage: print(tp.radius)
            13.5...
        """
        if not points:
            return
        points = [vector(RDF, pt) for pt in points]
        sd = self.__dict__

        def update(bound, new_value, points):
            if force or sd[bound] is None:
                new_value = eval(new_value)
                if sd[bound] is None:
                    sd[bound] = new_value
                elif abs(sd[bound]) < abs(new_value):
                    sd[bound] = new_value

        update("radius", "max(pt.norm() for pt in points) + 0.5", points)
        try:
            update("xmin", "min(pt[0] for pt in points) - 0.5", points)
            update("xmax", "max(pt[0] for pt in points) + 0.5", points)
            update("ymin", "min(pt[1] for pt in points) - 0.5", points)
            update("ymax", "max(pt[1] for pt in points) + 0.5", points)
            update("zmin", "min(pt[2] for pt in points) - 0.5", points)
            update("zmax", "max(pt[2] for pt in points) + 0.5", points)
        except IndexError:  # 1 or 2 dimensional case
            pass
Exemplo n.º 17
0
    def __init__(self, all_options, dimension, generators=None):
        r"""
        See :class:`ToricPlotter` for documentation.

        TESTS::

            sage: from sage.geometry.toric_plotter import ToricPlotter
            sage: tp = ToricPlotter(dict(), 2)
            sage: TestSuite(tp).run()
        """
        super(ToricPlotter, self).__init__()
        sd = self.__dict__
        extra_options = dict()
        self.extra_options = extra_options
        toric_options = options()
        for option, value in all_options.iteritems():
            if option in toric_options:
                sd[option] = value
            else:
                extra_options[option] = value
        for option, value in toric_options.iteritems():
            if option not in sd:
                sd[option] = value
        if dimension not in [1, 2, 3]:
            raise ValueError("toric objects can be plotted only for "
                             "dimensions 1, 2, and 3, not %s!" % dimension)
        self.dimension = dimension
        self.origin = vector(RDF, max(dimension, 2)) # 1-d is plotted in 2-d
        if self.mode not in ["box", "generators", "round"]:
            raise ValueError("unrecognized plotting mode: %s!" % mode)
        # If radius was explicitly set by the user, it sets other bounds too.
        # If we don't take it into account here, they will be replaced by
        # automatically computed values.
        if sd["radius"] is not None:
            for key in ["xmin", "ymin", "zmin"]:
                if sd[key] is None:
                    sd[key] = - sd["radius"]
            for key in ["xmax", "ymax", "zmax"]:
                if sd[key] is None:
                    sd[key] = sd["radius"]
        # We also set some of the "extra_options" if they were not given.
        if "axes" not in extra_options:
            extra_options["axes"] = False
        if "frame" not in extra_options:
            extra_options["frame"] = False
        if "aspect_ratio" not in extra_options:
            extra_options["aspect_ratio"] = 1
        if generators is not None:
            # Completely prepare the plotter
            self.include_points(generators)
            self.adjust_options()
            self.set_rays(generators)
Exemplo n.º 18
0
 def pullback_polynomial(p):
     result = R.zero()
     for coefficient, monomial in p:
         exponent = monomial.exponents()[0]
         exponent = [ exponent[i] for i in cone.ambient_ray_indices() ]
         exponent = vector(ZZ,exponent)
         m = n_rho_matrix.solve_right(exponent)
         assert all(x in ZZ for x in m), \
             'The polynomial '+str(p)+' does not define a ZZ-divisor!'
         m_coeffs = dualcone.Hilbert_coefficients(m)
         result += coefficient * prod(R.gen(i)**m_coeffs[i]
                                      for i in range(0,R.ngens()))
     return result
Exemplo n.º 19
0
def _check_homogeneity(polynomial, variables, weights, total_weight=None):
    """
    Raise ``ValueError`` if the polynomial is not weighted
    homogeneous.

    INPUT:

    - ``polynomial`` -- the input polynomial. See
      :func:`WeierstrassForm` for details.

    - ``variables`` -- the variables.  See :func:`WeierstrassForm` for
      details.

    - ``weights`` -- list of integers, one per variable. the weights
      of the variables.

    - ``total_weight`` -- an integer or ``None`` (default). If an
      integer is passed, it is also checked that the weighted total
      degree of polynomial is this value.

    OUTPUT:

    This function returns nothing. If the polynomial is not weighted
    homogeneous, a ``ValueError`` is raised.

    EXAMPLES::

        sage: from sage.schemes.toric.weierstrass import _check_homogeneity
        sage: R.<x,y,z,a30,a21,a12,a03,a20,a11,a02,a10,a01,a00> = QQ[]
        sage: p = (a30*x^3 + a21*x^2*y + a12*x*y^2 + a03*y^3 + a20*x^2*z +
        ....:      a11*x*y*z + a02*y^2*z + a10*x*z^2 + a01*y*z^2 + a00*z^3)
        sage: _check_homogeneity(p, [x,y,z], (1,1,1), 3)

        sage: _check_homogeneity(p+x^4, [x,y,z], (1,1,1), 3)
        Traceback (most recent call last):
        ...
        ValueError: The polynomial is not homogeneous with weights (1, 1, 1)
    """
    w = vector(weights)
    n = w.degree()
    all_variables = polynomial.parent().gens()
    variable_indices = [all_variables.index(x) for x in variables]
    total_weight = None
    for e in polynomial.exponents():
        weight_e = sum(e[variable_indices[i]] * weights[i] for i in range(n))
        if total_weight is None:
            total_weight = weight_e
        else:
            if weight_e != total_weight:
                raise ValueError('The polynomial is not homogeneous with '
                                 'weights '+str(weights))
Exemplo n.º 20
0
 def _from_jacobi_fourier_index(self, r) :
     r"""
     Return an element of ``self`` that corresponds to a given index of a Jacobi Fourier expansion.
     
     TESTS:
     
         sage: from psage.modform.vector_valued.discriminant_group import *
         sage: A = DiscriminantForm(matrix(2, [2, 1, 1, 2]))
         sage: A._from_jacobi_fourier_index((0,0))
         (0, 0)
         sage: A._from_jacobi_fourier_index((0,1))
         (0, 1)
     """
     return self(self._jacobi_fourier_index_matrices()[1] * vector(ZZ, r))
Exemplo n.º 21
0
    def reduce(self, s) :
        (n, r) = s
        # find a representative that corresponds to s
        for rred in self._r_reduced_representatives :
            r_rred = vector(r) - vector(rred)
            if r_rred in self.__L_span :
                break

            rred = tuple(map(operator.neg, rred))
            r_rred = vector(r) - vector(rred)
            if r_rred in self.__L_span :
                break
        else :
            raise RuntimeError( "Could not find reduced r" )
        
        nred = n - (self.__Ladj(r) - self.__Ladj(rred)) // (2 * self.__L.det())
        
        if rred in self._r_reduced_representatives :
            s = 1
        else :
            rred = tuple(map(operator.neg, rred))
            s = -1
        
        return ((nred, rred), s)
Exemplo n.º 22
0
 def _dft_seminormal(self):
     """
     Returns the seminormal form of the discrete Fourier for self.
     
     EXAMPLES::
     
         sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
         sage: QS3._dft_seminormal()
         [   1    1    1    1    1    1]
         [   1  1/2   -1 -1/2 -1/2  1/2]
         [   0  3/4    0  3/4 -3/4 -3/4]
         [   0    1    0   -1    1   -1]
         [   1 -1/2    1 -1/2 -1/2 -1/2]
         [   1   -1   -1    1    1   -1]
     """
     snb = self.seminormal_basis()
     return matrix( [vector(b) for b in snb] ).inverse().transpose()
Exemplo n.º 23
0
    def plot_lattice(self):
        r"""
        Plot the lattice (i.e. its points in the cut-off bounds of ``self``).

        OUTPUT:

        - a plot.

        EXAMPLES::

            sage: from sage.geometry.toric_plotter import ToricPlotter
            sage: tp = ToricPlotter(dict(), 2)
            sage: print tp.plot_lattice()
            Graphics object consisting of 1 graphics primitive
        """
        if not self.show_lattice:
            # Plot the origin anyway, otherwise rays/generators may look ugly.
            return self.plot_points([self.origin])
        d = self.dimension
        extra_options = self.extra_options
        if d == 1:
            points = ((x, 0) for x in range(ceil(self.xmin), floor(self.xmax) + 1))
        elif d == 2:
            points = (
                (x, y)
                for x in range(ceil(self.xmin), floor(self.xmax) + 1)
                for y in range(ceil(self.ymin), floor(self.ymax) + 1)
            )
        elif d == 3:
            points = (
                (x, y, z)
                for x in range(ceil(self.xmin), floor(self.xmax) + 1)
                for y in range(ceil(self.ymin), floor(self.ymax) + 1)
                for z in range(ceil(self.zmin), floor(self.zmax) + 1)
            )
        if self.mode == "round":
            r = 1.01 * self.radius  # To make sure integer values work OK.
            points = (pt for pt in points if vector(pt).norm() <= r)
        f = self.lattice_filter
        if f is not None:
            points = (pt for pt in points if f(pt))
        return self.plot_points(tuple(points))
Exemplo n.º 24
0
    def base_rays(self, fiber, points):
        """
        Return the primitive lattice vectors that generate the
        direction given by the base projection of points.

        INPUT:

        - ``fiber`` -- a sub-lattice polytope defining the
          :meth:`base_projection`.

        - ``points`` -- the points to project to the base.

        OUTPUT:

        A tuple of primitive `\ZZ`-vectors.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),(0,0,0,1),(0,0,1,0),(0,1,0,0),(1,0,0,0))
            sage: fiber = poly.fibration_generator(2).next()
            sage: poly.base_rays(fiber, poly.integral_points_not_interior_to_facets())
            ((-1, -1), (0, 1), (1, 0))

            sage: p = LatticePolytope_PPL((1,0),(1,2),(-1,0))
            sage: f = LatticePolytope_PPL((1,0),(-1,0))
            sage: p.base_rays(f, p.integral_points())
            ((1),)
        """
        quo = self.base_projection(fiber)
        vertices = []
        for p in points:
            v = vector(ZZ,quo(p))
            if v.is_zero():
                continue
            d =  GCD_list(v.list())
            if d>1:
                for i in range(0,v.degree()):
                    v[i] /= d
            v.set_immutable()
            vertices.append(v)
        return tuple(uniq(vertices))
Exemplo n.º 25
0
 def _to_jacobi_fourier_index(self, r) :
     r"""
     Return a tuple that corresponds to a given element of ``self``.
     
     TESTS:
     
         sage: from psage.modform.vector_valued.discriminant_group import *
         sage: A = DiscriminantForm(matrix(2, [2, 1, 1, 2]))
         sage: A._to_jacobi_fourier_index(self([0]))
         (0, 0)
         sage: A._to_jacobi_fourier_index(self([1]))
         (0, 1)
     """
     r = vector(ZZ, self._jacobi_fourier_index_matrices()[0] * r.lift())
     L_echelon = self._L_echelon
     for (i,j) in enumerate(L_echelon.pivots()) :
         m = r[j] // L_echelon[i,j]
         if m != 0 :
             r = r - m * L_echelon.row(i)
     
     return r
Exemplo n.º 26
0
 def add_unimodular_lattice(self, L = None) :
     r"""
     Add a unimodular lattice (which is not necessarily positive definite) to the underlying lattice
     and the resulting discriminant group and an isomorphism to ``self``.
     
     INPUT:
     
     - `L` -- The Gram matrix of a unimodular lattice or ``None`` (default: ``None``).
              If ``None``, `E_8` will be added.
     
     OUTPUT:
     
     - A pair of a discriminant group and a homomorphism from self to this group.
     """
     if L is None :
         L = matrix(8, [ 2,-1, 0, 0,  0, 0, 0, 0,
                        -1, 2,-1, 0,  0, 0, 0, 0,
                         0,-1, 2,-1,  0, 0, 0,-1,
                         0, 0,-1, 2, -1, 0, 0, 0,
                         0, 0, 0,-1,  2,-1, 0, 0,
                         0, 0, 0, 0, -1, 2,-1, 0,
                         0, 0, 0, 0,  0,-1, 2, 0,
                         0, 0,-1, 0,  0, 0, 0, 2])
     else :
         if L.base_ring() is not ZZ or all(e % 2 == 0 for e in L.diagonal()) \
           or L.det() != 1 :
              raise ValueError( "L must be the Gram matrix of an even unimodular lattice")   
     
     nL = self._L.block_sum(L)
     n_disc = DiscriminantForm(nL)
     
     basis_images = [ sum(map(operator.mul, b.lift(), self._dual_basis.columns()))
                      for b in self.smith_form_gens() ]
     basis_images = [ n_disc._dual_basis.solve_right(vector(QQ, b.list() + L.nrows() * [0])).list()
                      for b in basis_images ]
     coercion_hom = self.hom([ sum(map( operator.mul, map(ZZ, b), map(n_disc, FreeModule(ZZ, nL.nrows()).gens()) ))
                               for b in basis_images  ])
     
     return (n_disc, coercion_hom)
Exemplo n.º 27
0
def _local_restriction_matrix(R, S, reduction_function = None) :
    r"""
    Return a matrix whose rows correspond to the evaluations of the restriction
    vectors (s, r) in S.
    
    INPUT:
    
    - `R` -- A list of tuples or vectors in L \otimes QQ (with given coordinates).
    
    - `S` -- A list of pairs `(s, r)`, where `s` is a vector,
             and `r` is an integer.

    - ``reduction_function`` -- A function that takes a tuple representing an element in `L^\#`
                                and returs a pair of a reduced element in `L^\#` and a sign.
             
    OUTPUT:
    
    - A matrix with integer entries.
    
    TESTS::

        sage: from psage.modform.jacobiforms.jacobiformd1_fourierexpansion import *
        sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _find_complete_set_of_restriction_vectors
        sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _local_restriction_matrix                
        sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2])))
        sage: R = indices._r_representatives
        sage: S = _find_complete_set_of_restriction_vectors(indices.jacobi_index(), R, 4)        
        sage: _local_restriction_matrix(R, S)
        [1 1 1]
        [0 1 1]
        [0 1 1]
        [1 1 1]
        [0 1 1]
        [0 1 1]
        [0 2 0]
    """
    R = [map(vector, rs) for rs in R]
    
    return matrix([ _eval_restriction_vector(R, vector(s), r, reduction_function) for (s, r) in S ])
Exemplo n.º 28
0
    def P_to_S(self, P, S):
        r"""
        Given a finite Weierstrass point `P` and a point `S`
        in the same disc, computes the Coleman integrals `\{\int_P^S x^i dx/2y \}_{i=0}^{2g-1}`

        INPUT:

        - P: finite Weierstrass point
        - S: point in disc of P

        OUTPUT:

        Coleman integrals `\{\int_P^S x^i dx/2y \}_{i=0}^{2g-1}`

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^3-10*x+9)
            sage: K = Qp(5,4)
            sage: HK = H.change_ring(K)
            sage: P = HK(1,0)
            sage: HJ = HK.curve_over_ram_extn(10)
            sage: S = HK.get_boundary_point(HJ,P)
            sage: HK.P_to_S(P, S)
            (2*a + 4*a^3 + 2*a^11 + 4*a^13 + 2*a^17 + 2*a^19 + a^21 + 4*a^23 + a^25 + 2*a^27 + 2*a^29 + 3*a^31 + 4*a^33 + O(a^35), a^-5 + 2*a + 2*a^3 + a^7 + 3*a^11 + a^13 + 3*a^15 + 3*a^17 + 2*a^19 + 4*a^21 + 4*a^23 + 4*a^25 + 2*a^27 + a^29 + a^31 + O(a^33))

        AUTHOR:

        - Jennifer Balakrishnan

        """
        prec = self.base_ring().precision_cap()
        deg = (S[0]).parent().defining_polynomial().degree()
        prec2= prec*deg
        x,y = self.local_coord(P,prec2)
        g = self.genus()
        integrals = [((x**k*x.derivative()/(2*y)).integral()) for k in range(2*g)]
        val = [I(S[1]) for I in integrals]
        return vector(val)
    def __init__(self, A, b):
        """
        An element of the lattice Euclidean group.

        Note that this is just intended as a container for results from
        LatticePolytope_PPL. There is no group-theoretic functionality to
        speak of.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope \
            ....:     import LatticePolytope_PPL, C_Polyhedron
            sage: from sage.geometry.polyhedron.lattice_euclidean_group_element \
            ....:     import LatticeEuclideanGroupElement
            sage: M = LatticeEuclideanGroupElement([[1,2],[2,3],[-1,2]], [1,2,3])
            sage: M
            The map A*x+b with A=
            [ 1  2]
            [ 2  3]
            [-1  2]
            b =
            (1, 2, 3)
            sage: M._A
            [ 1  2]
            [ 2  3]
            [-1  2]
            sage: M._b
            (1, 2, 3)
            sage: M(vector([0,0]))
            (1, 2, 3)
            sage: M(LatticePolytope_PPL((0,0),(1,0),(0,1)))
            A 2-dimensional lattice polytope in ZZ^3 with 3 vertices
            sage: _.vertices()
            ((1, 2, 3), (2, 4, 2), (3, 5, 5))
        """
        self._A = matrix(ZZ, A)
        self._b = vector(ZZ, b)
        assert self._A.nrows() == self._b.degree()
Exemplo n.º 30
0
    def _eigendata(self):
        """
        Return all eigendata for self._easy_vector().

        EXAMPLES::

            sage: numerical_eigenforms(61)._eigendata() # random order
            ((1.0, 0.668205013164, 0.219198805797, 0.49263343893, 0.707106781187), (1.0, 1.49654668896, 4.5620686498, 2.02990686579, 1.41421356237), [0, 1], (1.0, 1.0))
        """
        try:
            return self.__eigendata
        except AttributeError:
            pass
        x = self._easy_vector()

        B = self._eigenvectors()
        def phi(y):
            """
            Take coefficients and a basis, and return that
            linear combination of basis vectors.

            EXAMPLES::

                sage: n = numerical_eigenforms(61) # indirect doctest
                sage: n._eigendata() # random order
                ((1.0, 0.668205013164, 0.219198805797, 0.49263343893, 0.707106781187), (1.0, 1.49654668896, 4.5620686498, 2.02990686579, 1.41421356237), [0, 1], (1.0, 1.0))
            """
            return y.element() * B

        phi_x = phi(x)
        V = phi_x.parent()
        phi_x_inv = V([a**(-1) for a in phi_x])
        eps = self._eps
        nzp = support(x, eps)
        x_nzp = vector(CDF, x.list_from_positions(nzp))
        self.__eigendata = (phi_x, phi_x_inv, nzp, x_nzp)
        return self.__eigendata
Exemplo n.º 31
0
    def __call__(self, v):
        """
        Apply the affine transformation to ``v``.

        INPUT:

        - ``v`` -- a polynomial, a multivariate polynomial, a polyhedron, a
          vector, or anything that can be converted into a vector.

        OUTPUT:

        The image of ``v`` under the affine group element.

        EXAMPLES::

            sage: G = AffineGroup(2, QQ)
            sage: g = G([0,1,-1,0],[2,3]);  g
                  [ 0  1]     [2]
            x |-> [-1  0] x + [3]
            sage: v = vector([4,5])
            sage: g(v)
            (7, -1)

            sage: R.<x,y> = QQ[]
            sage: g(x), g(y)
            (y + 2, -x + 3)
            sage: p = x^2 + 2*x*y + y + 1
            sage: g(p)
            -2*x*y + y^2 - 5*x + 10*y + 20

        The action on polynomials is such that it intertwines with
        evaluation. That is::

            sage: p(*g(v)) == g(p)(*v)
            True

        Test that the univariate polynomial ring is covered::

            sage: H = AffineGroup(1, QQ)
            sage: h = H([2],[3]);  h
            x |-> [2] x + [3]
            sage: R.<z> = QQ[]
            sage: h(z+1)
            3*z + 2

        The action on a polyhedron is defined (see :trac:`30327`)::

            sage: F = AffineGroup(3, QQ)
            sage: M = matrix(3, [-1, -2, 0, 0, 0, 1, -2, 1, -1])
            sage: v = vector(QQ,(1,2,3))
            sage: f = F(M, v)
            sage: cube = polytopes.cube()
            sage: f(cube)
            A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices

        """
        parent = self.parent()

        # start with the most probable case, i.e., v is in the vector space
        if v in parent.vector_space():
            return self._A*v + self._b

        from sage.rings.polynomial.polynomial_element import is_Polynomial
        if is_Polynomial(v) and parent.degree() == 1:
            ring = v.parent()
            return ring([self._A[0,0], self._b[0]])

        from sage.rings.polynomial.multi_polynomial import is_MPolynomial
        if is_MPolynomial(v) and parent.degree() == v.parent().ngens():
            ring = v.parent()
            from sage.modules.all import vector
            image_coords = self._A * vector(ring, ring.gens()) + self._b
            return v(*image_coords)

        from sage.geometry.polyhedron.base import is_Polyhedron
        if is_Polyhedron(v):
            return self._A*v + self._b

        # otherwise, coerce v into the vector space
        v = parent.vector_space()(v)
        return self._A*v + self._b
Exemplo n.º 32
0
def WeierstrassMap(polynomial, variables=None):
    r"""
    Return the Weierstrass form of an anticanonical hypersurface.

    You should use
    :meth:`sage.schemes.toric.weierstrass.WeierstrassForm` with
    ``transformation=True`` to get the transformation. This function
    is only for internal use.

    INPUT:

    - ``polynomial`` -- a polynomial. The toric hypersurface
      equation. Can be either a cubic, a biquadric, or the
      hypersurface in `\mathbb{P}^2[1,1,2]`. The equation need not be
      in any standard form, only its Newton polyhedron is used.

    - ``variables`` -- a list of variables of the parent polynomial
      ring or ``None`` (default). In the latter case, all variables
      are taken to be polynomial ring variables. If a subset of
      polynomial ring variables are given, the Weierstrass form is
      determined over the function field generated by the remaining
      variables.

    OUTPUT:

    A triple `(X,Y,Z)` of polynomials defining a rational map of the
    toric hypersurface to its Weierstrass form in
    `\mathbb{P}^2[2,3,1]`. That is, the triple satisfies

    .. MATH::

        Y^2 = X^3 + f X Z^4 + g Z^6

    when restricted to the toric hypersurface.

    EXAMPLES::

        sage: R.<x,y,z> = QQ[]
        sage: cubic = x^3 + y^3 + z^3
        sage: X,Y,Z = WeierstrassForm(cubic, transformation=True);  (X,Y,Z)
        (-x^3*y^3 - x^3*z^3 - y^3*z^3,
         1/2*x^6*y^3 - 1/2*x^3*y^6 - 1/2*x^6*z^3 + 1/2*y^6*z^3
             + 1/2*x^3*z^6 - 1/2*y^3*z^6,
         x*y*z)
         sage: f, g = WeierstrassForm(cubic);  (f,g)
         (0, -27/4)
         sage: cubic.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
         True

    Only the affine span of the Newton polytope of the polynomial
    matters. For example::

        sage: WeierstrassForm(cubic.subs(z=1), transformation=True)
        (-x^3*y^3 - x^3 - y^3,
         1/2*x^6*y^3 - 1/2*x^3*y^6 - 1/2*x^6
             + 1/2*y^6 + 1/2*x^3 - 1/2*y^3,
         x*y)
        sage: WeierstrassForm(x * cubic, transformation=True)
        (-x^3*y^3 - x^3*z^3 - y^3*z^3,
         1/2*x^6*y^3 - 1/2*x^3*y^6 - 1/2*x^6*z^3 + 1/2*y^6*z^3
             + 1/2*x^3*z^6 - 1/2*y^3*z^6,
         x*y*z)

    This allows you to work with either homogeneous or inhomogeneous
    variables. For example, here is the del Pezzo surface of degree 8::

        sage: dP8 = toric_varieties.dP8()
        sage: dP8.inject_variables()
        Defining t, x, y, z
        sage: WeierstrassForm(x*y^2 + y^2*z + t^2*x^3 + t^2*z^3, transformation=True)
        (-1/27*t^4*x^6 - 2/27*t^4*x^5*z - 5/27*t^4*x^4*z^2
             - 8/27*t^4*x^3*z^3 - 5/27*t^4*x^2*z^4 - 2/27*t^4*x*z^5
             - 1/27*t^4*z^6 - 4/81*t^2*x^4*y^2 - 4/81*t^2*x^3*y^2*z
             - 4/81*t^2*x*y^2*z^3 - 4/81*t^2*y^2*z^4 - 2/81*x^2*y^4
             - 4/81*x*y^4*z - 2/81*y^4*z^2,
        0,
        1/3*t^2*x^2*z + 1/3*t^2*x*z^2 - 1/9*x*y^2 - 1/9*y^2*z)
        sage: WeierstrassForm(x*y^2 + y^2 + x^3 + 1, transformation=True)
        (-1/27*x^6 - 4/81*x^4*y^2 - 2/81*x^2*y^4 - 2/27*x^5
             - 4/81*x^3*y^2 - 4/81*x*y^4 - 5/27*x^4 - 2/81*y^4 - 8/27*x^3
             - 4/81*x*y^2 - 5/27*x^2 - 4/81*y^2 - 2/27*x - 1/27,
         0,
         -1/9*x*y^2 + 1/3*x^2 - 1/9*y^2 + 1/3*x)

    By specifying only certain variables we can compute the
    Weierstrass form over the function field generated by the
    remaining variables. For example, here is a cubic over `\QQ[a]` ::

        sage: R.<a, x,y,z> = QQ[]
        sage: cubic = x^3 + a*y^3 + a^2*z^3
        sage: WeierstrassForm(cubic, variables=[x,y,z], transformation=True)
        (-a^9*y^3*z^3 - a^8*x^3*z^3 - a^7*x^3*y^3,
         -1/2*a^14*y^3*z^6 + 1/2*a^13*y^6*z^3 + 1/2*a^13*x^3*z^6
             - 1/2*a^11*x^3*y^6 - 1/2*a^11*x^6*z^3 + 1/2*a^10*x^6*y^3,
         a^3*x*y*z)

    TESTS::

        sage: for P in ReflexivePolytopes(2):
        ....:     S = ToricVariety(FaceFan(P))
        ....:     p = sum( (-S.K()).sections_monomials() )
        ....:     f, g = WeierstrassForm(p)
        ....:     X,Y,Z = WeierstrassForm(p, transformation=True)
        ....:     assert p.divides(-Y^2 + X^3 + f*X*Z^4 + g*Z^6)
    """
    if variables is None:
        variables = polynomial.variables()
    # switch to suitable inhomogeneous coordinates
    from sage.geometry.polyhedron.ppl_lattice_polygon import (
        polar_P2_polytope, polar_P1xP1_polytope, polar_P2_112_polytope)
    from sage.schemes.toric.weierstrass import Newton_polygon_embedded
    newton_polytope, polynomial_aff, variables_aff = \
        Newton_polygon_embedded(polynomial, variables)
    polygon = newton_polytope.embed_in_reflexive_polytope('polytope')
    # Compute the map in inhomogeneous coordinates
    if polygon is polar_P2_polytope():
        X, Y, Z = WeierstrassMap_P2(polynomial_aff, variables_aff)
    elif polygon is polar_P1xP1_polytope():
        X, Y, Z = WeierstrassMap_P1xP1(polynomial_aff, variables_aff)
    elif polygon is polar_P2_112_polytope():
        X, Y, Z = WeierstrassMap_P2_112(polynomial_aff, variables_aff)
    else:
        assert False, 'Newton polytope is not contained in a reflexive polygon'
    # homogenize again
    R = polynomial.parent()
    x = R.gens().index(variables_aff[0])
    y = R.gens().index(variables_aff[1])
    hom = newton_polytope.embed_in_reflexive_polytope('hom')

    def homogenize(inhomog, degree):
        e = tuple(hom._A * vector(ZZ, [inhomog[x], inhomog[y]]) +
                  degree * hom._b)
        result = list(inhomog)
        for i, var in enumerate(variables):
            result[R.gens().index(var)] = e[i]
        result = vector(ZZ, result)
        result.set_immutable()
        return result

    X_dict = dict((homogenize(e, 2), v) for e, v in iteritems(X.dict()))
    Y_dict = dict((homogenize(e, 3), v) for e, v in iteritems(Y.dict()))
    Z_dict = dict((homogenize(e, 1), v) for e, v in iteritems(Z.dict()))
    # shift to non-negative exponents if necessary
    min_deg = [0] * R.ngens()
    for var in variables:
        i = R.gens().index(var)
        min_X = min([e[i] for e in X_dict]) if len(X_dict) > 0 else 0
        min_Y = min([e[i] for e in Y_dict]) if len(Y_dict) > 0 else 0
        min_Z = min([e[i] for e in Z_dict]) if len(Z_dict) > 0 else 0
        min_deg[i] = min(min_X / 2, min_Y / 3, min_Z)
    min_deg = vector(min_deg)
    X_dict = dict((tuple(e - 2 * min_deg), v) for e, v in iteritems(X_dict))
    Y_dict = dict((tuple(e - 3 * min_deg), v) for e, v in iteritems(Y_dict))
    Z_dict = dict((tuple(e - 1 * min_deg), v) for e, v in iteritems(Z_dict))
    return (R(X_dict), R(Y_dict), R(Z_dict))
Exemplo n.º 33
0
    def _find_isomorphism_degenerate(self, polytope):
        """
        Helper to pick an isomorphism of degenerate polygons

        INPUT:

        - ``polytope`` -- a :class:`LatticePolytope_PPL_class`. The
          polytope to compare with.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL, C_Polyhedron
            sage: L1 = LatticePolytope_PPL(C_Polyhedron(2, 'empty'))
            sage: L2 = LatticePolytope_PPL(C_Polyhedron(3, 'empty'))
            sage: iso = L1.find_isomorphism(L2)   # indirect doctest
            sage: iso(L1) == L2
            True
            sage: iso = L1._find_isomorphism_degenerate(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,4))
            sage: L2 = LatticePolytope_PPL((2,1,5))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,), (3,))
            sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,-1), (3,-1))
            sage: L2 = LatticePolytope_PPL((2,1,5), (2,-3,5))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,2), (3,1))
            sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))
            sage: iso = L1.find_isomorphism(L2)
            sage: iso(L1) == L2
            True

            sage: L1 = LatticePolytope_PPL((-1,2), (3,2))
            sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,4))
            sage: L1.find_isomorphism(L2)
            Traceback (most recent call last):
            ...
            LatticePolytopesNotIsomorphicError: different number of integral points

            sage: L1 = LatticePolytope_PPL((-1,2), (3,1))
            sage: L2 = LatticePolytope_PPL((1,2,3),(1,2,5))
            sage: L1.find_isomorphism(L2)
            Traceback (most recent call last):
            ...
            LatticePolytopesNotIsomorphicError: different number of integral points
        """
        from sage.geometry.polyhedron.lattice_euclidean_group_element import \
            LatticePolytopesNotIsomorphicError
        polytope_vertices = polytope.vertices()
        self_vertices = self.ordered_vertices()
        # handle degenerate cases
        if self.n_vertices() == 0:
            A = zero_matrix(ZZ, polytope.space_dimension(),
                            self.space_dimension())
            b = zero_vector(ZZ, polytope.space_dimension())
            return LatticeEuclideanGroupElement(A, b)
        if self.n_vertices() == 1:
            A = zero_matrix(ZZ, polytope.space_dimension(),
                            self.space_dimension())
            b = polytope_vertices[0]
            return LatticeEuclideanGroupElement(A, b)
        if self.n_vertices() == 2:
            self_origin = self_vertices[0]
            self_ray = self_vertices[1] - self_origin
            polytope_origin = polytope_vertices[0]
            polytope_ray = polytope_vertices[1] - polytope_origin
            Ds, Us, Vs = self_ray.column().smith_form()
            Dp, Up, Vp = polytope_ray.column().smith_form()
            assert Vs.nrows() == Vs.ncols() == Vp.nrows() == Vp.ncols() == 1
            assert abs(Vs[0, 0]) == abs(Vp[0, 0]) == 1
            A = zero_matrix(ZZ, Dp.nrows(), Ds.nrows())
            A[0, 0] = 1
            A = Up.inverse() * A * Us * (Vs[0, 0] * Vp[0, 0])
            b = polytope_origin - A * self_origin
            try:
                A = matrix(ZZ, A)
                b = vector(ZZ, b)
            except TypeError:
                raise LatticePolytopesNotIsomorphicError('different lattice')
            hom = LatticeEuclideanGroupElement(A, b)
            if hom(self) == polytope:
                return hom
            raise LatticePolytopesNotIsomorphicError('different polygons')
Exemplo n.º 34
0
    def __init__(self, points):
        r"""
        See ``VoronoiDiagram`` for full documentation.

        EXAMPLES::

            sage: V = VoronoiDiagram([[1, 3, 3], [2, -2, 1], [-1 ,2, -1]]); V
            The Voronoi diagram of 3 points of dimension 3 in the Rational Field
        """
        self._P = {}
        self._points = PointConfiguration(points)
        self._n = self._points.n_points()
        if not self._n or self._points.base_ring().is_subring(QQ):
            self._base_ring = QQ
        elif self._points.base_ring() in [RDF, AA]:
            self._base_ring = self._points.base_ring()
        elif isinstance(self._points.base_ring(), RealField_class):
            self._base_ring = RDF
            self._points = PointConfiguration([[RDF(cor) for cor in poi]
                                               for poi in self._points])
        else:
            raise NotImplementedError('Base ring of the Voronoi diagram must '
                                      'be one of QQ, RDF, AA.')

        if self._n > 0:
            self._d = self._points.ambient_dim()
            e = [([sum(vector(i)[k]**2 for k in range(self._d))] +
                  [(-2) * vector(i)[l] for l in range(self._d)] + [1])
                 for i in self._points]
            # we attach hyperplane to the paraboloid

            e = [[self._base_ring(i) for i in k] for k in e]
            p = Polyhedron(ieqs=e, base_ring=self._base_ring)
            # To understand the reordering that takes place when
            # defining a rational polyhedron, we generate two sorted
            # lists, that are used a few lines below
            if self.base_ring() == QQ:
                enormalized = []
                for ineq in e:
                    if ineq[0] == 0:
                        enormalized.append(ineq)
                    else:
                        enormalized.append([i / ineq[0] for i in ineq[1:]])
                # print enormalized
                hlist = [list(ineq) for ineq in p.Hrepresentation()]
                hlistnormalized = []
                for ineq in hlist:
                    if ineq[0] == 0:
                        hlistnormalized.append(ineq)
                    else:
                        hlistnormalized.append([i / ineq[0] for i in ineq[1:]])
                # print hlistnormalized

        for i in range(self._n):
            # for base ring RDF and AA, Polyhedron keeps the order of the
            # points in the input, for QQ we resort
            if self.base_ring() == QQ:
                equ = p.Hrepresentation(hlistnormalized.index(enormalized[i]))
            else:
                equ = p.Hrepresentation(i)
            pvert = [[u[k] for k in range(self._d)] for u in equ.incident()
                     if u.is_vertex()]
            prays = [[u[k] for k in range(self._d)] for u in equ.incident()
                     if u.is_ray()]
            pline = [[u[k] for k in range(self._d)] for u in equ.incident()
                     if u.is_line()]
            (self._P)[self._points[i]] = Polyhedron(vertices=pvert,
                                                    lines=pline,
                                                    rays=prays,
                                                    base_ring=self._base_ring)
Exemplo n.º 35
0
    def tiny_integrals(self, F, P, Q):
        r"""
        Evaluate the integrals of $f_i dx/2y$ from $P$ to $Q$ for each $f_i$ in $F$
        by formally integrating a power series in a local parameter $t$

        $P$ and $Q$ MUST be in the same residue disc for this result to make sense.

        INPUT:

        - F a list of functions $f_i$
        - P a point on self
        - Q a point on self (in the same residue disc as P)

        OUTPUT:

        The integrals $\int_P^Q f_i dx/2y$

        EXAMPLES::

            sage: K = pAdicField(17, 5)
            sage: E = EllipticCurve(K, [-31/3, -2501/108]) # 11a
            sage: P = E(K(14/3), K(11/2))
            sage: TP = E.teichmuller(P);
            sage: x,y = E.monsky_washnitzer_gens()
            sage: E.tiny_integrals([1,x],P, TP) == E.tiny_integrals_on_basis(P,TP)
            True

        ::

            sage: K = pAdicField(11, 5)
            sage: x = polygen(K)
            sage: C = HyperellipticCurve(x^5 + 33/16*x^4 + 3/4*x^3 + 3/8*x^2 - 1/4*x + 1/16)
            sage: P = C.lift_x(11^(-2))
            sage: Q = C.lift_x(3*11^(-2))
            sage: C.tiny_integrals([1],P,Q)
            (3*11^3 + 7*11^4 + 4*11^5 + 7*11^6 + 5*11^7 + O(11^8))

        Note that this fails if the points are not in the same residue disc::

            sage: S = C(0,1/4)
            sage: C.tiny_integrals([1,x,x^2,x^3],P,S)
            Traceback (most recent call last):
            ...
            ValueError: (11^-2 + O(11^3) : 11^-5 + 8*11^-2 + O(11^0) : 1 + O(11^5)) and (0 : 3 + 8*11 + 2*11^2 + 8*11^3 + 2*11^4 + O(11^5) : 1 + O(11^5)) are not in the same residue disc

        """
        x, y, z = self.local_analytic_interpolation(P, Q)  #homogeneous coordinates
        x = x/z
        y = y/z
        dt = x.derivative() / (2*y)
        integrals = []
        g = self.genus()
        for f in F:
            try:
                f_dt = f(x,y)*dt
            except TypeError:   #if f is a constant, not callable
                f_dt = f*dt
            if x.valuation() != -2:
                I = sum([f_dt[n]/(n+1) for n in xrange(f_dt.degree()+1)]) # \int_0^1 f dt
            else:
                If_dt = f_dt.integral().laurent_polynomial()
                I = If_dt(Q[0]**g/Q[1]) - If_dt(P[0]**g/P[1])
            integrals.append(I)
        return vector(integrals)
Exemplo n.º 36
0
def p_projections(Eq, Plist, p, debug=False):
    r"""

    INPUT:

    - `Eq` -  An elliptic curve over a finite field.

    - `Plist` - a list of points on `Eq`.

    - `p` - a prime number.

    OUTPUT:

    A list of $r\le2$ vectors in $\GF{p^n}$, the images of the points in
    $G \otimes \GF{p}$, where $r$ is the number of vectors is the
    $p$-rank of `Eq`.

    ALGORITHM:

    First project onto the $p$-primary part of `Eq`.  If that has
    $p$-rank 1 (i.e. is cyclic), use discrete logs there to define a
    map to $\GF{p}$, otherwise use the Weil pairing to define two
    independent maps to $\GF{p}$.

    EXAMPLES:

    This curve has three independent rational points::

        sage: E = EllipticCurve([0,0,1,-7,6])

    We reduce modulo $409$ where its order is $3^2\cdot7^2$; the
    $3$-primary part is non-cyclic while the $7$-primary part is
    cyclic of order $49$::

        sage: F = GF(409)
        sage: EF = E.change_ring(F)
        sage: G = EF.abelian_group()
        sage: G
        Additive abelian group isomorphic to Z/147 + Z/3 embedded in Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 + 402*x + 6 over Finite Field of size 409
        sage: G.order().factor()
        3^2 * 7^2

    We construct three points and project them to the $p$-primary
    parts for $p=2,3,5,7$, yielding 0,2,0,1 vectors of length 3 modulo
    $p$ respectively.  The exact vectors output depend on the computed
    generators of `G`::

        sage: Plist = [EF([-2,3]), EF([0,2]), EF([1,0])]
        sage: from sage.schemes.elliptic_curves.saturation import p_projections
        sage: [(p,p_projections(EF,Plist,p)) for p in primes(11)]  # random
        [(2, []), (3, [(0, 2, 2), (2, 2, 1)]), (5, []), (7, [(5, 1, 1)])]
        sage: [(p,len(p_projections(EF,Plist,p))) for p in primes(11)]
        [(2, 0), (3, 2), (5, 0), (7, 1)]
    """
    if debug:
        print("In p_projections(Eq,Plist,p) with Eq = {}, Plist = {}, p = {}".
              format(Eq, Plist, p))
    n = Eq.cardinality()
    m = n.prime_to_m_part(p)  # prime-to-p part of order
    if debug:
        print("m={}, n={}".format(m, n))
    if m == n:  # p-primary part trivial, nothing to do
        return []
    G = Eq.abelian_group()
    if debug:
        print("gens = {}".format(G.gens()))

    # project onto p-primary part

    pts = [m * pt for pt in Plist]
    gens = [m * g.element() for g in G.gens()]
    gens = [g for g in gens if g]
    if debug:
        print("gens for {}-primary part of G: {}".format(p, gens))
        print("{}*points: {}".format(m, pts))
    from sage.groups.generic import discrete_log as dlog
    from sage.modules.all import vector
    Fp = GF(p)

    # If the p-primary part is cyclic we use elliptic discrete logs directly:

    if len(gens) == 1:
        g = gens[0]
        pp = g.order()
        if debug:
            print("Cyclic case, taking dlogs to base {} of order {}".format(
                g, pp))
        # logs are well-defined mod pp, hence mod p
        v = [dlog(pt, g, ord=pp, operation='+') for pt in pts]
        if debug:
            print("dlogs: {}".format(v))
        return [vector(Fp, v)]

    # We make no assumption about which generator order divides the
    # other, since conventions differ!

    orders = [g.order() for g in gens]
    p1, p2 = min(orders), max(orders)
    g1, g2 = gens
    if debug:
        print(
            "Non-cyclic case, orders = {}, p1={}, p2={}, g1={}, g2={}".format(
                orders, p1, p2, g1, g2))

    # Now the p-primary part of the reduction is non-cyclic of
    # exponent p2, and we use the Weil pairing, whose values are p1'th
    # roots of unity with p1|p2, together with discrete log in the
    # multiplicative group.

    zeta = g1.weil_pairing(g2, p2)  # a primitive p1'th root of unity
    if debug:
        print("wp of gens = {} with order {}".format(
            zeta, zeta.multiplicative_order()))
        assert zeta.multiplicative_order(
        ) == p1, "Weil pairing error during saturation: p={}, G={}, Plist={}".format(
            p, G, Plist)

    # logs are well-defined mod p1, hence mod p

    return [
        vector(Fp, [
            dlog(pt.weil_pairing(g1, p2), zeta, ord=p1, operation='*')
            for pt in pts
        ]),
        vector(Fp, [
            dlog(pt.weil_pairing(g2, p2), zeta, ord=p1, operation='*')
            for pt in pts
        ])
    ]
Exemplo n.º 37
0
    def get_homology_kernel(self, hecke_data=None):
        verbose('Entering get_homology_kernel...')
        verb = get_verbose()
        set_verbose(0)
        if hecke_data is None:
            hecke_data = []
        wp = self.wp()
        Gn = self.large_group()
        B = ArithHomology(self, ZZ**1, trivial_action=True)
        C = HomologyGroup(Gn, ZZ**1, trivial_action=True)
        group = B.group()
        Bsp = B.space()

        def phif(x):
            ans = C(0)
            for g, v in zip(group.gens(), x.values()):
                if not self.use_shapiro():
                    ans += C((Gn(g), v))
                else:
                    for a, ti in zip(v.values(), self.coset_reps()):
                        # We are considering a * (g tns t_i)
                        g0, _ = self.get_coset_ti(
                            set_immutable(ti * g.quaternion_rep))
                        ans += C((Gn(g0), a))
            return ans

        f = Bsp.hom([vector(C(phif(o))) for o in B.gens()])

        def phig(x):
            ans = C(0)
            for g, v in zip(group.gens(), x.values()):
                if not self.use_shapiro():
                    ans += C((Gn(wp**-1 * g.quaternion_rep * wp), v))
                else:
                    for a, ti in zip(v.values(), self.coset_reps()):
                        # We are considering a * (g tns t_i)
                        g0, _ = self.get_coset_ti(
                            set_immutable(ti * g.quaternion_rep))
                        ans += C((Gn(wp**-1 * g0 * wp), a))
            return ans

        g = Bsp.hom([vector(C(phig(o))) for o in B.gens()])
        maplist = [f, g]

        for ell, T in hecke_data:
            Aq = B.hecke_matrix(ell, with_torsion=True)
            tmap = Bsp.hom([
                sum([ZZ(a) * o for a, o in zip(col, Bsp.gens())])
                for col in T.charpoly()(Aq).columns()
            ])
            maplist.append(tmap)
        fg = direct_sum_of_maps(maplist)
        ker = fg.kernel()
        try:
            kerV = ker.V()
            good_ker = [
                o.lift() for o, inv in zip(ker.gens(), ker.invariants())
                if inv == 0
            ]
        except AttributeError:
            kerV = ker
            try:
                good_ker = [kerV.lift(o) for o in ker.gens()]
            except AttributeError:
                good_ker = ker.gens()
        kerVZ_amb = ZZ**(kerV.ambient_module().dimension())
        kerVZ = kerVZ_amb.submodule(
            [kerVZ_amb(o.denominator() * o) for o in kerV.basis()])
        good_ker = kerVZ.span_of_basis(
            [kerVZ((o.denominator() * o).change_ring(ZZ)) for o in good_ker])
        good_ker = [B(o.denominator() * o) for o in good_ker.LLL().rows()]
        set_verbose(verb)
        verbose('Done with get_homology_kernel')
        return good_ker
Exemplo n.º 38
0
 def _an_element_(self):
     return DiscrimiantFormElement(self, vector(ZZ, self._L.nrows() * [0]))
Exemplo n.º 39
0
def solve_mod(eqns, modulus, solution_dict=False):
    r"""
    Return all solutions to an equation or list of equations modulo the
    given integer modulus. Each equation must involve only polynomials
    in 1 or many variables.

    By default the solutions are returned as `n`-tuples, where `n`
    is the number of variables appearing anywhere in the given
    equations. The variables are in alphabetical order.

    INPUT:


    -  ``eqns`` - equation or list of equations

    -  ``modulus`` - an integer

    -  ``solution_dict`` - bool (default: False); if True or non-zero,
       return a list of dictionaries containing the solutions. If there
       are no solutions, return an empty list (rather than a list containing
       an empty dictionary). Likewise, if there's only a single solution,
       return a list containing one dictionary with that solution.


    EXAMPLES::

        sage: var('x,y')
        (x, y)
        sage: solve_mod([x^2 + 2 == x, x^2 + y == y^2], 14)
        [(4, 2), (4, 6), (4, 9), (4, 13)]
        sage: solve_mod([x^2 == 1, 4*x  == 11], 15)
        [(14,)]

    Fermat's equation modulo 3 with exponent 5::

        sage: var('x,y,z')
        (x, y, z)
        sage: solve_mod([x^5 + y^5 == z^5], 3)
        [(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 2), (2, 1, 0), (2, 2, 1)]

    We can solve with respect to a bigger modulus if it consists only of small prime factors::

        sage: [d] = solve_mod([5*x + y == 3, 2*x - 3*y == 9], 3*5*7*11*19*23*29, solution_dict = True)
        sage: d[x]
        12915279
        sage: d[y]
        8610183

    For cases where there are relatively few solutions and the prime
    factors are small, this can be efficient even if the modulus itself
    is large::

        sage: sorted(solve_mod([x^2 == 41], 10^20))
        [(4538602480526452429,), (11445932736758703821,), (38554067263241296179,),
        (45461397519473547571,), (54538602480526452429,), (61445932736758703821,),
        (88554067263241296179,), (95461397519473547571,)]

    We solve a simple equation modulo 2::

        sage: x,y = var('x,y')
        sage: solve_mod([x == y], 2)
        [(0, 0), (1, 1)]

    .. warning::

       The current implementation splits the modulus into prime
       powers, then naively enumerates all possible solutions
       (starting modulo primes and then working up through prime
       powers), and finally combines the solution using the Chinese
       Remainder Theorem.  The interface is good, but the algorithm is
       very inefficient if the modulus has some larger prime factors! Sage
       *does* have the ability to do something much faster in certain
       cases at least by using Groebner basis, linear algebra
       techniques, etc. But for a lot of toy problems this function as
       is might be useful. At least it establishes an interface.


    TESTS:

    Make sure that we short-circuit in at least some cases::

        sage: solve_mod([2*x==1], 2*next_prime(10^50))
        []

    Try multi-equation cases::

        sage: x, y, z = var("x y z")
        sage: solve_mod([2*x^2 + x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 12)
        [(0, 0), (4, 4), (0, 3), (4, 7)]
        sage: eqs = [-y^2+z^2, -x^2+y^2-3*z^2-z-1, -y*z-z^2-x-y+2, -x^2-12*z^2-y+z]
        sage: solve_mod(eqs, 11)
        [(8, 5, 6)]

    Confirm that modulus 1 now behaves as it should::

        sage: x, y = var("x y")
        sage: solve_mod([x==1], 1)
        [(0,)]
        sage: solve_mod([2*x^2+x*y, -x*y+2*y^2+x-2*y, -2*x^2+2*x*y-y^2-x-y], 1)
        [(0, 0)]


    """
    from sage.rings.all import Integer, Integers, crt_basis
    from sage.symbolic.expression import is_Expression
    from sage.misc.all import cartesian_product_iterator
    from sage.modules.all import vector
    from sage.matrix.all import matrix

    if not isinstance(eqns, (list, tuple)):
        eqns = [eqns]
    eqns = [eq if is_Expression(eq) else (eq.lhs() - eq.rhs()) for eq in eqns]
    modulus = Integer(modulus)
    if modulus < 1:
        raise ValueError("the modulus must be a positive integer")
    vars = list(set(sum([list(e.variables()) for e in eqns], [])))
    vars.sort(key=repr)

    if modulus == 1:  # degenerate case
        ans = [tuple(Integers(1)(0) for v in vars)]
        return ans

    factors = modulus.factor()
    crt_basis = vector(Integers(modulus),
                       crt_basis([p**i for p, i in factors]))
    solutions = []

    has_solution = True
    for p, i in factors:
        solution = _solve_mod_prime_power(eqns, p, i, vars)
        if len(solution) > 0:
            solutions.append(solution)
        else:
            has_solution = False
            break

    ans = []
    if has_solution:
        for solution in cartesian_product_iterator(solutions):
            solution_mat = matrix(Integers(modulus), solution)
            ans.append(
                tuple(
                    c.dot_product(crt_basis) for c in solution_mat.columns()))

    # if solution_dict == True:
    # Relaxed form suggested by Mike Hansen (#8553):
    if solution_dict:
        sol_dict = [dict(zip(vars, solution)) for solution in ans]
        return sol_dict
    else:
        return ans
Exemplo n.º 40
0
def _solve_mod_prime_power(eqns, p, m, vars):
    r"""
    Internal help function for solve_mod, does little checking since it expects
    solve_mod to do that

    Return all solutions to an equation or list of equations modulo p^m.
    Each equation must involve only polynomials
    in 1 or many variables.

    The solutions are returned as `n`-tuples, where `n`
    is the number of variables in vars.

    INPUT:


    -  ``eqns`` - equation or list of equations

    -  ``p`` - a prime

    -  ``i`` - an integer > 0

    -  ``vars`` - a list of variables to solve for


    EXAMPLES::

        sage: var('x,y')
        (x, y)
        sage: solve_mod([x^2 + 2 == x, x^2 + y == y^2], 14)
        [(4, 2), (4, 6), (4, 9), (4, 13)]
        sage: solve_mod([x^2 == 1, 4*x  == 11], 15)
        [(14,)]

    Fermat's equation modulo 3 with exponent 5::

        sage: var('x,y,z')
        (x, y, z)
        sage: solve_mod([x^5 + y^5 == z^5], 3)
        [(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 2), (2, 1, 0), (2, 2, 1)]

    We solve a simple equation modulo 2::

        sage: x,y = var('x,y')
        sage: solve_mod([x == y], 2)
        [(0, 0), (1, 1)]


    .. warning::

       Currently this constructs possible solutions by building up
       from the smallest prime factor of the modulus.  The interface
       is good, but the algorithm is horrible if the modulus isn't the
       product of many small primes! Sage *does* have the ability to
       do something much faster in certain cases at least by using the
       Chinese Remainder Theorem, Groebner basis, linear algebra
       techniques, etc. But for a lot of toy problems this function as
       is might be useful. At the very least, it establishes an
       interface.

    TESTS:

    Confirm we can reproduce the first few terms of OEIS A187719::

        sage: from sage.symbolic.relation import _solve_mod_prime_power
        sage: [sorted(_solve_mod_prime_power([x^2==41], 10, i, [x]))[0][0] for i in [1..13]]
        [1, 21, 71, 1179, 2429, 47571, 1296179, 8703821, 26452429, 526452429,
        13241296179, 19473547571, 2263241296179]

    """
    from sage.rings.all import Integers, PolynomialRing
    from sage.modules.all import vector
    from sage.misc.all import cartesian_product_iterator

    mrunning = 1
    ans = []
    for mi in xrange(m):
        mrunning *= p
        R = Integers(mrunning)
        S = PolynomialRing(R, len(vars), vars)
        eqns_mod = [S(eq) for eq in eqns]
        if mi == 0:
            possibles = cartesian_product_iterator(
                [xrange(len(R)) for _ in xrange(len(vars))])
        else:
            shifts = cartesian_product_iterator(
                [xrange(p) for _ in xrange(len(vars))])
            pairs = cartesian_product_iterator([shifts, ans])
            possibles = (tuple(vector(t) + vector(shift) * (mrunning // p))
                         for shift, t in pairs)
        ans = list(t for t in possibles if all(e(*t) == 0 for e in eqns_mod))
        if not ans: return ans

    return ans
Exemplo n.º 41
0
def dimension__vector_valued(k, L, conjugate=False):
    r"""
    Compute the dimension of the space of weight `k` vector valued modular forms
    for the Weil representation (or its conjugate) attached to the lattice `L`.
    
    See [Borcherds, Borcherds - Reflection groups of Lorentzian lattices] for a proof
    of the formula that we use here.
    
    INPUT:
    
    - `k` -- A half-integer.
    
    - ``L`` -- An quadratic form.
    
    - ``conjugate`` -- A boolean; If ``True``, then compute the dimension for
                       the conjugated Weil representation.
    
    OUTPUT:
        An integer.

    TESTS::

        sage: dimension__vector_valued(3, QuadraticForm(-matrix(2, [2, 1, 1, 2])))
        1
        sage: dimension__vector_valued(3, QuadraticForm(-matrix(2, [2, 0, 0, 2])))
        1
        sage: dimension__vector_valued(3, QuadraticForm(-matrix(2, [2, 0, 0, 4])))
        1
    """
    if 2 * k not in ZZ:
        raise ValueError("Weight must be half-integral")
    if k <= 0:
        return 0
    if k < 2:
        raise NotImplementedError("Weight <2 is not implemented.")

    if L.matrix().rank() != L.matrix().nrows():
        raise ValueError(
            "The lattice (={0}) must be non-degenerate.".format(L))

    L_dimension = L.matrix().nrows()
    if L_dimension % 2 != ZZ(2 * k) % 2:
        return 0

    plus_basis = ZZ(L_dimension + 2 * k) % 4 == 0

    ## The bilinear and the quadratic form attached to L
    quadratic = lambda x: L(x) // 2
    bilinear = lambda x, y: L(x + y) - L(x) - L(y)

    ## A dual basis for L
    (elementary_divisors, dual_basis_pre, _) = L.matrix().smith_form()
    elementary_divisors = elementary_divisors.diagonal()
    dual_basis = map(operator.div, list(dual_basis_pre), elementary_divisors)

    L_level = ZZ(lcm([b.denominator() for b in dual_basis]))

    (elementary_divisors, _, discriminant_basis_pre) = (
        L_level * matrix(dual_basis)).change_ring(ZZ).smith_form()
    elementary_divisors = filter(lambda d: d not in ZZ,
                                 (elementary_divisors / L_level).diagonal())
    elementary_divisors_inv = map(ZZ, [ed**-1 for ed in elementary_divisors])
    discriminant_basis = matrix(
        map(operator.mul,
            discriminant_basis_pre.inverse().rows()[:len(elementary_divisors)],
            elementary_divisors)).transpose()
    ## This is a form over QQ, so that we cannot use an instance of QuadraticForm
    discriminant_form = discriminant_basis.transpose() * L.matrix(
    ) * discriminant_basis

    if conjugate:
        discriminant_form = -discriminant_form

    if prod(elementary_divisors_inv) > 100:
        disc_den = discriminant_form.denominator()
        disc_bilinear_pre = \
            cython_lambda( ', '.join(   ['int a{0}'.format(i) for i in range(discriminant_form.nrows())]
                                        + ['int b{0}'.format(i) for i in range(discriminant_form.nrows())] ),
                           ' + '.join('{0} * a{1} * b{2}'.format(disc_den * discriminant_form[i,j], i, j)
                                      for i in range(discriminant_form.nrows())
                                      for j in range(discriminant_form.nrows())) )
        disc_bilinear = lambda *a: disc_bilinear_pre(*a) / disc_den
    else:
        disc_bilinear = lambda *xy: vector(ZZ, xy[:discriminant_form.nrows(
        )]) * discriminant_form * vector(ZZ, xy[discriminant_form.nrows():])

    disc_quadratic = lambda *a: disc_bilinear(*(2 * a)) / 2

    ## red gives a normal form for elements in the discriminant group
    red = lambda x: map(operator.mod, x, elementary_divisors_inv)

    def is_singl(x):
        y = red(map(operator.neg, x))
        for (e, f) in zip(x, y):
            if e < f:
                return -1
            elif e > f:
                return 1
        return 0

    ## singls and pairs are elements of the discriminant group that are, respectively,
    ## fixed and not fixed by negation.
    singls = list()
    pairs = list()
    for x in mrange(elementary_divisors_inv):
        si = is_singl(x)
        if si == 0:
            singls.append(x)
        elif si == 1:
            pairs.append(x)

    if plus_basis:
        subspace_dimension = len(singls + pairs)
    else:
        subspace_dimension = len(pairs)

    ## 200 bits are, by far, sufficient to distinguish 12-th roots of unity
    ## by increasing the precision by 4 for each additional dimension, we
    ## compensate, by far, the errors introduced by the QR decomposition,
    ## which are of the size of (absolute error) * dimension
    CC = ComplexIntervalField(200 + subspace_dimension * 4)

    zeta_order = ZZ(
        lcm([8, 12] + map(lambda ed: 2 * ed, elementary_divisors_inv)))

    zeta = CC(exp(2 * pi * I / zeta_order))
    sqrt2 = CC(sqrt(2))
    drt = CC(sqrt(L.det()))

    Tmat = diagonal_matrix(CC, [
        zeta**(zeta_order * disc_quadratic(*a))
        for a in (singls + pairs if plus_basis else pairs)
    ])
    if plus_basis:
        Smat = zeta**(zeta_order / 8 * L_dimension) / drt  \
               * matrix( CC, [  [zeta**(-zeta_order * disc_bilinear(*(gamma + delta))) for delta in singls]
                              + [sqrt2 * zeta**(-zeta_order * disc_bilinear(*(gamma + delta))) for delta in pairs]
                              for gamma in singls] \
                           + [  [sqrt2 * zeta**(-zeta_order * disc_bilinear(*(gamma + delta))) for delta in singls]
                              + [zeta**(-zeta_order * disc_bilinear(*(gamma + delta))) + zeta**(-zeta_order * disc_bilinear(*(gamma + map(operator.neg, delta)))) for delta in pairs]
                              for gamma in pairs] )
    else:
        Smat = zeta**(zeta_order / 8 * L_dimension) / drt  \
               * matrix( CC, [  [zeta**(-zeta_order * disc_bilinear(*(gamma + delta))) - zeta**(-zeta_order * disc_bilinear(*(gamma + map(operator.neg,delta))))  for delta in pairs]
                               for gamma in pairs ] )
    STmat = Smat * Tmat

    ## This function overestimates the number of eigenvalues, if it is not correct
    def eigenvalue_multiplicity(mat, ev):
        mat = matrix(CC, mat - ev * identity_matrix(subspace_dimension))
        return len(
            filter(lambda row: all(e.contains_zero() for e in row),
                   _qr(mat).rows()))

    rti = CC(exp(2 * pi * I / 8))
    S_ev_multiplicity = [
        eigenvalue_multiplicity(Smat, rti**n) for n in range(8)
    ]
    ## Together with the fact that eigenvalue_multiplicity overestimates the multiplicities
    ## this asserts that the computed multiplicities are correct
    assert sum(S_ev_multiplicity) == subspace_dimension

    rho = CC(exp(2 * pi * I / 12))
    ST_ev_multiplicity = [
        eigenvalue_multiplicity(STmat, rho**n) for n in range(12)
    ]
    ## Together with the fact that eigenvalue_multiplicity overestimates the multiplicities
    ## this asserts that the computed multiplicities are correct
    assert sum(ST_ev_multiplicity) == subspace_dimension

    T_evs = [
        ZZ((zeta_order * disc_quadratic(*a)) % zeta_order) / zeta_order
        for a in (singls + pairs if plus_basis else pairs)
    ]

    return subspace_dimension * (1 + QQ(k) / 12) \
           - ZZ(sum( (ST_ev_multiplicity[n] * ((-2 * k - n) % 12)) for n in range(12) )) / 12 \
           - ZZ(sum( (S_ev_multiplicity[n] * ((2 * k + n) % 8)) for n in range(8) )) / 8 \
           - sum(T_evs)
Exemplo n.º 42
0
    def projections(Q, p):
        r"""
        Project points onto (E mod Q)(K mod Q) \otimes \F_p.

        Returns a list of 0, 1 or 2 vectors in \F_p^n
        """
        # NB we do not do E.reduction(Q) since that may change the
        # model which will cause problems when we reduce points.
        # see trac #27387
        Ek = E.change_ring(K.residue_field(Q))
        G = Ek.abelian_group()  # cached!

        if not p.divides(G.order()):
            return []

        if verbose:
            print("There is %s-torsion modulo %s, projecting points" % (p, Q))

        def proj1(pt):
            try:
                return Ek(pt)
            except ZeroDivisionError:
                return Ek(0)

        projPlist = [proj1(pt) for pt in Plist]

        if verbose:
            print(" --> %s" % projPlist)

        gens = G.gens()
        orders = G.generator_orders()
        from sage.groups.generic import discrete_log_lambda
        from sage.modules.all import vector

        if len(gens) == 1:  # cyclic case
            n = orders[0]
            m = n.prime_to_m_part(p)  # prime-to-p part of order
            p2 = n // m  # p-primary order
            g = (m * gens[0]).element()  # generator of p-primary part
            assert g.order() == p2
            # if verbose:
            #     print("   cyclic, %s-primary part generated by %s of order %s" % (p,g,p2))

            # multiplying any point by m takes it into the p-primary
            # part -- this way we do discrete logs in a cyclic group
            # of order p2 (which will often just be p) and not order n

            v = [
                discrete_log_lambda(m * pt, g, (0, p2), '+')
                for pt in projPlist
            ]
            # entries of v are well-defined mod p2, hence mod p
            return [vector(GF(p), v)]

        # Now the reduction is not cyclic, but we still handle
        # separately the case where the p-primary part is cyclic,
        # where a similar discrete log computation suffices; in the
        # remaining case (p-rank 2) we use the Weil pairing instead.

        # Note the code makes no assumption about which generator
        # order divides the other, since conventions differ!

        mm = [ni.prime_to_m_part(p) for ni in orders]
        pp = [ni // mi for ni, mi in zip(orders, mm)]  # p-powers
        gg = [(mi * gi).element()
              for mi, gi in zip(mm, gens)]  # p-power order gens
        m = max(mm)  # = lcm(mm), the prime-to-p exponent of G;
        # multiply by this to map onto the p-primary part.
        p2 = max(pp)  # p-exponent of G
        p1 = min(pp)  # == 1 iff p-primary part is cyclic

        if p1 == 1:  # p-primary part is cyclic of order p2
            g = gg[pp.index(p2)]  # g generates the p-primary part
            # and we do discrete logs there:

            assert g.order() == p2
            # if verbose:
            #     print("   non-cyclic but %s-primary part cyclic generated by %s of order %s" % (p,g,p2))
            v = [
                discrete_log_lambda(m * pt, g, (0, p2), '+')
                for pt in projPlist
            ]
            # entries of v are well-defined mod p2, hence mod p
            return [vector(GF(p), v)]

        # Now the p-primary part is non-cyclic of exponent p2; we use
        # Weil pairings of this order, whose values are p1'th roots of unity.

        # if verbose:
        #     print("   %s-primary part non-cyclic generated by %s of orders %s" % (p,gg,pp))
        zeta = gg[0].weil_pairing(gg[1], p2)  # a primitive p1'th root of unity
        if zeta.multiplicative_order() != p1:
            if verbose:
                print("Ek = ", Ek)
                print("(p1,p2) = (%s,%s)" % (p1, p2))
                print("gg = (%s,%s)" % (gg[0], gg[1]))
            raise RuntimeError("Weil pairing error during saturation.")

        # these are the homomorphisms from E to F_p (for g in gg):
        def a(pt, g):
            """Return the zeta-based log of the Weil pairing of ``m*pt`` with ``g``.
            """
            w = (m * pt).weil_pairing(g, p2)  # result is a p1'th root of unity
            return discrete_log_lambda(w, zeta, (0, p1), '*')

        return [vector(GF(p), [a(pt, gen) for pt in projPlist]) for gen in gg]
Exemplo n.º 43
0
def symbolic_expression(x):
    """
    Create a symbolic expression or vector of symbolic expressions from x.

    INPUT:

    - ``x`` - an object

    OUTPUT:

    - a symbolic expression.

    EXAMPLES::

        sage: a = symbolic_expression(3/2); a
        3/2
        sage: type(a)
        <type 'sage.symbolic.expression.Expression'>
        sage: R.<x> = QQ[]; type(x)
        <type 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint'>
        sage: a = symbolic_expression(2*x^2 + 3); a
        2*x^2 + 3
        sage: type(a)
        <type 'sage.symbolic.expression.Expression'>
        sage: from sage.symbolic.expression import is_Expression
        sage: is_Expression(a)
        True
        sage: a in SR
        True
        sage: a.parent()
        Symbolic Ring

    Note that equations exist in the symbolic ring::

        sage: E = EllipticCurve('15a'); E
        Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field
        sage: symbolic_expression(E)
        x*y + y^2 + y == x^3 + x^2 - 10*x - 10
        sage: symbolic_expression(E) in SR
        True

    If x is a list or tuple, create a vector of symbolic expressions::

        sage: v=symbolic_expression([x,1]); v
        (x, 1)
        sage: v.base_ring()
        Symbolic Ring
        sage: v=symbolic_expression((x,1)); v
        (x, 1)
        sage: v.base_ring()
        Symbolic Ring
        sage: v=symbolic_expression((3,1)); v
        (3, 1)
        sage: v.base_ring()
        Symbolic Ring
        sage: E = EllipticCurve('15a'); E
        Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 10*x - 10 over Rational Field
        sage: v=symbolic_expression([E,E]); v
        (x*y + y^2 + y == x^3 + x^2 - 10*x - 10, x*y + y^2 + y == x^3 + x^2 - 10*x - 10)
        sage: v.base_ring()
        Symbolic Ring
    """
    from sage.symbolic.expression import Expression
    from sage.symbolic.ring import SR
    if isinstance(x, Expression):
        return x
    elif hasattr(x, '_symbolic_'):
        return x._symbolic_(SR)
    elif isinstance(x, (tuple, list)):
        return vector(SR, x)
    else:
        return SR(x)
Exemplo n.º 44
0
    def _find_cyclic_isomorphism_matching_edge(self, polytope, polytope_origin,
                                               p_ray_left, p_ray_right):
        """
        Helper to find an isomorphism of polygons

        INPUT:

        - ``polytope`` -- the lattice polytope to compare to.

        - ``polytope_origin`` -- `\ZZ`-vector. a vertex of ``polytope``

        - ``p_ray_left`` - vector. the vector from ``polytope_origin``
          to one of its neighboring vertices.

        - ``p_ray_right`` - vector. the vector from
          ``polytope_origin`` to the other neighboring vertices.

        OUTPUT:

        The element of the lattice Euclidean group that maps ``self``
        to ``polytope`` with given origin and left/right neighboring
        vertex. A
        :class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopesNotIsomorphicError`
        is raised if no such isomorphism exists.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: L1 = LatticePolytope_PPL((1,0),(0,1),(0,0))
            sage: L2 = LatticePolytope_PPL((1,0,3),(0,1,0),(0,0,1))
            sage: v0, v1, v2 = L2.vertices()
            sage: L1._find_cyclic_isomorphism_matching_edge(L2, v0, v1-v0, v2-v0)
            The map A*x+b with A=
            [ 0  1]
            [-1 -1]
            [ 1  3]
            b =
            (0, 1, 0)
        """
        from sage.geometry.polyhedron.lattice_euclidean_group_element import \
            LatticePolytopesNotIsomorphicError
        polytope_matrix = block_matrix(
            1, 2,
            [p_ray_left.column(), p_ray_right.column()])
        self_vertices = self.ordered_vertices()
        for i in range(len(self_vertices)):
            # three consecutive vertices
            v_left = self_vertices[(i + 0) % len(self_vertices)]
            v_origin = self_vertices[(i + 1) % len(self_vertices)]
            v_right = self_vertices[(i + 2) % len(self_vertices)]
            r_left = v_left - v_origin
            r_right = v_right - v_origin
            self_matrix = block_matrix(
                1, 2, [r_left.column(), r_right.column()])
            A = self_matrix.solve_left(polytope_matrix)
            b = polytope_origin - A * v_origin
            try:
                A = matrix(ZZ, A)
                b = vector(ZZ, b)
            except TypeError:
                continue
            if A.elementary_divisors()[0:2] != [1, 1]:
                continue
            hom = LatticeEuclideanGroupElement(A, b)
            if hom(self) == polytope:
                return hom
        raise LatticePolytopesNotIsomorphicError('different polygons')
Exemplo n.º 45
0
    def plot_walls(self, walls):
        r"""
        Plot ``walls``, i.e. 2-d cones, and their labels.

        Ray generators must be specified during construction or using
        :meth:`set_rays` before calling this method and these specified
        ray generators will be used in conjunction with
        :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.ambient_ray_indices`
        of ``walls``.

        INPUT:

        - ``walls`` -- a list of 2-d cones.

        OUTPUT:

        - a plot.

        EXAMPLES::

            sage: quadrant = Cone([(1,0), (0,1)])
            sage: from sage.geometry.toric_plotter import ToricPlotter
            sage: tp = ToricPlotter(dict(), 2, quadrant.rays())
            sage: print tp.plot_walls([quadrant])
            Graphics object consisting of 2 graphics primitives
            
        Let's also check that the truncating polyhedron is functioning
        correctly::
        
            sage: tp = ToricPlotter({"mode": "box"}, 2, quadrant.rays())
            sage: print tp.plot_walls([quadrant])
            Graphics object consisting of 2 graphics primitives
        """
        result = Graphics()
        if not walls or not self.show_walls:
            return result
        rays = self.rays
        extra_options = self.extra_options
        mode = self.mode
        alpha = self.wall_alpha
        colors = color_list(self.wall_color, len(walls))
        zorder = self.wall_zorder
        if mode == "box":
            if self.dimension <= 2:
                ieqs = [(self.xmax, -1, 0), (- self.xmin, 1, 0),
                        (self.ymax, 0, -1), (- self.ymin, 0, 1)]
            else:
                ieqs = [(self.xmax, -1, 0, 0), (- self.xmin, 1, 0, 0),
                        (self.ymax, 0, -1, 0), (- self.ymin, 0, 1, 0),
                        (self.zmax, 0, 0, -1), (- self.zmin, 0, 0, 1)]
            box = Polyhedron(ieqs=ieqs, base_ring=RDF)
            for wall, color in zip(walls, colors):
                result += box.intersection(wall.polyhedron()).render_solid(
                    alpha=alpha, color=color, zorder=zorder, **extra_options)
        elif mode == "generators":
            origin = self.origin
            for wall, color in zip(walls, colors):
                vertices = [rays[i] for i in wall.ambient_ray_indices()]
                vertices.append(origin)
                result += Polyhedron(vertices=vertices, base_ring=RDF).render_solid(
                    alpha=alpha, color=color, zorder=zorder, **extra_options)
        label_sectors = []
        round = mode == "round"
        for wall, color in zip(walls, colors):
            S = wall.linear_subspace()
            lsd = S.dimension()
            if lsd == 0:    # Strictly convex wall
                r1, r2 = (rays[i] for i in wall.ambient_ray_indices())
            elif lsd == 1:  # wall is a half-plane
                for i, ray in zip(wall.ambient_ray_indices(), wall.rays()):
                    if ray in S:
                        r1 = rays[i]
                    else:
                        r2 = rays[i]
                if round:
                    # Plot one "extra" sector
                    result += sector(- r1, r2,
                      alpha=alpha, color=color, zorder=zorder, **extra_options)
            else:           # wall is a plane
                r1, r2 = S.basis()
                r1 = vector(RDF, r1)
                r1 = r1 / r1.norm() * self.radius
                r2 = vector(RDF, r2)
                r2 = r2 / r2.norm() * self.radius
                if round:
                    # Plot three "extra" sectors
                    result += sector(r1, - r2,
                      alpha=alpha, color=color, zorder=zorder, **extra_options)
                    result += sector(- r1, r2,
                      alpha=alpha, color=color, zorder=zorder, **extra_options)
                    result += sector(- r1, - r2,
                      alpha=alpha, color=color, zorder=zorder, **extra_options)
            label_sectors.append([r1, r2])
            if round:
                result += sector(r1, r2,
                    alpha=alpha, color=color, zorder=zorder, **extra_options)
        result += self.plot_labels(self.wall_label,
                    [sum(label_sector) / 3 for label_sector in label_sectors])
        return result
Exemplo n.º 46
0
    jacobi_indices = [m for (_, m, _, _) in row_groups]
    index_filters = dict((m, JacobiFormD1NNFilter(precision.index(), m))
                         for m in Set(jacobi_indices))
    jacobi_forms = dict(
        (m, JacobiFormsD1NN(QQ, JacobiFormD1NNGamma(k, m), prec))
        for (m, prec) in index_filters.iteritems())

    forms = list()
    nmb_forms_coords = row_groups[-1][2] + row_groups[-1][3]
    ch1 = JacobiFormD1WeightCharacter(k)
    for (s, m, start, length) in row_groups:
        row_labels_dict = row_labels[m]
        for f in jacobi_forms[m].graded_submodule(None).basis():
            f = f.fourier_expansion()
            v = vector(ZZ, len(row_labels_dict))
            for (l, i) in row_labels_dict.iteritems():
                v[i] = f[(ch1, l)]

            forms.append(
                vector(start * [0] + v.list() +
                       (nmb_forms_coords - start - length) * [0]))

    if relation_precision == precision:
        restriction_expansion = FreeModule(QQ, nmb_forms_coords).span(forms)
    else:
        restriction_expansion_matrix__big = matrix(len(forms),
                                                   nmb_forms_coords,
                                                   forms).transpose()
        restriction_expansion_matrix = restriction_expansion_matrix__big.matrix_from_rows(
            row_indices__small)
Exemplo n.º 47
0
def process_generators_chain(gen_string, dim, base_ring=None):
    r"""
    Process CHomP generator information for simplicial complexes.

    :param gen_string: generator output from CHomP
    :type gen_string: string
    :param dim: dimension in which to find generators
    :type dim: integer
    :param base_ring: base ring over which to do the computations
    :type base_ring: optional, default ZZ
    :return: list of generators in each dimension, as described below

    ``gen_string`` has the form ::

        [H_0]
        a1

        [H_1]
        a2
        a3

        [H_2]
        a1 - a2

    For each homology group, each line lists a homology generator as a
    linear combination of generators ``ai`` of the group of chains in
    the appropriate dimension.  The elements ``ai`` are indexed
    starting with `i=1`.  Each generator is converted, using regular
    expressions, from a string to a vector (an element in the free
    module over ``base_ring``), with ``ai`` representing the unit
    vector in coordinate `i-1`.  For example, the string ``a1 - a2``
    gets converted to the vector ``(1, -1)``.

    Therefore the return value is a list of vectors.

    EXAMPLES::

        sage: from sage.interfaces.chomp import process_generators_chain
        sage: s = "[H_0]\na1\n\n[H_1]\na2\na3\n"
        sage: process_generators_chain(s, 1)
        [(0, 1), (0, 0, 1)]
        sage: s = "[H_0]\na1\n\n[H_1]\n5 * a2 - a1\na3\n"
        sage: process_generators_chain(s, 1, base_ring=ZZ)
        [(-1, 5), (0, 0, 1)]
        sage: process_generators_chain(s, 1, base_ring=GF(2))
        [(1, 1), (0, 0, 1)]
    """
    from sage.modules.all import vector
    from sage.rings.all import ZZ
    if base_ring is None:
        base_ring = ZZ
    # each dim in gens starts with a string like
    # "[H_3]".  So search for "[" to find the end of
    # the current list of generators.
    g_srch = re.compile(r'\[H_%s\]\n([^]]*)(?:\[|$)' % dim)
    g = g_srch.search(gen_string)
    if g:
        g = g.group(1)
    if g:
        # each line in the string g is a linear
        # combination of things like "a2", "a31", etc.
        # indexing on the a's starts at 1.
        lines = g.splitlines()
        new_gens = []
        for l in lines:
            gen = re.compile(r"([+-]?)\s?([0-9]+)?\s?[*]?\s?a([0-9]*)(?:\s|$)")
            v = {}
            for term in gen.finditer(l):
                if term.group(1) and re.search("-", term.group(1)):
                    sign = -1
                else:
                    sign = 1
                if term.group(2) and len(term.group(2)) > 0:
                    coeff = sign * int(term.group(2))
                else:
                    coeff = sign * 1
                idx = int(term.group(3))
                v[idx - 1] = coeff
            if v:
                new_gens.append(vector(base_ring, v))
        g = new_gens
    return g
Exemplo n.º 48
0
def _find_complete_set_of_restriction_vectors(L,
                                              R,
                                              additional_s=0,
                                              reduction_function=None):
    r"""
    Given a set R of elements in L^# (e.g. representatives for ( L^# / L ) / \pm 1)
    find a complete set of restriction vectors. (See [GKR])
    
    INPUT:
    
    - `L` -- A quadratic form.
    
    - `R` -- A list of tuples or vectors in L \otimes QQ (with
             given coordinates).

    - ``additional_s`` -- A non-negative integer; Number of additional
                          elements of `L` that should be returned.
    
    - ``reduction_function`` -- A function that takes a tuple representing an element in `L^\#`
                                and returs a pair of a reduced element in `L^\#` and a sign.
    
    OUTPUT:
    
    - A set S of pairs, the first of which is a vector corresponding to
      an element in L, and the second of which is an integer.
    
    TESTS::
    
        sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _find_complete_set_of_restriction_vectors
        sage: from psage.modform.jacobiforms.jacobiformd1_fourierexpansion import *
        sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2])))
        sage: _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives)
        [((1, 0), 0), ((1, 0), 1), ((-2, 1), 1)]
        sage: _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives, reduction_function = indices.reduce_r)
        [((1, 0), 0), ((1, 0), 1), ((-2, 1), 1)]
        
    ::
     
        sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _local_restriction_matrix
        sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(4, [2,0,0,1, 0,2,0,1, 0,0,2,1, 1,1,1,2])))
        sage: S = _find_complete_set_of_restriction_vectors(indices.jacobi_index(), indices._r_representatives)
        sage: _local_restriction_matrix(indices._r_representatives, S).rank()
        4
    """
    R = [map(vector, rs) for rs in R]

    length_inc = 5
    max_length = 5
    cur_length = 1
    short_vectors = L.short_vector_list_up_to_length(max_length, True)

    S = list()
    restriction_space = FreeModule(QQ, len(R)).span([])

    while (len(S) < len(R) + additional_s):
        while len(short_vectors[cur_length]) == 0:
            cur_length += 1
            if max_length >= cur_length:
                max_length += length_inc
                short_vectors = L.short_vector_list_up_to_length(
                    max_length, True)

        s = vector(short_vectors[cur_length].pop())

        rcands = Set([s.dot_product(r) for rs in R for r in rs])

        for r in rcands:
            v = _eval_restriction_vector(R, s, r, reduction_function)
            if len(S) - restriction_space.rank() < additional_s \
              or v not in restriction_space :
                S.append((s, r))
                restriction_space = restriction_space + FreeModule(
                    QQ, len(R)).span([v])

                if len(S) == len(R) + additional_s:
                    break

    return S
Exemplo n.º 49
0
    def cusp_reduction_table(self):
        r'''
        Returns a dictionary and the set of cusps.

        Assumes we have a finite set surjecting to the cusps (namely, P^1(O_F/N)). Runs through
        and computes a subset which represents the cusps, and shows how to go from any element 
        of P^1(O_F/N) to the chosen equivalent cusp.

        Takes as input the object representing P^1(O_F/N), where F is a number field
        (that is possibly Q), and N is some ideal in the field.  Runs the following algorithm:
                - take a remaining element C = (c:d) of P^1(O_F/N);
                - add this to the set of cusps, declaring it to be our chosen rep;
                - run through every translate C' = (c':d') of C under the stabiliser of infinity, and
                        remove this translate from the set of remaining elements;
                - store the matrix T in the stabiliser such that C' * T = C (as elements in P^1)
                        in the dictionary, with key C'.
        '''
        P = self.get_P1List()
        if hasattr(P.N(),'number_field'):
            K = P.N().number_field()
        else:
            K = QQ

        # from sage.modular.modsym.p1list_nf import lift_to_sl2_Ok
        from .my_p1list_nf import lift_to_sl2_Ok
        from sage.modular.modsym.p1list import lift_to_sl2z
        ## Define new function on the fly to pick which of Q/more general field we work in
        ## lift_to_matrix takes parameters c,d, then lifts (c:d) to a 2X2 matrix over the NF representing it
        lift_to_matrix = lambda c, d: lift_to_sl2z(c,d,P.N()) if K.degree() == 1 else lift_to_sl2_Ok(P.N(), c, d)

        ## Put all the points of P^1(O_F/N) into a list; these will corr. to our dictionary keys
        remaining_points = set(list(P)) if K == QQ else set([c.tuple() for c in P])
        reduction_table = {}
        cusp_set = []

        initial_points = len(remaining_points)

        ## Loop over all points of P^1(O_F/N)
        while len(remaining_points) > 0:
            ## Pick a new cusp representative
            c = remaining_points.pop()
            update_progress(1 - float(len(remaining_points)) / float(initial_points), "Finding cusps...")
            ## c is an MSymbol so not hashable. Create tuple that is
            ## Represent the cusp as a matrix, add to list of cusps, and add to dictionary
            new_cusp = Matrix(2,2,lift_to_matrix(c[0], c[1])) 
            new_cusp.set_immutable()
            cusp_set.append(new_cusp)
            reduction_table[c]=(new_cusp,matrix(2,2,1)) ## Set the value to I_2
            ## Now run over the whole orbit of this point under the stabiliser at infinity.
            ## For each elt of the orbit, explain how to reduce to the chosen cusp.

            ## Run over lifts of elements of O_F/N:
            if K == QQ:
                residues = Zmod(P.N())
                units = [1, -1]
            else:
                residues = P.N().residues()
                units = K.roots_of_unity()

            for hh in residues:
                h = K(hh) ## put into the number field
                ## Run over all finite order units in the number field
                for u in units:
                    ## Now have the matrix (u,h; 0,u^-1).
                    ## Compute the action of this matrix on c
                    new_c = P.normalize(u * c[0], u**-1 * c[1] + h * c[0])
                    if K != QQ: 
                        new_c = new_c.tuple()
                    if new_c not in reduction_table:
                        ## We've not seen this point before! But it's equivalent to c, so kill it!
                        ## (and also store the matrix we used to get to it)
                        remaining_points.remove(new_c)
                        T = matrix(2,2,[u,h,0,u**-1]) ## we used this matrix to get from c to new_c
                        reduction_table[new_c]=(new_cusp, T) ## update dictionary with the new_c + the matrix
                        if K != QQ:
                            assert P.normalize(*(vector(c) * T)).tuple() == new_c ## sanity check
                        else:
                            assert P.normalize(*(vector(c) * T)) == new_c ## sanity check

        return reduction_table, cusp_set
Exemplo n.º 50
0
    def restricted_automorphism_group(self, vertex_labels=None):
        r"""
        Return the restricted automorphism group.

        First, let the linear automorphism group be the subgroup of
        the Euclidean group `E(d) = GL(d,\RR) \ltimes \RR^d`
        preserving the `d`-dimensional polyhedron. The Euclidean group
        acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the
        ambient space. The restricted automorphism group is the
        subgroup of the linear automorphism group generated by
        permutations of vertices. If the polytope is full-dimensional,
        it is equal to the full (unrestricted) automorphism group.

        INPUT:

        - ``vertex_labels`` -- a tuple or ``None`` (default). The
          labels of the vertices that will be used in the output
          permutation group. By default, the vertices are used
          themselves.

        OUTPUT:

        A
        :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`
        acting on the vertices (or the ``vertex_labels``, if
        specified).

        REFERENCES:

        [BSS2009]_

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
            sage: Z3square.restricted_automorphism_group(vertex_labels=(1,2,3,4)) == PermutationGroup([[(2,3)],[(1,2),(3,4)]])
            True
            sage: G = Z3square.restricted_automorphism_group()
            sage: G == PermutationGroup([[((1,2),(2,1))],[((0,0),(1,2)),((2,1),(3,3))],[((0,0),(3,3))]])
            True
            sage: set(G.domain()) == set(Z3square.vertices())
            True
            sage: set(map(tuple,G.orbit(Z3square.vertices()[0]))) == set([(0, 0), (1, 2), (3, 3), (2, 1)])
            True
            sage: cell24 = LatticePolytope_PPL(
            ....: (1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1),(1,-1,-1,1),(0,0,-1,1),
            ....: (0,-1,0,1),(-1,0,0,1),(1,0,0,-1),(0,1,0,-1),(0,0,1,-1),(-1,1,1,-1),
            ....: (1,-1,-1,0),(0,0,-1,0),(0,-1,0,0),(-1,0,0,0),(1,-1,0,0),(1,0,-1,0),
            ....: (0,1,1,-1),(-1,1,1,0),(-1,1,0,0),(-1,0,1,0),(0,-1,-1,1),(0,0,0,-1))
            sage: cell24.restricted_automorphism_group().cardinality()
            1152
        """
        if not self.is_full_dimensional():
            return self.affine_lattice_polytope().\
                restricted_automorphism_group(vertex_labels=vertex_labels)
        if vertex_labels is None:
            vertex_labels = self.vertices()
        from sage.groups.perm_gps.permgroup import PermutationGroup
        from sage.graphs.graph import Graph
        # good coordinates for the vertices
        v_list = []
        for v in self.minimized_generators():
            assert v.divisor().is_one()
            v_coords = (1, ) + v.coefficients()
            v_list.append(vector(v_coords))

        # Finally, construct the graph
        Qinv = sum(v.column() * v.row() for v in v_list).inverse()
        G = Graph()
        for i in range(0, len(v_list)):
            for j in range(i + 1, len(v_list)):
                v_i = v_list[i]
                v_j = v_list[j]
                G.add_edge(vertex_labels[i], vertex_labels[j],
                           v_i * Qinv * v_j)
        return G.automorphism_group(edge_labels=True)
Exemplo n.º 51
0
    def __call__(self, program, complex, subcomplex=None, **kwds):
        """
        Call a CHomP program to compute the homology of a chain
        complex, simplicial complex, or cubical complex.

        See :class:`CHomP` for full documentation.

        EXAMPLES::

        sage: from sage.interfaces.chomp import CHomP
        sage: T = cubical_complexes.Torus()
        sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP
        {0: 0, 1: Z x Z, 2: Z}
        """
        from sage.misc.misc import tmp_filename
        from sage.homology.all import CubicalComplex, cubical_complexes
        from sage.homology.all import SimplicialComplex, Simplex
        from sage.homology.cubical_complex import Cube
        from sage.homology.chain_complex import HomologyGroup, ChainComplex
        from subprocess import Popen, PIPE
        from sage.rings.all import QQ, ZZ
        from sage.modules.all import VectorSpace, vector
        from sage.combinat.free_module import CombinatorialFreeModule

        if not have_chomp(program):
            raise OSError("Program %s not found" % program)

        verbose = kwds.get('verbose', False)
        generators = kwds.get('generators', False)
        extra_opts = kwds.get('extra_opts', '')
        base_ring = kwds.get('base_ring', ZZ)

        if extra_opts:
            extra_opts = extra_opts.split()
        else:
            extra_opts = []

        # type of complex:
        cubical = False
        simplicial = False
        chain = False
        # CHomP seems to have problems with cubical complexes if the
        # first interval in the first cube defining the complex is
        # degenerate.  So replace the complex X with [0,1] x X.
        if isinstance(complex, CubicalComplex):
            cubical = True
            edge = cubical_complexes.Cube(1)
            original_complex = complex
            complex = edge.product(complex)
            if verbose:
                print "Cubical complex"
        elif isinstance(complex, SimplicialComplex):
            simplicial = True
            if verbose:
                print "Simplicial complex"
        else:
            chain = True
            base_ring = kwds.get('base_ring', complex.base_ring())
            if verbose:
                print "Chain complex over %s" % base_ring

        if base_ring == QQ:
            raise ValueError(
                "CHomP doesn't compute over the rationals, only over Z or F_p."
            )
        if base_ring.is_prime_field():
            p = base_ring.characteristic()
            extra_opts.append('-p%s' % p)
            mod_p = True
        else:
            mod_p = False

        #
        #    complex
        #
        try:
            data = complex._chomp_repr_()
        except AttributeError:
            raise AttributeError(
                "Complex can not be converted to use with CHomP.")

        datafile = tmp_filename()
        f = open(datafile, 'w')
        f.write(data)
        f.close()

        #
        #    subcomplex
        #
        if subcomplex is None:
            if cubical:
                subcomplex = CubicalComplex([complex.n_cells(0)[0]])
            elif simplicial:
                m = re.search(r'\(([^,]*),', data)
                v = int(m.group(1))
                subcomplex = SimplicialComplex([[v]])
        else:
            # replace subcomplex with [0,1] x subcomplex.
            if cubical:
                subcomplex = edge.product(subcomplex)
        #
        #    generators
        #
        if generators:
            genfile = tmp_filename()
            extra_opts.append('-g%s' % genfile)

        #
        #    call program
        #
        if subcomplex is not None:
            try:
                sub = subcomplex._chomp_repr_()
            except AttributeError:
                raise AttributeError(
                    "Subcomplex can not be converted to use with CHomP.")
            subfile = tmp_filename()
            f = open(subfile, 'w')
            f.write(sub)
            f.close()
        else:
            subfile = ''
        if verbose:
            print "Popen called with arguments",
            print[program, datafile, subfile] + extra_opts
            print
            print "CHomP output:"
            print
        # output = Popen([program, datafile, subfile, extra_opts],
        cmd = [program, datafile]
        if subfile:
            cmd.append(subfile)
        if extra_opts:
            cmd.extend(extra_opts)
        output = Popen(cmd, stdout=PIPE).communicate()[0]
        if verbose:
            print output
            print "End of CHomP output"
            print
        if generators:
            gens = open(genfile, 'r').read()
            if verbose:
                print "Generators:"
                print gens

        #
        #    process output
        #
        # output contains substrings of one of the forms
        # "H_1 = Z", "H_1 = Z_2 + Z", "H_1 = Z_2 + Z^2",
        # "H_1 = Z + Z_2 + Z"
        if output.find('trivial') != -1:
            if mod_p:
                return {0: VectorSpace(base_ring, 0)}
            else:
                return {0: HomologyGroup(0, ZZ)}
        d = {}
        h = re.compile("^H_([0-9]*) = (.*)$", re.M)
        tors = re.compile("Z_([0-9]*)")
        #
        #    homology groups
        #
        for m in h.finditer(output):
            if verbose:
                print m.groups()
            # dim is the dimension of the homology group
            dim = int(m.group(1))
            # hom_str is the right side of the equation "H_n = Z^r + Z_k + ..."
            hom_str = m.group(2)
            # need to read off number of summands and their invariants
            if hom_str.find("0") == 0:
                if mod_p:
                    hom = VectorSpace(base_ring, 0)
                else:
                    hom = HomologyGroup(0, ZZ)
            else:
                rk = 0
                if hom_str.find("^") != -1:
                    rk_srch = re.search(r'\^([0-9]*)\s?', hom_str)
                    rk = int(rk_srch.group(1))
                rk += len(re.findall("(Z$)|(Z\s)", hom_str))
                if mod_p:
                    rk = rk if rk != 0 else 1
                    if verbose:
                        print "dimension = %s, rank of homology = %s" % (dim,
                                                                         rk)
                    hom = VectorSpace(base_ring, rk)
                else:
                    n = rk
                    invts = []
                    for t in tors.finditer(hom_str):
                        n += 1
                        invts.append(int(t.group(1)))
                    for i in range(rk):
                        invts.append(0)
                    if verbose:
                        print "dimension = %s, number of factors = %s, invariants = %s" % (
                            dim, n, invts)
                    hom = HomologyGroup(n, ZZ, invts)

            #
            #    generators
            #
            if generators:
                if cubical:
                    g = process_generators_cubical(gens, dim)
                    if verbose:
                        print "raw generators: %s" % g
                    if g:
                        module = CombinatorialFreeModule(
                            base_ring,
                            original_complex.n_cells(dim),
                            prefix="",
                            bracket=True)
                        basis = module.basis()
                        output = []
                        for x in g:
                            v = module(0)
                            for term in x:
                                v += term[0] * basis[term[1]]
                            output.append(v)
                        g = output
                elif simplicial:
                    g = process_generators_simplicial(gens, dim, complex)
                    if verbose:
                        print "raw generators: %s" % gens
                    if g:
                        module = CombinatorialFreeModule(base_ring,
                                                         complex.n_cells(dim),
                                                         prefix="",
                                                         bracket=False)
                        basis = module.basis()
                        output = []
                        for x in g:
                            v = module(0)
                            for term in x:
                                if complex._is_numeric():
                                    v += term[0] * basis[term[1]]
                                else:
                                    translate = complex._translation_from_numeric(
                                    )
                                    simplex = Simplex(
                                        [translate[a] for a in term[1]])
                                    v += term[0] * basis[simplex]
                            output.append(v)
                        g = output
                elif chain:
                    g = process_generators_chain(gens, dim, base_ring)
                    if verbose:
                        print "raw generators: %s" % gens
                if g:
                    if not mod_p:
                        # sort generators to match up with corresponding invariant
                        g = [
                            _[1]
                            for _ in sorted(zip(invts, g),
                                            cmp=lambda x, y: cmp(x[0], y[0]))
                        ]
                    d[dim] = (hom, g)
                else:
                    d[dim] = hom
            else:
                d[dim] = hom

        if chain:
            new_d = {}
            diff = complex.differential()
            if len(diff) == 0:
                return {}
            bottom = min(diff)
            top = max(diff)
            for dim in d:
                if complex._degree_of_differential == -1:  # chain complex
                    new_dim = bottom + dim
                else:  # cochain complex
                    new_dim = top - dim
                if isinstance(d[dim], tuple):
                    # generators included.
                    group = d[dim][0]
                    gens = d[dim][1]
                    new_gens = []
                    dimension = complex.differential(new_dim).ncols()
                    # make sure that each vector is embedded in the
                    # correct ambient space: pad with a zero if
                    # necessary.
                    for v in gens:
                        v_dict = v.dict()
                        if dimension - 1 not in v.dict():
                            v_dict[dimension - 1] = 0
                            new_gens.append(vector(base_ring, v_dict))
                        else:
                            new_gens.append(v)
                    new_d[new_dim] = (group, new_gens)
                else:
                    new_d[new_dim] = d[dim]
            d = new_d
        return d
Exemplo n.º 52
0
    def lattice_automorphism_group(self, points=None, point_labels=None):
        """
        The integral subgroup of the restricted automorphism group.

        INPUT:

        - ``points`` -- A tuple of coordinate vectors or ``None``
          (default). If specified, the points must form complete
          orbits under the lattice automorphism group. If ``None`` all
          vertices are used.

        - ``point_labels`` -- A tuple of labels for the ``points`` or
          ``None`` (default). These will be used as labels for the do
          permutation group. If ``None`` the ``points`` will be used
          themselves.

        OUTPUT:

        The integral subgroup of the restricted automorphism group
        acting on the given ``points``, or all vertices if not
        specified.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
            sage: Z3square.lattice_automorphism_group()
            Permutation Group with generators [(), ((1,2),(2,1)),
            ((0,0),(3,3)), ((0,0),(3,3))((1,2),(2,1))]

            sage: G1 = Z3square.lattice_automorphism_group(point_labels=(1,2,3,4));  G1
            Permutation Group with generators [(), (2,3), (1,4), (1,4)(2,3)]
            sage: G1.cardinality()
            4

            sage: G2 = Z3square.restricted_automorphism_group(vertex_labels=(1,2,3,4))
            sage: G2 == PermutationGroup([[(2,3)], [(1,2),(3,4)], [(1,4)]])
            True
            sage: G2.cardinality()
            8

            sage: points = Z3square.integral_points();  points
            ((0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (3, 3))
            sage: Z3square.lattice_automorphism_group(points, point_labels=(1,2,3,4,5,6))
            Permutation Group with generators [(), (3,4), (1,6)(2,5), (1,6)(2,5)(3,4)]

        Point labels also work for lattice polytopes that are not
        full-dimensional, see :trac:`16669`::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: lp = LatticePolytope_PPL((1,0,0),(0,1,0),(-1,-1,0))
            sage: lp.lattice_automorphism_group(point_labels=(0,1,2))
            Permutation Group with generators [(), (1,2), (0,1), (0,1,2), (0,2,1), (0,2)]
        """
        if not self.is_full_dimensional():
            return self.affine_lattice_polytope().lattice_automorphism_group(
                point_labels=point_labels)

        if points is None:
            points = self.vertices()
        if point_labels is None:
            point_labels = tuple(points)
        points = [vector(ZZ, [1] + v.list()) for v in points]
        for p in points:
            p.set_immutable()

        vertices = [vector(ZZ, [1] + v.list()) for v in self.vertices()]
        pivots = matrix(ZZ, vertices).pivot_rows()
        basis = matrix(ZZ, [vertices[i] for i in pivots])
        Mat_ZZ = basis.parent()
        basis_inverse = basis.inverse()

        from sage.groups.perm_gps.permgroup import PermutationGroup
        from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
        lattice_gens = []
        G = self.restricted_automorphism_group(
            vertex_labels=tuple(range(len(vertices))))
        for g in G:
            image = matrix(ZZ, [vertices[g(i)] for i in pivots])
            m = basis_inverse * image
            if m not in Mat_ZZ:
                continue
            perm_list = [point_labels[points.index(p * m)] for p in points]
            lattice_gens.append(perm_list)
        return PermutationGroup(lattice_gens, domain=point_labels)
Exemplo n.º 53
0
    def row_coefficients(self, v):
        r"""
        Return coefficients of the basic variable ``v``.

        These are the coefficients with which nonbasic variables are subtracted
        in the relation for ``v``.

        INPUT:

        - ``v`` -- a basic variable of ``self``, can be given as a string, an
          actual variable, or an integer interpreted as the index of a variable

        OUTPUT:

        - a vector

        EXAMPLES::

            sage: from sage_numerical_interactive_mip.backends.glpk_backend_dictionary \
                  import LPGLPKBackendDictionary
            sage: p = MixedIntegerLinearProgram(maximization=True, \
                                                solver="GLPK")
            sage: x = p.new_variable(nonnegative=True)
            sage: p.add_constraint(x[0] + x[1] - 7*x[2] + x[3] <= 22)
            sage: p.add_constraint(x[1] + 2*x[2] - x[3] <= 13)
            sage: p.add_constraint(5*x[0] + x[2] <= 11)
            sage: p.set_objective(2*x[0] + 3*x[1] + 4*x[2] + 13*x[3])
            sage: b = p.get_backend()
            sage: import sage.numerical.backends.glpk_backend as backend
            sage: b.solver_parameter(\
                backend.glp_simplex_or_intopt, backend.glp_simplex_only)
            sage: b.solve()
            0
            sage: d = LPGLPKBackendDictionary(b)
            sage: vars = d.basic_variables()
            sage: vars
            (x_2, x_3, w_1)
            sage: d.leave(vars[0])
            sage: d.leaving_coefficients()   # indirect doctest
            (5.0, 0.0, 0.0, 1.0)
            sage: d.leave(vars[1])
            sage: d.leaving_coefficients()   # indirect doctest
            (36.0, 1.0, 1.0, 7.0)
        """
        if v is not None:
            v = variable(self.coordinate_ring(), v)
            if v not in self.basic_variables():
                raise ValueError("variable must be basic")
        index = tuple(self._x).index(v)

        # Reverse signs for auxiliary variables
        def reverse_sign_for_auxiliary(i_v):
            i, v = i_v
            return (i, v) if i < self._backend.nrows() else (i, -v)

        def reverse_sign_for_nonauxiliary(i_v):
            i, v = i_v
            return (i, -v) if i < self._backend.nrows() else (i, v)

        if index < self._backend.ncols():
            raw_row = self._backend.eval_tab_row(index + self._backend.nrows())
            tab_row = map(reverse_sign_for_auxiliary, zip(*raw_row))
        else:
            raw_row = self._backend.eval_tab_row(index - self._backend.ncols())
            tab_row = map(reverse_sign_for_nonauxiliary, zip(*raw_row))

        l = [0] * (self._backend.ncols())
        for (i, v) in tab_row:
            if i < self._backend.nrows():
                symbol = self._x[i + self._backend.ncols()]
            else:
                symbol = self._x[i - self._backend.nrows()]
            pos = tuple(self.nonbasic_variables()).index(symbol)
            l[pos] = v

        return vector(l)
Exemplo n.º 54
0
    def S_to_Q(self,S,Q):
        r"""
        Given $S$ a point on self over an extension field, computes the
        Coleman integrals $\{\int_S^Q x^i dx/2y \}_{i=0}^{2g-1}$

        **one should be able to feed $S,Q$ into coleman_integral,
        but currently that segfaults**

        INPUT:

        - S: a point with coordinates in an extension of $\Q_p$ (with unif. a)
        - Q: a non-Weierstrass point defined over $\Q_p$

        OUTPUT:

        the Coleman integrals $\{\int_S^Q x^i dx/2y \}_{i=0}^{2g-1}$ in terms of $a$

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^3-10*x+9)
            sage: K = Qp(5,6)
            sage: HK = H.change_ring(K)
            sage: J.<a> = K.extension(x^20-5)
            sage: HJ  = H.change_ring(J)
            sage: w = HK.invariant_differential()
            sage: x,y = HK.monsky_washnitzer_gens()
            sage: P = HK(1,0)
            sage: Q = HK(0,3)
            sage: S = HK.get_boundary_point(HJ,P)
            sage: P_to_S = HK.P_to_S(P,S)
            sage: S_to_Q = HJ.S_to_Q(S,Q)
            sage: P_to_S + S_to_Q
            (2*a^40 + a^80 + a^100 + O(a^105), a^20 + 2*a^40 + 4*a^60 + 2*a^80 + O(a^103))
            sage: HK.coleman_integrals_on_basis(P,Q)
            (2*5^2 + 5^4 + 5^5 + 3*5^6 + O(5^7), 5 + 2*5^2 + 4*5^3 + 2*5^4 + 5^6 + O(5^7))

        AUTHOR:

        - Jennifer Balakrishnan

        """
        FS = self.frobenius(S)
        FS = (FS[0],FS[1])
        FQ = self.frobenius(Q)
        import sage.schemes.elliptic_curves.monsky_washnitzer as monsky_washnitzer
        try:
            M_frob, forms = self._frob_calc
        except AttributeError:
            M_frob, forms = self._frob_calc = monsky_washnitzer.matrix_of_frobenius_hyperelliptic(self)
        try:
            HJ = self._curve_over_ram_extn
            K = HJ.base_ring()
        except AttributeError:
            HJ = S.scheme()
            K = self.base_ring()
        g = self.genus()
        prec2 = K.precision_cap()
        p = K.prime()
        dim = 2*g
        V = VectorSpace(K,dim)
        if S == FS:
            S_to_FS = V(dim*[0])
        else:
            P = self(ZZ(FS[0][0]),ZZ(FS[1][0]))
            x,y = self.local_coord(P,prec2)
            integrals = [(x**i*x.derivative()/(2*y)).integral() for i in range(dim)]
            S_to_FS = vector([I.polynomial()(FS[1]) - I.polynomial()(S[1]) for I in integrals])
        if HJ(Q[0],Q[1]) == HJ(FQ):
            FQ_to_Q = V(dim*[0])
        else:
            FQ_to_Q = V(self.tiny_integrals_on_basis(FQ, Q))
        try:
            L = [f(K(S[0]), K(S[1])) - f(K(Q[0]), K(Q[1])) for f in forms]
        except ValueError:
            forms = [f.change_ring(K) for f in forms]
            L = [f(S[0], S[1]) - f(Q[0], Q[1]) for f in forms]
        b = V(L)
        M_sys = matrix(K, M_frob).transpose() - 1
        B = (~M_sys)
        v = [B.list()[i].valuation() for i in range(len(B.list()))]
        vv= min(v)
        B = (p**(-vv)*B).change_ring(K)
        B = p**(vv)*B
        return B*(b-S_to_FS-FQ_to_Q)
Exemplo n.º 55
0
def _test__coefficient_by_restriction(precision,
                                      k,
                                      relation_precision=None,
                                      additional_lengths=1):
    r"""
    TESTS::
    
        sage: from psage.modform.jacobiforms.jacobiformd1_fourierexpansion import *
        sage: from psage.modform.jacobiforms.jacobiformd1_fegenerators import _test__coefficient_by_restriction

    ::

        sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2])))
        sage: precision = indices.filter(10)
        sage: _test__coefficient_by_restriction(precision, 10, additional_lengths = 10)
        
    ::

        sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(2, [4,1,1,2])))
        sage: precision = JacobiFormD1Filter(5, indices.jacobi_index())
        sage: _test__coefficient_by_restriction(precision, 40, additional_lengths = 4) # long test
        
    We use different precisions for relations and restrictions::

        sage: indices = JacobiFormD1Indices(QuadraticForm(matrix(2, [2,1,1,2])))
        sage: precision = indices.filter(20)
        sage: relation_precision = indices.filter(2)
        sage: _test__coefficient_by_restriction(precision, 10, relation_precision, additional_lengths = 4)
    """
    from sage.misc.misc import verbose

    L = precision.jacobi_index()

    if relation_precision is not None and not relation_precision <= precision:
        raise ValueError(
            "Relation precision must be less than or equal to precision.")

    expansions = _coefficient_by_restriction(precision, k, relation_precision)
    verbose(
        "Start testing restrictions of {2} Jacobi forms of weight {0} and index {1}"
        .format(k, L, len(expansions)))

    ch1 = JacobiFormD1WeightCharacter(k)
    chL = JacobiFormD1WeightCharacter(k, L.matrix().nrows())

    R = precision.monoid()._r_representatives
    S_extended = _find_complete_set_of_restriction_vectors(L, R)

    S = list()
    for (s, _) in S_extended:
        if s not in S:
            S.append(s)
    max_S_length = max([L(s) for s in S])

    S = L.short_vector_list_up_to_length(max_S_length + 1 + additional_lengths,
                                         True)[1:]
    Sold = flatten(S[:max_S_length + 1], max_level=1)
    Snew = flatten(S[max_S_length + 1:], max_level=1)
    S = flatten(S, max_level=1)
    verbose("Will use the following restriction vectors: {0}".format(S))

    jacobi_forms_dict = dict()
    non_zero_expansions = list()
    for s in S:
        m = L(s)
        verbose("Restriction to index {0} via {1}".format(m, s))

        try:
            jacobi_forms = jacobi_forms_dict[m]
        except KeyError:
            jacobi_forms = JacobiFormsD1NN(
                QQ, JacobiFormD1NNGamma(k, m),
                JacobiFormD1NNFilter(precision.index(), m))
            jacobi_forms_dict[m] = jacobi_forms
        jacobi_forms_module = span([
            vector(b[(ch1, k)]
                   for k in jacobi_forms.fourier_expansion_precision())
            for b in map(lambda b: b.fourier_expansion(),
                         jacobi_forms.graded_submodule(None).basis())
        ])

        fourier_expansion_module = jacobi_forms.fourier_expansion_ambient()

        for (i, expansion) in enumerate(expansions):
            verbose("Testing restriction of {0}-th form".format(i))
            restricted_expansion_dict = dict()
            for (n, r) in precision.monoid_filter():
                rres = s.dot_product(vector(r))
                try:
                    restricted_expansion_dict[(n, rres)] += expansion[(chL,
                                                                       (n, r))]
                except KeyError:
                    restricted_expansion_dict[(n, rres)] = expansion[(chL,
                                                                      (n, r))]

            restricted_expansion = vector(
                restricted_expansion_dict.get(k, 0)
                for k in jacobi_forms.fourier_expansion_precision())
            if restricted_expansion not in jacobi_forms_module:
                raise RuntimeError(
                    "{0}-th restricted via {1} is not a Jacobi form".format(
                        i, s))

            if restricted_expansion != 0:
                non_zero_expansions.append(i)

    assert Set(non_zero_expansions) == Set(range(len(expansions)))