# Ticket #11975: trac_11975-part4.patch

File trac_11975-part4.patch, 9.1 KB (added by was, 9 years ago)

part 4, addressing the major issue John Cremona found in equivalence checking.

• ## sage/schemes/elliptic_curves/chow_heegner.py

```# HG changeset patch
# User William Stein <wstein@gmail.com>
# Date 1327089751 28800
# Node ID 3c2d896e6b69aeaa6bb1aff3b3429ea806130f68
# Parent  8df8e78d666e362282d4f0604128662f3c44e9df
trac 11975 -- Chow-Heegner points; part 4

diff --git a/sage/schemes/elliptic_curves/chow_heegner.py b/sage/schemes/elliptic_curves/chow_heegner.py```
 a sage: z1 = ComplexField(200)(5,7) sage: all(is_gamma0N_equivalent(z1, g.acton(z1), 15, 150) for g in Gamma0(15).gens()) True True These two points have the property that z1 and z2 are equivalent modulo SL2(Z), and 5*z1 and 5*z2 are *also* equivalent modulo SL2(Z), but z1 and z2 are not equivalent modulo Gamma0(N).  Note that both points are equivalent to I, which has extra automorphisms.  This is interesting because generators for the modular function field j(z) and j(5*z) agree on z1 and z2, but the points are inequivalent (this illustrates that this model for the modular curve X0(5) is singular):: sage: from sage.schemes.elliptic_curves.chow_heegner import is_gamma0N_equivalent, is_sl2z_equivalent sage: z1 = CDF(-2,1)/5 sage: z2 = CDF(2,1)/5 sage: is_gamma0N_equivalent(z1,z2,5, 30) False sage: is_sl2z_equivalent(z1, z2, 30) True sage: is_sl2z_equivalent(5*z1, 5*z2, 30) True """ # The algorithm we use below was suggested by Samit Dasgupta at a # UC Santa Cruz seminar in 2011. if not is_sl2z_equivalent(z1, z2, prec): # points are not even sl2z-equivalent, so they can't # be Gamma_0(N) equivalent C = ComplexField(prec) eps = RR(2)**(-prec) w1, g1 = sl2z_rep_in_fundom(z1, eps)  # g1(z1) = w1 = canonical rep w2, g2 = sl2z_rep_in_fundom(z2, eps)  # g2(z2) = w2 = canonical rep a1, g1 = canonicalize_sl2z(w1, g1) a2, g2 = canonicalize_sl2z(w2, g2) if abs(a1 - a2) >= eps: # The points are not even sl2z-equivalent, so they can't be # Gamma_0(N) equivalent return False # Now we know the two points are SL2Z-equivalent, so j(z) takes # the same value on z1 and z2.  The modular function j_N(z)=j(N*z) # on Gamma_0(N) take on the same value on both z1 and z2 if and # only if N*z1 is equivalent to N*z2. # It is a classical theorem that j(z) and j_N(z) generate the # field of modular functions for Gamma_0(N), i.e., (via standard # identifications) the field of rational functions on the # algebraic curve X_0(N)/C.  Thus: if two points are equivalent # modulo Gamma_0(N), then j(z) and j_N(z) must take on the same # values on those points; conversely, if j(z) and j_N(z) take the # same values on two points, then every rational function takes # the same values on the image of the two points in X_0(N), which # implies that the points define the same element of X_0(N) (since # a point on a nonsingular algebraic curve is characterized by the # set of functions that vanish at it). # Now we may assume that g1(z1) = g2(z2), because of the adjustments # made above.  We double check. # This C2 is purely used for the assert below and nothing else, so this # "magic constant" not so evil. C2 = ComplexField(2*prec+10) assert abs(g1.acton(C2(z1)) - g2.acton(C2(z2))) < eps # So now we have z := g1(z1) = g2(z2), both in the standard # fundamental domain. # # The nontrivial elements of PSL2Z with a fixed point z in the # standard fundamental domain for the upper half plane are # Stab(z), where # #     * z = i, so Stab(z) generated by S (where S has order 2) #     * z = rho = exp(2*pi*i/3) so Stab(z) generated by S*T #     * z = -rhobar = exp(pi*i/3) so Stab(z) generated by T*S # # The elements in PSL2Z that send z1 to z2 are the elements # g2^(-1)*A*g1 for A in Stab(z), so we just check if any are in # Gamma0(N). g2i = g2**(-1) g = g2i*g1 i = C.gen() pi = C.pi() if g[1,0]%N == 0: return True S, T = g1.parent().gens() if a1 == i: return (g2i*S*g1)[1,0]%N == 0 elif a1 == ((2*pi*i)/3).exp(): return (g2i*S*T*g1)[1,0]%N == 0 or (g2i*S*T*S*T*g1)[1,0]%N == 0 elif a1 == ((pi*i)/3).exp(): return (g2i*T*S*g1)[1,0]%N == 0 or (g2i*T*S*T*S*g1)[1,0]%N == 0 return False # Thus: we check whether or not N*z1 is equivalent to N*z2. return is_sl2z_equivalent(N*z1, N*z2, prec) class X0NPoint(object): EXAMPLES:: sage: from sage.schemes.elliptic_curves.chow_heegner import X0NPoint sage: z = CDF(1,1); x=X0NPoint(z,11,30); y=X0NPoint((-5*z-1)/(11*z+2),11,30) sage: x==y sage: g = SL2Z([-5,-1,11,2]) sage: z = CDF(1,1); x = X0NPoint(z, 11, 30); y = X0NPoint(g.acton(z), 11, 30) sage: x == y True sage: z = CDF(1,1); x=X0NPoint(z,11,30); y=X0NPoint(z/2,11,30) sage: x==y sage: z = CDF(1,1); x = X0NPoint(z, 11, 30); y = X0NPoint(z/2, 11, 30) sage: x == y False Points with different levels:: [-0.26016260 + 0.0081300813*I] sage: x.atkin_lehner(3).atkin_lehner(3) [1.0000000 + 1.0000000*I] sage: x.atkin_lehner(3).atkin_lehner(3) == x True sage: x.atkin_lehner(2) Traceback (most recent call last): ... [0.11049724 + 0.00055248619*I] sage: P.atkin_lehner(10).atkin_lehner(10) [0.10554020 + 0.000013888696*I] This is because applying Atkin-Lehner results in substantial precision loss:: sage: P.atkin_lehner(10).atkin_lehner(10) == P True False Using higher precision for the underlying point fixes the problem:: sage: P = X0NPoint(ComplexField(200)(1,1),90,30) sage: P.atkin_lehner(10).atkin_lehner(10) == P True """ if q is None: # Main involution low precision (silly example):: sage: phi.points_in_h(RDF(1), equiv_prec=2, min_imag=1e-3) [[-0.38 + 0.0020*I], [-0.047 + 0.016*I]] [[-0.38 + 0.0020*I], [-0.094 + 0.0015*I], [-0.047 + 0.016*I]] Consider points equivalent only if equivalent to high precision, which will result in falsely considering points as inequivalent:: sage: len(phi.points_in_h(RDF(1), equiv_prec=50, min_imag=1e-3)) 10 sage: len(phi.points_in_h(RDF(1), equiv_prec=40, min_imag=1e-3)) 9 sage: len(phi.points_in_h(RDF(1), equiv_prec=40, min_imag=1e-3))  # correct, since moddeg=2 2 """ C = parent(z) the ``numerical_approx`` method. EXAMPLES:: sage: P = EllipticCurve('37a').chow_heegner_point(EllipticCurve('37b')) sage: P.point_exact(deg1=100) (6 : 14 : 1) Another example that illustrates precision issues when testing equivalence of points in the upper half plane:: sage: P = EllipticCurve('99a').chow_heegner_point(EllipticCurve('99b')) sage: P.numerical_approx() (1.64062499999... : -1.75195312499... : 1.00000000000000) sage: P.point_exact() Traceback (most recent call last): ... RuntimeError: Found too many points (13 > 12) -- try (good) increasing precision of z (now=53) or (bad) *decreasing* equiv_prec (now=17) As suggested, either increasing the precision (slower, but safer) or somewhat decreasing the number equiv_prec of bits used for checking equivalence (faster, but less safe) both work in this case:: sage: P.point_exact(equiv_prec=12) (105/64 : -897/512 : 1) sage: P.index() sage: P.point_exact(prec=60) (105/64 : -897/512 : 1) sage: P.index(equiv_prec=12) 4 """ P = self.numerical_approx(**kwds)
• ## sage/schemes/elliptic_curves/ell_rational_field.py

`diff --git a/sage/schemes/elliptic_curves/ell_rational_field.py b/sage/schemes/elliptic_curves/ell_rational_field.py`
 a sage: P = EllipticCurve('37a').chow_heegner_point(EllipticCurve('37b')); P Chow-Heegner point on 37a1 associated to 37b1 sage: P.point_exact() sage: P.point_exact(deg1=100) (6 : 14 : 1) sage: P.numerical_approx() sage: P.numerical_approx(deg1=100) (6.00000000000... : 14.0000000000... : 1.00000000000000) """ import chow_heegner