def is_cm_j_invariant(j): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ from sage.rings.all import NumberFieldElement if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") if not j.is_integral(): return False, None jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly() h = jpol.degree() if h>100: raise NotImplementedError("CM data only available for class numbers up to 100") for d,f in cm_orders(h): if jpol == hilbert_class_polynomial(d*f**2): return True, (d,f) return False, None
def is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError( "is_cm_j_invariant() is only implemented for number field elements" ) # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj, (d, f)) for d, f, jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ, 'x')([-j, 1 ]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method == 'old': if h > 100: raise NotImplementedError( "CM data only available for class numbers up to 100") for d, f in cm_orders(h): if jpol == hilbert_class_polynomial(d * f**2): return True, (d, f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminants to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *= 2 if D.valuation(P) > 0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4 * P.norm() dP = DP.squarefree_part() fP = ZZ(DP // dP).isqrt() if cmd == 0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd == 0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd % 4 != 1: cmd = cmd * 4 cmf = cmf // 2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd * f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j) == 0: return True, (cmd, f) return False, None
def is_cm_j_invariant(j): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ from sage.rings.all import NumberFieldElement if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError( "is_cm_j_invariant() is only implemented for number field elements" ) if not j.is_integral(): return False, None jpol = PolynomialRing(QQ, 'x')([-j, 1 ]) if j in QQ else j.absolute_minpoly() h = jpol.degree() if h > 100: raise NotImplementedError( "CM data only available for class numbers up to 100") for d, f in cm_orders(h): if jpol == hilbert_class_polynomial(d * f**2): return True, (d, f) return False, None
def is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj,(d,f)) for d,f,jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method=='old': if h>100: raise NotImplementedError("CM data only available for class numbers up to 100") for d,f in cm_orders(h): if jpol == hilbert_class_polynomial(d*f**2): return True, (d,f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminats to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *=2 if D.valuation(P)>0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4*P.norm() dP = DP.squarefree_part() fP = ZZ(DP//dP).isqrt() if cmd==0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd==0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd%4!=1: cmd = cmd*4 cmf = cmf//2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd*f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j)==0: return True, (cmd,f) return False, None