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
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
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
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 ])
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)
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
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))
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)
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
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)
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)
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)
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
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
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)
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
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))
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))
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)
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()
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))
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))
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
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)
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 ])
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()
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
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
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))
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')
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)
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)
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 ]) ]
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
def _an_element_(self): return DiscrimiantFormElement(self, vector(ZZ, self._L.nrows() * [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
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
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)
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]
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)
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')
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
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)
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
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
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
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)
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
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)
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)
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)
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)))