def clear_denominators(self): r""" scales by the least common multiple of the denominators. OUTPUT: None. EXAMPLES:: sage: R.<t>=PolynomialRing(QQ) sage: P.<x,y,z>=ProjectiveSpace(FractionField(R),2) sage: Q=P([t,3/t^2,1]) sage: Q.clear_denominators(); Q (t^3 : 3 : t^2) :: sage: R.<x>=PolynomialRing(QQ) sage: K.<w>=NumberField(x^2-3) sage: P.<x,y,z>=ProjectiveSpace(K,2) sage: Q=P([1/w,3,0]) sage: Q.clear_denominators(); Q (w : 9 : 0) :: sage: P.<x,y,z>=ProjectiveSpace(QQ,2) sage: X=P.subscheme(x^2-y^2); sage: Q=X([1/2,1/2,1]); sage: Q.clear_denominators(); Q (1 : 1 : 2) """ self.scale_by(lcm([self[i].denominator() for i in range(self.codomain().ambient_space().dimension_relative())]))
def _make_monic(self, f): r""" Let alpha be a root of f. This function returns a monic integral polynomial g and an element d of the base field such that g(alpha*d) = 0. EXAMPLES:: sage: K.<x> = FunctionField(QQ); R.<y> = K[]; sage: L.<alpha> = K.extension(x^2*y^5 - 1/x) sage: g, d = L._make_monic(L.polynomial()); g,d (y^5 - x^12, x^3) sage: g(alpha*d) 0 """ R = f.base_ring() if not isinstance(R, RationalFunctionField): raise NotImplementedError # make f monic n = f.degree() c = f.leading_coefficient() if c != 1: f = f / c # find lcm of denominators from sage.rings.arith import lcm # would be good to replace this by minimal... d = lcm([b.denominator() for b in f.list() if b]) if d != 1: x = f.parent().gen() g = (d**n) * f(x/d) else: g = f return g, d
def ncusps(self): r""" Return the number of orbits of cusps (regular or otherwise) for this subgroup. EXAMPLE:: sage: GammaH(33,[2]).ncusps() 8 sage: GammaH(32079, [21676]).ncusps() 28800 AUTHORS: - Jordi Quer """ N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in N.divisors() if d**2 <= N]: Nd = lcm(d,N//d) Hd = set([x % Nd for x in H]) lenHd = len(Hd) if Nd-1 not in Hd: lenHd *= 2 summand = euler_phi(d)*euler_phi(N//d)//lenHd if d**2 == N: c = c + summand else: c = c + 2*summand return c
def generalised_level(self): r""" Return the generalised level of self, i.e. the least common multiple of the widths of all cusps. If self is *even*, Wohlfart's theorem tells us that this is equal to the (conventional) level of self when self is a congruence subgroup. This can fail if self is odd, but the actual level is at most twice the generalised level. See the paper by Kiming, Schuett and Verrill for more examples. EXAMPLE:: sage: Gamma0(18).generalised_level() 18 sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5).index() Traceback (most recent call last): ... NotImplementedError In the following example, the actual level is twice the generalised level. This is the group `G_2` from Example 17 of K-S-V. :: sage: G = CongruenceSubgroup(8, [ [1,1,0,1], [3,-1,4,-1] ]) sage: G.level() 8 sage: G.generalised_level() 4 """ return arith.lcm([self.cusp_width(c) for c in self.cusps()])
def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algorithm=None): r""" Return a list of pairs ``(root,multiplicity)`` of roots of the polynomial ``p``. If the argument ``multiplicities`` is set to ``False`` then return the list of roots. .. SEEALSO:: :meth:`_factor_univariate_polynomial` EXAMPLES:: sage: R.<x> = PolynomialRing(GF(5),'x') sage: K = GF(5).algebraic_closure('t') sage: sorted((x^6 - 1).roots(K,multiplicities=False)) [1, 4, 2*t2 + 1, 2*t2 + 2, 3*t2 + 3, 3*t2 + 4] sage: ((K.gen(2)*x - K.gen(3))**2).roots(K) [(3*t6^5 + 2*t6^4 + 2*t6^2 + 3, 2)] sage: for _ in xrange(10): ....: p = R.random_element(degree=randint(2,8)) ....: for r in p.roots(K, multiplicities=False): ....: assert p(r).is_zero(), "r={} is not a root of p={}".format(r,p) """ from sage.rings.arith import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # first build a polynomial over some finite field coeffs = [v.as_finite_field_element(minimal=True) for v in p.list()] l = lcm([c[0].degree() for c in coeffs]) F, phi = self.subfield(l) P = p.parent().change_ring(F) new_coeffs = [self.inclusion(c[0].degree(), l)(c[1]) for c in coeffs] polys = [(g,m,l,phi) for g,m in P(new_coeffs).factor()] roots = [] # a list of pair (root,multiplicity) while polys: g,m,l,phi = polys.pop() if g.degree() == 1: # found a root r = phi(-g.constant_coefficient()) roots.append((r,m)) else: # look at the extension of degree g.degree() which contains at # least one root of g ll = l * g.degree() psi = self.inclusion(l, ll) FF, pphi = self.subfield(ll) # note: there is no coercion from the l-th subfield to the ll-th # subfield. The line below does the conversion manually. g = PolynomialRing(FF, 'x')(map(psi, g)) polys.extend((gg,m,ll,pphi) for gg,_ in g.factor()) if multiplicities: return roots else: return [r[0] for r in roots]
def _iterator_discriminant(self, epsilon, offset) : """ Return iterator over all possible discriminants of a Fourier index given a content ``eps`` and a discriminant offset modulo `D`. INPUT: - `\epsilon` -- Integer; Content of an index. - ``offset`` -- Integer; Offset modulo D of the discriminant. OUTPUT: - Iterator over Integers. TESTS:: sage: fac = HermitianModularFormD2Factory(4,-3) sage: list(fac._iterator_discriminant(2, 3)) [0, 12, 24] sage: list(fac._iterator_discriminant(2, 2)) [8, 20] sage: list(fac._iterator_discriminant(1, 2)) [2, 5, 8, 11, 14, 17, 20, 23, 26] """ mD = -self.__D periode = lcm(mD, epsilon**2) offset = crt(offset, 0, mD, epsilon**2) % periode return xrange( offset, self.__precision._enveloping_discriminant_bound(), periode )
def _init_from_Vrepresentation(self, ambient_dim, vertices, rays, lines, minimize=True): """ Construct polyhedron from V-representation data. INPUT: - ``ambient_dim`` -- integer. The dimension of the ambient space. - ``vertices`` -- list of point. Each point can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - ``rays`` -- list of rays. Each ray can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - ``lines`` -- list of lines. Each line can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. EXAMPLES:: sage: p = Polyhedron(backend='ppl') sage: from sage.geometry.polyhedron.backend_ppl import Polyhedron_QQ_ppl sage: Polyhedron_QQ_ppl._init_from_Vrepresentation(p, 2, [], [], []) """ gs = Generator_System() if vertices is None: vertices = [] for v in vertices: d = lcm([denominator(v_i) for v_i in v]) dv = [ ZZ(d*v_i) for v_i in v ] gs.insert(point(Linear_Expression(dv, 0), d)) if rays is None: rays = [] for r in rays: d = lcm([denominator(r_i) for r_i in r]) dr = [ ZZ(d*r_i) for r_i in r ] gs.insert(ray(Linear_Expression(dr, 0))) if lines is None: lines = [] for l in lines: d = lcm([denominator(l_i) for l_i in l]) dl = [ ZZ(d*l_i) for l_i in l ] gs.insert(line(Linear_Expression(dl, 0))) self._ppl_polyhedron = C_Polyhedron(gs) self._init_Vrepresentation_from_ppl(minimize) self._init_Hrepresentation_from_ppl(minimize)
def circle_drops(A,B): # Drops going around the unit circle for those A and B. # See http://user.math.uzh.ch/dehaye/thesis_students/Nicolas_Wider-Integrality_of_factorial_ratios.pdf # for longer description (not original, better references exist) marks = lcm(lcm(A),lcm(B)) tmp = [0 for i in range(marks)] for a in A: # print tmp for i in range(a): if gcd(i, a) == 1: tmp[i*marks/a] -= 1 for b in B: # print tmp for i in range(b): if gcd(i, b) == 1: tmp[i*marks/b] += 1 # print tmp return [sum(tmp[:j]) for j in range(marks)]
def __init__( self, q): """ We initialize by a list of integers $[a_1,...,a_N]$. The lattice self is then $L = (L,\beta) = (G^{-1}\ZZ^n/\ZZ^n, G[x])$, where $G$ is the symmetric matrix $G=[a_1,...,a_n;*,a_{n+1},....;..;* ... * a_N]$, i.e.~the $a_j$ denote the elements above the diagonal of $G$. """ self.__form = q # We compute the rank N = len(q) assert is_square( 1+8*N) n = Integer( (-1+isqrt(1+8*N))/2) self.__rank = n # We set up the Gram matrix self.__G = matrix( IntegerRing(), n, n) i = j = 0 for a in q: self.__G[i,j] = self.__G[j,i] = Integer(a) if j < n-1: j += 1 else: i += 1 j = i # We compute the level Gi = self.__G**-1 self.__Gi = Gi a = lcm( map( lambda x: x.denominator(), Gi.list())) I = Gi.diagonal() b = lcm( map( lambda x: (x/2).denominator(), I)) self.__level = lcm( a, b) # We define the undelying module and the ambient space self.__module = FreeModule( IntegerRing(), n) self.__space = self.__module.ambient_vector_space() # We compute a shadow vector self.__shadow_vector = self.__space([(a%2)/2 for a in self.__G.diagonal()])*Gi # We define a basis M = Matrix( IntegerRing(), n, n, 1) self.__basis = self.__module.basis() # We prepare a cache self.__dual_vectors = None self.__values = None self.__chi = {}
def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algorithm=None): r""" Return a list of pairs ``(root,multiplicity)`` of roots of the polynomial ``p``. If the argument ``multiplicities`` is set to ``False`` then return the list of roots. .. SEEALSO:: :meth:`_factor_univariate_polynomial` EXAMPLES:: sage: R.<x> = PolynomialRing(GF(5),'x') sage: K = GF(5).algebraic_closure('t') sage: sorted((x^6 - 1).roots(K,multiplicities=False)) [1, 4, 2*t2 + 1, 2*t2 + 2, 3*t2 + 3, 3*t2 + 4] sage: ((K.gen(2)*x - K.gen(3))**2).roots(K) [(3*t6^5 + 2*t6^4 + 2*t6^2 + 3, 2)] sage: for _ in xrange(10): ....: p = R.random_element(degree=randint(2,8)) ....: for r in p.roots(K, multiplicities=False): ....: assert p(r).is_zero() """ from sage.rings.arith import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # first build a polynomial over some finite field coeffs = [v.as_finite_field_element(minimal=True) for v in p.list()] l = lcm([c[0].degree() for c in coeffs]) F, phi = self.subfield(l) P = p.parent().change_ring(F) new_coeffs = [self.inclusion(c[0].degree(), l)(c[1]) for c in coeffs] roots = [] # a list of pair (root,multiplicity) for g, m in P(new_coeffs).factor(): if g.degree() == 1: r = phi(-g.constant_coefficient()) roots.append((r,m)) else: ll = l * g.degree() psi = self.inclusion(l, ll) FF, pphi = self.subfield(ll) gg = PolynomialRing(FF, 'x')(map(psi, g)) for r, _ in gg.roots(): # note: we know that multiplicity is 1 roots.append((pphi(r), m)) if multiplicities: return roots else: return [r[0] for r in roots]
def shadow_level( self): """ Return the level of $L_ev$, where $L_ev$ is the kernel of the map $x\mapsto e(G[x]/2)$. REMARK Lemma: If $L$ is odd, if $s$ is a shadow vector, and if $N$ denotes the denominator of $G[s]/2$, then the level of $L_ev$ equals the lcm of the order of $s$, $N$ and the level of $L$. (For the proof: the requested level is the smallest integer $l$ such that $lG[x]/2$ is integral for all $x$ in $L^*$ and $x$ in $s+L^*$ since $L_ev^* = L^* \cup s+L^*$. This $l$ is the smallest integer such that $lG[s]/2$, $lG[x]/2$ and $ls^tGx$ are integral for all $x$ in $L^*$.) """ if self.is_even(): return self.level() s = self.a_shadow_vector() N = self.beta(s).denominator() h = lcm( [x.denominator() for x in s]) return lcm( [h, N, self.level()])
def _init_from_Hrepresentation(self, ambient_dim, ieqs, eqns, minimize=True): """ Construct polyhedron from H-representation data. INPUT: - ``ambient_dim`` -- integer. The dimension of the ambient space. - ``ieqs`` -- list of inequalities. Each line can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. - ``eqns`` -- list of equalities. Each line can be specified as any iterable container of :meth:`~sage.geometry.polyhedron.base.base_ring` elements. EXAMPLES:: sage: p = Polyhedron(backend='ppl') sage: from sage.geometry.polyhedron.backend_ppl import Polyhedron_QQ_ppl sage: Polyhedron_QQ_ppl._init_from_Hrepresentation(p, 2, [], []) """ cs = Constraint_System() if ieqs is None: ieqs = [] for ieq in ieqs: d = lcm([denominator(ieq_i) for ieq_i in ieq]) dieq = [ ZZ(d*ieq_i) for ieq_i in ieq ] b = dieq[0] A = dieq[1:] cs.insert(Linear_Expression(A, b) >= 0) if eqns is None: eqns = [] for eqn in eqns: d = lcm([denominator(eqn_i) for eqn_i in eqn]) deqn = [ ZZ(d*eqn_i) for eqn_i in eqn ] b = deqn[0] A = deqn[1:] cs.insert(Linear_Expression(A, b) == 0) self._ppl_polyhedron = C_Polyhedron(cs) self._init_Vrepresentation_from_ppl(minimize) self._init_Hrepresentation_from_ppl(minimize)
def _heckebasis(M): r""" Gives a basis of the hecke algebra of M as a ZZ-module INPUT: - ``M`` - a hecke module OUTPUT: - a list of hecke algebra elements represented as matrices EXAMPLES:: sage: M = ModularSymbols(11,2,1) sage: sage.modular.hecke.algebra._heckebasis(M) [Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by: [1 0] [0 1], Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by: [0 1] [0 5]] """ d = M.rank() VV = QQ ** (d ** 2) WW = ZZ ** (d ** 2) MM = MatrixSpace(QQ, d) MMZ = MatrixSpace(ZZ, d) S = [] Denom = [] B = [] B1 = [] for i in xrange(1, M.hecke_bound() + 1): v = M.hecke_operator(i).matrix() den = v.denominator() Denom.append(den) S.append(v) den = lcm(Denom) for m in S: B.append(WW((den * m).list())) UU = WW.submodule(B) B = UU.basis() for u in B: u1 = u.list() m1 = M.hecke_algebra()(MM(u1), check=False) # m1 = MM(u1) B1.append((1 / den) * m1) return B1
def exponent(self): """ Return the exponent of this abelian group. EXAMPLES:: sage: G = AbelianGroup([2,3,7]); G Multiplicative Abelian Group isomorphic to C2 x C3 x C7 sage: G.exponent() 42 sage: G = AbelianGroup([2,4,6]); G Multiplicative Abelian Group isomorphic to C2 x C4 x C6 sage: G.exponent() 12 """ try: return self.__exponent except AttributeError: self.__exponent = e = lcm(self.invariants()) return e
def summand(part, n): """ Create the summand used in the Harrison count for a given partition. Args: part (tuple): A partition of `n` represented as a tuple. n (int): The integer for which `part` is a partition. Returns: int: The summand corresponding to the partition `part` of `n`. """ t = 1 count = list(cycle_count(part, n)) + (factorial(n)-n)*[0] for i in range(1,n+1): for j in range(1,n+1): s = sum([d*(count[d-1]) for d in divisors(lcm(i,j))]) t = t*(s**(count[i-1]*count[j-1]*gcd(i,j))) t = t*factorial(n)/(prod(factorial(count[d-1])*(d**(count[d-1])) for d in range(1,n+1))) return t
def clear_denominators(self): r""" scales by the least common multiple of the denominators. OUTPUT: None. EXAMPLES:: sage: R.<t> = PolynomialRing(QQ) sage: P.<x,y,z> = ProjectiveSpace(FractionField(R), 2) sage: Q = P([t, 3/t^2, 1]) sage: Q.clear_denominators(); Q (t^3 : 3 : t^2) :: sage: R.<x> = PolynomialRing(QQ) sage: K.<w> = NumberField(x^2 - 3) sage: P.<x,y,z> = ProjectiveSpace(K, 2) sage: Q = P([1/w, 3, 0]) sage: Q.clear_denominators(); Q (w : 9 : 0) :: sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: X = P.subscheme(x^2 - y^2); sage: Q = X([1/2, 1/2, 1]); sage: Q.clear_denominators(); Q (1 : 1 : 2) :: sage: PS.<x,y> = ProjectiveSpace(QQ, 1) sage: Q = PS.point([1, 2/3], False); Q (1 : 2/3) sage: Q.clear_denominators(); Q (3 : 2) """ self.scale_by(lcm([t.denominator() for t in self]))
def nregcusps(self): r""" Return the number of orbits of regular cusps for this subgroup. A cusp is regular if we may find a parabolic element generating the stabiliser of that cusp whose eigenvalues are both +1 rather than -1. If G contains -1, all cusps are regular. EXAMPLES:: sage: GammaH(20, [17]).nregcusps() 4 sage: GammaH(20, [17]).nirregcusps() 2 sage: GammaH(3212, [2045, 2773]).nregcusps() 1440 sage: GammaH(3212, [2045, 2773]).nirregcusps() 720 AUTHOR: - Jordi Quer """ if self.is_even(): return self.ncusps() N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in divisors(N) if d**2 <= N]: Nd = lcm(d,N//d) Hd = set([x%Nd for x in H]) if Nd - 1 not in Hd: summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd)) if d**2==N: c = c + summand else: c = c + 2*summand return c
def homogenize(self, n, newvar='h'): r""" Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of the homogenized map is the projective embedding of ``self.domain()`` INPUT: - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space) - ``n`` -- the n-th projective embedding into projective space OUTPUT: - :class:`SchemMorphism_polynomial_projective_space` EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/x^5,y^2]) sage: f.homogenize(2,'z') Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2) :: sage: A.<x,y>=AffineSpace(CC,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(x*y),y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y : z) to (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*y^2,3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: -x1^2 + x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x0*x2 : 3*x1*x2 : x2^2) :: sage: R.<t>=PolynomialRing(ZZ) sage: A.<x,y>=AffineSpace(R,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/y,y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z) """ A = self.domain() B = self.codomain() N = A.ambient_space().dimension_relative() NB = B.ambient_space().dimension_relative() Vars = list(A.ambient_space().variable_names()) + [newvar] S = PolynomialRing(A.base_ring(), Vars) try: l = lcm([self[i].denominator() for i in range(N)]) except Exception: #no lcm l = prod([self[i].denominator() for i in range(N)]) from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring() == RealField() or self.domain().base_ring( ) == ComplexField(): F = [ S(((self[i] * l).numerator())._maxima_().divide( self[i].denominator())[0].sage()) for i in range(N) ] elif isinstance(self.domain().base_ring(), (PolynomialRing_general, MPolynomialRing_generic)): F = [ S(((self[i] * l).numerator())._maxima_().divide( self[i].denominator())[0].sage()) for i in range(N) ] else: F = [S(self[i] * l) for i in range(N)] F.insert(n, S(l)) d = max([F[i].degree() for i in range(N + 1)]) F = [ F[i].homogenize(newvar) * S.gen(N)**(d - F[i].degree()) for i in range(N + 1) ] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(A) == True: from sage.schemes.projective.projective_space import ProjectiveSpace X = ProjectiveSpace(A.base_ring(), NB, Vars) else: X = A.projective_embedding(n).codomain() phi = S.hom(X.ambient_space().gens(), X.ambient_space().coordinate_ring()) F = [phi(f) for f in F] H = Hom(X, X) return (H(F))
def normalize_coordinates(self): """ Scales by 1/gcd of the coordinate functions. Also, scales to clear any denominators from the coefficients. This is done in place. OUTPUT: - None. EXAMPLES:: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([5/4*x^3,5*x*y^2]) sage: f.normalize_coordinates(); f Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^2 : 4*y^2) :: sage: P.<x,y,z>=ProjectiveSpace(GF(7),2) sage: X=P.subscheme(x^2-y^2) sage: H=Hom(X,X) sage: f=H([x^3+x*y^2,x*y^2,x*z^2]) sage: f.normalize_coordinates(); f Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Finite Field of size 7 defined by: x^2 - y^2 Defn: Defined on coordinates by sending (x : y : z) to (2*y^2 : y^2 : z^2) .. NOTE:: - gcd raises an error if the base_ring does not support gcds. """ GCD = gcd(self[0],self[1]) index=2 if self[0].lc()>0 or self[1].lc() >0: neg=0 else: neg=1 N=self.codomain().ambient_space().dimension_relative()+1 while GCD!=1 and index < N: if self[index].lc()>0: neg=0 GCD=gcd(GCD,self[index]) index+=+1 if GCD != 1: R=self.domain().base_ring() if neg==1: self.scale_by(R(-1)/GCD) else: self.scale_by(R(1)/GCD) else: if neg==1: self.scale_by(-1) #clears any denominators from the coefficients LCM = lcm([self[i].denominator() for i in range(N)]) self.scale_by(LCM) #scales by 1/gcd of the coefficients. GCD = gcd([self[i].content() for i in range(N)]) if GCD!=1: self.scale_by(1/GCD)
def pthpowers(self, p, Bound): """ Find the indices of proveably all pth powers in the recurrence sequence bounded by Bound. Let `u_n` be a binary recurrence sequence. A ``p`` th power in `u_n` is a solution to `u_n = y^p` for some integer `y`. There are only finitely many ``p`` th powers in any recurrence sequence [SS]. INPUT: - ``p`` - a rational prime integer (the fixed p in `u_n = y^p`) - ``Bound`` - a natural number (the maximum index `n` in `u_n = y^p` that is checked). OUTPUT: - A list of the indices of all ``p`` th powers less bounded by ``Bound``. If the sequence is degenerate and there are many ``p`` th powers, raises ``ValueError``. EXAMPLES:: sage: R = BinaryRecurrenceSequence(1,1) #the Fibonacci sequence sage: R.pthpowers(2, 10**30) # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06] [0, 1, 2, 12] sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence sage: S.pthpowers(3,10**30) # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30 [0, 1, 2] sage: Q = BinaryRecurrenceSequence(3,3,2,1) sage: Q.pthpowers(11,10**30) # long time (7.5 seconds) [1] If the sequence is degenerate, and there are are no ``p`` th powers, returns `[]`. Otherwise, if there are many ``p`` th powers, raises ``ValueError``. :: sage: T = BinaryRecurrenceSequence(2,0,1,2) sage: T.is_degenerate() True sage: T.is_geometric() True sage: T.pthpowers(7,10**30) Traceback (most recent call last): ... ValueError: The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers. sage: L = BinaryRecurrenceSequence(4,0,2,2) sage: [L(i).factor() for i in xrange(10)] [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17] sage: L.is_quasigeometric() True sage: L.pthpowers(2,10**30) [] NOTE: This function is primarily optimized in the range where ``Bound`` is much larger than ``p``. """ #Thanks to Jesse Silliman for helpful conversations! #Reset the dictionary of good primes, as this depends on p self._PGoodness = {} #Starting lower bound on good primes self._ell = 1 #If the sequence is geometric, then the `n`th term is `a*r^n`. Thus the #property of being a ``p`` th power is periodic mod ``p``. So there are either #no ``p`` th powers if there are none in the first ``p`` terms, or many if there #is at least one in the first ``p`` terms. if self.is_geometric() or self.is_quasigeometric(): no_powers = True for i in xrange(1, 6 * p + 1): if _is_p_power(self(i), p): no_powers = False break if no_powers: if _is_p_power(self.u0, p): return [0] return [] else: raise ValueError, "The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers." #If the sequence is degenerate without being geometric or quasigeometric, there #may be many ``p`` th powers or no ``p`` th powers. elif (self.b**2 + 4 * self.c) == 0: #This is the case if the matrix F is not diagonalizable, ie b^2 +4c = 0, and alpha/beta = 1. alpha = self.b / 2 #In this case, u_n = u_0*alpha^n + (u_1 - u_0*alpha)*n*alpha^(n-1) = alpha^(n-1)*(u_0 +n*(u_1 - u_0*alpha)), #that is, it is a geometric term (alpha^(n-1)) times an arithmetic term (u_0 + n*(u_1-u_0*alpha)). #Look at classes n = k mod p, for k = 1,...,p. for k in xrange(1, p + 1): #The linear equation alpha^(k-1)*u_0 + (k+pm)*(alpha^(k-1)*u1 - u0*alpha^k) #must thus be a pth power. This is a linear equation in m, namely, A + B*m, where A = (alpha**(k - 1) * self.u0 + k * (alpha**(k - 1) * self.u1 - self.u0 * alpha**k)) B = p * (alpha**(k - 1) * self.u1 - self.u0 * alpha**k) #This linear equation represents a pth power iff A is a pth power mod B. if _is_p_power_mod(A, p, B): raise ValueError, "The degenerate binary recurrence sequence has many pth powers." return [] #We find ``p`` th powers using an elementary sieve. Term `u_n` is a ``p`` th #power if and only if it is a ``p`` th power modulo every prime `\\ell`. This condition #gives nontrivial information if ``p`` divides the order of the multiplicative group of #`\\Bold(F)_{\\ell}`, i.e. if `\\ell` is ` 1 \mod{p}`, as then only `1/p` terms are ``p`` th #powers modulo `\\ell``. #Thus, given such an `\\ell`, we get a set of necessary congruences for the index modulo the #the period of the sequence mod `\\ell`. Then we intersect these congruences for many primes #to get a tight list modulo a growing modulus. In order to keep this step manageable, we #only use primes `\\ell` that are have particularly smooth periods. #Some congruences in the list will remain as the modulus grows. If a congruence remains through #7 rounds of increasing the modulus, then we check if this corresponds to a perfect power (if #it does, we add it to our list of indices corresponding to ``p`` th powers). The rest of the congruences #are transient and grow with the modulus. Once the smallest of these is greater than the bound, #the list of known indices corresponding to ``p`` th powers is complete. else: if Bound < 3 * p: powers = [] ell = p + 1 while not is_prime(ell): ell = ell + p F = GF(ell) a0 = F(self.u0) a1 = F(self.u1) #a0 and a1 are variables for terms in sequence bf, cf = F(self.b), F(self.c) for n in xrange(Bound): # n is the index of the a0 #Check whether a0 is a perfect power mod ell if _is_p_power_mod(a0, p, ell): #if a0 is a perfect power mod ell, check if nth term is ppower if _is_p_power(self(n), p): powers.append(n) a0, a1 = a1, bf * a1 + cf * a0 #step up the variables else: powers = [ ] #documents the indices of the sequence that provably correspond to pth powers cong = [ 0 ] #list of necessary congruences on the index for it to correspond to pth powers Possible_count = { } #keeps track of the number of rounds a congruence lasts in cong #These parameters are involved in how we choose primes to increase the modulus qqold = 1 #we believe that we know complete information coming from primes good by qqold M1 = 1 #we have congruences modulo M1, this may not be the tightest list M2 = p #we want to move to have congruences mod M2 qq = 1 #the largest prime power divisor of M1 is qq #This loop ups the modulus. while True: #Try to get good data mod M2 #patience of how long we should search for a "good prime" patience = 0.01 * _estimated_time( lcm(M2, p * next_prime_power(qq)), M1, len(cong), p) tries = 0 #This loop uses primes to get a small set of congruences mod M2. while True: #only proceed if took less than patience time to find the next good prime ell = _next_good_prime(p, self, qq, patience, qqold) if ell: #gather congruence data for the sequence mod ell, which will be mod period(ell) = modu cong1, modu = _find_cong1(p, self, ell) CongNew = [ ] #makes a new list from cong that is now mod M = lcm(M1, modu) instead of M1 M = lcm(M1, modu) for k in xrange(M / M1): for i in cong: CongNew.append(k * M1 + i) cong = set(CongNew) M1 = M killed_something = False #keeps track of when cong1 can rule out a congruence in cong #CRT by hand to gain speed for i in list(cong): if not ( i % modu in cong1 ): #congruence in cong is inconsistent with any in cong1 cong.remove(i) #remove that congruence killed_something = True if M1 == M2: if not killed_something: tries += 1 if tries == 2: #try twice to rule out congruences cong = list(cong) qqold = qq qq = next_prime_power(qq) M2 = lcm(M2, p * qq) break else: qq = next_prime_power(qq) M2 = lcm(M2, p * qq) cong = list(cong) break #Document how long each element of cong has been there for i in cong: if i in Possible_count: Possible_count[i] = Possible_count[i] + 1 else: Possible_count[i] = 1 #Check how long each element has persisted, if it is for at least 7 cycles, #then we check to see if it is actually a perfect power for i in Possible_count: if Possible_count[i] == 7: n = Integer(i) if n < Bound: if _is_p_power(self(n), p): powers.append(n) #check for a contradiction if len(cong) > len(powers): if cong[len(powers)] > Bound: break elif M1 > Bound: break return powers
def homogenize(self,n,newvar='h'): r""" Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of the homogenized map is the projective embedding of ``self.domain()``. The domain and codomain can be homogenized at different coordinates: ``n[0]`` for the domain and ``n[1]`` for the codomain. INPUT: - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space) - ``n`` -- a tuple of nonnegative integers. If ``n`` is an integer, then the two values of the tuple are assumed to be the same. OUTPUT: - :class:`SchemMorphism_polynomial_projective_space` EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/x^5,y^2]) sage: f.homogenize(2,'z') Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2) :: sage: A.<x,y>=AffineSpace(CC,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(x*y),y^2-x]) sage: f.homogenize((2,0),'z') Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y : z) to (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*y^2,3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: -x1^2 + x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x0*x2 : 3*x1*x2 : x2^2) :: sage: R.<t>=PolynomialRing(ZZ) sage: A.<x,y>=AffineSpace(R,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/y,y^2-x]) sage: f.homogenize((2,0),'z') Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z) :: sage: A.<x>=AffineSpace(QQ,1) sage: H=End(A) sage: f=H([x^2-1]) sage: f.homogenize((1,0),'y') Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (y^2 : x^2 - y^2) """ A=self.domain() B=self.codomain() N=A.ambient_space().dimension_relative() NB=B.ambient_space().dimension_relative() #it is possible to homogenize the domain and codomain at different coordinates if isinstance(n,(tuple,list)): ind=tuple(n) else: ind=(n,n) #homogenize the domain Vars=list(A.ambient_space().variable_names()) Vars.insert(ind[0],newvar) S=PolynomialRing(A.base_ring(),Vars) #find the denominators if a rational function try: l=lcm([self[i].denominator() for i in range(N)]) except Exception: #no lcm l=prod([self[i].denominator() for i in range(N)]) from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField(): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] else: F=[S(self[i]*l) for i in range(N)] #homogenize the codomain F.insert(ind[1],S(l)) d=max([F[i].degree() for i in range(N+1)]) F=[F[i].homogenize(newvar)*S.gen(N)**(d-F[i].degree()) for i in range(N+1)] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(A)==True: from sage.schemes.projective.projective_space import ProjectiveSpace X=ProjectiveSpace(A.base_ring(),NB,Vars) else: X=A.projective_embedding(ind[1]).codomain() phi=S.hom(X.ambient_space().gens(),X.ambient_space().coordinate_ring()) F=[phi(f) for f in F] H=Hom(X,X) return(H(F))
def carmichael_lambda(n): r""" Return the Carmichael function of a positive integer ``n``. The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. INPUT: - ``n`` -- a positive integer. OUTPUT: - The Carmichael function of ``n``. ALGORITHM: If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd prime and let `k` be a positive integer. Then `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the prime factorization of `n`. Then .. MATH:: \lambda(n) = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) EXAMPLES: The Carmichael function of all positive integers up to and including 10:: sage: from sage.crypto.util import carmichael_lambda sage: map(carmichael_lambda, [1..10]) [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] The Carmichael function of the first ten primes:: sage: map(carmichael_lambda, primes_first_n(10)) [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] Cases where the Carmichael function is equivalent to the Euler phi function:: sage: carmichael_lambda(2) == euler_phi(2) True sage: carmichael_lambda(4) == euler_phi(4) True sage: p = random_prime(1000, lbound=3, proof=True) sage: k = randint(1, 1000) sage: carmichael_lambda(p^k) == euler_phi(p^k) True A case where `\lambda(n) \neq \varphi(n)`:: sage: k = randint(1, 1000) sage: carmichael_lambda(2^k) == 2^(k - 2) True sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) False Verifying the current implementation of the Carmichael function using another implemenation. The other implementation that we use for verification is an exhaustive search for the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 500) sage: c = carmichael_lambda(n) sage: def coprime(n): ... return [i for i in xrange(n) if gcd(i, n) == 1] ... sage: def znpower(n, k): ... L = coprime(n) ... return map(power_mod, L, [k]*len(L), [n]*len(L)) ... sage: def my_carmichael(n): ... for k in xrange(1, n): ... L = znpower(n, k) ... ones = [1] * len(L) ... T = [L[i] == ones[i] for i in xrange(len(L))] ... if all(T): ... return k ... sage: c == my_carmichael(n) True Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. Here, we verify Carmichael's theorem. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 1000) sage: c = carmichael_lambda(n) sage: ZnZ = IntegerModRing(n) sage: M = ZnZ.list_of_elements_of_multiplicative_group() sage: ones = [1] * len(M) sage: P = [power_mod(a, c, n) for a in M] sage: P == ones True TESTS: The input ``n`` must be a positive integer:: sage: from sage.crypto.util import carmichael_lambda sage: carmichael_lambda(0) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. sage: carmichael_lambda(randint(-10, 0)) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. Bug reported in trac #8283:: sage: from sage.crypto.util import carmichael_lambda sage: type(carmichael_lambda(16)) <type 'sage.rings.integer.Integer'> REFERENCES: .. [Carmichael2010] Carmichael function, http://en.wikipedia.org/wiki/Carmichael_function """ n = Integer(n) # sanity check if n < 1: raise ValueError("Input n must be a positive integer.") L = n.factor() t = [] # first get rid of the prime factor 2 if n & 1 == 0: e = L[0][1] L = L[1:] # now, n = 2**e * L.value() if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) e = e - 1 else: # for k >= 3, lambda(2**k) = 2**(k - 2) e = e - 2 t.append(1 << e) # 2**e # then other prime factors t += [p**(k - 1) * (p - 1) for p, k in L] # finish the job return lcm(t)
def weak_popov_form(M,ascend=True): """ This function computes a weak Popov form of a matrix over a rational function field `k(x)`, for `k` a field. INPUT: - `M` - matrix - `ascend` - if True, rows of output matrix `W` are sorted so degree (= the maximum of the degrees of the elements in the row) increases monotonically, and otherwise degrees decrease. OUTPUT: A 3-tuple `(W,N,d)` consisting of two matrices over `k(x)` and a list of integers: 1. `W` - matrix giving a weak the Popov form of M 2. `N` - matrix representing row operations used to transform `M` to `W` 3. `d` - degree of respective columns of W; the degree of a column is the maximum of the degree of its elements `N` is invertible over `k(x)`. These matrices satisfy the relation `N*M = W`. EXAMPLES: The routine expects matrices over the rational function field, but other examples below show how one can provide matrices over the ring of polynomials (whose quotient field is the rational function field). :: sage: R.<t> = GF(3)['t'] sage: K = FractionField(R) sage: import sage.matrix.matrix_misc sage: sage.matrix.matrix_misc.weak_popov_form(matrix([[(t-1)^2/t],[(t-1)]])) ( [ 0] [ t 2*t + 1] [(2*t + 1)/t], [ 1 2], [-Infinity, 0] ) NOTES: See docstring for weak_popov_form method of matrices for more information. """ # determine whether M has polynomial or rational function coefficients R0 = M.base_ring() from sage.rings.ring import is_Field #Compute the base polynomial ring if is_Field(R0): R = R0.base() else: R = R0 from sage.rings.polynomial.polynomial_ring import is_PolynomialRing if not is_PolynomialRing(R): raise TypeError("the coefficients of M must lie in a univariate polynomial ring") t = R.gen() # calculate least-common denominator of matrix entries and clear # denominators. The result lies in R from sage.rings.arith import lcm from sage.matrix.constructor import matrix from sage.misc.functional import numerator if is_Field(R0): den = lcm([a.denominator() for a in M.list()]) num = matrix([(lambda x : map(numerator, x))(v) for v in map(list,(M*den).rows())]) else: # No need to clear denominators den = R.one_element() num = M r = [list(v) for v in num.rows()] N = matrix(num.nrows(), num.nrows(), R(1)).rows() from sage.rings.infinity import Infinity if M.is_zero(): return (M, matrix(N), [-Infinity for i in range(num.nrows())]) rank = 0 num_zero = 0 while rank != len(r) - num_zero: # construct matrix of leading coefficients v = [] for w in map(list, r): # calculate degree of row (= max of degree of entries) d = max([e.numerator().degree() for e in w]) # extract leading coefficients from current row x = [] for y in w: if y.degree() >= d and d >= 0: x.append(y.coeffs()[d]) else: x.append(0) v.append(x) l = matrix(v) # count number of zero rows in leading coefficient matrix # because they do *not* contribute interesting relations num_zero = 0 for v in l.rows(): is_zero = 1 for w in v: if w != 0: is_zero = 0 if is_zero == 1: num_zero += 1 # find non-trivial relations among the columns of the # leading coefficient matrix kern = l.kernel().basis() rank = num.nrows() - len(kern) # do a row operation if there's a non-trivial relation if not rank == len(r) - num_zero: for rel in kern: # find the row of num involved in the relation and of # maximal degree indices = [] degrees = [] for i in range(len(rel)): if rel[i] != 0: indices.append(i) degrees.append(max([e.degree() for e in r[i]])) # find maximum degree among rows involved in relation max_deg = max(degrees) # check if relation involves non-zero rows if max_deg != -1: i = degrees.index(max_deg) rel /= rel[indices[i]] for j in range(len(indices)): if j != i: # do row operation and record it v = [] for k in range(len(r[indices[i]])): v.append(r[indices[i]][k] + rel[indices[j]] * t**(max_deg-degrees[j]) * r[indices[j]][k]) r[indices[i]] = v v = [] for k in range(len(N[indices[i]])): v.append(N[indices[i]][k] + rel[indices[j]] * t**(max_deg-degrees[j]) * N[indices[j]][k]) N[indices[i]] = v # remaining relations (if any) are no longer valid, # so continue onto next step of algorithm break # sort the rows in order of degree d = [] from sage.rings.all import infinity for i in range(len(r)): d.append(max([e.degree() for e in r[i]])) if d[i] < 0: d[i] = -infinity else: d[i] -= den.degree() for i in range(len(r)): for j in range(i+1,len(r)): if (ascend and d[i] > d[j]) or (not ascend and d[i] < d[j]): (r[i], r[j]) = (r[j], r[i]) (d[i], d[j]) = (d[j], d[i]) (N[i], N[j]) = (N[j], N[i]) # return reduced matrix and operations matrix return (matrix(r)/den, matrix(N), d)
def prove_BSD(E, verbosity=0, two_desc='mwrank', proof=None, secs_hi=5, return_BSD=False): r""" Attempts to prove the Birch and Swinnerton-Dyer conjectural formula for `E`, returning a list of primes `p` for which this function fails to prove BSD(E,p). Here, BSD(E,p) is the statement: "the Birch and Swinnerton-Dyer formula holds up to a rational number coprime to `p`." INPUT: - ``E`` - an elliptic curve - ``verbosity`` - int, how much information about the proof to print. - 0 - print nothing - 1 - print sketch of proof - 2 - print information about remaining primes - ``two_desc`` - string (default ``'mwrank'``), what to use for the two-descent. Options are ``'mwrank', 'simon', 'sage'`` - ``proof`` - bool or None (default: None, see proof.elliptic_curve or sage.structure.proof). If False, this function just immediately returns the empty list. - ``secs_hi`` - maximum number of seconds to try to compute the Heegner index before switching over to trying to compute the Heegner index bound. (Rank 0 only!) - ``return_BSD`` - bool (default: False) whether to return an object which contains information to reconstruct a proof NOTE: When printing verbose output, phrases such as "by Mazur" are referring to the following list of papers: REFERENCES: .. [Cha] B. Cha. Vanishing of some cohomology goups and bounds for the Shafarevich-Tate groups of elliptic curves. J. Number Theory, 111:154- 178, 2005. .. [Jetchev] D. Jetchev. Global divisibility of Heegner points and Tamagawa numbers. Compos. Math. 144 (2008), no. 4, 811--826. .. [Kato] K. Kato. p-adic Hodge theory and values of zeta functions of modular forms. Astérisque, (295):ix, 117-290, 2004. .. [Kolyvagin] V. A. Kolyvagin. On the structure of Shafarevich-Tate groups. Algebraic geometry, 94--121, Lecture Notes in Math., 1479, Springer, Berlin, 1991. .. [LumStein] A. Lum, W. Stein. Verification of the Birch and Swinnerton-Dyer Conjecture for Elliptic Curves with Complex Multiplication (unpublished) .. [Mazur] B. Mazur. Modular curves and the Eisenstein ideal. Inst. Hautes Études Sci. Publ. Math. No. 47 (1977), 33--186 (1978). .. [Rubin] K. Rubin. The "main conjectures" of Iwasawa theory for imaginary quadratic fields. Invent. Math. 103 (1991), no. 1, 25--68. .. [SteinWuthrich] W. Stein and C. Wuthrich. Computations about Tate-Shafarevich groups using Iwasawa theory. http://wstein.org/papers/shark, February 2008. .. [SteinEtAl] G. Grigorov, A. Jorza, S. Patrikis, W. Stein, C. Tarniţǎ. Computational verification of the Birch and Swinnerton-Dyer conjecture for individual elliptic curves. Math. Comp. 78 (2009), no. 268, 2397--2425. EXAMPLES:: sage: EllipticCurve('11a').prove_BSD(verbosity=2) p = 2: True by 2-descent... True for p not in {2, 5} by Kolyvagin. True for p=5 by Mazur [] sage: EllipticCurve('14a').prove_BSD(verbosity=2) p = 2: True by 2-descent True for p not in {2, 3} by Kolyvagin. Remaining primes: p = 3: reducible, not surjective, good ordinary, divides a Tamagawa number (no bounds found) ord_p(#Sha_an) = 0 [3] sage: EllipticCurve('14a').prove_BSD(two_desc='simon') [3] A rank two curve:: sage: E = EllipticCurve('389a') We know nothing with proof=True:: sage: E.prove_BSD() Set of all prime numbers: 2, 3, 5, 7, ... We (think we) know everything with proof=False:: sage: E.prove_BSD(proof=False) [] A curve of rank 0 and prime conductor:: sage: E = EllipticCurve('19a') sage: E.prove_BSD(verbosity=2) p = 2: True by 2-descent... True for p not in {2, 3} by Kolyvagin. True for p=3 by Mazur [] sage: E = EllipticCurve('37a') sage: E.rank() 1 sage: E._EllipticCurve_rational_field__rank {True: 1} sage: E.analytic_rank = lambda : 0 sage: E.prove_BSD() Traceback (most recent call last): ... RuntimeError: It seems that the rank conjecture does not hold for this curve (Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field)! This may be a counterexample to BSD, but is more likely a bug. We test the consistency check for the 2-part of Sha:: sage: E = EllipticCurve('37a') sage: S = E.sha(); S Tate-Shafarevich group for the Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field sage: def foo(use_database): ... return 4 sage: S.an = foo sage: E.prove_BSD() Traceback (most recent call last): ... RuntimeError: Apparent contradiction: 0 <= rank(sha[2]) <= 0, but ord_2(sha_an) = 2 An example with a Tamagawa number at 5:: sage: E = EllipticCurve('123a1') sage: E.prove_BSD(verbosity=2) p = 2: True by 2-descent True for p not in {2, 5} by Kolyvagin. Remaining primes: p = 5: reducible, not surjective, good ordinary, divides a Tamagawa number (no bounds found) ord_p(#Sha_an) = 0 [5] A curve for which 3 divides the order of the Tate-Shafarevich group:: sage: E = EllipticCurve('681b') sage: E.prove_BSD(verbosity=2) # long time p = 2: True by 2-descent... True for p not in {2, 3} by Kolyvagin.... Remaining primes: p = 3: irreducible, surjective, non-split multiplicative (0 <= ord_p <= 2) ord_p(#Sha_an) = 2 [3] A curve for which we need to use ``heegner_index_bound``:: sage: E = EllipticCurve('198b') sage: E.prove_BSD(verbosity=1, secs_hi=1) p = 2: True by 2-descent True for p not in {2, 3} by Kolyvagin. [3] The ``return_BSD`` option gives an object with detailed information about the proof:: sage: E = EllipticCurve('26b') sage: B = E.prove_BSD(return_BSD=True) sage: B.two_tor_rk 0 sage: B.N 26 sage: B.gens [] sage: B.primes [7] sage: B.heegner_indexes {-23: 2} TESTS: This was fixed by trac #8184 and #7575:: sage: EllipticCurve('438e1').prove_BSD(verbosity=1) p = 2: True by 2-descent... True for p not in {2} by Kolyvagin. [] :: sage: E = EllipticCurve('960d1') sage: E.prove_BSD(verbosity=1) # long time (4s on sage.math, 2011) p = 2: True by 2-descent True for p not in {2} by Kolyvagin. [] """ if proof is None: from sage.structure.proof.proof import get_flag proof = get_flag(proof, "elliptic_curve") else: proof = bool(proof) if not proof: return [] from copy import copy BSD = BSD_data() # We replace this curve by the optimal curve, which we can do since # truth of BSD(E,p) is invariant under isogeny. BSD.curve = E.optimal_curve() if BSD.curve.has_cm(): # ensure that CM is by a maximal order non_max_j_invs = [-12288000, 54000, 287496, 16581375] if BSD.curve.j_invariant() in non_max_j_invs: # is this possible for optimal curves? if verbosity > 0: print 'CM by non maximal order: switching curves' for E in BSD.curve.isogeny_class(): if E.j_invariant() not in non_max_j_invs: BSD.curve = E break BSD.update() galrep = BSD.curve.galois_representation() if two_desc=='mwrank': M = mwrank_two_descent_work(BSD.curve, BSD.two_tor_rk) elif two_desc=='simon': M = simon_two_descent_work(BSD.curve, BSD.two_tor_rk) elif two_desc=='sage': M = native_two_isogeny_descent_work(BSD.curve, BSD.two_tor_rk) else: raise NotImplementedError() rank_lower_bd, rank_upper_bd, sha2_lower_bd, sha2_upper_bd, gens = M assert sha2_lower_bd <= sha2_upper_bd if gens is not None: gens = BSD.curve.saturation(gens)[0] if rank_lower_bd > rank_upper_bd: raise RuntimeError("Apparent contradiction: %d <= rank <= %d."%(rank_lower_bd, rank_upper_bd)) BSD.two_selmer_rank = rank_upper_bd + sha2_lower_bd + BSD.two_tor_rk if sha2_upper_bd == sha2_lower_bd: BSD.rank = rank_lower_bd BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd) else: BSD.rank = BSD.curve.rank(use_database=True) sha2_upper_bd -= (BSD.rank - rank_lower_bd) BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd) if verbosity > 0: print "Unable to compute the rank exactly -- used database." if rank_lower_bd > 1: # We do not know BSD(E,p) for even a single p, since it's # an open problem to show that L^r(E,1)/(Reg*Omega) is # rational for any curve with r >= 2. from sage.sets.all import Primes BSD.primes = Primes() if return_BSD: BSD.rank = rank_lower_bd return BSD return BSD.primes if (BSD.sha_an.ord(2) == 0) != (BSD.bounds[2][1] == 0): raise RuntimeError("Apparent contradiction: %d <= rank(sha[2]) <= %d, but ord_2(sha_an) = %d"%(sha2_lower_bd, sha2_upper_bd, BSD.sha_an.ord(2))) if BSD.bounds[2][0] == BSD.sha_an.ord(2) and BSD.sha_an.ord(2) == BSD.bounds[2][1]: if verbosity > 0: print 'p = 2: True by 2-descent' BSD.primes = [] BSD.bounds.pop(2) BSD.proof[2] = ['2-descent'] else: BSD.primes = [2] BSD.proof[2] = [('2-descent',)+BSD.bounds[2]] if len(gens) > rank_lower_bd or \ rank_lower_bd > rank_upper_bd: raise RuntimeError("Something went wrong with 2-descent.") if BSD.rank != len(gens): if BSD.rank != len(BSD.curve._EllipticCurve_rational_field__gens[True]): raise RuntimeError("Could not get generators") gens = BSD.curve._EllipticCurve_rational_field__gens[True] BSD.gens = [BSD.curve.point(x, check=True) for x in gens] if BSD.rank != BSD.curve.analytic_rank(): raise RuntimeError("It seems that the rank conjecture does not hold for this curve (%s)! This may be a counterexample to BSD, but is more likely a bug."%(BSD.curve)) # reduce set of remaining primes to a finite set import signal kolyvagin_primes = [] heegner_index = None if BSD.rank == 0: for D in BSD.curve.heegner_discriminants_list(10): max_height = max(13,BSD.curve.quadratic_twist(D).CPS_height_bound()) heegner_primes = -1 while heegner_primes == -1: if max_height > 21: break heegner_primes, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height) max_height += 1 if isinstance(heegner_primes, list): break if not isinstance(heegner_primes, list): raise RuntimeError("Tried 10 Heegner discriminants, and heegner_index_bound failed each time.") if exact is not False: heegner_index = exact BSD.heegner_indexes[D] = exact else: BSD.heegner_index_upper_bound[D] = max(heegner_primes+[1]) if 2 in heegner_primes: heegner_primes.remove(2) else: # rank 1 for D in BSD.curve.heegner_discriminants_list(10): I = BSD.curve.heegner_index(D) J = I.is_int() if J[0] and J[1]>0: I = J[1] else: J = (2*I).is_int() if J[0] and J[1]>0: I = J[1] else: continue heegner_index = I BSD.heegner_indexes[D] = I break heegner_primes = [p for p in arith.prime_divisors(heegner_index) if p!=2] assert BSD.sha_an in ZZ and BSD.sha_an > 0 if BSD.curve.has_cm(): if BSD.curve.analytic_rank() == 0: if verbosity > 0: print ' p >= 5: true by Rubin' BSD.primes.append(3) else: K = rings.QuadraticField(BSD.curve.cm_discriminant(), 'a') D_K = K.disc() D_E = BSD.curve.discriminant() if len(K.factor(3)) == 1: # 3 does not split in K BSD.primes.append(3) for p in arith.prime_divisors(D_K): if p >= 5: BSD.primes.append(p) for p in arith.prime_divisors(D_E): if p >= 5 and D_K%p and len(K.factor(p)) == 1: # p is inert in K BSD.primes.append(p) for p in heegner_primes: if p >= 5 and D_E%p != 0 and D_K%p != 0 and len(K.factor(p)) == 1: # p is good for E and inert in K kolyvagin_primes.append(p) for p in arith.prime_divisors(BSD.sha_an): if p >= 5 and D_K%p != 0 and len(K.factor(p)) == 1: if BSD.curve.is_good(p): if verbosity > 2 and p in heegner_primes and heegner_index is None: print 'ALERT: Prime p (%d) >= 5 dividing sha_an, good for E, inert in K, in heegner_primes, should not divide the actual Heegner index' # Note that the following check is not entirely # exhaustive, in case there is a p not dividing # the Heegner index in heegner_primes, # for which only an outer bound was computed if p not in heegner_primes: raise RuntimeError("p = %d divides sha_an, is of good reduction for E, inert in K, and does not divide the Heegner index. This may be a counterexample to BSD, but is more likely a bug. %s"%(p,BSD.curve)) if verbosity > 0: print 'True for p not in {%s} by Kolyvagin (via Stein & Lum -- unpublished) and Rubin.'%str(list(set(BSD.primes).union(set(kolyvagin_primes))))[1:-1] BSD.proof['finite'] = copy(BSD.primes) else: # no CM # do some tricks to get to a finite set without calling bound_kolyvagin BSD.primes += [p for p in galrep.non_surjective() if p != 2] for p in heegner_primes: if p not in BSD.primes: BSD.primes.append(p) for p in arith.prime_divisors(BSD.sha_an): if p not in BSD.primes and p != 2: BSD.primes.append(p) if verbosity > 0: s = str(BSD.primes)[1:-1] if 2 not in BSD.primes: if len(s) == 0: s = '2' else: s = '2, '+s print 'True for p not in {' + s + '} by Kolyvagin.' BSD.proof['finite'] = copy(BSD.primes) primes_to_remove = [] for p in BSD.primes: if p == 2: continue if galrep.is_surjective(p) and not BSD.curve.has_additive_reduction(p): if BSD.curve.has_nonsplit_multiplicative_reduction(p): if BSD.rank > 0: continue if p==3: if (not (BSD.curve.is_ordinary(p) and BSD.curve.is_good(p))) and (not BSD.curve.has_split_multiplicative_reduction(p)): continue if BSD.rank > 0: continue if verbosity > 1: print ' p = %d: Trying p_primary_bound'%p p_bound = BSD.Sha.p_primary_bound(p) if BSD.proof.has_key(p): BSD.proof[p].append(('Stein-Wuthrich', p_bound)) else: BSD.proof[p] = [('Stein-Wuthrich', p_bound)] if BSD.sha_an.ord(p) == 0 and p_bound == 0: if verbosity > 0: print 'True for p=%d by Stein-Wuthrich.'%p primes_to_remove.append(p) else: if BSD.bounds.has_key(p): BSD.bounds[p][1] = min(BSD.bounds[p][1], p_bound) else: BSD.bounds[p] = (0, p_bound) print 'Analytic %d-rank is '%p + str(BSD.sha_an.ord(p)) + ', actual %d-rank is at most %d.'%(p, p_bound) print ' by Stein-Wuthrich.\n' for p in primes_to_remove: BSD.primes.remove(p) kolyvagin_primes = [] for p in BSD.primes: if p == 2: continue if galrep.is_surjective(p): kolyvagin_primes.append(p) for p in kolyvagin_primes: BSD.primes.remove(p) # apply other hypotheses which imply Kolyvagin's bound holds bounded_primes = [] D_K = rings.QuadraticField(D, 'a').disc() # Cha's hypothesis for p in BSD.primes: if p == 2: continue if D_K%p != 0 and BSD.N%(p**2) != 0 and galrep.is_irreducible(p): if verbosity > 0: print 'Kolyvagin\'s bound for p = %d applies by Cha.'%p if BSD.proof.has_key(p): BSD.proof[p].append('Cha') else: BSD.proof[p] = ['Cha'] kolyvagin_primes.append(p) # Stein et al. if not BSD.curve.has_cm(): L = arith.lcm([F.torsion_order() for F in BSD.curve.isogeny_class()]) for p in BSD.primes: if p in kolyvagin_primes or p == 2: continue if L%p != 0: if len(arith.prime_divisors(D_K)) == 1: if D_K%p == 0: continue if verbosity > 0: print 'Kolyvagin\'s bound for p = %d applies by Stein et al.'%p kolyvagin_primes.append(p) if BSD.proof.has_key(p): BSD.proof[p].append('Stein et al.') else: BSD.proof[p] = ['Stein et al.'] for p in kolyvagin_primes: if p in BSD.primes: BSD.primes.remove(p) # apply Kolyvagin's bound primes_to_remove = [] for p in kolyvagin_primes: if p == 2: continue if p not in heegner_primes: ord_p_bound = 0 elif heegner_index is not None: # p must divide heegner_index ord_p_bound = 2*heegner_index.ord(p) # Here Jetchev's results apply. m_max = max([BSD.curve.tamagawa_number(q).ord(p) for q in BSD.N.prime_divisors()]) if m_max > 0: if verbosity > 0: print 'Jetchev\'s results apply (at p = %d) with m_max ='%p, m_max if BSD.proof.has_key(p): BSD.proof[p].append(('Jetchev',m_max)) else: BSD.proof[p] = [('Jetchev',m_max)] ord_p_bound -= 2*m_max else: # Heegner index is None for D in BSD.heegner_index_upper_bound: M = BSD.heegner_index_upper_bound[D] ord_p_bound = 0 while p**(ord_p_bound+1) <= M**2: ord_p_bound += 1 # now ord_p_bound is one on I_K!!! ord_p_bound *= 2 # by Kolyvagin, now ord_p_bound is one on #Sha break if BSD.proof.has_key(p): BSD.proof[p].append(('Kolyvagin',ord_p_bound)) else: BSD.proof[p] = [('Kolyvagin',ord_p_bound)] if BSD.sha_an.ord(p) == 0 and ord_p_bound == 0: if verbosity > 0: print 'True for p = %d by Kolyvagin bound'%p primes_to_remove.append(p) elif BSD.sha_an.ord(p) > ord_p_bound: raise RuntimeError("p = %d: ord_p_bound == %d, but sha_an.ord(p) == %d. This appears to be a counterexample to BSD, but is more likely a bug."%(p,ord_p_bound,BSD.sha_an.ord(p))) else: # BSD.sha_an.ord(p) <= ord_p_bound != 0: if BSD.bounds.has_key(p): low = BSD.bounds[p][0] BSD.bounds[p] = (low, min(BSD.bounds[p][1], ord_p_bound)) else: BSD.bounds[p] = (0, ord_p_bound) for p in primes_to_remove: kolyvagin_primes.remove(p) BSD.primes = list( set(BSD.primes).union(set(kolyvagin_primes)) ) # Kato's bound if BSD.rank == 0 and not BSD.curve.has_cm(): L_over_Omega = BSD.curve.lseries().L_ratio() kato_primes = BSD.Sha.bound_kato() primes_to_remove = [] for p in BSD.primes: if p == 2: continue if p not in kato_primes: if verbosity > 0: print 'Kato further implies that #Sha[%d] is trivial.'%p primes_to_remove.append(p) if BSD.proof.has_key(p): BSD.proof[p].append(('Kato',0)) else: BSD.proof[p] = [('Kato',0)] if p not in [2,3] and BSD.N%p != 0: if galrep.is_surjective(p): bd = L_over_Omega.valuation(p) if verbosity > 1: print 'Kato implies that ord_p(#Sha[%d]) <= %d '%(p,bd) if BSD.proof.has_key(p): BSD.proof[p].append(('Kato',bd)) else: BSD.proof[p] = [('Kato',bd)] if BSD.bounds.has_key(p): low = BSD.bounds[p][0] BSD.bounds[p][1] = (low, min(BSD.bounds[p][1], bd)) else: BSD.bounds[p] = (0, bd) for p in primes_to_remove: BSD.primes.remove(p) # Mazur primes_to_remove = [] if BSD.N.is_prime(): for p in BSD.primes: if p == 2: continue if galrep.is_reducible(p): primes_to_remove.append(p) if verbosity > 0: print 'True for p=%s by Mazur'%p for p in primes_to_remove: BSD.primes.remove(p) if BSD.proof.has_key(p): BSD.proof[p].append('Mazur') else: BSD.proof[p] = ['Mazur'] BSD.primes.sort() # Try harder to compute the Heegner index, where it matters if heegner_index is None: if max_height < 18: max_height = 18 for D in BSD.heegner_index_upper_bound: M = BSD.heegner_index_upper_bound[D] for p in kolyvagin_primes: if p not in BSD.primes or p == 3: continue if verbosity > 0: print ' p = %d: Trying harder for Heegner index'%p obt = 0 while p**(BSD.sha_an.ord(p)/2+1) <= M and max_height < 22: if verbosity > 2: print ' trying max_height =', max_height old_bound = M M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_dc) if M == -1: max_height += 1 continue if exact is not False: heegner_index = exact BSD.heegner_indexes[D] = exact M = exact if verbosity > 2: print ' heegner index =', M else: M = max(M+[1]) if verbosity > 2: print ' bound =', M if old_bound == M: obt += 1 if obt == 2: break max_height += 1 BSD.heegner_index_upper_bound[D] = min(M,BSD.heegner_index_upper_bound[D]) low, upp = BSD.bounds[p] expn = 0 while p**(expn+1) <= M: expn += 1 if 2*expn < upp: upp = 2*expn BSD.bounds[p] = (low,upp) if verbosity > 0: print ' got better bound on ord_p =', upp if low == upp: if upp != BSD.sha_an.ord(p): raise RuntimeError else: if verbosity > 0: print ' proven!' BSD.primes.remove(p) break for p in kolyvagin_primes: if p not in BSD.primes or p == 3: continue for D in BSD.curve.heegner_discriminants_list(4): if D in BSD.heegner_index_upper_bound: continue print ' discriminant', D if verbosity > 0: print 'p = %d: Trying discriminant = %d for Heegner index'%(p,D) max_height = max(10, BSD.curve.quadratic_twist(D).CPS_height_bound()) obt = 0 while True: if verbosity > 2: print ' trying max_height =', max_height old_bound = M if p**(BSD.sha_an.ord(p)/2+1) > M or max_height >= 22: break M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_dc) if M == -1: max_height += 1 continue if exact is not False: heegner_index = exact BSD.heegner_indexes[D] = exact M = exact if verbosity > 2: print ' heegner index =', M else: M = max(M+[1]) if verbosity > 2: print ' bound =', M if old_bound == M: obt += 1 if obt == 2: break max_height += 1 BSD.heegner_index_upper_bound[D] = M low, upp = BSD.bounds[p] expn = 0 while p**(expn+1) <= M: expn += 1 if 2*expn < upp: upp = 2*expn BSD.bounds[p] = (low,upp) if verbosity > 0: print ' got better bound =', upp if low == upp: if upp != BSD.sha_an.ord(p): raise RuntimeError else: if verbosity > 0: print ' proven!' BSD.primes.remove(p) break # print some extra information if verbosity > 1: if len(BSD.primes) > 0: print 'Remaining primes:' for p in BSD.primes: s = 'p = ' + str(p) + ': ' if galrep.is_irreducible(p): s += 'ir' s += 'reducible, ' if not galrep.is_surjective(p): s += 'not ' s += 'surjective, ' a_p = BSD.curve.an(p) if BSD.curve.is_good(p): if a_p%p != 0: s += 'good ordinary' else: s += 'good, non-ordinary' else: assert BSD.curve.is_minimal() if a_p == 0: s += 'additive' elif a_p == 1: s += 'split multiplicative' elif a_p == -1: s += 'non-split multiplicative' if BSD.curve.tamagawa_product()%p==0: s += ', divides a Tamagawa number' if BSD.bounds.has_key(p): s += '\n (%d <= ord_p <= %d)'%BSD.bounds[p] else: s += '\n (no bounds found)' s += '\n ord_p(#Sha_an) = %d'%BSD.sha_an.ord(p) if heegner_index is None: may_divide = True for D in BSD.heegner_index_upper_bound: if p > BSD.heegner_index_upper_bound[D] or p not in kolyvagin_primes: may_divide = False if may_divide: s += '\n may divide the Heegner index, for which only a bound was computed' print s if BSD.curve.has_cm(): if BSD.rank == 1: BSD.proof['reason_finite'] = 'Rubin&Kolyvagin' else: BSD.proof['reason_finite'] = 'Rubin' else: BSD.proof['reason_finite'] = 'Kolyvagin' # reduce memory footprint of BSD object: BSD.curve = BSD.curve.label() BSD.Sha = None return BSD if return_BSD else BSD.primes
def weak_popov_form(M, ascend=True): """ This function computes a weak Popov form of a matrix over a rational function field `k(x)`, for `k` a field. INPUT: - `M` - matrix - `ascend` - if True, rows of output matrix `W` are sorted so degree (= the maximum of the degrees of the elements in the row) increases monotonically, and otherwise degrees decrease. OUTPUT: A 3-tuple `(W,N,d)` consisting of two matrices over `k(x)` and a list of integers: 1. `W` - matrix giving a weak the Popov form of M 2. `N` - matrix representing row operations used to transform `M` to `W` 3. `d` - degree of respective columns of W; the degree of a column is the maximum of the degree of its elements `N` is invertible over `k(x)`. These matrices satisfy the relation `N*M = W`. EXAMPLES: The routine expects matrices over the rational function field, but other examples below show how one can provide matrices over the ring of polynomials (whose quotient field is the rational function field). :: sage: R.<t> = GF(3)['t'] sage: K = FractionField(R) sage: import sage.matrix.matrix_misc sage: sage.matrix.matrix_misc.weak_popov_form(matrix([[(t-1)^2/t],[(t-1)]])) ( [ 0] [ t 2*t + 1] [(2*t + 1)/t], [ 1 2], [-Infinity, 0] ) NOTES: See docstring for weak_popov_form method of matrices for more information. """ # determine whether M has polynomial or rational function coefficients R0 = M.base_ring() from sage.rings.ring import is_Field #Compute the base polynomial ring if is_Field(R0): R = R0.base() else: R = R0 from sage.rings.polynomial.polynomial_ring import is_PolynomialRing if not is_PolynomialRing(R): raise TypeError( "the coefficients of M must lie in a univariate polynomial ring") t = R.gen() # calculate least-common denominator of matrix entries and clear # denominators. The result lies in R from sage.rings.arith import lcm from sage.matrix.constructor import matrix from sage.misc.functional import numerator if is_Field(R0): den = lcm([a.denominator() for a in M.list()]) num = matrix([(lambda x: map(numerator, x))(v) for v in map(list, (M * den).rows())]) else: # No need to clear denominators den = R.one_element() num = M r = [list(v) for v in num.rows()] N = matrix(num.nrows(), num.nrows(), R(1)).rows() from sage.rings.infinity import Infinity if M.is_zero(): return (M, matrix(N), [-Infinity for i in range(num.nrows())]) rank = 0 num_zero = 0 while rank != len(r) - num_zero: # construct matrix of leading coefficients v = [] for w in map(list, r): # calculate degree of row (= max of degree of entries) d = max([e.numerator().degree() for e in w]) # extract leading coefficients from current row x = [] for y in w: if y.degree() >= d and d >= 0: x.append(y.coeffs()[d]) else: x.append(0) v.append(x) l = matrix(v) # count number of zero rows in leading coefficient matrix # because they do *not* contribute interesting relations num_zero = 0 for v in l.rows(): is_zero = 1 for w in v: if w != 0: is_zero = 0 if is_zero == 1: num_zero += 1 # find non-trivial relations among the columns of the # leading coefficient matrix kern = l.kernel().basis() rank = num.nrows() - len(kern) # do a row operation if there's a non-trivial relation if not rank == len(r) - num_zero: for rel in kern: # find the row of num involved in the relation and of # maximal degree indices = [] degrees = [] for i in range(len(rel)): if rel[i] != 0: indices.append(i) degrees.append(max([e.degree() for e in r[i]])) # find maximum degree among rows involved in relation max_deg = max(degrees) # check if relation involves non-zero rows if max_deg != -1: i = degrees.index(max_deg) rel /= rel[indices[i]] for j in range(len(indices)): if j != i: # do row operation and record it v = [] for k in range(len(r[indices[i]])): v.append(r[indices[i]][k] + rel[indices[j]] * t**(max_deg - degrees[j]) * r[indices[j]][k]) r[indices[i]] = v v = [] for k in range(len(N[indices[i]])): v.append(N[indices[i]][k] + rel[indices[j]] * t**(max_deg - degrees[j]) * N[indices[j]][k]) N[indices[i]] = v # remaining relations (if any) are no longer valid, # so continue onto next step of algorithm break # sort the rows in order of degree d = [] from sage.rings.all import infinity for i in range(len(r)): d.append(max([e.degree() for e in r[i]])) if d[i] < 0: d[i] = -infinity else: d[i] -= den.degree() for i in range(len(r)): for j in range(i + 1, len(r)): if (ascend and d[i] > d[j]) or (not ascend and d[i] < d[j]): (r[i], r[j]) = (r[j], r[i]) (d[i], d[j]) = (d[j], d[i]) (N[i], N[j]) = (N[j], N[i]) # return reduced matrix and operations matrix return (matrix(r) / den, matrix(N), d)
def green_function(self, G, v, **kwds): r""" Evaluates the local Green's function with respect to the morphism ``G`` at the place ``v`` for ``self`` with ``N`` terms of the series or to within a given error bound. Must be over a number field or order of a number field. Note that this is the absolute local Green's function so is scaled by the degree of the base field. Use ``v=0`` for the archimedean place over `\QQ` or field embedding. Non-archimedean places are prime ideals for number fields or primes over `\QQ`. ALGORITHM: See Exercise 5.29 and Figure 5.6 of ``The Arithmetic of Dynamics Systems``, Joseph H. Silverman, Springer, GTM 241, 2007. INPUT: - ``G`` - a projective morphism whose local Green's function we are computing - ``v`` - non-negative integer. a place, use v=0 for the archimedean place kwds: - ``N`` - positive integer. number of terms of the series to use, default: 10 - ``prec`` - positive integer, float point or p-adic precision, default: 100 - ``error_bound`` - a positive real number OUTPUT: - a real number EXAMPLES:: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y]); sage: Q=P(5,1) sage: f.green_function(Q,0,N=30) 1.6460930159932946233759277576 :: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y]); sage: Q=P(5,1) sage: Q.green_function(f,0,N=200,prec=200) 1.6460930160038721802875250367738355497198064992657997569827 :: sage: K.<w> = QuadraticField(3) sage: P.<x,y> = ProjectiveSpace(K,1) sage: H = Hom(P,P) sage: f = H([17*x^2+1/7*y^2,17*w*x*y]) sage: f.green_function(P.point([w,2],False), K.places()[1]) 1.7236334013785676107373093775 sage: print f.green_function(P([2,1]), K.ideal(7), N=7) 0.48647753726382832627633818586 sage: print f.green_function(P([w,1]), K.ideal(17), error_bound=0.001) -0.70761163353747779889947530309 .. TODO:: Implement general p-adic extensions so that the flip trick can be used for number fields. """ N = kwds.get('N', 10) #Get number of iterates (if entered) err = kwds.get('error_bound', None) #Get error bound (if entered) prec = kwds.get('prec', 100) #Get precision (if entered) R = RealField(prec) localht = R(0) BR = FractionField(self.codomain().base_ring()) GBR = G.change_ring(BR) #so the heights work if not BR in NumberFields(): raise NotImplementedError("Must be over a NumberField or a NumberField Order") #For QQ the 'flip-trick' works better over RR or Qp if isinstance(v, (NumberFieldFractionalIdeal, RingHomomorphism_im_gens)): K = BR elif is_prime(v): K = Qp(v, prec) elif v == 0: K = R v = BR.places(prec=prec)[0] else: raise ValueError("Invalid valuation (=%s) entered."%v) #Coerce all polynomials in F into polynomials with coefficients in K F = G.change_ring(K, False) d = F.degree() dim = F.codomain().ambient_space().dimension_relative() P = self.change_ring(K, False) if err is not None: err = R(err) if not err>0: raise ValueError("Error bound (=%s) must be positive."%err) if G.is_endomorphism() == False: raise NotImplementedError("Error bounds only for endomorphisms") #if doing error estimates, compute needed number of iterates D = (dim + 1) * (d - 1) + 1 #compute upper bound if isinstance(v, RingHomomorphism_im_gens): #archimedean vindex = BR.places(prec=prec).index(v) U = GBR.local_height_arch(vindex, prec=prec) + R(binomial(dim + d, d)).log() else: #non-archimedean U = GBR.local_height(v, prec=prec) #compute lower bound - from explicit polynomials of Nullstellensatz CR = GBR.codomain().ambient_space().coordinate_ring() #.lift() only works over fields I = CR.ideal(GBR.defining_polynomials()) maxh = 0 for k in range(dim + 1): CoeffPolys = (CR.gen(k) ** D).lift(I) Res = 1 h = 1 for poly in CoeffPolys: if poly != 0: for c in poly.coefficients(): Res = lcm(Res, c.denominator()) for poly in CoeffPolys: if poly != 0: if isinstance(v, RingHomomorphism_im_gens): #archimedean if BR == QQ: h = max([(Res*c).local_height_arch(prec=prec) for c in poly.coefficients()]) else: h = max([(Res*c).local_height_arch(vindex, prec=prec) for c in poly.coefficients()]) else: #non-archimedean h = max([c.local_height(v, prec=prec) for c in poly.coefficients()]) if h > maxh: maxh=h if isinstance(v, RingHomomorphism_im_gens): #archimedean L = R(Res / ((dim + 1) * binomial(dim + D - d, D - d) * maxh)).log().abs() else: #non-archimedean L = R(1 / maxh).log().abs() C = max([U, L]) if C != 0: N = R(C/(err)).log(d).abs().ceil() else: #we just need log||P||_v N=1 #START GREEN FUNCTION CALCULATION if isinstance(v, RingHomomorphism_im_gens): #embedding for archimedean local height for i in range(N+1): Pv = [ (v(t).abs()) for t in P ] m = -1 #compute the maximum absolute value of entries of a, and where it occurs for n in range(dim + 1): if Pv[n] > m: j = n m = Pv[n] # add to sum for the Green's function localht += ((1/R(d))**R(i)) * (R(m).log()) #get the next iterate if i < N: P.scale_by(1/P[j]) P = F(P, False) return (1/BR.absolute_degree()) * localht #else - prime or prime ideal for non-archimedean for i in range(N + 1): if BR == QQ: Pv = [ R(K(t).abs()) for t in P ] else: Pv = [ R(t.abs_non_arch(v)) for t in P ] m = -1 #compute the maximum absolute value of entries of a, and where it occurs for n in range(dim + 1): if Pv[n] > m: j = n m = Pv[n] # add to sum for the Green's function localht += ((1/R(d))**R(i)) * (R(m).log()) #get the next iterate if i < N: P.scale_by(1/P[j]) P = F(P, False) return (1/BR.absolute_degree()) * localht
def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algorithm=None): r""" Return a list of pairs ``(root,multiplicity)`` of roots of the polynomial ``p``. If the argument ``multiplicities`` is set to ``False`` then return the list of roots. .. SEEALSO:: :meth:`_factor_univariate_polynomial` EXAMPLES:: sage: R.<x> = PolynomialRing(GF(5),'x') sage: K = GF(5).algebraic_closure('t') sage: sorted((x^6 - 1).roots(K,multiplicities=False)) [1, 4, 2*t2 + 1, 2*t2 + 2, 3*t2 + 3, 3*t2 + 4] sage: ((K.gen(2)*x - K.gen(3))**2).roots(K) [(3*t6^5 + 2*t6^4 + 2*t6^2 + 3, 2)] sage: for _ in xrange(10): ....: p = R.random_element(degree=randint(2,8)) ....: for r in p.roots(K, multiplicities=False): ....: assert p(r).is_zero(), "r={} is not a root of p={}".format(r,p) """ from sage.rings.arith import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # first build a polynomial over some finite field coeffs = [v.as_finite_field_element(minimal=True) for v in p.list()] l = lcm([c[0].degree() for c in coeffs]) F, phi = self.subfield(l) P = p.parent().change_ring(F) new_coeffs = [self.inclusion(c[0].degree(), l)(c[1]) for c in coeffs] polys = [(g, m, l, phi) for g, m in P(new_coeffs).factor()] roots = [] # a list of pair (root,multiplicity) while polys: g, m, l, phi = polys.pop() if g.degree() == 1: # found a root r = phi(-g.constant_coefficient()) roots.append((r, m)) else: # look at the extension of degree g.degree() which contains at # least one root of g ll = l * g.degree() psi = self.inclusion(l, ll) FF, pphi = self.subfield(ll) # note: there is no coercion from the l-th subfield to the ll-th # subfield. The line below does the conversion manually. g = PolynomialRing(FF, 'x')(map(psi, g)) polys.extend((gg, m, ll, pphi) for gg, _ in g.factor()) if multiplicities: return roots else: return [r[0] for r in roots]
def period(self, m): """ Return the period of the binary recurrence sequence modulo an integer ``m``. If `n_1` is congruent to `n_2` modulu ``period(m)``, then `u_{n_1}` is is congruent to `u_{n_2}` modulo ``m``. INPUT: - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated). OUTPUT: - The integer (the period of the sequence modulo m) EXAMPLES: If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS06]). :: sage: R = BinaryRecurrenceSequence(1,1) sage: R.period(31) 30 sage: [R(i) % 4 for i in xrange(12)] [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1] sage: R.period(4) 6 This function works for degenerate sequences as well. :: sage: S = BinaryRecurrenceSequence(2,0,1,2) sage: S.is_degenerate() True sage: S.is_geometric() True sage: [S(i) % 17 for i in xrange(16)] [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9] sage: S.period(17) 8 Note: the answer is cached. """ #If we have already computed the period mod m, then we return the stored value. if m in self._period_dict: return self._period_dict[m] else: R = Integers(m) A = matrix(R, [[0,1],[self.c,self.b]]) w = matrix(R, [[self.u0],[self.u1]]) Fac = list(m.factor()) Periods = {} #To compute the period mod m, we compute the least integer n such that A^n*w == w. This necessarily #divides the order of A as a matrix in GL_2(Z/mZ). #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm. #To compute the period mod p^e, we first compute the order mod p. Then the period mod p^e #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to #the identity mod p is of order (p^{e-1})^4. So we compute the period mod p^e by successively #multiplying the period mod p by powers of p. for i in Fac: p = i[0]; e = i[1] #first compute the period mod p if p in self._period_dict: perp = self._period_dict[p] else: F = A.change_ring(GF(p)) v = w.change_ring(GF(p)) FF = F**(p-1) p1fac = list((p-1).factor()) #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1). #The order divides p-1 if it is diagaonalizable. In any case, det(F^(p-1))=1, #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]]. #The order of the subgroup of matrices of this form is p, so the order must divide #p(p-1) -- in fact it must be a multiple of p. If this is not the case, then the #order divides (p-1)(p+1). As the period divides the order of the matrix in GL_2(F_p), #these conditions hold for the period as well. #check if the order divides (p-1) if FF*v == v: M = p-1 Mfac = p1fac #check if the trace is 2, then the order is a multiple of p dividing p*(p-1) elif (FF).trace() == 2: M = p-1 Mfac = p1fac F = F**p #replace F by F^p as now we only need to determine the factor dividing (p-1) #otherwise it will divide (p+1)(p-1) else : M = (p+1)*(p-1) p2fac = list((p+1).factor()) #factor the (p+1) and (p-1) terms seperately and then combine for speed Mfac_dic = {} for i in list(p1fac + p2fac): if i[0] not in Mfac_dic: Mfac_dic[i[0]] = i[1] else : Mfac_dic[i[0]] = Mfac_dic[i[0]] + i[1] Mfac = [(i,Mfac_dic[i]) for i in Mfac_dic] #Now use a fast order algorithm to compute the period. We know that the period divides #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors. As #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j). After #all factors have been iterated over, the result is the period mod p. Mfac = list(Mfac) C=[] #expand the list of prime factors so every factor is with multiplicity 1 for i in xrange(len(Mfac)): for j in xrange(Mfac[i][1]): C.append(Mfac[i][0]) Mfac = C n = M for i in Mfac: b = Integer(n/i) if F**b*v == v: n = b perp = n #Now compute the period mod p^e by steping up by multiples of p F = A.change_ring(Integers(p**e)) v = w.change_ring(Integers(p**e)) FF = F**perp if FF*v == v: perpe = perp else : tries = 0 while True: tries += 1 FF = FF**p if FF*v == v: perpe = perp*p**tries break Periods[p] = perpe #take the lcm of the periods mod all distinct primes dividing m period = 1 for p in Periods: period = lcm(Periods[p],period) self._period_dict[m] = period #cache the period mod m return period
def pthpowers(self, p, Bound): """ Find the indices of proveably all pth powers in the recurrence sequence bounded by Bound. Let `u_n` be a binary recurrence sequence. A ``p`` th power in `u_n` is a solution to `u_n = y^p` for some integer `y`. There are only finitely many ``p`` th powers in any recurrence sequence [SS]. INPUT: - ``p`` - a rational prime integer (the fixed p in `u_n = y^p`) - ``Bound`` - a natural number (the maximum index `n` in `u_n = y^p` that is checked). OUTPUT: - A list of the indices of all ``p`` th powers less bounded by ``Bound``. If the sequence is degenerate and there are many ``p`` th powers, raises ``ValueError``. EXAMPLES:: sage: R = BinaryRecurrenceSequence(1,1) #the Fibonacci sequence sage: R.pthpowers(2, 10**30) # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06] [0, 1, 2, 12] sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence sage: S.pthpowers(3,10**30) # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30 [0, 1, 2] sage: Q = BinaryRecurrenceSequence(3,3,2,1) sage: Q.pthpowers(11,10**30) # long time (7.5 seconds) [1] If the sequence is degenerate, and there are are no ``p`` th powers, returns `[]`. Otherwise, if there are many ``p`` th powers, raises ``ValueError``. :: sage: T = BinaryRecurrenceSequence(2,0,1,2) sage: T.is_degenerate() True sage: T.is_geometric() True sage: T.pthpowers(7,10**30) Traceback (most recent call last): ... ValueError: The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers. sage: L = BinaryRecurrenceSequence(4,0,2,2) sage: [L(i).factor() for i in xrange(10)] [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17] sage: L.is_quasigeometric() True sage: L.pthpowers(2,10**30) [] NOTE: This function is primarily optimized in the range where ``Bound`` is much larger than ``p``. """ #Thanks to Jesse Silliman for helpful conversations! #Reset the dictionary of good primes, as this depends on p self._PGoodness = {} #Starting lower bound on good primes self._ell = 1 #If the sequence is geometric, then the `n`th term is `a*r^n`. Thus the #property of being a ``p`` th power is periodic mod ``p``. So there are either #no ``p`` th powers if there are none in the first ``p`` terms, or many if there #is at least one in the first ``p`` terms. if self.is_geometric() or self.is_quasigeometric(): no_powers = True for i in xrange(1,6*p+1): if _is_p_power(self(i), p) : no_powers = False break if no_powers: if _is_p_power(self.u0,p): return [0] return [] else : raise ValueError, "The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers." #If the sequence is degenerate without being geometric or quasigeometric, there #may be many ``p`` th powers or no ``p`` th powers. elif (self.b**2+4*self.c) == 0 : #This is the case if the matrix F is not diagonalizable, ie b^2 +4c = 0, and alpha/beta = 1. alpha = self.b/2 #In this case, u_n = u_0*alpha^n + (u_1 - u_0*alpha)*n*alpha^(n-1) = alpha^(n-1)*(u_0 +n*(u_1 - u_0*alpha)), #that is, it is a geometric term (alpha^(n-1)) times an arithmetic term (u_0 + n*(u_1-u_0*alpha)). #Look at classes n = k mod p, for k = 1,...,p. for k in xrange(1,p+1): #The linear equation alpha^(k-1)*u_0 + (k+pm)*(alpha^(k-1)*u1 - u0*alpha^k) #must thus be a pth power. This is a linear equation in m, namely, A + B*m, where A = (alpha**(k-1)*self.u0 + k*(alpha**(k-1)*self.u1 - self.u0*alpha**k)) B = p*(alpha**(k-1)*self.u1 - self.u0*alpha**k) #This linear equation represents a pth power iff A is a pth power mod B. if _is_p_power_mod(A, p, B): raise ValueError, "The degenerate binary recurrence sequence has many pth powers." return [] #We find ``p`` th powers using an elementary sieve. Term `u_n` is a ``p`` th #power if and only if it is a ``p`` th power modulo every prime `\\ell`. This condition #gives nontrivial information if ``p`` divides the order of the multiplicative group of #`\\Bold(F)_{\\ell}`, i.e. if `\\ell` is ` 1 \mod{p}`, as then only `1/p` terms are ``p`` th #powers modulo `\\ell``. #Thus, given such an `\\ell`, we get a set of necessary congruences for the index modulo the #the period of the sequence mod `\\ell`. Then we intersect these congruences for many primes #to get a tight list modulo a growing modulus. In order to keep this step manageable, we #only use primes `\\ell` that are have particularly smooth periods. #Some congruences in the list will remain as the modulus grows. If a congruence remains through #7 rounds of increasing the modulus, then we check if this corresponds to a perfect power (if #it does, we add it to our list of indices corresponding to ``p`` th powers). The rest of the congruences #are transient and grow with the modulus. Once the smallest of these is greater than the bound, #the list of known indices corresponding to ``p`` th powers is complete. else: if Bound < 3 * p : powers = [] ell = p + 1 while not is_prime(ell): ell = ell + p F = GF(ell) a0 = F(self.u0); a1 = F(self.u1) #a0 and a1 are variables for terms in sequence bf, cf = F(self.b), F(self.c) for n in xrange(Bound): # n is the index of the a0 #Check whether a0 is a perfect power mod ell if _is_p_power_mod(a0, p, ell) : #if a0 is a perfect power mod ell, check if nth term is ppower if _is_p_power(self(n), p): powers.append(n) a0, a1 = a1, bf*a1 + cf*a0 #step up the variables else : powers = [] #documents the indices of the sequence that provably correspond to pth powers cong = [0] #list of necessary congruences on the index for it to correspond to pth powers Possible_count = {} #keeps track of the number of rounds a congruence lasts in cong #These parameters are involved in how we choose primes to increase the modulus qqold = 1 #we believe that we know complete information coming from primes good by qqold M1 = 1 #we have congruences modulo M1, this may not be the tightest list M2 = p #we want to move to have congruences mod M2 qq = 1 #the largest prime power divisor of M1 is qq #This loop ups the modulus. while True: #Try to get good data mod M2 #patience of how long we should search for a "good prime" patience = 0.01 * _estimated_time(lcm(M2,p*next_prime_power(qq)), M1, len(cong), p) tries = 0 #This loop uses primes to get a small set of congruences mod M2. while True: #only proceed if took less than patience time to find the next good prime ell = _next_good_prime(p, self, qq, patience, qqold) if ell: #gather congruence data for the sequence mod ell, which will be mod period(ell) = modu cong1, modu = _find_cong1(p, self, ell) CongNew = [] #makes a new list from cong that is now mod M = lcm(M1, modu) instead of M1 M = lcm(M1, modu) for k in xrange(M/M1): for i in cong: CongNew.append(k*M1+i) cong = set(CongNew) M1 = M killed_something = False #keeps track of when cong1 can rule out a congruence in cong #CRT by hand to gain speed for i in list(cong): if not (i % modu in cong1): #congruence in cong is inconsistent with any in cong1 cong.remove(i) #remove that congruence killed_something = True if M1 == M2: if not killed_something: tries += 1 if tries == 2: #try twice to rule out congruences cong = list(cong) qqold = qq qq = next_prime_power(qq) M2 = lcm(M2,p*qq) break else : qq = next_prime_power(qq) M2 = lcm(M2,p*qq) cong = list(cong) break #Document how long each element of cong has been there for i in cong: if i in Possible_count: Possible_count[i] = Possible_count[i] + 1 else : Possible_count[i] = 1 #Check how long each element has persisted, if it is for at least 7 cycles, #then we check to see if it is actually a perfect power for i in Possible_count: if Possible_count[i] == 7: n = Integer(i) if n < Bound: if _is_p_power(self(n),p): powers.append(n) #check for a contradiction if len(cong) > len(powers): if cong[len(powers)] > Bound: break elif M1 > Bound: break return powers
def primes_of_bad_reduction(self, check=True): r""" Determines the primes of bad reduction for a map `self: \mathbb{P}^N \to \mathbb{P}^N` defined over `\ZZ` or `\QQ`. If ``check`` is ``True``, each prime is verified to be of bad reduction. ALGORITHM: `p` is a prime of bad reduction if and only if the defining polynomials of self have a common zero. Or stated another way, `p` is a prime of bad reducion if and only if the radical of the ideal defined by the defining polynomials of self is not `(x_0,x_1,\ldots,x_N)`. This happens if and only if some power of each `x_i` is not in the ideal defined by the defining polynomials of self. This last condition is what is checked. The lcm of the coefficients of the monomials `x_i` in a groebner basis is computed. This may return extra primes. INPUT: - ``check`` -- Boolean (optional - default: ``True``) OUTPUT: - a list of integer primes. EXAMPLES:: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([1/3*x^2+1/2*y^2,y^2]) sage: print f.primes_of_bad_reduction() [2, 3] :: sage: P.<x,y,z,w>=ProjectiveSpace(QQ,3) sage: H=Hom(P,P) sage: f=H([12*x*z-7*y^2,31*x^2-y^2,26*z^2,3*w^2-z*w]) sage: f.primes_of_bad_reduction() [2, 3, 7, 13, 31] :: This is an example where check=False returns extra primes sage: P.<x,y,z>=ProjectiveSpace(ZZ,2) sage: H=Hom(P,P) sage: f=H([3*x*y^2 + 7*y^3 - 4*y^2*z + 5*z^3, -5*x^3 + x^2*y + y^3 + 2*x^2*z, -2*x^2*y + x*y^2 + y^3 - 4*y^2*z + x*z^2]) sage: f.primes_of_bad_reduction(False) [2, 5, 37, 2239, 304432717] sage: f.primes_of_bad_reduction() [5, 37, 2239, 304432717] """ if self.base_ring() != ZZ and self.base_ring() != QQ: raise TypeError("Must be ZZ or QQ") from sage.schemes.projective.projective_space import is_ProjectiveSpace if is_ProjectiveSpace(self.domain())==False or is_ProjectiveSpace(self.codomain())==False: raise NotImplementedError R=self.coordinate_ring() F=self._polys if R.base_ring().is_field(): J=R.ideal(F) else: S=PolynomialRing(R.base_ring().fraction_field(),R.gens(),R.ngens()) J=S.ideal([S.coerce(F[i]) for i in range(R.ngens())]) if J.dimension()>0: raise TypeError("Not a morphism.") #normalize to coefficients in the ring not the fraction field. F=[F[i]*lcm([F[j].denominator() for j in range(len(F))]) for i in range(len(F))] #move the ideal to the ring of integers if R.base_ring().is_field(): S=PolynomialRing(R.base_ring().ring_of_integers(),R.gens(),R.ngens()) F=[F[i].change_ring(R.base_ring().ring_of_integers()) for i in range(len(F))] J=S.ideal(F) else: J=R.ideal(F) GB=J.groebner_basis() badprimes=[] #get the primes dividing the coefficients of the monomials x_i^k_i for i in range(len(GB)): LT=GB[i].lt().degrees() power=0 for j in range(R.ngens()): if LT[j]!=0: power+=1 if power==1: badprimes=badprimes+GB[i].lt().coefficients()[0].support() badprimes=list(set(badprimes)) badprimes.sort() #check to return only the truly bad primes if check==True: index=0 while index < len(badprimes): #figure out which primes are really bad primes... S=PolynomialRing(GF(badprimes[index]),R.gens(),R.ngens()) J=S.ideal([S.coerce(F[j]) for j in range(R.ngens())]) if J.dimension()==0: badprimes.pop(index) else: index+=1 return(badprimes)
def modform_cusp_info(calc, S, l, precLimit): """ This goes through all the cusps and compares the space given by `(f|R)[S]` with the space of Elliptic modular forms expansion at those cusps. """ assert l == S.det() assert list(calc.curlS) == [S] D = calc.D HermWeight = calc.HermWeight reducedCurlFSize = calc.matrixColumnCount herm_modform_fe_expannsion = FreeModule(QQ, reducedCurlFSize) if not Integer(l).is_squarefree(): # The calculation of the cusp expansion space takes very long here, thus # we skip them for now. return None for cusp in Gamma0(l).cusps(): if cusp == Infinity: continue M = cusp_matrix(cusp) try: gamma, R, tM = solveR(M, S, space=CurlO(D)) except Exception: print (M, S) raise R.set_immutable() # for caching, we need it hashable herm_modforms = herm_modform_fe_expannsion.echelonized_basis_matrix().transpose() ell_R_denom, ell_R_order, M_R = calcMatrixTrans(calc, R) CycloDegree_R = CyclotomicField(ell_R_order).degree() print "M_R[0] nrows, ell_R_denom, ell_R_order, Cyclo degree:", \ M_R[0].nrows(), ell_R_denom, ell_R_order, CycloDegree_R # The maximum precision we can use is M_R[0].nrows(). # However, that can be quite huge (e.g. 600). ce_prec = min(precLimit, M_R[0].nrows()) ce = cuspExpansions(level=l, weight=2*HermWeight, prec=ce_prec) ell_M_denom, ell_M = ce.expansion_at(SL2Z(M)) print "ell_M_denom, ell_M nrows:", ell_M_denom, ell_M.nrows() ell_M_order = ell_R_order # not sure here. just try the one from R. toCyclPowerBase would fail if this doesn't work # CyclotomicField(l / prod(l.prime_divisors())) should also work. # Transform to same denom. denom_lcm = int(lcm(ell_R_denom, ell_M_denom)) ell_M = addRows(ell_M, denom_lcm / ell_M_denom) M_R = [addRows(M_R_i, denom_lcm / ell_R_denom) for M_R_i in M_R] ell_R_denom = ell_M_denom = denom_lcm print "new denom:", denom_lcm assert ell_R_denom == ell_M_denom # ell_M rows are the elliptic FE. M_R[i] columns are the elliptic FE. # We expect that M_R gives a higher precision for the ell FE. I'm not sure # if this is always true but we expect it here (maybe not needed, though). print "precision of M_R[0], ell_M, wanted:", M_R[0].nrows(), ell_M.ncols(), ce_prec assert ell_M.ncols() >= ce_prec prec = min(M_R[0].nrows(), ell_M.ncols()) # cut to have same precision M_R = [M_R_i[:prec,:] for M_R_i in M_R] ell_M = ell_M[:,:prec] assert ell_M.ncols() == M_R[0].nrows() == prec print "M_R[0] rank, herm rank, mult rank:", \ M_R[0].rank(), herm_modforms.rank(), (M_R[0] * herm_modforms).rank() ell_R = [M_R_i * herm_modforms for M_R_i in M_R] # I'm not sure on this. Seems to be true and it simplifies things in the following. assert ell_M_order <= ell_R_order, "{0}".format((ell_M_order, ell_R_order)) assert ell_R_order % ell_M_order == 0, "{0}".format((ell_M_order, ell_R_order)) # Transform to same Cyclomotic Field in same power base. ell_M2 = toCyclPowerBase(ell_M, ell_M_order) ell_R2 = toLowerCyclBase(ell_R, ell_R_order, ell_M_order) # We must work with the matrix. maybe we should transform hf_M instead to a # higher order field instead, if this ever fails (I'm not sure). assert ell_R2 is not None assert len(ell_M2) == len(ell_R2) # They should have the same power base & same degree now. print "ell_M2[0], ell_R2[0] rank with order %i:" % ell_M_order, ell_M2[0].rank(), ell_R2[0].rank() assert len(M_R) == len(ell_M2) for i in range(len(ell_M2)): ell_M_space = ell_M2[i].row_space() ell_R_space = ell_R2[i].column_space() merged = ell_M_space.intersection(ell_R_space) herm_modform_fe_expannsion_Ci = M_R[i].solve_right( merged.basis_matrix().transpose() ) herm_modform_fe_expannsion_Ci_module = herm_modform_fe_expannsion_Ci.column_module() herm_modform_fe_expannsion_Ci_module += M_R[i].right_kernel() extra_check_on_herm_superspace( vs=herm_modform_fe_expannsion_Ci_module, D=D, B_cF=calc.B_cF, HermWeight=HermWeight ) herm_modform_fe_expannsion = herm_modform_fe_expannsion.intersection( herm_modform_fe_expannsion_Ci_module ) print "power", i, merged.dimension(), herm_modform_fe_expannsion_Ci_module.dimension(), \ herm_modform_fe_expannsion.dimension() current_dimension = herm_modform_fe_expannsion.dimension() return herm_modform_fe_expannsion
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. ALGORITHM: Uses Denis Simon's GP script ``qfparam``. See ``sage.quadratic_forms.qfsolve.qfparam``. EXAMPLES :: sage: c = Conic([1,1,-1]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Rational Field To: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 Defn: Defined on coordinates by sending (x : y) to (2*x*y : -x^2 + y^2 : x^2 + y^2), Scheme morphism: From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 To: Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to (1/2*x : 1/2*y + 1/2*z)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, 7*x^2 + y^2, -2*y^2], [-1/2*x, -1/2*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + 2*y^2 + z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self) if point == None: point = self.rational_point() point = Sequence(point) Q = PolynomialRing(QQ, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() M = self.symmetric_matrix() M *= lcm([ t.denominator() for t in M.list() ]) par1 = qfparam(M, point) B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)]) # self is in the image of B and does not lie on a line, # hence B is invertible A = B.inverse() par2 = [sum([A[i,j]*gens[j] for j in range(3)]) for i in [1,0]] par = ([Q(pol(x/y)*y**2) for pol in par1], par2) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
def homogenize(self,n,newvar='h'): r""" Return the homogenization of ``self``. If ``self.domain()`` is a subscheme, the domain of the homogenized map is the projective embedding of ``self.domain()`` INPUT: - ``newvar`` -- the name of the homogenization variable (only used when ``self.domain()`` is affine space) - ``n`` -- the n-th projective embedding into projective space OUTPUT: - :class:`SchemMorphism_polynomial_projective_space` EXAMPLES:: sage: A.<x,y>=AffineSpace(ZZ,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/x^5,y^2]) sage: f.homogenize(2,'z') Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2*z^5 - 2*z^7 : x^5*y^2 : x^5*z^2) :: sage: A.<x,y>=AffineSpace(CC,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/(x*y),y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y : z) to (x*y*z^2 : x^2*z^2 + (-2.00000000000000)*z^4 : x*y^3 - x^2*y*z) :: sage: A.<x,y>=AffineSpace(ZZ,2) sage: X=A.subscheme([x-y^2]) sage: H=Hom(X,X) sage: f=H([9*y^2,3*y]) sage: f.homogenize(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Integer Ring defined by: -x1^2 + x0*x2 Defn: Defined on coordinates by sending (x0 : x1 : x2) to (9*x0*x2 : 3*x1*x2 : x2^2) :: sage: R.<t>=PolynomialRing(ZZ) sage: A.<x,y>=AffineSpace(R,2) sage: H=Hom(A,A) sage: f=H([(x^2-2)/y,y^2-x]) sage: f.homogenize(0,'z') Scheme endomorphism of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (y*z^2 : x^2*z + (-2)*z^3 : y^3 - x*y*z) """ A=self.domain() B=self.codomain() N=A.ambient_space().dimension_relative() NB=B.ambient_space().dimension_relative() Vars=list(A.ambient_space().variable_names())+[newvar] S=PolynomialRing(A.base_ring(),Vars) try: l=lcm([self[i].denominator() for i in range(N)]) except Exception: #no lcm l=prod([self[i].denominator() for i in range(N)]) from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField(): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)): F=[S(((self[i]*l).numerator())._maxima_().divide(self[i].denominator())[0].sage()) for i in range(N)] else: F=[S(self[i]*l) for i in range(N)] F.insert(n,S(l)) d=max([F[i].degree() for i in range(N+1)]) F=[F[i].homogenize(newvar)*S.gen(N)**(d-F[i].degree()) for i in range(N+1)] from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(A)==True: from sage.schemes.projective.projective_space import ProjectiveSpace X=ProjectiveSpace(A.base_ring(),NB,Vars) else: X=A.projective_embedding(n).codomain() phi=S.hom(X.ambient_space().gens(),X.ambient_space().coordinate_ring()) F=[phi(f) for f in F] H=Hom(X,X) return(H(F))
def normal_cone(self): r""" Return the (closure of the) normal cone of the triangulation. Recall that a regular triangulation is one that equals the "crease lines" of a convex piecewise-linear function. This support function is not unique, for example, you can scale it by a positive constant. The set of all piecewise-linear functions with fixed creases forms an open cone. This cone can be interpreted as the cone of normal vectors at a point of the secondary polytope, which is why we call it normal cone. See [GKZ]_ Section 7.1 for details. OUTPUT: The closure of the normal cone. The `i`-th entry equals the value of the piecewise-linear function at the `i`-th point of the configuration. For an irregular triangulation, the normal cone is empty. In this case, a single point (the origin) is returned. EXAMPLES:: sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal') sage: triangulation (<0,1,3>, <0,2,3>) sage: N = triangulation.normal_cone(); N 4-d cone in 4-d lattice sage: N.rays() (-1, 0, 0, 0), ( 1, 0, 1, 0), (-1, 0, -1, 0), ( 1, 0, 0, -1), (-1, 0, 0, 1), ( 1, 1, 0, 0), (-1, -1, 0, 0) in Ambient free module of rank 4 over the principal ideal domain Integer Ring sage: N.dual().rays() (-1, 1, 1, -1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring TESTS:: sage: polytopes.n_simplex(2).triangulate().normal_cone() 3-d cone in 3-d lattice sage: _.dual().is_trivial() True """ if not self.point_configuration().base_ring().is_subring(QQ): raise NotImplementedError( 'Only base rings ZZ and QQ are supported') from sage.libs.ppl import Variable, Constraint, Constraint_System, Linear_Expression, C_Polyhedron from sage.matrix.constructor import matrix from sage.misc.misc import uniq from sage.rings.arith import lcm pc = self.point_configuration() cs = Constraint_System() for facet in self.interior_facets(): s0, s1 = self._boundary_simplex_dictionary()[facet] p = set(s0).difference(facet).pop() q = set(s1).difference(facet).pop() origin = pc.point(p).reduced_affine_vector() base_indices = [i for i in s0 if i != p] base = matrix([ pc.point(i).reduced_affine_vector() - origin for i in base_indices ]) sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin) relation = [0] * pc.n_points() relation[p] = sum(sol) - 1 relation[q] = 1 for i, base_i in enumerate(base_indices): relation[base_i] = -sol[i] rel_denom = lcm([QQ(r).denominator() for r in relation]) relation = [ZZ(r * rel_denom) for r in relation] ex = Linear_Expression(relation, 0) cs.insert(ex >= 0) from sage.modules.free_module import FreeModule ambient = FreeModule(ZZ, self.point_configuration().n_points()) if cs.empty(): cone = C_Polyhedron(ambient.dimension(), 'universe') else: cone = C_Polyhedron(cs) from sage.geometry.cone import _Cone_from_PPL return _Cone_from_PPL(cone, lattice=ambient)
def prove_BSD(E, verbosity=0, two_desc='mwrank', proof=None, secs_hi=5, return_BSD=False): r""" Attempts to prove the Birch and Swinnerton-Dyer conjectural formula for `E`, returning a list of primes `p` for which this function fails to prove BSD(E,p). Here, BSD(E,p) is the statement: "the Birch and Swinnerton-Dyer formula holds up to a rational number coprime to `p`." INPUT: - ``E`` - an elliptic curve - ``verbosity`` - int, how much information about the proof to print. - 0 - print nothing - 1 - print sketch of proof - 2 - print information about remaining primes - ``two_desc`` - string (default ``'mwrank'``), what to use for the two-descent. Options are ``'mwrank', 'simon', 'sage'`` - ``proof`` - bool or None (default: None, see proof.elliptic_curve or sage.structure.proof). If False, this function just immediately returns the empty list. - ``secs_hi`` - maximum number of seconds to try to compute the Heegner index before switching over to trying to compute the Heegner index bound. (Rank 0 only!) - ``return_BSD`` - bool (default: False) whether to return an object which contains information to reconstruct a proof NOTE: When printing verbose output, phrases such as "by Mazur" are referring to the following list of papers: REFERENCES: .. [Cha] B. Cha. Vanishing of some cohomology goups and bounds for the Shafarevich-Tate groups of elliptic curves. J. Number Theory, 111:154- 178, 2005. .. [Jetchev] D. Jetchev. Global divisibility of Heegner points and Tamagawa numbers. Compos. Math. 144 (2008), no. 4, 811--826. .. [Kato] K. Kato. p-adic Hodge theory and values of zeta functions of modular forms. Astérisque, (295):ix, 117-290, 2004. .. [Kolyvagin] V. A. Kolyvagin. On the structure of Shafarevich-Tate groups. Algebraic geometry, 94--121, Lecture Notes in Math., 1479, Springer, Berlin, 1991. .. [LumStein] A. Lum, W. Stein. Verification of the Birch and Swinnerton-Dyer Conjecture for Elliptic Curves with Complex Multiplication (unpublished) .. [Mazur] B. Mazur. Modular curves and the Eisenstein ideal. Inst. Hautes Études Sci. Publ. Math. No. 47 (1977), 33--186 (1978). .. [Rubin] K. Rubin. The "main conjectures" of Iwasawa theory for imaginary quadratic fields. Invent. Math. 103 (1991), no. 1, 25--68. .. [SteinWuthrich] W. Stein and C. Wuthrich. Computations about Tate-Shafarevich groups using Iwasawa theory. http://wstein.org/papers/shark, February 2008. .. [SteinEtAl] G. Grigorov, A. Jorza, S. Patrikis, W. Stein, C. Tarniţǎ. Computational verification of the Birch and Swinnerton-Dyer conjecture for individual elliptic curves. Math. Comp. 78 (2009), no. 268, 2397--2425. EXAMPLES:: sage: EllipticCurve('11a').prove_BSD(verbosity=2) p = 2: True by 2-descent... True for p not in {2, 5} by Kolyvagin. True for p=5 by Mazur [] sage: EllipticCurve('14a').prove_BSD(verbosity=2) p = 2: True by 2-descent True for p not in {2, 3} by Kolyvagin. Remaining primes: p = 3: reducible, not surjective, good ordinary, divides a Tamagawa number (no bounds found) ord_p(#Sha_an) = 0 [3] sage: EllipticCurve('14a').prove_BSD(two_desc='simon') [3] A rank two curve:: sage: E = EllipticCurve('389a') We know nothing with proof=True:: sage: E.prove_BSD() Set of all prime numbers: 2, 3, 5, 7, ... We (think we) know everything with proof=False:: sage: E.prove_BSD(proof=False) [] A curve of rank 0 and prime conductor:: sage: E = EllipticCurve('19a') sage: E.prove_BSD(verbosity=2) p = 2: True by 2-descent... True for p not in {2, 3} by Kolyvagin. True for p=3 by Mazur [] sage: E = EllipticCurve('37a') sage: E.rank() 1 sage: E._EllipticCurve_rational_field__rank {True: 1} sage: E.analytic_rank = lambda : 0 sage: E.prove_BSD() Traceback (most recent call last): ... RuntimeError: It seems that the rank conjecture does not hold for this curve (Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field)! This may be a counterexample to BSD, but is more likely a bug. We test the consistency check for the 2-part of Sha:: sage: E = EllipticCurve('37a') sage: S = E.sha(); S Tate-Shafarevich group for the Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field sage: def foo(use_database): ... return 4 sage: S.an = foo sage: E.prove_BSD() Traceback (most recent call last): ... RuntimeError: Apparent contradiction: 0 <= rank(sha[2]) <= 0, but ord_2(sha_an) = 2 An example with a Tamagawa number at 5:: sage: E = EllipticCurve('123a1') sage: E.prove_BSD(verbosity=2) p = 2: True by 2-descent True for p not in {2, 5} by Kolyvagin. Remaining primes: p = 5: reducible, not surjective, good ordinary, divides a Tamagawa number (no bounds found) ord_p(#Sha_an) = 0 [5] A curve for which 3 divides the order of the Tate-Shafarevich group:: sage: E = EllipticCurve('681b') sage: E.prove_BSD(verbosity=2) # long time p = 2: True by 2-descent... True for p not in {2, 3} by Kolyvagin.... Remaining primes: p = 3: irreducible, surjective, non-split multiplicative (0 <= ord_p <= 2) ord_p(#Sha_an) = 2 [3] A curve for which we need to use ``heegner_index_bound``:: sage: E = EllipticCurve('198b') sage: E.prove_BSD(verbosity=1, secs_hi=1) p = 2: True by 2-descent True for p not in {2, 3} by Kolyvagin. [3] The ``return_BSD`` option gives an object with detailed information about the proof:: sage: E = EllipticCurve('26b') sage: B = E.prove_BSD(return_BSD=True) sage: B.two_tor_rk 0 sage: B.N 26 sage: B.gens [] sage: B.primes [7] sage: B.heegner_indexes {-23: 2} TESTS: This was fixed by trac #8184 and #7575:: sage: EllipticCurve('438e1').prove_BSD(verbosity=1) p = 2: True by 2-descent... True for p not in {2} by Kolyvagin. [] :: sage: E = EllipticCurve('960d1') sage: E.prove_BSD(verbosity=1) # long time (4s on sage.math, 2011) p = 2: True by 2-descent True for p not in {2} by Kolyvagin. [] """ if proof is None: from sage.structure.proof.proof import get_flag proof = get_flag(proof, "elliptic_curve") else: proof = bool(proof) if not proof: return [] from copy import copy BSD = BSD_data() # We replace this curve by the optimal curve, which we can do since # truth of BSD(E,p) is invariant under isogeny. BSD.curve = E.optimal_curve() if BSD.curve.has_cm(): # ensure that CM is by a maximal order non_max_j_invs = [-12288000, 54000, 287496, 16581375] if BSD.curve.j_invariant() in non_max_j_invs: # is this possible for optimal curves? if verbosity > 0: print 'CM by non maximal order: switching curves' for E in BSD.curve.isogeny_class(use_tuple=False): if E.j_invariant() not in non_max_j_invs: BSD.curve = E break BSD.update() galrep = BSD.curve.galois_representation() if two_desc=='mwrank': M = mwrank_two_descent_work(BSD.curve, BSD.two_tor_rk) elif two_desc=='simon': M = simon_two_descent_work(BSD.curve, BSD.two_tor_rk) elif two_desc=='sage': M = native_two_isogeny_descent_work(BSD.curve, BSD.two_tor_rk) else: raise NotImplementedError() rank_lower_bd, rank_upper_bd, sha2_lower_bd, sha2_upper_bd, gens = M assert sha2_lower_bd <= sha2_upper_bd if gens is not None: gens = BSD.curve.saturation(gens)[0] if rank_lower_bd > rank_upper_bd: raise RuntimeError("Apparent contradiction: %d <= rank <= %d."%(rank_lower_bd, rank_upper_bd)) BSD.two_selmer_rank = rank_upper_bd + sha2_lower_bd + BSD.two_tor_rk if sha2_upper_bd == sha2_lower_bd: BSD.rank = rank_lower_bd BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd) else: BSD.rank = BSD.curve.rank(use_database=True) sha2_upper_bd -= (BSD.rank - rank_lower_bd) BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd) if verbosity > 0: print "Unable to compute the rank exactly -- used database." if rank_lower_bd > 1: # We do not know BSD(E,p) for even a single p, since it's # an open problem to show that L^r(E,1)/(Reg*Omega) is # rational for any curve with r >= 2. from sage.sets.all import Primes BSD.primes = Primes() if return_BSD: BSD.rank = rank_lower_bd return BSD return BSD.primes if (BSD.sha_an.ord(2) == 0) != (BSD.bounds[2][1] == 0): raise RuntimeError("Apparent contradiction: %d <= rank(sha[2]) <= %d, but ord_2(sha_an) = %d"%(sha2_lower_bd, sha2_upper_bd, BSD.sha_an.ord(2))) if BSD.bounds[2][0] == BSD.sha_an.ord(2) and BSD.sha_an.ord(2) == BSD.bounds[2][1]: if verbosity > 0: print 'p = 2: True by 2-descent' BSD.primes = [] BSD.bounds.pop(2) BSD.proof[2] = ['2-descent'] else: BSD.primes = [2] BSD.proof[2] = [('2-descent',)+BSD.bounds[2]] if len(gens) > rank_lower_bd or \ rank_lower_bd > rank_upper_bd: raise RuntimeError("Something went wrong with 2-descent.") if BSD.rank != len(gens): if BSD.rank != len(BSD.curve._EllipticCurve_rational_field__gens[True]): raise RuntimeError("Could not get generators") gens = BSD.curve._EllipticCurve_rational_field__gens[True] BSD.gens = [BSD.curve.point(x, check=True) for x in gens] if BSD.rank != BSD.curve.analytic_rank(): raise RuntimeError("It seems that the rank conjecture does not hold for this curve (%s)! This may be a counterexample to BSD, but is more likely a bug."%(BSD.curve)) # reduce set of remaining primes to a finite set import signal kolyvagin_primes = [] heegner_index = None if BSD.rank == 0: for D in BSD.curve.heegner_discriminants_list(10): max_height = max(13,BSD.curve.quadratic_twist(D).CPS_height_bound()) heegner_primes = -1 while heegner_primes == -1: if max_height > 21: break heegner_primes, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height) max_height += 1 if isinstance(heegner_primes, list): break if not isinstance(heegner_primes, list): raise RuntimeError("Tried 10 Heegner discriminants, and heegner_index_bound failed each time.") if exact is not False: heegner_index = exact BSD.heegner_indexes[D] = exact else: BSD.heegner_index_upper_bound[D] = max(heegner_primes+[1]) if 2 in heegner_primes: heegner_primes.remove(2) else: # rank 1 for D in BSD.curve.heegner_discriminants_list(10): I = BSD.curve.heegner_index(D) J = I.is_int() if J[0] and J[1]>0: I = J[1] else: J = (2*I).is_int() if J[0] and J[1]>0: I = J[1] else: continue heegner_index = I BSD.heegner_indexes[D] = I break heegner_primes = [p for p in arith.prime_divisors(heegner_index) if p!=2] assert BSD.sha_an in ZZ and BSD.sha_an > 0 if BSD.curve.has_cm(): if BSD.curve.analytic_rank() == 0: if verbosity > 0: print ' p >= 5: true by Rubin' BSD.primes.append(3) else: K = rings.QuadraticField(BSD.curve.cm_discriminant(), 'a') D_K = K.disc() D_E = BSD.curve.discriminant() if len(K.factor(3)) == 1: # 3 does not split in K BSD.primes.append(3) for p in arith.prime_divisors(D_K): if p >= 5: BSD.primes.append(p) for p in arith.prime_divisors(D_E): if p >= 5 and D_K%p and len(K.factor(p)) == 1: # p is inert in K BSD.primes.append(p) for p in heegner_primes: if p >= 5 and D_E%p != 0 and D_K%p != 0 and len(K.factor(p)) == 1: # p is good for E and inert in K kolyvagin_primes.append(p) for p in arith.prime_divisors(BSD.sha_an): if p >= 5 and D_K%p != 0 and len(K.factor(p)) == 1: if BSD.curve.is_good(p): if verbosity > 2 and p in heegner_primes and heegner_index is None: print 'ALERT: Prime p (%d) >= 5 dividing sha_an, good for E, inert in K, in heegner_primes, should not divide the actual Heegner index' # Note that the following check is not entirely # exhaustive, in case there is a p not dividing # the Heegner index in heegner_primes, # for which only an outer bound was computed if p not in heegner_primes: raise RuntimeError("p = %d divides sha_an, is of good reduction for E, inert in K, and does not divide the Heegner index. This may be a counterexample to BSD, but is more likely a bug. %s"%(p,BSD.curve)) if verbosity > 0: print 'True for p not in {%s} by Kolyvagin (via Stein & Lum -- unpublished) and Rubin.'%str(list(set(BSD.primes).union(set(kolyvagin_primes))))[1:-1] BSD.proof['finite'] = copy(BSD.primes) else: # no CM # do some tricks to get to a finite set without calling bound_kolyvagin BSD.primes += [p for p in galrep.non_surjective() if p != 2] for p in heegner_primes: if p not in BSD.primes: BSD.primes.append(p) for p in arith.prime_divisors(BSD.sha_an): if p not in BSD.primes and p != 2: BSD.primes.append(p) if verbosity > 0: s = str(BSD.primes)[1:-1] if 2 not in BSD.primes: if len(s) == 0: s = '2' else: s = '2, '+s print 'True for p not in {' + s + '} by Kolyvagin.' BSD.proof['finite'] = copy(BSD.primes) primes_to_remove = [] for p in BSD.primes: if p == 2: continue if galrep.is_surjective(p) and not BSD.curve.has_additive_reduction(p): if BSD.curve.has_nonsplit_multiplicative_reduction(p): if BSD.rank > 0: continue if p==3: if (not (BSD.curve.is_ordinary(p) and BSD.curve.is_good(p))) and (not BSD.curve.has_split_multiplicative_reduction(p)): continue if BSD.rank > 0: continue if verbosity > 1: print ' p = %d: Trying p_primary_bound'%p p_bound = BSD.Sha.p_primary_bound(p) if BSD.proof.has_key(p): BSD.proof[p].append(('Stein-Wuthrich', p_bound)) else: BSD.proof[p] = [('Stein-Wuthrich', p_bound)] if BSD.sha_an.ord(p) == 0 and p_bound == 0: if verbosity > 0: print 'True for p=%d by Stein-Wuthrich.'%p primes_to_remove.append(p) else: if BSD.bounds.has_key(p): BSD.bounds[p][1] = min(BSD.bounds[p][1], p_bound) else: BSD.bounds[p] = (0, p_bound) print 'Analytic %d-rank is '%p + str(BSD.sha_an.ord(p)) + ', actual %d-rank is at most %d.'%(p, p_bound) print ' by Stein-Wuthrich.\n' for p in primes_to_remove: BSD.primes.remove(p) kolyvagin_primes = [] for p in BSD.primes: if p == 2: continue if galrep.is_surjective(p): kolyvagin_primes.append(p) for p in kolyvagin_primes: BSD.primes.remove(p) # apply other hypotheses which imply Kolyvagin's bound holds bounded_primes = [] D_K = rings.QuadraticField(D, 'a').disc() # Cha's hypothesis for p in BSD.primes: if p == 2: continue if D_K%p != 0 and BSD.N%(p**2) != 0 and galrep.is_irreducible(p): if verbosity > 0: print 'Kolyvagin\'s bound for p = %d applies by Cha.'%p if BSD.proof.has_key(p): BSD.proof[p].append('Cha') else: BSD.proof[p] = ['Cha'] kolyvagin_primes.append(p) # Stein et al. if not BSD.curve.has_cm(): L = arith.lcm([F.torsion_order() for F in BSD.curve.isogeny_class(use_tuple=False)]) for p in BSD.primes: if p in kolyvagin_primes or p == 2: continue if L%p != 0: if len(arith.prime_divisors(D_K)) == 1: if D_K%p == 0: continue if verbosity > 0: print 'Kolyvagin\'s bound for p = %d applies by Stein et al.'%p kolyvagin_primes.append(p) if BSD.proof.has_key(p): BSD.proof[p].append('Stein et al.') else: BSD.proof[p] = ['Stein et al.'] for p in kolyvagin_primes: if p in BSD.primes: BSD.primes.remove(p) # apply Kolyvagin's bound primes_to_remove = [] for p in kolyvagin_primes: if p == 2: continue if p not in heegner_primes: ord_p_bound = 0 elif heegner_index is not None: # p must divide heegner_index ord_p_bound = 2*heegner_index.ord(p) # Here Jetchev's results apply. m_max = max([BSD.curve.tamagawa_number(q).ord(p) for q in BSD.N.prime_divisors()]) if m_max > 0: if verbosity > 0: print 'Jetchev\'s results apply (at p = %d) with m_max ='%p, m_max if BSD.proof.has_key(p): BSD.proof[p].append(('Jetchev',m_max)) else: BSD.proof[p] = [('Jetchev',m_max)] ord_p_bound -= 2*m_max else: # Heegner index is None for D in BSD.heegner_index_upper_bound: M = BSD.heegner_index_upper_bound[D] ord_p_bound = 0 while p**(ord_p_bound+1) <= M**2: ord_p_bound += 1 # now ord_p_bound is one on I_K!!! ord_p_bound *= 2 # by Kolyvagin, now ord_p_bound is one on #Sha break if BSD.proof.has_key(p): BSD.proof[p].append(('Kolyvagin',ord_p_bound)) else: BSD.proof[p] = [('Kolyvagin',ord_p_bound)] if BSD.sha_an.ord(p) == 0 and ord_p_bound == 0: if verbosity > 0: print 'True for p = %d by Kolyvagin bound'%p primes_to_remove.append(p) elif BSD.sha_an.ord(p) > ord_p_bound: raise RuntimeError("p = %d: ord_p_bound == %d, but sha_an.ord(p) == %d. This appears to be a counterexample to BSD, but is more likely a bug."%(p,ord_p_bound,BSD.sha_an.ord(p))) else: # BSD.sha_an.ord(p) <= ord_p_bound != 0: if BSD.bounds.has_key(p): low = BSD.bounds[p][0] BSD.bounds[p] = (low, min(BSD.bounds[p][1], ord_p_bound)) else: BSD.bounds[p] = (0, ord_p_bound) for p in primes_to_remove: kolyvagin_primes.remove(p) BSD.primes = list( set(BSD.primes).union(set(kolyvagin_primes)) ) # Kato's bound if BSD.rank == 0 and not BSD.curve.has_cm(): L_over_Omega = BSD.curve.lseries().L_ratio() kato_primes = BSD.Sha.bound_kato() primes_to_remove = [] for p in BSD.primes: if p == 2: continue if p not in kato_primes: if verbosity > 0: print 'Kato further implies that #Sha[%d] is trivial.'%p primes_to_remove.append(p) if BSD.proof.has_key(p): BSD.proof[p].append(('Kato',0)) else: BSD.proof[p] = [('Kato',0)] if p not in [2,3] and BSD.N%p != 0: if galrep.is_surjective(p): bd = L_over_Omega.valuation(p) if verbosity > 1: print 'Kato implies that ord_p(#Sha[%d]) <= %d '%(p,bd) if BSD.proof.has_key(p): BSD.proof[p].append(('Kato',bd)) else: BSD.proof[p] = [('Kato',bd)] if BSD.bounds.has_key(p): low = BSD.bounds[p][0] BSD.bounds[p][1] = (low, min(BSD.bounds[p][1], bd)) else: BSD.bounds[p] = (0, bd) for p in primes_to_remove: BSD.primes.remove(p) # Mazur primes_to_remove = [] if BSD.N.is_prime(): for p in BSD.primes: if p == 2: continue if galrep.is_reducible(p): primes_to_remove.append(p) if verbosity > 0: print 'True for p=%s by Mazur'%p for p in primes_to_remove: BSD.primes.remove(p) if BSD.proof.has_key(p): BSD.proof[p].append('Mazur') else: BSD.proof[p] = ['Mazur'] BSD.primes.sort() # Try harder to compute the Heegner index, where it matters if heegner_index is None: if max_height < 18: max_height = 18 for D in BSD.heegner_index_upper_bound: M = BSD.heegner_index_upper_bound[D] for p in kolyvagin_primes: if p not in BSD.primes or p == 3: continue if verbosity > 0: print ' p = %d: Trying harder for Heegner index'%p obt = 0 while p**(BSD.sha_an.ord(p)/2+1) <= M and max_height < 22: if verbosity > 2: print ' trying max_height =', max_height old_bound = M M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_dc) if M == -1: max_height += 1 continue if exact is not False: heegner_index = exact BSD.heegner_indexes[D] = exact M = exact if verbosity > 2: print ' heegner index =', M else: M = max(M+[1]) if verbosity > 2: print ' bound =', M if old_bound == M: obt += 1 if obt == 2: break max_height += 1 BSD.heegner_index_upper_bound[D] = min(M,BSD.heegner_index_upper_bound[D]) low, upp = BSD.bounds[p] expn = 0 while p**(expn+1) <= M: expn += 1 if 2*expn < upp: upp = 2*expn BSD.bounds[p] = (low,upp) if verbosity > 0: print ' got better bound on ord_p =', upp if low == upp: if upp != BSD.sha_an.ord(p): raise RuntimeError else: if verbosity > 0: print ' proven!' BSD.primes.remove(p) break for p in kolyvagin_primes: if p not in BSD.primes or p == 3: continue for D in BSD.curve.heegner_discriminants_list(4): if D in BSD.heegner_index_upper_bound: continue print ' discriminant', D if verbosity > 0: print 'p = %d: Trying discriminant = %d for Heegner index'%(p,D) max_height = max(10, BSD.curve.quadratic_twist(D).CPS_height_bound()) obt = 0 while True: if verbosity > 2: print ' trying max_height =', max_height old_bound = M if p**(BSD.sha_an.ord(p)/2+1) > M or max_height >= 22: break M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_dc) if M == -1: max_height += 1 continue if exact is not False: heegner_index = exact BSD.heegner_indexes[D] = exact M = exact if verbosity > 2: print ' heegner index =', M else: M = max(M+[1]) if verbosity > 2: print ' bound =', M if old_bound == M: obt += 1 if obt == 2: break max_height += 1 BSD.heegner_index_upper_bound[D] = M low, upp = BSD.bounds[p] expn = 0 while p**(expn+1) <= M: expn += 1 if 2*expn < upp: upp = 2*expn BSD.bounds[p] = (low,upp) if verbosity > 0: print ' got better bound =', upp if low == upp: if upp != BSD.sha_an.ord(p): raise RuntimeError else: if verbosity > 0: print ' proven!' BSD.primes.remove(p) break # print some extra information if verbosity > 1: if len(BSD.primes) > 0: print 'Remaining primes:' for p in BSD.primes: s = 'p = ' + str(p) + ': ' if galrep.is_irreducible(p): s += 'ir' s += 'reducible, ' if not galrep.is_surjective(p): s += 'not ' s += 'surjective, ' a_p = BSD.curve.an(p) if BSD.curve.is_good(p): if a_p%p != 0: s += 'good ordinary' else: s += 'good, non-ordinary' else: assert BSD.curve.is_minimal() if a_p == 0: s += 'additive' elif a_p == 1: s += 'split multiplicative' elif a_p == -1: s += 'non-split multiplicative' if BSD.curve.tamagawa_product()%p==0: s += ', divides a Tamagawa number' if BSD.bounds.has_key(p): s += '\n (%d <= ord_p <= %d)'%BSD.bounds[p] else: s += '\n (no bounds found)' s += '\n ord_p(#Sha_an) = %d'%BSD.sha_an.ord(p) if heegner_index is None: may_divide = True for D in BSD.heegner_index_upper_bound: if p > BSD.heegner_index_upper_bound[D] or p not in kolyvagin_primes: may_divide = False if may_divide: s += '\n may divide the Heegner index, for which only a bound was computed' print s if BSD.curve.has_cm(): if BSD.rank == 1: BSD.proof['reason_finite'] = 'Rubin&Kolyvagin' else: BSD.proof['reason_finite'] = 'Rubin' else: BSD.proof['reason_finite'] = 'Kolyvagin' # reduce memory footprint of BSD object: BSD.curve = BSD.curve.label() BSD.Sha = None return BSD if return_BSD else BSD.primes
def has_rational_point(self, point = False, obstruction = False, algorithm = 'default', read_cache = True): r""" Returns True if and only if ``self`` has a point defined over `\QQ`. If ``point`` and ``obstruction`` are both False (default), then the output is a boolean ``out`` saying whether ``self`` has a rational point. If ``point`` or ``obstruction`` is True, then the output is a pair ``(out, S)``, where ``out`` is as above and the following holds: - if ``point`` is True and ``self`` has a rational point, then ``S`` is a rational point, - if ``obstruction`` is True and ``self`` has no rational point, then ``S`` is a prime such that no rational point exists over the completion at ``S`` or `-1` if no point exists over `\RR`. Points and obstructions are cached, whenever they are found. Cached information is used if and only if ``read_cache`` is True. ALGORITHM: The parameter ``algorithm`` specifies the algorithm to be used: - ``'qfsolve'`` -- Use Denis Simon's pari script Qfsolve (see ``sage.quadratic_forms.qfsolve.qfsolve``) - ``'rnfisnorm'`` -- Use PARI's function rnfisnorm (cannot be combined with ``obstruction = True``) - ``'local'`` -- Check if a local solution exists for all primes and infinite places of `\QQ` and apply the Hasse principle (cannot be combined with ``point = True``) - ``'default'`` -- Use ``'qfsolve'`` - ``'magma'`` (requires Magma to be installed) -- delegates the task to the Magma computer algebra system. EXAMPLES:: sage: C = Conic(QQ, [1, 2, -3]) sage: C.has_rational_point(point = True) (True, (1 : 1 : 1)) sage: D = Conic(QQ, [1, 3, -5]) sage: D.has_rational_point(point = True) (False, 3) sage: P.<X,Y,Z> = QQ[] sage: E = Curve(X^2 + Y^2 + Z^2); E Projective Conic Curve over Rational Field defined by X^2 + Y^2 + Z^2 sage: E.has_rational_point(obstruction = True) (False, -1) The following would not terminate quickly with ``algorithm = 'rnfisnorm'`` :: sage: C = Conic(QQ, [1, 113922743, -310146482690273725409]) sage: C.has_rational_point(point = True) (True, (-76842858034579/5424 : -5316144401/5424 : 1)) sage: C.has_rational_point(algorithm = 'local', read_cache = False) True sage: C.has_rational_point(point=True, algorithm='magma', read_cache=False) # optional - magma (True, (30106379962113/7913 : 12747947692/7913 : 1)) TESTS: Create a bunch of conics over `\QQ`, check if ``has_rational_point`` runs without errors and returns consistent answers for all algorithms. Check if all points returned are valid. :: sage: l = Sequence(cartesian_product_iterator([[-1, 0, 1] for i in range(6)])) sage: c = [Conic(QQ, a) for a in l if a != [0,0,0] and a != (0,0,0,0,0,0)] sage: d = [] sage: d = [[C]+[C.has_rational_point(algorithm = algorithm, read_cache = False, obstruction = (algorithm != 'rnfisnorm'), point = (algorithm != 'local')) for algorithm in ['local', 'qfsolve', 'rnfisnorm']] for C in c[::10]] # long time: 7 seconds sage: assert all([e[1][0] == e[2][0] and e[1][0] == e[3][0] for e in d]) sage: assert all([e[0].defining_polynomial()(Sequence(e[i][1])) == 0 for e in d for i in [2,3] if e[1][0]]) """ if read_cache: if self._rational_point is not None: if point or obstruction: return True, self._rational_point else: return True if self._local_obstruction is not None: if point or obstruction: return False, self._local_obstruction else: return False if (not point) and self._finite_obstructions == [] and \ self._infinite_obstructions == []: if obstruction: return True, None return True if self.has_singular_point(): if point: return self.has_singular_point(point = True) if obstruction: return True, None return True if algorithm == 'default' or algorithm == 'qfsolve': M = self.symmetric_matrix() M *= lcm([ t.denominator() for t in M.list() ]) pt = qfsolve(M) if pt in ZZ: if self._local_obstruction == None: self._local_obstruction = pt if point or obstruction: return False, pt return False pt = self.point([pt[0], pt[1], pt[2]]) if point or obstruction: return True, pt return True ret = ProjectiveConic_number_field.has_rational_point( \ self, point = point, \ obstruction = obstruction, \ algorithm = algorithm, \ read_cache = read_cache) if point or obstruction: if is_RingHomomorphism(ret[1]): ret[1] = -1 return ret
def has_rational_point(self, point = False, obstruction = False, algorithm = 'default', read_cache = True): r""" Returns True if and only if ``self`` has a point defined over `\QQ`. If ``point`` and ``obstruction`` are both False (default), then the output is a boolean ``out`` saying whether ``self`` has a rational point. If ``point`` or ``obstruction`` is True, then the output is a pair ``(out, S)``, where ``out`` is as above and the following holds: - if ``point`` is True and ``self`` has a rational point, then ``S`` is a rational point, - if ``obstruction`` is True and ``self`` has no rational point, then ``S`` is a prime such that no rational point exists over the completion at ``S`` or `-1` if no point exists over `\RR`. Points and obstructions are cached, whenever they are found. Cached information is used if and only if ``read_cache`` is True. ALGORITHM: The parameter ``algorithm`` specifies the algorithm to be used: - ``'qfsolve'`` -- Use Denis Simon's GP script ``qfsolve`` (see ``sage.quadratic_forms.qfsolve.qfsolve``) - ``'rnfisnorm'`` -- Use PARI's function rnfisnorm (cannot be combined with ``obstruction = True``) - ``'local'`` -- Check if a local solution exists for all primes and infinite places of `\QQ` and apply the Hasse principle (cannot be combined with ``point = True``) - ``'default'`` -- Use ``'qfsolve'`` - ``'magma'`` (requires Magma to be installed) -- delegates the task to the Magma computer algebra system. EXAMPLES:: sage: C = Conic(QQ, [1, 2, -3]) sage: C.has_rational_point(point = True) (True, (1 : 1 : 1)) sage: D = Conic(QQ, [1, 3, -5]) sage: D.has_rational_point(point = True) (False, 3) sage: P.<X,Y,Z> = QQ[] sage: E = Curve(X^2 + Y^2 + Z^2); E Projective Conic Curve over Rational Field defined by X^2 + Y^2 + Z^2 sage: E.has_rational_point(obstruction = True) (False, -1) The following would not terminate quickly with ``algorithm = 'rnfisnorm'`` :: sage: C = Conic(QQ, [1, 113922743, -310146482690273725409]) sage: C.has_rational_point(point = True) (True, (-76842858034579/5424 : -5316144401/5424 : 1)) sage: C.has_rational_point(algorithm = 'local', read_cache = False) True sage: C.has_rational_point(point=True, algorithm='magma', read_cache=False) # optional - magma (True, (30106379962113/7913 : 12747947692/7913 : 1)) TESTS: Create a bunch of conics over `\QQ`, check if ``has_rational_point`` runs without errors and returns consistent answers for all algorithms. Check if all points returned are valid. :: sage: l = Sequence(cartesian_product_iterator([[-1, 0, 1] for i in range(6)])) sage: c = [Conic(QQ, a) for a in l if a != [0,0,0] and a != (0,0,0,0,0,0)] sage: d = [] sage: d = [[C]+[C.has_rational_point(algorithm = algorithm, read_cache = False, obstruction = (algorithm != 'rnfisnorm'), point = (algorithm != 'local')) for algorithm in ['local', 'qfsolve', 'rnfisnorm']] for C in c[::10]] # long time: 7 seconds sage: assert all([e[1][0] == e[2][0] and e[1][0] == e[3][0] for e in d]) sage: assert all([e[0].defining_polynomial()(Sequence(e[i][1])) == 0 for e in d for i in [2,3] if e[1][0]]) """ if read_cache: if self._rational_point is not None: if point or obstruction: return True, self._rational_point else: return True if self._local_obstruction is not None: if point or obstruction: return False, self._local_obstruction else: return False if (not point) and self._finite_obstructions == [] and \ self._infinite_obstructions == []: if obstruction: return True, None return True if self.has_singular_point(): if point: return self.has_singular_point(point = True) if obstruction: return True, None return True if algorithm == 'default' or algorithm == 'qfsolve': M = self.symmetric_matrix() M *= lcm([ t.denominator() for t in M.list() ]) pt = qfsolve(M) if pt in ZZ: if self._local_obstruction == None: self._local_obstruction = pt if point or obstruction: return False, pt return False pt = self.point([pt[0], pt[1], pt[2]]) if point or obstruction: return True, pt return True ret = ProjectiveConic_number_field.has_rational_point( \ self, point = point, \ obstruction = obstruction, \ algorithm = algorithm, \ read_cache = read_cache) if point or obstruction: if is_RingHomomorphism(ret[1]): ret[1] = -1 return ret
def update(G,B,h): """ Update ``G`` using the list of critical pairs ``B`` and the polynomial ``h`` as presented in [BW93]_, page 230. For this, Buchberger's first and second criterion are tested. This function uses the Gebauer-Moeller Installation. INPUT: - ``G`` - an intermediate Groebner basis - ``B`` - a list of critical pairs - ``h`` - a polynomial OUTPUT: ``G,B`` where ``G`` and ``B`` are updated EXAMPLE:: sage: from sage.rings.polynomial.toy_d_basis import update sage: A.<x,y> = PolynomialRing(ZZ, 2) sage: G = set([3*x^2 + 7, 2*y + 1, x^3 - y^2 + 7*x - y + 1]) sage: B = set([]) sage: h = x^2*y - x^2 + y - 3 sage: update(G,B,h) (set([3*x^2 + 7, 2*y + 1, x^2*y - x^2 + y - 3, x^3 - y^2 + 7*x - y + 1]), set([(x^2*y - x^2 + y - 3, x^3 - y^2 + 7*x - y + 1), (x^2*y - x^2 + y - 3, 3*x^2 + 7), (x^2*y - x^2 + y - 3, 2*y + 1)])) """ R = h.parent() LCM = R.monomial_lcm lt_divides = lambda x,y: (R.monomial_divides( LM(h), LM(g) ) and LC(h).divides(LC(g)) ) lt_pairwise_prime = lambda x,y : R.monomial_pairwise_prime(LM(x),LM(y)) and gcd(LC(x),LC(y)) == 1 lcm_divides = lambda f,g1,h: R.monomial_divides( LCM(LM(h),LM(f[1])), LCM(LM(h),LM(g1)) ) and \ lcm(LC(h),LC(f[1])).divides(lcm(LC(h),LC(g1))) C = set([(h,g) for g in G]) D = set() while C != set(): (h,g1) = C.pop() if lt_pairwise_prime(h,g) or \ (\ not any( lcm_divides(f,g1,h) for f in C ) \ and not any( lcm_divides(f,g1,h) for f in D ) \ ): D.add( (h,g1) ) E = set() while D != set(): (h,g) = D.pop() if not lt_pairwise_prime(h,g): E.add( (h,g) ) B_new = set() while B != set(): g1,g2 = B.pop() lcm_lg1_lg2 = lcm(LC(g1),LC(g2)) * LCM(LM(g1),LM(g2)) if not lt_divides(lcm_lg1_lg2, h) or \ lcm(LC(g1),LC( h)) * R.monomial_lcm(LM(g1),LM( h)) == lcm_lg1_lg2 or \ lcm(LC( h),LC(g2)) * R.monomial_lcm(LM( h),LM(g2)) == lcm_lg1_lg2 : B_new.add( (g1,g2) ) B_new = B_new.union( E ) G_new = set() while G != set(): g = G.pop() if not lt_divides(g,h): G_new.add(g) G_new.add(h) return G_new,B_new
def period(self, m): """ Return the period of the binary recurrence sequence modulo an integer ``m``. If `n_1` is congruent to `n_2` modulu ``period(m)``, then `u_{n_1}` is is congruent to `u_{n_2}` modulo ``m``. INPUT: - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated). OUTPUT: - The integer (the period of the sequence modulo m) EXAMPLES: If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS06]). :: sage: R = BinaryRecurrenceSequence(1,1) sage: R.period(31) 30 sage: [R(i) % 4 for i in xrange(12)] [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1] sage: R.period(4) 6 This function works for degenerate sequences as well. :: sage: S = BinaryRecurrenceSequence(2,0,1,2) sage: S.is_degenerate() True sage: S.is_geometric() True sage: [S(i) % 17 for i in xrange(16)] [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9] sage: S.period(17) 8 Note: the answer is cached. """ #If we have already computed the period mod m, then we return the stored value. if m in self._period_dict: return self._period_dict[m] else: R = Integers(m) A = matrix(R, [[0, 1], [self.c, self.b]]) w = matrix(R, [[self.u0], [self.u1]]) Fac = list(m.factor()) Periods = {} #To compute the period mod m, we compute the least integer n such that A^n*w == w. This necessarily #divides the order of A as a matrix in GL_2(Z/mZ). #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm. #To compute the period mod p^e, we first compute the order mod p. Then the period mod p^e #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to #the identity mod p is of order (p^{e-1})^4. So we compute the period mod p^e by successively #multiplying the period mod p by powers of p. for i in Fac: p = i[0] e = i[1] #first compute the period mod p if p in self._period_dict: perp = self._period_dict[p] else: F = A.change_ring(GF(p)) v = w.change_ring(GF(p)) FF = F**(p - 1) p1fac = list((p - 1).factor()) #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1). #The order divides p-1 if it is diagaonalizable. In any case, det(F^(p-1))=1, #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]]. #The order of the subgroup of matrices of this form is p, so the order must divide #p(p-1) -- in fact it must be a multiple of p. If this is not the case, then the #order divides (p-1)(p+1). As the period divides the order of the matrix in GL_2(F_p), #these conditions hold for the period as well. #check if the order divides (p-1) if FF * v == v: M = p - 1 Mfac = p1fac #check if the trace is 2, then the order is a multiple of p dividing p*(p-1) elif (FF).trace() == 2: M = p - 1 Mfac = p1fac F = F**p #replace F by F^p as now we only need to determine the factor dividing (p-1) #otherwise it will divide (p+1)(p-1) else: M = (p + 1) * (p - 1) p2fac = list( (p + 1).factor() ) #factor the (p+1) and (p-1) terms seperately and then combine for speed Mfac_dic = {} for i in list(p1fac + p2fac): if i[0] not in Mfac_dic: Mfac_dic[i[0]] = i[1] else: Mfac_dic[i[0]] = Mfac_dic[i[0]] + i[1] Mfac = [(i, Mfac_dic[i]) for i in Mfac_dic] #Now use a fast order algorithm to compute the period. We know that the period divides #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors. As #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j). After #all factors have been iterated over, the result is the period mod p. Mfac = list(Mfac) C = [] #expand the list of prime factors so every factor is with multiplicity 1 for i in xrange(len(Mfac)): for j in xrange(Mfac[i][1]): C.append(Mfac[i][0]) Mfac = C n = M for i in Mfac: b = Integer(n / i) if F**b * v == v: n = b perp = n #Now compute the period mod p^e by steping up by multiples of p F = A.change_ring(Integers(p**e)) v = w.change_ring(Integers(p**e)) FF = F**perp if FF * v == v: perpe = perp else: tries = 0 while True: tries += 1 FF = FF**p if FF * v == v: perpe = perp * p**tries break Periods[p] = perpe #take the lcm of the periods mod all distinct primes dividing m period = 1 for p in Periods: period = lcm(Periods[p], period) self._period_dict[m] = period #cache the period mod m return period
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. ALGORITHM: Uses Denis Simon's pari script Qfparam. See ``sage.quadratic_forms.qfsolve.qfparam``. EXAMPLES :: sage: c = Conic([1,1,-1]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Rational Field To: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 Defn: Defined on coordinates by sending (x : y) to (2*x*y : x^2 - y^2 : x^2 + y^2), Scheme morphism: From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 To: Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to (1/2*x : -1/2*y + 1/2*z)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, 7*x^2 + y^2, -2*y^2], [-1/2*x, -1/2*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + 2*y^2 + z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError, "The conic self (=%s) is not smooth, hence does not have a parametrization." % self if point == None: point = self.rational_point() point = Sequence(point) Q = PolynomialRing(QQ, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() M = self.symmetric_matrix() M *= lcm([ t.denominator() for t in M.list() ]) par1 = qfparam(M, point) B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)]) # self is in the image of B and does not lie on a line, # hence B is invertible A = B.inverse() par2 = [sum([A[i,j]*gens[j] for j in range(3)]) for i in [1,0]] par = ([Q(pol(x/y)*y**2) for pol in par1], par2) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
def CyclicSievingPolynomial(L, cyc_act=None, order=None, get_order=False): """ Returns the unique polynomial p of degree smaller than order such that the triple ( L, cyc_act, p ) exhibits the CSP. If ``cyc_act`` is None, ``L`` is expected to contain the orbit lengths. INPUT: - L -- if cyc_act is None: list of orbit sizes, otherwise list of objects - cyc_act -- (default:None) function taking an element of L and returning an element of L (must define a bijection on L) - order -- (default:None) if set to an integer, this cyclic order of cyc_act is used (must be an integer multiple of the order of cyc_act) otherwise, the order of cyc_action is used - get_order -- (default:False) if True, a tuple [p,n] is returned where p as above, and n is the order EXAMPLES:: sage: from sage.combinat.cyclic_sieving_phenomenon import CyclicSievingPolynomial sage: S42 = [ Set(S) for S in subsets([1,2,3,4]) if len(S) == 2 ]; S42 [{1, 2}, {1, 3}, {2, 3}, {1, 4}, {2, 4}, {3, 4}] sage: cyc_act = lambda S: Set( i.mod(4)+1 for i in S) sage: cyc_act([1,3]) {2, 4} sage: cyc_act([1,4]) {1, 2} sage: CyclicSievingPolynomial( S42, cyc_act ) q^3 + 2*q^2 + q + 2 sage: CyclicSievingPolynomial( S42, cyc_act, get_order=True ) [q^3 + 2*q^2 + q + 2, 4] sage: CyclicSievingPolynomial( S42, cyc_act, order=8 ) q^6 + 2*q^4 + q^2 + 2 sage: CyclicSievingPolynomial([4,2]) q^3 + 2*q^2 + q + 2 TESTS: We check that :trac:`13997` is handled:: sage: CyclicSievingPolynomial( S42, cyc_act, order=8, get_order=True ) [q^6 + 2*q^4 + q^2 + 2, 8] """ if cyc_act: orbits = orbit_decomposition(L, cyc_act) else: orbits = [range(k) for k in L] R = QQ['q'] q = R.gen() p = R(0) orbit_sizes = {} for orbit in orbits: l = len(orbit) if l in orbit_sizes: orbit_sizes[l] += 1 else: orbit_sizes[l] = 1 keys = orbit_sizes.keys() n = lcm(keys) if order: if order.mod(n) != 0: raise ValueError( "The given order is not valid as it is not a multiple of the order of the given cyclic action" ) else: order = n for i in range(n): if i == 0: j = sum(orbit_sizes[l] for l in keys) else: j = sum(orbit_sizes[l] for l in keys if ZZ(i).mod(n / l) == 0) p += j * q**i p = p(q**ZZ(order / n)) if get_order: return [p, order] else: return p
def update(G, B, h): """ Update ``G`` using the list of critical pairs ``B`` and the polynomial ``h`` as presented in [BW93]_, page 230. For this, Buchberger's first and second criterion are tested. This function uses the Gebauer-Moeller Installation. INPUT: - ``G`` - an intermediate Groebner basis - ``B`` - a list of critical pairs - ``h`` - a polynomial OUTPUT: ``G,B`` where ``G`` and ``B`` are updated EXAMPLE:: sage: from sage.rings.polynomial.toy_d_basis import update sage: A.<x,y> = PolynomialRing(ZZ, 2) sage: G = set([3*x^2 + 7, 2*y + 1, x^3 - y^2 + 7*x - y + 1]) sage: B = set([]) sage: h = x^2*y - x^2 + y - 3 sage: update(G,B,h) ({2*y + 1, 3*x^2 + 7, x^2*y - x^2 + y - 3, x^3 - y^2 + 7*x - y + 1}, {(x^2*y - x^2 + y - 3, 2*y + 1), (x^2*y - x^2 + y - 3, 3*x^2 + 7), (x^2*y - x^2 + y - 3, x^3 - y^2 + 7*x - y + 1)}) """ R = h.parent() LCM = R.monomial_lcm lt_divides = lambda x, y: (R.monomial_divides(LM(h), LM(g)) and LC(h). divides(LC(g))) lt_pairwise_prime = lambda x, y: R.monomial_pairwise_prime(LM(x), LM( y)) and gcd(LC(x), LC(y)) == 1 lcm_divides = lambda f,g1,h: R.monomial_divides( LCM(LM(h),LM(f[1])), LCM(LM(h),LM(g1)) ) and \ lcm(LC(h),LC(f[1])).divides(lcm(LC(h),LC(g1))) C = set([(h, g) for g in G]) D = set() while C != set(): (h, g1) = C.pop() if lt_pairwise_prime(h,g) or \ (\ not any( lcm_divides(f,g1,h) for f in C ) \ and not any( lcm_divides(f,g1,h) for f in D ) \ ): D.add((h, g1)) E = set() while D != set(): (h, g) = D.pop() if not lt_pairwise_prime(h, g): E.add((h, g)) B_new = set() while B != set(): g1, g2 = B.pop() lcm_lg1_lg2 = lcm(LC(g1), LC(g2)) * LCM(LM(g1), LM(g2)) if not lt_divides(lcm_lg1_lg2, h) or \ lcm(LC(g1),LC( h)) * R.monomial_lcm(LM(g1),LM( h)) == lcm_lg1_lg2 or \ lcm(LC( h),LC(g2)) * R.monomial_lcm(LM( h),LM(g2)) == lcm_lg1_lg2 : B_new.add((g1, g2)) B_new = B_new.union(E) G_new = set() while G != set(): g = G.pop() if not lt_divides(g, h): G_new.add(g) G_new.add(h) return G_new, B_new
def normal_cone(self): r""" Return the (closure of the) normal cone of the triangulation. Recall that a regular triangulation is one that equals the "crease lines" of a convex piecewise-linear function. This support function is not unique, for example, you can scale it by a positive constant. The set of all piecewise-linear functions with fixed creases forms an open cone. This cone can be interpreted as the cone of normal vectors at a point of the secondary polytope, which is why we call it normal cone. See [GKZ]_ Section 7.1 for details. OUTPUT: The closure of the normal cone. The `i`-th entry equals the value of the piecewise-linear function at the `i`-th point of the configuration. For an irregular triangulation, the normal cone is empty. In this case, a single point (the origin) is returned. EXAMPLES:: sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal') sage: triangulation (<0,1,3>, <0,2,3>) sage: N = triangulation.normal_cone(); N 4-d cone in 4-d lattice sage: N.rays() (-1, 0, 0, 0), ( 1, 0, 1, 0), (-1, 0, -1, 0), ( 1, 0, 0, -1), (-1, 0, 0, 1), ( 1, 1, 0, 0), (-1, -1, 0, 0) in Ambient free module of rank 4 over the principal ideal domain Integer Ring sage: N.dual().rays() (-1, 1, 1, -1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring TESTS:: sage: polytopes.n_simplex(2).triangulate().normal_cone() 3-d cone in 3-d lattice sage: _.dual().is_trivial() True """ if not self.point_configuration().base_ring().is_subring(QQ): raise NotImplementedError("Only base rings ZZ and QQ are supported") from sage.libs.ppl import Variable, Constraint, Constraint_System, Linear_Expression, C_Polyhedron from sage.matrix.constructor import matrix from sage.misc.misc import uniq from sage.rings.arith import lcm pc = self.point_configuration() cs = Constraint_System() for facet in self.interior_facets(): s0, s1 = self._boundary_simplex_dictionary()[facet] p = set(s0).difference(facet).pop() q = set(s1).difference(facet).pop() origin = pc.point(p).reduced_affine_vector() base_indices = [i for i in s0 if i != p] base = matrix([pc.point(i).reduced_affine_vector() - origin for i in base_indices]) sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin) relation = [0] * pc.n_points() relation[p] = sum(sol) - 1 relation[q] = 1 for i, base_i in enumerate(base_indices): relation[base_i] = -sol[i] rel_denom = lcm([QQ(r).denominator() for r in relation]) relation = [ZZ(r * rel_denom) for r in relation] ex = Linear_Expression(relation, 0) cs.insert(ex >= 0) from sage.modules.free_module import FreeModule ambient = FreeModule(ZZ, self.point_configuration().n_points()) if cs.empty(): cone = C_Polyhedron(ambient.dimension(), "universe") else: cone = C_Polyhedron(cs) from sage.geometry.cone import _Cone_from_PPL return _Cone_from_PPL(cone, lattice=ambient)
def CyclicSievingPolynomial( L, cyc_act=None, order=None, get_order=False): """ Returns the unique polynomial p of degree smaller than order such that the triple ( L, cyc_act, p ) exhibits the CSP. If ``cyc_act`` is None, ``L`` is expected to contain the orbit lengths. INPUT: - L -- if cyc_act is None: list of orbit sizes, otherwise list of objects - cyc_act -- (default:None) function taking an element of L and returning an element of L (must define a bijection on L) - order -- (default:None) if set to an integer, this cyclic order of cyc_act is used (must be an integer multiple of the order of cyc_act) otherwise, the order of cyc_action is used - get_order -- (default:False) if True, a tuple [p,n] is returned where p as above, and n is the order EXAMPLES:: sage: from sage.combinat.cyclic_sieving_phenomenon import CyclicSievingPolynomial sage: S42 = [ Set(S) for S in subsets([1,2,3,4]) if len(S) == 2 ]; S42 [{1, 2}, {1, 3}, {2, 3}, {1, 4}, {2, 4}, {3, 4}] sage: cyc_act = lambda S: Set( i.mod(4)+1 for i in S) sage: cyc_act([1,3]) {2, 4} sage: cyc_act([1,4]) {1, 2} sage: CyclicSievingPolynomial( S42, cyc_act ) q^3 + 2*q^2 + q + 2 sage: CyclicSievingPolynomial( S42, cyc_act, get_order=True ) [q^3 + 2*q^2 + q + 2, 4] sage: CyclicSievingPolynomial( S42, cyc_act, order=8 ) q^6 + 2*q^4 + q^2 + 2 sage: CyclicSievingPolynomial([4,2]) q^3 + 2*q^2 + q + 2 TESTS: We check that :trac:`13997` is handled:: sage: CyclicSievingPolynomial( S42, cyc_act, order=8, get_order=True ) [q^6 + 2*q^4 + q^2 + 2, 8] """ if cyc_act: orbits = orbit_decomposition( L, cyc_act ) else: orbits = [ range(k) for k in L ] R = QQ['q'] q = R.gen() p = R(0) orbit_sizes = {} for orbit in orbits: l = len( orbit ) if l in orbit_sizes: orbit_sizes[ l ] += 1 else: orbit_sizes[ l ] = 1 keys = orbit_sizes.keys() n = lcm( keys ) if order: if order.mod(n) != 0: raise ValueError("The given order is not valid as it is not a multiple of the order of the given cyclic action") else: order = n for i in range(n): if i == 0: j = sum( orbit_sizes[ l ] for l in keys ) else: j = sum( orbit_sizes[ l ] for l in keys if ZZ(i).mod(n/l) == 0 ) p += j*q**i p = p(q**ZZ(order/n)) if get_order: return [p,order] else: return p