def number_field_elements_from_algebraics(elts, name='a'): r""" The native Sage function ``number_field_elements_from_algebraics`` currently returns number field *without* embedding. This function return field with embedding! EXAMPLES:: sage: from flatsurf.geometry.polygon import number_field_elements_from_algebraics sage: z = QQbar.zeta(5) sage: c = z.real() sage: s = z.imag() sage: number_field_elements_from_algebraics((c,s)) (Number Field in a with defining polynomial y^4 - 5*y^2 + 5, [1/2*a^2 - 3/2, 1/2*a]) """ from sage.rings.qqbar import number_field_elements_from_algebraics from sage.rings.number_field.number_field import NumberField field,elts,phi = number_field_elements_from_algebraics(elts, minimal=True) polys = [x.polynomial() for x in elts] K = NumberField(field.polynomial(), name, embedding=AA(phi(field.gen()))) gen = K.gen() return K, [x.polynomial()(gen) for x in elts]
def platonic_dodecahedron(): r"""Produce a triple consisting of a polyhedral version of the platonic dodecahedron, the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface to the polyhedron. EXAMPLES:: sage: from flatsurf.geometry.polyhedra import platonic_dodecahedron sage: polyhedron,surface,surface_to_polyhedron = platonic_dodecahedron() sage: TestSuite(surface).run() r""" vertices = [] phi = AA(1 + sqrt(5)) / 2 F = NumberField(phi.minpoly(), "phi", embedding=phi) phi = F.gen() for x in range(-1, 3, 2): for y in range(-1, 3, 2): for z in range(-1, 3, 2): vertices.append(vector(F, (x, y, z))) for x in range(-1, 3, 2): for y in range(-1, 3, 2): vertices.append(vector(F, (0, x * phi, y / phi))) vertices.append(vector(F, (y / phi, 0, x * phi))) vertices.append(vector(F, (x * phi, y / phi, 0))) scale = AA(2 / sqrt(1 + (phi - 1)**2 + (1 / phi - 1)**2)) p = Polyhedron(vertices=vertices) s, m = polyhedron_to_cone_surface(p, scaling_factor=scale) return p, s, m
def platonic_dodecahedron(): r"""Produce a triple consisting of a polyhedral version of the platonic dodecahedron, the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface to the polyhedron. EXAMPLES:: sage: from flatsurf.geometry.polyhedra import platonic_dodecahedron sage: polyhedron,surface,surface_to_polyhedron = platonic_dodecahedron() sage: TestSuite(surface).run() r""" vertices=[] phi=AA(1+sqrt(5))/2 F=NumberField(phi.minpoly(),"phi",embedding=phi) phi=F.gen() for x in xrange(-1,3,2): for y in xrange(-1,3,2): for z in xrange(-1,3,2): vertices.append(vector(F,(x,y,z))) for x in xrange(-1,3,2): for y in xrange(-1,3,2): vertices.append(vector(F,(0,x*phi,y/phi))) vertices.append(vector(F,(y/phi,0,x*phi))) vertices.append(vector(F,(x*phi,y/phi,0))) scale=AA(2/sqrt(1+(phi-1)**2+(1/phi-1)**2)) p=Polyhedron(vertices=vertices) s,m = polyhedron_to_cone_surface(p,scaling_factor=scale) return p,s,m
def platonic_icosahedron(): r"""Produce a triple consisting of a polyhedral version of the platonic icosahedron, the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface to the polyhedron. EXAMPLES:: sage: from flatsurf.geometry.polyhedra import platonic_icosahedron sage: polyhedron,surface,surface_to_polyhedron = platonic_icosahedron() sage: TestSuite(surface).run() r""" vertices=[] phi=AA(1+sqrt(5))/2 F=NumberField(phi.minpoly(),"phi",embedding=phi) phi=F.gen() for i in xrange(3): for s1 in xrange(-1,3,2): for s2 in xrange(-1,3,2): p=3*[None] p[i]=s1*phi p[(i+1)%3]=s2 p[(i+2)%3]=0 vertices.append(vector(F,p)) p=Polyhedron(vertices=vertices) s,m = polyhedron_to_cone_surface(p) return p,s,m
def platonic_icosahedron(): r"""Produce a triple consisting of a polyhedral version of the platonic icosahedron, the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface to the polyhedron. EXAMPLES:: sage: from flatsurf.geometry.polyhedra import platonic_icosahedron sage: polyhedron,surface,surface_to_polyhedron = platonic_icosahedron() sage: TestSuite(surface).run() r""" vertices = [] phi = AA(1 + sqrt(5)) / 2 F = NumberField(phi.minpoly(), "phi", embedding=phi) phi = F.gen() for i in range(3): for s1 in range(-1, 3, 2): for s2 in range(-1, 3, 2): p = 3 * [None] p[i] = s1 * phi p[(i + 1) % 3] = s2 p[(i + 2) % 3] = 0 vertices.append(vector(F, p)) p = Polyhedron(vertices=vertices) s, m = polyhedron_to_cone_surface(p) return p, s, m
def test_type_2_primes(f, class_number): K = NumberField(f, "a") Kgal = K.galois_closure("b") embeddings = K.embeddings(Kgal) type_two_not_momose = get_type_2_not_momose(K, embeddings) _ = type_2_primes(K, embeddings, **TEST_SETTINGS) if class_number == 1: assert type_two_not_momose == set()
def _number_field_from_algebraics(self): r""" Given a projective point defined over ``QQbar``, return the same point, but defined over a number field. This is only implemented for points of projective space. OUTPUT: scheme point EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: P.<x,y> = ProjectiveSpace(QQbar,1) sage: Q = P([-1/2*QQbar(sqrt(2)) + QQbar(I), 1]) sage: S = Q._number_field_from_algebraics(); S (1/2*a^3 + a^2 - 1/2*a : 1) sage: S.codomain() Projective Space of dimension 1 over Number Field in a with defining polynomial y^4 + 1 with a = 0.7071067811865475? + 0.7071067811865475?*I The following was fixed in :trac:`23808`:: sage: R.<x> = PolynomialRing(QQ) sage: P.<x,y> = ProjectiveSpace(QQbar,1) sage: Q = P([-1/2*QQbar(sqrt(2)) + QQbar(I), 1]);Q (-0.7071067811865475? + 1*I : 1) sage: S = Q._number_field_from_algebraics(); S (1/2*a^3 + a^2 - 1/2*a : 1) sage: T = S.change_ring(QQbar) # Used to fail sage: T (-0.7071067811865475? + 1.000000000000000?*I : 1) sage: Q[0] == T[0] True """ from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(self.codomain()): raise NotImplementedError("not implemented for subschemes") # Trac #23808: Keep the embedding info associated with the number field K # used below, instead of in the separate embedding map phi which is # forgotten. K_pre, P, phi = number_field_elements_from_algebraics(list(self)) if K_pre is QQ: K = QQ else: from sage.rings.number_field.number_field import NumberField K = NumberField(K_pre.polynomial(), embedding=phi(K_pre.gen()), name='a') psi = K_pre.hom([K.gen()], K) # Identification of K_pre with K P = [psi(p) for p in P] # The elements of P were elements of K_pre from sage.schemes.projective.projective_space import ProjectiveSpace PS = ProjectiveSpace(K, self.codomain().dimension_relative(), 'z') return PS(P)
def as_embedded_number_field_element(alg): from sage.rings.number_field.number_field import NumberField nf, elt, emb = alg.as_number_field_element() if nf is QQ: res = elt else: embnf = NumberField(nf.polynomial(), nf.variable_name(), embedding=emb(nf.gen())) res = elt.polynomial()(embnf.gen()) # assert QQbar.coerce(res) == alg return res
def test_type_three_not_momose(f, class_number, strong_L_count): K = NumberField(f, "a") Kgal = K.galois_closure("b") embeddings = K.embeddings(Kgal) strong_type_3_epsilons = get_strong_type_3_epsilons(K, embeddings) assert len(strong_type_3_epsilons) == 2 * strong_L_count type_3_not_momose_primes, _ = type_three_not_momose( K, embeddings, strong_type_3_epsilons) if class_number == 1: assert type_3_not_momose_primes == []
def get_cocycle_from_elliptic_curve(self,E,sign = 1,use_magma = True): if sign == 0: return self.get_cocycle_from_elliptic_curve(E,1,use_magma) + self.get_cocycle_from_elliptic_curve(E,-1,use_magma) if not sign in [1, -1]: raise NotImplementedError F = self.group().base_ring() if F.signature()[1] == 0 or (F.signature() == (0,1) and 'G' not in self.group()._grouptype): K = (self.hecke_matrix(oo).transpose()-sign).kernel().change_ring(QQ) else: K = Matrix(QQ,self.dimension(),self.dimension(),0).kernel() disc = self.S_arithgroup().Gpn._O_discriminant discnorm = disc.norm() try: N = ZZ(discnorm.gen()) except AttributeError: N = ZZ(discnorm) if F == QQ: x = QQ['x'].gen() F = NumberField(x,names='a') E = E.change_ring(F) def getap(q): if F == QQ: return E.ap(q) else: Q = F.ideal(q).factor()[0][0] return ZZ(Q.norm() + 1 - E.reduction(Q).count_points()) q = ZZ(1) g0 = None while K.dimension() > 1: q = q.next_prime() for qq,e in F.ideal(q).factor(): if ZZ(qq.norm()).is_prime() and not qq.divides(F.ideal(disc.gens_reduced()[0])): try: ap = getap(qq) except (ValueError,ArithmeticError): continue try: K1 = (self.hecke_matrix(qq.gens_reduced()[0],g0 = g0,use_magma = use_magma).transpose()-ap).kernel() except RuntimeError: continue K = K.intersection(K1) if K.dimension() != 1: raise ValueError,'Did not obtain a one-dimensional space corresponding to E' col = [ZZ(o) for o in (K.denominator()*K.matrix()).list()] return sum([a * self.gen(i) for i,a in enumerate(col) if a != 0],self(0))
def number_field_with_integer_gen(K): r""" TESTS:: sage: from ore_algebra.analytic.utilities import number_field_with_integer_gen sage: K = NumberField(6*x^2 + (2/3)*x - 9/17, 'a') sage: number_field_with_integer_gen(K)[0] Number Field in x306a with defining polynomial x^2 + 34*x - 8262 ... """ if K is QQ: return QQ, ZZ den = K.polynomial().monic().denominator() if den.is_one(): # Ensure that we return the same number field object (coercions can be # slow!) intNF = K else: intgen = K.gen() * den ### Attempt to work around various problems with embeddings emb = K.coerce_embedding() embgen = emb(intgen) if emb else intgen intNF = NumberField(intgen.minpoly(), "x" + str(den) + str(K.gen()), embedding=embgen) assert intNF != K intNF, _ = good_number_field(intNF) # Work around weaknesses in coercions involving order elements, # including #14982 (fixed). Used to trigger #14989 (fixed). #return intNF, intNF.order(intNF.gen()) return intNF, intNF
def __init__(self,lambda_squared=None, field=None): if lambda_squared==None: from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R=PolynomialRing(ZZ,'x') x = R.gen() from sage.rings.qqbar import AA field=NumberField(x**3-ZZ(5)*x**2+ZZ(4)*x-ZZ(1), 'r', embedding=AA(ZZ(4))) self._l=field.gen() else: if field is None: self._l=lambda_squared field=lambda_squared.parent() else: self._l=field(lambda_squared) Surface.__init__(self,field, ZZ.zero(), finite=False)
def test_galois_action_on_embeddings(f, gal_deg): K = NumberField(f, "a") G_K = K.galois_group() G_K_emb, to_emb, from_emb, Kgal, embeddings = galois_action_on_embeddings( G_K) # test that to_emb and from_emb are isomorphism assert G_K.hom(G_K) == from_emb * to_emb assert G_K_emb.hom(G_K_emb) == to_emb * from_emb assert to_emb.domain() == G_K assert to_emb.codomain() == G_K_emb assert from_emb.domain() == G_K_emb assert from_emb.codomain() == G_K assert embeddings[0] == G_K.gen(0).as_hom() * embeddings[G_K_emb.gen(0) (1) - 1] assert Kgal.degree() == G_K_emb.cardinality() == gal_deg assert G_K_emb.degree() == len(embeddings) == K.degree()
def _over_numberfield(E): r"""Return `E`, defined over a NumberField object. This is necessary since if `E` is defined over `\QQ`, then we cannot use SAGE commands available for number fields. INPUT: - ``E`` - EllipticCurve - over a number field. OUTPUT: - If `E` is defined over a NumberField, returns E. - If `E` is defined over QQ, returns E defined over the NumberField QQ. EXAMPLES:: sage: E = EllipticCurve([1, 2]) sage: sage.schemes.elliptic_curves.gal_reps_number_field._over_numberfield(E) Elliptic Curve defined by y^2 = x^3 + x + 2 over Number Field in a with defining polynomial x """ K = E.base_field() if K == QQ: x = QQ['x'].gen() K = NumberField(x, 'a') E = E.change_ring(K) return E
def self_similar_interval_exchange_transformation(self): r""" Return the dilatation and the self-similar interval exchange transformation associated to this path. EXAMPLES:: sage: from surface_dynamics import iet The golden rotation:: sage: p = iet.Permutation('a b', 'b a') sage: g = iet.FlipSequence(p, ['t', 'b']) sage: a, T = g.self_similar_iet() sage: T.lengths().parent() Vector space of dimension 2 over Number Field ... sage: T.lengths().n() (1.00000000000000, 1.61803398874989) An example from Do-Schmidt:: sage: code = [1,0,1,0,1,0,0,0,1,0,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,0] sage: p = iet.Permutation([0,1,2,3,4,5,6],[6,5,4,3,2,1,0]) sage: g = iet.FlipSequence(p, code) sage: a, T = g.self_similar_iet() sage: T.sah_arnoux_fathi_invariant() (0, 0, 0) """ if not self.is_complete(): raise ValueError('not a complete loop') from sage.rings.qqbar import AA m = self.matrix() poly = m.charpoly() rho = max(poly.roots(AA, False)) try: K, a, _ = rho.as_number_field_element(minimal=True, embedded=True) except TypeError: # NOTE: the embedded option appeared in sage 8.8 from sage.rings.number_field.number_field import NumberField L, b, _ = rho.as_number_field_element(minimal=True) K = NumberField(L.polynomial(), str(L.gen()), embedding=rho) a = K(b.polynomial()) lengths = (m - a).right_kernel().basis()[0] if lengths[0] < 0: lengths = -lengths if any(x <= 0 for x in lengths): raise RuntimeError( "wrong Perron-Frobenius eigenvector: {}".format(lengths)) # NOTE: the above code makes "lengths" with parent being the right kernel (that is # a submodule of R^d) lengths = lengths.parent().ambient_vector_space()(lengths) from .iet import IntervalExchangeTransformation return a, IntervalExchangeTransformation(self._start, lengths)
def is_algebraic_integer(x): """ Determine whether a number is an algebraic integer, or whether the given minimal polynomial defines one. """ if x is None: return None if not isinstance(x, Polynomial): x = SR(x).minpoly() return NumberField(x, names=('a', )).gen().is_integral()
def test_cm_type_2(f, extra_isogenies, potenial_isogenies): K = NumberField(f, "a") superset, _ = get_isogeny_primes(K, **TEST_SETTINGS) assert set(EC_Q_ISOGENY_PRIMES).difference(superset) == set() # for p in extra_isogenies: # assert satisfies_condition_CC(K, p), "Not of type 2" pnip = sorted(superset.difference(set(EC_Q_ISOGENY_PRIMES))) print("f = {} disc = {} pnip = {}".format(f, K.discriminant(), pnip)) assert (extra_isogenies.difference(superset) == set() ), "Known CM isogenies are filtered out!" upperbound = potenial_isogenies.union(EC_Q_ISOGENY_PRIMES).union( extra_isogenies) upperbound = upperbound.union(bad_cubic_formal_immersion_primes) unlisted_potential_isogenies = superset.difference(upperbound) assert len(unlisted_potential_isogenies) <= 2, "We got worse at filtering" if unlisted_potential_isogenies: assert max( unlisted_potential_isogenies) <= 109, "We got worse at filtering"
def extract(cls, obj): """ Takes an object extracted by the json parser and decodes the special-formating dictionaries used to store special types. """ if isinstance(obj, dict) and 'data' in obj: if len(obj) == 2 and '__ComplexList__' in obj: return [complex(*v) for v in obj['data']] elif len(obj) == 2 and '__QQList__' in obj: return [QQ(tuple(v)) for v in obj['data']] elif len(obj) == 3 and '__NFList__' in obj and 'base' in obj: base = cls.extract(obj['base']) return [cls._extract(base, c) for c in obj['data']] elif len(obj) == 2 and '__IntDict__' in obj: return {Integer(k): cls.extract(v) for k,v in obj['data']} elif len(obj) == 3 and '__Vector__' in obj and 'base' in obj: base = cls.extract(obj['base']) return vector([cls._extract(base, v) for v in obj['data']]) elif len(obj) == 2 and '__Rational__' in obj: return Rational(*obj['data']) elif len(obj) == 3 and '__RealLiteral__' in obj and 'prec' in obj: return LmfdbRealLiteral(RealField(obj['prec']), obj['data']) elif len(obj) == 2 and '__complex__' in obj: return complex(*obj['data']) elif len(obj) == 3 and '__Complex__' in obj and 'prec' in obj: return ComplexNumber(ComplexField(obj['prec']), *obj['data']) elif len(obj) == 3 and '__NFElt__' in obj and 'parent' in obj: return cls._extract(cls.extract(obj['parent']), obj['data']) elif len(obj) == 3 and ('__NFRelative__' in obj or '__NFAbsolute__' in obj) and 'vname' in obj: poly = cls.extract(obj['data']) return NumberField(poly, name=obj['vname']) elif len(obj) == 2 and '__NFCyclotomic__' in obj: return CyclotomicField(obj['data']) elif len(obj) == 2 and '__IntegerRing__' in obj: return ZZ elif len(obj) == 2 and '__RationalField__' in obj: return QQ elif len(obj) == 3 and '__RationalPoly__' in obj and 'vname' in obj: return QQ[obj['vname']]([QQ(tuple(v)) for v in obj['data']]) elif len(obj) == 4 and '__Poly__' in obj and 'vname' in obj and 'base' in obj: base = cls.extract(obj['base']) return base[obj['vname']]([cls._extract(base, c) for c in obj['data']]) elif len(obj) == 5 and '__PowerSeries__' in obj and 'vname' in obj and 'base' in obj and 'prec' in obj: base = cls.extract(obj['base']) prec = infinity if obj['prec'] == 'inf' else int(obj['prec']) return base[[obj['vname']]]([cls._extract(base, c) for c in obj['data']], prec=prec) elif len(obj) == 2 and '__date__' in obj: return datetime.datetime.strptime(obj['data'], "%Y-%m-%d").date() elif len(obj) == 2 and '__time__' in obj: return datetime.datetime.strptime(obj['data'], "%H:%M:%S.%f").time() elif len(obj) == 2 and '__datetime__' in obj: return datetime.datetime.strptime(obj['data'], "%Y-%m-%d %H:%M:%S.%f") return obj
def test_galois(f, extra_isogenies, potenial_isogenies): K = NumberField(f, "a") superset, _ = get_isogeny_primes(K, **TEST_SETTINGS) assert set(EC_Q_ISOGENY_PRIMES).difference(superset) == set() assert ( extra_isogenies.difference(superset) == set() ), "Known isogenies are filtered out!" upperbound = potenial_isogenies.union(EC_Q_ISOGENY_PRIMES).union(extra_isogenies) unlisted_potential_isogenies = superset.difference(upperbound) assert unlisted_potential_isogenies == set(), "We got worse at filtering" assert set(upperbound.difference(superset)) == set(), "We got better at filtering"
def get_rational_cocycle_from_ap(self,getap,sign = 1,use_magma = True): F = self.group().base_ring() if F.signature()[1] == 0 or (F.signature() == (0,1) and 'G' not in self.group()._grouptype): K = (self.hecke_matrix(oo).transpose()-sign).kernel().change_ring(QQ) else: K = Matrix(QQ,self.dimension(),self.dimension(),0).kernel() disc = self.S_arithgroup().Gpn._O_discriminant discnorm = disc.norm() try: N = ZZ(discnorm.gen()) except AttributeError: N = ZZ(discnorm) if F == QQ: x = QQ['x'].gen() F = NumberField(x,names='a') q = ZZ(1) g0 = None while K.dimension() > 1: q = q.next_prime() for qq,e in F.ideal(q).factor(): if ZZ(qq.norm()).is_prime() and not qq.divides(F.ideal(disc.gens_reduced()[0])): try: ap = getap(qq) except (ValueError,ArithmeticError): continue try: K1 = (self.hecke_matrix(qq.gens_reduced()[0],g0 = g0,use_magma = use_magma).transpose()-ap).kernel() except RuntimeError: continue K = K.intersection(K1) if K.dimension() != 1: raise ValueError,'Group does not have the required system of eigenvalues' col = [ZZ(o) for o in (K.denominator()*K.matrix()).list()] return sum([ a * self.gen(i) for i,a in enumerate(col) if a != 0], self(0))
def field(self): r""" EXAMPLES:: sage: from EkEkstar import GeoSub sage: sub = {1:[1,2], 2:[1,3], 3:[1]} sage: E = GeoSub(sub,2) sage: E.field() Number Field in b with defining polynomial x^3 - x^2 - x - 1 """ M = self._sigma.incidence_matrix() b1 = max(M.eigenvalues(), key=abs) f = b1.minpoly() K = NumberField(f, 'b') return K
def __init__(self,lambda_squared=None, field=None): TranslationSurface_generic.__init__(self) if lambda_squared==None: from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R=PolynomialRing(ZZ,'x') x = R.gen() from sage.rings.qqbar import AA self._field=NumberField(x**3-ZZ(5)*x**2+ZZ(4)*x-ZZ(1), 'r', embedding=AA(ZZ(4))) self._l=self._field.gen() else: if field is None: self._l=lambda_squared self._field=lambda_squared.parent() else: self._field=field self._l=field(lambda_squared)
def as_embedded_number_field_elements(algs): # number_field_elements_from_algebraics() now takes an embedded=... # argument, but doesn't yet support all cases we need nf, elts, emb = number_field_elements_from_algebraics(algs) if nf is not QQ: nf = NumberField(nf.polynomial(), nf.variable_name(), embedding=emb(nf.gen())) elts = [elt.polynomial()(nf.gen()) for elt in elts] nf, hom = good_number_field(nf) elts = [hom(elt) for elt in elts] # assert [QQbar.coerce(elt) == alg for alg, elt in zip(algs, elts)] return nf, elts
def as_embedded_number_field_elements(algs): try: nf, elts, _ = number_field_elements_from_algebraics(algs, embedded=True) except NotImplementedError: # compatibility with Sage <= 9.3 nf, elts, emb = number_field_elements_from_algebraics(algs) if nf is not QQ: nf = NumberField(nf.polynomial(), nf.variable_name(), embedding=emb(nf.gen())) elts = [elt.polynomial()(nf.gen()) for elt in elts] nf, hom = good_number_field(nf) elts = [hom(elt) for elt in elts] assert [QQbar.coerce(elt) == alg for alg, elt in zip(algs, elts)] return nf, elts
def _number_field_with_integer_gen(K): if K is QQ: return QQ, ZZ den = K.defining_polynomial().denominator() if den.is_one(): # Ensure that we return the same number field object (coercions can be # slow!) intNF = K else: intgen = K.gen() * den ### Attempt to work around various problems with embeddings emb = K.coerce_embedding() embgen = emb(intgen) if emb else intgen intNF = NumberField(intgen.minpoly(), str(K.gen) + str(den), embedding=embgen) assert intNF != K # Work around weaknesses in coercions involving order elements, # including #14982 (fixed). Used to trigger #14989 (fixed). #return intNF, intNF.order(intNF.gen()) return intNF, intNF
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, return_pari_objects=True): r""" Enumerates *all* totally real fields of degree ``n`` with discriminant at most ``B``, primitive or otherwise. INPUT: - ``n`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] sage: enumerate_totallyreal_fields_all(1, 10) [[1, x - 1]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_all(2, 10) [[5, x^2 - x - 1], [8, x^2 - 2]] sage: type(enumerate_totallyreal_fields_all(2, 10)[0][1]) <type 'cypari2.gen.Gen'> sage: enumerate_totallyreal_fields_all(2, 10, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0, 0, 0, 0] if len(divisors(n)) > 4: raise ValueError("Only implemented for n = p*q with p,q prime") for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim( d, int(math.floor((1. * B)**(1. * d / n))), verbose=verbose) for i in range(len(Sds)): if verbose: print("=" * 80) print("Taking F =", Sds[i][1]) F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n / d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(4): counts[i] += T[0][i] S += [[t[0], pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0], t[1]] for t in T] for E in enumerate_totallyreal_fields_prim( n / d, int( math.floor((1. * B)**(1. / d) / (1. * Sds[i][0])**(n * 1. / d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append( [EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort(key=lambda x: (x[0], [QQ(cf) for cf in x[1].polrecip().Vec()])) weed_fields(S) # Output. if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print("=" * 80) print("Polynomials tested: {}".format(counts[0])) print("Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print("Irreducible polynomials: {}".format(counts[2])) print("Polynomials with nfdisc <= B: {}".format(counts[3])) for i in range(len(S)): print(S[i]) if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(_) for _ in counts], [[ZZ(s[0]), [QQ(_) for _ in s[1].polrecip().Vec()]] for s in S]] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[ZZ(s[0]), Px([QQ(_) for _ in s[1].list()])] for s in S]
def get_rational_cocycle(self,sign = 1,use_magma = True,bound = 3, return_all = False): F = self.group().base_ring() if F.signature()[1] == 0 or (F.signature()[1] == 1 and 'G' not in self.group()._grouptype): K = (self.hecke_matrix(oo).transpose()-sign).kernel().change_ring(QQ) else: K = Matrix(QQ,self.dimension(),self.dimension(),0).kernel() component_list = [] good_components = [] if K.dimension() == 1: good_components.append(K) else: component_list.append(K) disc = self.S_arithgroup().Gpn._O_discriminant discnorm = disc.norm() try: N = ZZ(discnorm.gen()) except AttributeError: N = ZZ(discnorm) if F == QQ: x = QQ['x'].gen() F = NumberField(x,names='a') q = ZZ(1) g0 = None num_hecke_operators = 0 while len(component_list) > 0 and num_hecke_operators < bound: verbose('num_hecke_ops = %s'%num_hecke_operators) verbose('len(components_list) = %s'%len(component_list)) q = q.next_prime() for qq,e in F.ideal(q).factor(): if ZZ(qq.norm()).is_prime() and not qq.divides(F.ideal(disc.gens_reduced()[0])): try: Aq = self.hecke_matrix(qq.gens_reduced()[0],g0 = g0,use_magma = use_magma).transpose().change_ring(QQ) except (RuntimeError,TypeError) as e: continue verbose('Computed hecke matrix at qq = %s'%qq) old_component_list = component_list component_list = [] num_hecke_operators += 1 for U in old_component_list: V = Aq.decomposition_of_subspace(U) for U0,is_irred in V: if Aq.restrict(U0).eigenvalues()[0] == ZZ(qq.norm()) + 1: continue # Do not take Eisenstein classes. if U0.dimension() == 1: good_components.append(U0) elif is_irred: # Bad continue else: # U0.dimension() > 1 and not is_irred component_list.append(U0) if len(good_components) > 0 and not return_all: K = good_components[0] col = [ZZ(o) for o in (K.denominator()*K.matrix()).list()] return sum([a*self.gen(i) for i,a in enumerate(col) if a != 0],self(0)) if len(component_list) == 0 or num_hecke_operators >= bound: break if len(good_components) == 0: raise ValueError('Group does not seem to be attached to an elliptic curve') else: if return_all: ans = [] for K in good_components: col = [ZZ(o) for o in (K.denominator()*K.matrix()).list()] ans.append( sum([a*self.gen(i) for i,a in enumerate(col) if a != 0],self(0))) return ans else: K = good_components[0] col = [ZZ(o) for o in (K.denominator()*K.matrix()).list()] return sum([ a * self.gen(i) for i,a in enumerate(col) if a != 0], self(0))
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False): r""" Enumerates *all* totally real fields of degree `n` with discriminant `\le B`, primitive or otherwise. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). """ S = [] counts = [0, 0, 0] if len(divisors(n)) > 4: raise ValueError, "Only implemented for n = p*q with p,q prime" for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim(d, int(math.floor((1.0 * B) ** (1.0 * d / n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "=" * 80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), "t") T = enumerate_totallyreal_fields_rel(F, n / d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(3): counts[i] += T[0][i] S += [[t[0], pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0], t[1]] for t in T] j = i + 1 for E in enumerate_totallyreal_fields_prim( n / d, int(math.floor((1.0 * B) ** (1.0 / d) / (1.0 * Sds[i][0]) ** (n * 1.0 / d ** 2))) ): for EF in F.composite_fields(NumberField(ZZx(E[1]), "u")): if EF.degree() == n and EF.disc() <= B: S.append([EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort() weed_fields(S) # Output. if verbose: saveout = sys.stdout if type(verbose) == str: fsock = open(verbose, "w") sys.stdout = fsock # Else, print to screen print "=" * 80 print "Polynomials tested:", counts[0] print "Irreducible polynomials:", counts[1] print "Polynomials with nfdisc <= B:", counts[2] for i in range(len(S)): print S[i] if type(verbose) == str: fsock.close() sys.stdout = saveout if return_seqs: return [counts, [[s[0], s[1].reverse().Vec()] for s in S]] else: return S
def _semistable_reducible_primes(E): r"""Find a list containing all semistable primes l unramified in K/QQ for which the Galois image for E could be reducible. INPUT: - ``E`` - EllipticCurve - over a number field. OUTPUT: list - A list of primes, which contains all primes l unramified in K/QQ, such that E is semistable at all primes lying over l, and the Galois image at l is reducible. If E has CM defined over its ground field, a ValueError is raised. EXAMPLES:: sage: E = EllipticCurve([0, -1, 1, -10, -20]) # X_0(11) sage: 5 in sage.schemes.elliptic_curves.gal_reps_number_field._semistable_reducible_primes(E) True """ E = _over_numberfield(E) K = E.base_field() deg_one_primes = K.primes_of_degree_one_iter() bad_primes = set([]) # This will store the output. # We find two primes (of distinct residue characteristics) which are # of degree 1, unramified in K/Q, and at which E has good reduction. # Both of these primes will give us a nontrivial divisibility constraint # on the exceptional primes l. For both of these primes P, we precompute # a generator and the trace of Frob_P^12. precomp = [] last_char = 0 # The residue characteristic of the most recent prime. while len(precomp) < 2: P = deg_one_primes.next() if not P.is_principal(): continue det = P.norm() if det == last_char: continue if P.ramification_index() != 1: continue try: tr = E.change_ring(P.residue_field()).trace_of_frobenius() except ArithmeticError: # Bad reduction at P. continue x = P.gens_reduced()[0] precomp.append((x, _tr12(tr, det))) last_char = det x, tx = precomp[0] y, ty = precomp[1] Kgal = K.galois_closure('b') maps = K.embeddings(Kgal) for i in xrange(2 ** (K.degree() - 1)): ## We iterate through all possible characters. ## # Here, if i = i_{l-1} i_{l-2} cdots i_1 i_0 in binary, then i # corresponds to the character prod sigma_j^{i_j}. phi1x = 1 phi2x = 1 phi1y = 1 phi2y = 1 # We compute the two algebraic characters at x and y: for j in xrange(K.degree()): if i % 2 == 1: phi1x *= maps[j](x) phi1y *= maps[j](y) else: phi2x *= maps[j](x) phi2y *= maps[j](y) i = int(i/2) # Any prime with reducible image must divide both of: gx = phi1x**12 + phi2x**12 - tx gy = phi1y**12 + phi2y**12 - ty if (gx != 0) or (gy != 0): for prime in Integer(Kgal.ideal([gx, gy]).norm()).prime_factors(): bad_primes.add(prime) continue ## It is possible that our curve has CM. ## # Our character must be of the form Nm^K_F for an imaginary # quadratic subfield F of K (which is the CM field if E has CM). # We compute F: a = (Integer(phi1x + phi2x)**2 - 4 * x.norm()).squarefree_part() y = QQ['y'].gen() F = NumberField(y**2 - a, 'a') # Next, we turn K into relative number field over F. K = K.relativize(F.embeddings(K)[0], 'b') E = E.change_ring(K.structure()[1]) ## We try to find a nontrivial divisibility condition. ## patience = 5 * K.absolute_degree() # Number of Frobenius elements to check before suspecting that E # has CM and computing the set of CM j-invariants of K to check. # TODO: Is this the best value for this parameter? while True: P = deg_one_primes.next() if not P.is_principal(): continue try: tr = E.change_ring(P.residue_field()).trace_of_frobenius() except ArithmeticError: # Bad reduction at P. continue x = P.gens_reduced()[0].norm(F) div = (x**12).trace() - _tr12(tr, x.norm()) patience -= 1 if div != 0: # We found our divisibility constraint. for prime in Integer(div).prime_factors(): bad_primes.add(prime) # Turn K back into an absolute number field. E = E.change_ring(K.structure()[0]) K = K.structure()[0].codomain() break if patience == 0: # We suspect that E has CM, so we check: f = K.structure()[0] if f(E.j_invariant()) in cm_j_invariants(f.codomain()): raise ValueError("The curve E should not have CM.") L = sorted(bad_primes) return L
along with this program. If not, see <https://www.gnu.org/licenses/>. The authors can be reached at: [email protected] and [email protected]. ==================================================================== """ from sage.all import QQ, prime_range from sage.arith.misc import next_prime, gcd from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring import polygen x = polygen(QQ) K = NumberField(x**2 - 11 * 17 * 9011 * 23629, "b") test_cases_true = [(K, int(q), q, False) for q in prime_range(1000)] test_cases_false = [(K, int(q), next_prime(q), True) for q in prime_range(1000)] def test_warmup(): for K, q, p, result in test_cases_true + test_cases_false: qq = (q * K).factor()[0][0] assert qq.is_coprime(p) == (not qq.divides(p)) == result def test_is_coprime(): for K, q, p, result in test_cases_true + test_cases_false: qq = (q * K).factor()[0][0] is_coprime = qq.is_coprime(p)
def lift_map(target): """ Create a lift map, to be used for lifting the cross ratios of a matroid representation. .. SEEALSO:: :meth:`lift_cross_ratios() <sage.matroids.utilities.lift_cross_ratios>` INPUT: - ``target`` -- a string describing the target (partial) field. OUTPUT: - a dictionary Depending on the value of ``target``, the following lift maps will be created: - "reg": a lift map from `\GF3` to the regular partial field `(\ZZ, <-1>)`. - "sru": a lift map from `\GF7` to the sixth-root-of-unity partial field `(\QQ(z), <z>)`, where `z` is a sixth root of unity. The map sends 3 to `z`. - "dyadic": a lift map from `\GF{11}` to the dyadic partial field `(\QQ, <-1, 2>)`. - "gm": a lift map from `\GF{19}` to the golden mean partial field `(\QQ(t), <-1,t>)`, where `t` is a root of `t^2-t-1`. The map sends `5` to `t`. The example below shows that the latter map satisfies three necessary conditions stated in :meth:`lift_cross_ratios() <sage.matroids.utilities.lift_cross_ratios>` EXAMPLES:: sage: from sage.matroids.utilities import lift_map sage: lm = lift_map('gm') sage: for x in lm: ....: if (x == 1) is not (lm[x] == 1): ....: print('not a proper lift map') ....: for y in lm: ....: if (x+y == 0) and not (lm[x]+lm[y] == 0): ....: print('not a proper lift map') ....: if (x+y == 1) and not (lm[x]+lm[y] == 1): ....: print('not a proper lift map') ....: for z in lm: ....: if (x*y==z) and not (lm[x]*lm[y]==lm[z]): ....: print('not a proper lift map') """ if target == "reg": R = GF(3) return {R(1): ZZ(1)} if target == "sru": R = GF(7) z = ZZ['z'].gen() S = NumberField(z * z - z + 1, 'z') return {R(1): S(1), R(3): S(z), R(3)**(-1): S(z)**5} if target == "dyadic": R = GF(11) return {R(1): QQ(1), R(-1): QQ(-1), R(2): QQ(2), R(6): QQ(1 / 2)} if target == "gm": R = GF(19) t = QQ['t'].gen() G = NumberField(t * t - t - 1, 't') return { R(1): G(1), R(5): G(t), R(1) / R(5): G(1) / G(t), R(-5): G(-t), R(-5)**(-1): G(-t)**(-1), R(5)**2: G(t)**2, R(5)**(-2): G(t)**(-2) } raise NotImplementedError(target)
def get_twodim_cocycle(self,sign = 1,use_magma = True,bound = 5, hecke_data = None, return_all = False, outfile = None): F = self.group().base_ring() if F == QQ: F = NumberField(PolynomialRing(QQ,'x').gen(),names='r') component_list = [] good_components = [] if F.signature()[1] == 0 or (F.signature() == (0,1) and 'G' not in self.group()._grouptype): Tinf = self.hecke_matrix(oo).transpose() K = (Tinf-sign).kernel().change_ring(QQ) if K.dimension() >= 2: component_list.append((K, [(oo,Tinf)])) fwrite('Too charpoly = %s'%Tinf.charpoly().factor(),outfile) else: K = Matrix(QQ,self.dimension(),self.dimension(),0).kernel() if K.dimension() >= 2: component_list.append((K, [])) disc = self.S_arithgroup().Gpn._O_discriminant discnorm = disc.norm() try: N = ZZ(discnorm.gen()) except AttributeError: N = ZZ(discnorm) if F == QQ: x = QQ['x'].gen() F = NumberField(x,names='a') q = ZZ(1) g0 = None num_hecke_operators = 0 if hecke_data is not None: qq = F.ideal(hecke_data[0]) pol = hecke_data[1] Aq = self.hecke_matrix(qq.gens_reduced()[0], use_magma = use_magma).transpose().change_ring(QQ) fwrite('ell = (%s,%s), T_ell charpoly = %s'%(qq.norm(), qq.gens_reduced()[0], Aq.charpoly().factor()),outfile) U0 = component_list[0][0].intersection(pol.subs(Aq).left_kernel()) if U0.dimension() != 2: raise ValueError('Hecke data does not suffice to cut out space') good_component = (U0.denominator() * U0,component_list[0][1] + [(qq.gens_reduced()[0],Aq)]) row0 = good_component[0].matrix().rows()[0] col0 = [QQ(o) for o in row0.list()] clcm = lcm([o.denominator() for o in col0]) col0 = [ZZ(clcm * o ) for o in col0] # phi1 = sum([a * phi for a,phi in zip(col0,self.gens())],self(0)) # phi2 = self.apply_hecke_operator(phi1,qq.gens_reduced()[0], use_magma = use_magma) # return [phi1, phi2], [(ell, o.restrict(good_component[0])) for ell, o in good_component[1]] flist = [] for row0 in good_component[0].matrix().rows(): col0 = [QQ(o) for o in row0.list()] clcm = lcm([o.denominator() for o in col0]) col0 = [ZZ(clcm * o ) for o in col0] flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0))) return flist,[(ell, o.restrict(good_component[0])) for ell, o in good_component[1]] while len(component_list) > 0 and num_hecke_operators < bound: verbose('num_hecke_ops = %s'%num_hecke_operators) verbose('len(components_list) = %s'%len(component_list)) q = q.next_prime() verbose('q = %s'%q) fact = F.ideal(q).factor() dfact = F.ideal(disc.gens_reduced()[0]).factor() for qq,e in fact: verbose('Trying qq = %s'%qq) if qq in [o for o,_ in dfact]: verbose('Skipping because qq divides D...') continue if ZZ(qq.norm()).is_prime() and not qq.divides(F.ideal(disc.gens_reduced()[0])): try: Aq = self.hecke_matrix(qq.gens_reduced()[0],g0 = g0,use_magma = use_magma).transpose().change_ring(QQ) except (RuntimeError,TypeError) as e: verbose('Skipping qq (=%s) because Hecke matrix could not be computed...'%qq.gens_reduced()[0]) continue except KeyboardInterrupt: verbose('Skipping qq (=%s) by user request...'%qq.gens_reduced()[0]) num_hecke_operators += 1 sleep(1) continue verbose('Computed hecke matrix at qq = %s'%qq) fwrite('ell = (%s,%s), T_ell charpoly = %s'%(qq.norm(), qq.gens_reduced()[0], Aq.charpoly().factor()),outfile) old_component_list = component_list component_list = [] num_hecke_operators += 1 for U,hecke_data in old_component_list: V = Aq.decomposition_of_subspace(U) for U0,is_irred in V: if U0.dimension() == 1: continue if U0.dimension() == 2 and is_irred: good_components.append((U0.denominator() * U0,hecke_data+[(qq.gens_reduced()[0],Aq)])) else: # U0.dimension() > 2 or not is_irred component_list.append((U0.denominator() * U0,hecke_data + [(qq.gens_reduced()[0],Aq)])) if len(good_components) > 0 and not return_all: flist = [] for row0 in good_components[0][0].matrix().rows(): col0 = [QQ(o) for o in row0.list()] clcm = lcm([o.denominator() for o in col0]) col0 = [ZZ(clcm * o ) for o in col0] flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0))) return flist,[(ell, o.restrict(good_components[0][0])) for ell, o in good_components[0][1]] if len(component_list) == 0 or num_hecke_operators >= bound: break if len(good_components) == 0: if not return_all: raise ValueError('Group does not seem to be attached to an abelian surface') else: return [] if return_all: ans = [] for K,hecke_data in good_components: flist = [] for row0 in K.matrix().rows(): col0 = [QQ(o) for o in row0.list()] clcm = lcm([o.denominator() for o in col0]) col0 = [ZZ(clcm * o ) for o in col0] flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0))) ans.append((flist,[(ell, o.restrict(K)) for ell, o in hecke_data])) return ans else: flist = [] for row0 in good_components[0][0].matrix().rows(): col0 = [QQ(o) for o in row0.list()] clcm = lcm([o.denominator() for o in col0]) col0 = [ZZ(clcm * o ) for o in col0] flist.append(sum([a * phi for a,phi in zip(col0,self.gens())],self(0))) return flist,[(ell, o.restrict(good_components[0][0])) for ell, o in good_components[0][1]]
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, return_pari_objects=True): r""" Enumerates *all* totally real fields of degree ``n`` with discriminant at most ``B``, primitive or otherwise. INPUT: - ``n`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] sage: enumerate_totallyreal_fields_all(1, 10) [[1, x - 1]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_all(2, 10) [[5, x^2 - x - 1], [8, x^2 - 2]] sage: enumerate_totallyreal_fields_all(2, 10)[0][1].parent() Interface to the PARI C library sage: enumerate_totallyreal_fields_all(2, 10, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0,0,0,0] if len(divisors(n)) > 4: raise ValueError("Only implemented for n = p*q with p,q prime") for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim(d, int(math.floor((1.*B)**(1.*d/n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "="*80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n/d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(4): counts[i] += T[0][i] S += [[t[0],pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0],t[1]] for t in T] j = i+1 for E in enumerate_totallyreal_fields_prim(n/d, int(math.floor((1.*B)**(1./d)/(1.*Sds[i][0])**(n*1./d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append([EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort(cmp=lambda x, y: cmp(x[0], y[0]) or cmp(x[1], y[1])) weed_fields(S) # Output. if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print "="*80 print "Polynomials tested: {}".format(counts[0]) print ( "Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print "Irreducible polynomials: {}".format(counts[2]) print "Polynomials with nfdisc <= B: {}".format(counts[3]) for i in range(len(S)): print S[i] if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(_) for _ in counts], [[ZZ(s[0]), [QQ(_) for _ in s[1].polrecip().Vec()]] for s in S]] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[ZZ(s[0]), Px([QQ(_) for _ in s[1].list()])] for s in S]
def _solve_two_equations(eqn1, eqn2, x_val, y_val): """ Given two polynomial equations with rational coefficients in 'x' and 'y' and real intervals for x and y to isolate a solution to the system of equations, return the number field generated by x and the value of y in that number field. If y is not contained in the number field generated by x, return None. """ # Ring we work in Q[x][y] Rx = PolynomialRing(RationalField(), 'x') R = PolynomialRing(Rx, 'y') Reqn1 = R(eqn1) # Compute the resultant in Q[x] resultant = Reqn1.resultant(R(eqn2)) # Factorize the resultant. Find the unique factor that has the given value # for x as a root. def eval_factor_res(p): """ Evaluation method for the factors of the resultant. Apply polynomial to the given interval for x. """ return p(x_val) resultant_factor = _find_unique_good_factor(resultant, eval_factor_res) resultant_factor = Rx(resultant_factor) # The number field generated by x. # # (The embedding passed to the NumberField is a real number, so sage # will raise an exception if the resultant_factor has no real roots. # However, such a factor should not make it through # _find_unique_good_factor) result_number_field = NumberField(resultant_factor, 'x', embedding=x_val.center()) # Get one of the equations and think of it as an element in NumberField[y] yEqn = Reqn1.change_ring(result_number_field) # Factorize that equation over the NumberField. Find the unique factor # such that the given value y is a root when setting x to the given value # for x. def eval_factor_yEqn(p): """ Evaluation method for the factors of the equation factored over the number field. We take the factor and turn it into a polynomial in Q[x][y]. We then put in the given intervals for x and y. """ lift = p.map_coefficients(lambda c: c.lift('x'), Rx) return lift.substitute(x=x_val, y=y_val) yEqn_factor = _find_unique_good_factor(yEqn, eval_factor_yEqn) # If the equation for y in x is not of degree 1, then y is in a field # extension of the number field generated by x. # Bail if this happens. if not yEqn_factor.degree() == 1: return None # The equation of y is of the form # linear_term * y + constant_term = 0 constant_term, linear_term = yEqn_factor.coefficients(sparse=False) # Thus, y is given by - constant_term / linear_term return result_number_field, -constant_term / linear_term
from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import NumberField from sage.misc.sage_eval import sage_eval R = ZZ['x'] x = R.gen() path = Path(__file__).parent CM_FIELDS = {} for fn in [ fn for fn in listdir(path) if isfile(path / fn) and fn.startswith("cm_fields_") ]: with open(path / fn) as f: s = "\n".join(l for l in f.read().splitlines() if not l.startswith("#")) for g in sage_eval(s, locals={"x": x}): d = g.degree() if d not in CM_FIELDS: CM_FIELDS[d] = [] CM_FIELDS[d].append(NumberField(g, f"a_{d}_{len(CM_FIELDS[d])+1}")) assert len(CM_FIELDS[2]) == 9 assert len(CM_FIELDS[4]) == 91 assert len(CM_FIELDS[6]) == 403 #for d,arr in CM_FIELDS.items(): # assert all(K.is_CM() and K.class_number() == 1 for K in arr) # assert all(not L1.is_isomorphic(L2) for L1,L2 in Subsets(arr,2))
def polyhedron_to_cone_surface(polyhedron, use_AA=False, scaling_factor=ZZ(1)): r"""Construct the Euclidean Cone Surface associated to the surface of a polyhedron and a map from the cone surface to the polyhedron. INPUT: - ``polyhedron`` -- A 3-dimensional polyhedron, which should be define over something that coerces into AA - ``use_AA`` -- If True, the surface returned will be defined over AA. If false, the algorithm will find the smallest NumberField and write the field there. - ``scaling_factor`` -- The surface returned will have a metric scaled by multiplication by this factor (compared with the original polyhendron). This can be used to produce a surface defined over a smaller NumberField. OUTPUT: A pair consisting of a ConeSurface and a ConeSurfaceToPolyhedronMap. EXAMPLES:: sage: from flatsurf.geometry.polyhedra import * sage: vertices=[] sage: for i in xrange(3): ....: temp=vector([1 if k==i else 0 for k in xrange(3)]) ....: for j in xrange(-1,3,2): ....: vertices.append(j*temp) sage: octahedron=Polyhedron(vertices=vertices) sage: surface,surface_to_octahedron = \ ....: polyhedron_to_cone_surface(octahedron,scaling_factor=AA(1/sqrt(2))) sage: TestSuite(surface).run() sage: TestSuite(surface_to_octahedron).run(skip="_test_pickling") sage: surface.num_polygons() 8 sage: surface.base_ring() Number Field in a with defining polynomial y^2 - 3 sage: sqrt3=surface.base_ring().gen() sage: tangent_bundle=surface.tangent_bundle() sage: v=tangent_bundle(0,(0,0),(sqrt3,2)) sage: traj=v.straight_line_trajectory() sage: traj.flow(10) sage: traj.is_saddle_connection() True sage: traj.combinatorial_length() 8 sage: surface_to_octahedron(traj) [(-1, 0, 0), (0.?e-18, -0.8000000000000000?, -0.2000000000000000?), (0.2500000000000000?, -0.750000000000000?, 0.?e-18), (0.4000000000000000?, 0.?e-19, 0.6000000000000000?), (0.?e-19, 0.500000000000000?, 0.500000000000000?), (-2/5, 3/5, 0), (-0.2500000000000000?, 0.?e-18, -0.750000000000000?), (0.?e-18, -0.2000000000000000?, -0.8000000000000000?), (1, 0, 0)] sage: surface_to_octahedron(traj.segment(0)) ((-1, 0, 0), (0.?e-18, -0.8000000000000000?, -0.2000000000000000?)) sage: surface_to_octahedron(traj.segment(0).start()) ((-1, 0, 0), (2.886751345948129?, -2.309401076758503?, -0.5773502691896258?)) sage: # octahedron.plot(wireframe=None,frame=False)+line3d(surface_to_octahedron(traj),radius=0.01) """ assert polyhedron.dim()==3 c=polyhedron.center() vertices=polyhedron.vertices() vertex_order={} for i,v in enumerate(vertices): vertex_order[v]=i faces=polyhedron.faces(2) face_order={} face_edges=[] face_vertices=[] face_map_data=[] for i,f in enumerate(faces): face_order[f]=i edges=f.as_polyhedron().faces(1) face_edges_temp = set() for edge in edges: edge_temp=set() for vertex in edge.vertices(): v=vertex.vector() v.set_immutable() edge_temp.add(v) face_edges_temp.add(frozenset(edge_temp)) last_edge = next(iter(face_edges_temp)) v = next(iter(last_edge)) face_vertices_temp=[v] for j in xrange(len(face_edges_temp)-1): for edge in face_edges_temp: if v in edge and edge!=last_edge: # bingo last_edge=edge for vv in edge: if vv!=v: v=vv face_vertices_temp.append(vv) break break v0=face_vertices_temp[0] v1=face_vertices_temp[1] v2=face_vertices_temp[2] n=(v1-v0).cross_product(v2-v0) if (v0-c).dot_product(n)<0: n=-n face_vertices_temp.reverse() v0=face_vertices_temp[0] v1=face_vertices_temp[1] v2=face_vertices_temp[2] face_vertices.append(face_vertices_temp) n=n/AA(n.norm()) w=v1-v0 w=w/AA(w.norm()) m=1/scaling_factor*matrix(AA,[w,n.cross_product(w),n]).transpose() mi=~m mis=mi.submatrix(0,0,2,3) face_map_data.append(( v0, # translation to bring origin in plane to v0 m.submatrix(0,0,3,2), -mis*v0, mis )) it=iter(face_vertices_temp) v_last=next(it) face_edge_dict={} j=0 for v in it: edge=frozenset([v_last,v]) face_edge_dict[edge]=j j+=1 v_last=v v=next(iter(face_vertices_temp)) edge=frozenset([v_last,v]) face_edge_dict[edge]=j face_edges.append(face_edge_dict) gluings={} for p1,face_edge_dict1 in enumerate(face_edges): for edge, e1 in face_edge_dict1.items(): found=False for p2, face_edge_dict2 in enumerate(face_edges): if p1!=p2 and edge in face_edge_dict2: e2=face_edge_dict2[edge] gluings[(p1,e1)]=(p2,e2) found=True break if not found: print(p1) print(e1) print(edge) raise RuntimeError("Failed to find glued edge") polygon_vertices_AA=[] for p,vs in enumerate(face_vertices): trans=face_map_data[p][2] m=face_map_data[p][3] polygon_vertices_AA.append([trans+m*v for v in vs]) if use_AA==True: Polys=Polygons(AA) polygons=[] for vs in polygon_vertices_AA: polygons.append(Polys(vertices=vs)) S=ConeSurface(surface_list_from_polygons_and_gluings(polygons,gluings)) return S, \ ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data) else: elts=[] for vs in polygon_vertices_AA: for v in vs: elts.append(v[0]) elts.append(v[1]) # Find the best number field: field,elts2,hom = number_field_elements_from_algebraics(elts,minimal=True) if field==QQ: # Defined over the rationals! polygon_vertices_field2=[] j=0 for vs in polygon_vertices_AA: vs2=[] for v in vs: vs2.append(vector(field,[elts2[j],elts2[j+1]])) j=j+2 polygon_vertices_field2.append(vs2) Polys=Polygons(field) polygons=[] for vs in polygon_vertices_field2: polygons.append(Polys(vertices=vs)) S=ConeSurface(surface_list_from_polygons_and_gluings(polygons,gluings)) return S, \ ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data) else: # Unfortunately field doesn't come with an real embedding (which is given by hom!) # So, we make a copy of the field, and add the embedding. field2=NumberField(field.polynomial(),name="a",embedding=hom(field.gen())) # The following converts from field to field2: hom2=field.hom(im_gens=[field2.gen()]) polygon_vertices_field2=[] j=0 for vs in polygon_vertices_AA: vs2=[] for v in vs: vs2.append(vector(field2,[hom2(elts2[j]),hom2(elts2[j+1])])) j=j+2 polygon_vertices_field2.append(vs2) Polys=Polygons(field2) polygons=[] for vs in polygon_vertices_field2: polygons.append(Polys(vertices=vs)) S=ConeSurface(surface_list_from_polygons_and_gluings(polygons,gluings)) return S, \ ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data)
def _semistable_reducible_primes(E): r"""Find a list containing all semistable primes l unramified in K/QQ for which the Galois image for E could be reducible. INPUT: - ``E`` - EllipticCurve - over a number field. OUTPUT: A list of primes, which contains all primes `l` unramified in `K/\mathbb{QQ}`, such that `E` is semistable at all primes lying over `l`, and the Galois image at `l` is reducible. If `E` has CM defined over its ground field, a ``ValueError`` is raised. EXAMPLES:: sage: E = EllipticCurve([0, -1, 1, -10, -20]) # X_0(11) sage: 5 in sage.schemes.elliptic_curves.gal_reps_number_field._semistable_reducible_primes(E) True """ E = _over_numberfield(E) K = E.base_field() deg_one_primes = K.primes_of_degree_one_iter() bad_primes = set([]) # This will store the output. # We find two primes (of distinct residue characteristics) which are # of degree 1, unramified in K/Q, and at which E has good reduction. # Both of these primes will give us a nontrivial divisibility constraint # on the exceptional primes l. For both of these primes P, we precompute # a generator and the trace of Frob_P^12. precomp = [] last_char = 0 # The residue characteristic of the most recent prime. while len(precomp) < 2: P = next(deg_one_primes) if not P.is_principal(): continue det = P.norm() if det == last_char: continue if P.ramification_index() != 1: continue try: tr = E.change_ring(P.residue_field()).trace_of_frobenius() except ArithmeticError: # Bad reduction at P. continue x = P.gens_reduced()[0] precomp.append((x, _tr12(tr, det))) last_char = det x, tx = precomp[0] y, ty = precomp[1] Kgal = K.galois_closure('b') maps = K.embeddings(Kgal) for i in xrange(2**(K.degree() - 1)): ## We iterate through all possible characters. ## # Here, if i = i_{l-1} i_{l-2} cdots i_1 i_0 in binary, then i # corresponds to the character prod sigma_j^{i_j}. phi1x = 1 phi2x = 1 phi1y = 1 phi2y = 1 # We compute the two algebraic characters at x and y: for j in xrange(K.degree()): if i % 2 == 1: phi1x *= maps[j](x) phi1y *= maps[j](y) else: phi2x *= maps[j](x) phi2y *= maps[j](y) i = int(i / 2) # Any prime with reducible image must divide both of: gx = phi1x**12 + phi2x**12 - tx gy = phi1y**12 + phi2y**12 - ty if (gx != 0) or (gy != 0): for prime in Integer(Kgal.ideal([gx, gy]).norm()).prime_factors(): bad_primes.add(prime) continue ## It is possible that our curve has CM. ## # Our character must be of the form Nm^K_F for an imaginary # quadratic subfield F of K (which is the CM field if E has CM). # We compute F: a = (Integer(phi1x + phi2x)**2 - 4 * x.norm()).squarefree_part() y = QQ['y'].gen() F = NumberField(y**2 - a, 'a') # Next, we turn K into relative number field over F. K = K.relativize(F.embeddings(K)[0], 'b') E = E.change_ring(K.structure()[1]) ## We try to find a nontrivial divisibility condition. ## patience = 5 * K.absolute_degree() # Number of Frobenius elements to check before suspecting that E # has CM and computing the set of CM j-invariants of K to check. # TODO: Is this the best value for this parameter? while True: P = next(deg_one_primes) if not P.is_principal(): continue try: tr = E.change_ring(P.residue_field()).trace_of_frobenius() except ArithmeticError: # Bad reduction at P. continue x = P.gens_reduced()[0].norm(F) div = (x**12).trace() - _tr12(tr, x.norm()) patience -= 1 if div != 0: # We found our divisibility constraint. for prime in Integer(div).prime_factors(): bad_primes.add(prime) # Turn K back into an absolute number field. E = E.change_ring(K.structure()[0]) K = K.structure()[0].codomain() break if patience == 0: # We suspect that E has CM, so we check: f = K.structure()[0] if f(E.j_invariant()) in cm_j_invariants(f.codomain()): raise ValueError("The curve E should not have CM.") L = sorted(bad_primes) return L
def is_Q_curve(E, maxp=100, certificate=False, verbose=False): r""" Return whether ``E`` is a `\QQ`-curve, with optional certificate. INPUT: - ``E`` (elliptic curve) -- an elliptic curve over a number field. - ``maxp`` (int, default 100): bound on primes used for checking necessary local conditions. The result will not depend on this, but using a larger value may return ``False`` faster. - ``certificate`` (bool, default ``False``): if ``True`` then a second value is returned giving a certificate for the `\QQ`-curve property. OUTPUT: If ``certificate`` is ``False``: either ``True`` (if `E` is a `\QQ`-curve), or ``False``. If ``certificate`` is ``True``: a tuple consisting of a boolean flag as before and a certificate, defined as follows: - when the flag is ``True``, so `E` is a `\QQ`-curve: - either {'CM':`D`} where `D` is a negative discriminant, when `E` has potential CM with discriminant `D`; - otherwise {'CM': `0`, 'core_poly': `f`, 'rho': `\rho`, 'r': `r`, 'N': `N`}, when `E` is a non-CM `\QQ`-curve, where the core polynomial `f` is an irreducible monic polynomial over `QQ` of degree `2^\rho`, all of whose roots are `j`-invariants of curves isogenous to `E`, the core level `N` is a square-free integer with `r` prime factors which is the LCM of the degrees of the isogenies between these conjugates. For example, if there exists a curve `E'` isogenous to `E` with `j(E')=j\in\QQ`, then the certificate is {'CM':0, 'r':0, 'rho':0, 'core_poly': x-j, 'N':1}. - when the flag is ``False``, so `E` is not a `\QQ`-curve, the certificate is a prime `p` such that the reductions of `E` at the primes dividing `p` are inconsistent with the property of being a `\QQ`-curve. See the ALGORITHM section for details. ALGORITHM: See [CrNa2020]_ for details. 1. If `E` has rational `j`-invariant, or has CM, then return ``True``. 2. Replace `E` by a curve defined over `K=\QQ(j(E))`. Let `N` be the conductor norm. 3. For all primes `p\mid N` check that the valuations of `j` at all `P\mid p` are either all negative or all non-negative; if not, return ``False``. 4. For `p\le maxp`, `p\not\mid N`, check that either `E` is ordinary mod `P` for all `P\mid p`, or `E` is supersingular mod `P` for all `P\mid p`; if neither, return ``False``. If all are ordinary, check that the integers `a_P(E)^2-4N(P)` have the same square-free part; if not, return ``False``. 5. Compute the `K`-isogeny class of `E` using the "heuristic" option (which is faster, but not guaranteed to be complete). Check whether the set of `j`-invariants of curves in the class of `2`-power degree contains a complete Galois orbit. If so, return ``True``. 6. Otherwise repeat step 4 for more primes, and if still undecided, repeat Step 5 without the "heuristic" option, to get the complete `K`-isogeny class (which will probably be no bigger than before). Now return ``True`` if the set of `j`-invariants of curves in the class contains a complete Galois orbit, otherwise return ``False``. EXAMPLES: A non-CM curve over `\QQ` and a CM curve over `\QQ` are both trivially `\QQ`-curves:: sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve sage: E = EllipticCurve([1,2,3,4,5]) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0} sage: E = EllipticCurve(j=8000) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': -8} A non-`\QQ`-curve over a quartic field. The local data at bad primes above `3` is inconsistent:: sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(R([3, 0, -5, 0, 1])) sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])]) sage: is_Q_curve(E, certificate=True, verbose=True) Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve No: inconsistency at the 2 primes dividing 3 - potentially multiplicative: [True, False] (False, 3) A non-`\QQ`-curve over a quadratic field. The local data at bad primes is consistent, but the local test at good primes above `13` is not:: sage: K.<a> = NumberField(R([-10, 0, 1])) sage: E = EllipticCurve([K([0,1]),K([-1,-1]),K([0,0]),K([-236,40]),K([-1840,464])]) sage: is_Q_curve(E, certificate=True, verbose=True) Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve Applying local tests at good primes above p<=100 No: inconsistency at the 2 ordinary primes dividing 13 - Frobenius discriminants mod squares: [-1, -3] No: local test at p=13 failed (False, 13) A quadratic `\QQ`-curve with CM discriminant `-15` (`j`-invariant not in `\QQ`):: sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(R([-1, -1, 1])) sage: E = EllipticCurve([K([1,0]),K([-1,0]),K([0,1]),K([0,-2]),K([0,1])]) sage: is_Q_curve(E, certificate=True, verbose=True) Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve Yes: E is CM (discriminant -15) (True, {'CM': -15}) An example over `\QQ(\sqrt{2},\sqrt{3})`. The `j`-invariant is in `\QQ(\sqrt{6})`, so computations will be done over that field, and in fact there is an isogenous curve with rational `j`, so we have a so-called rational `\QQ`-curve:: sage: K.<a> = NumberField(R([1, 0, -4, 0, 1])) sage: E = EllipticCurve([K([-2,-4,1,1]),K([0,1,0,0]),K([0,1,0,0]),K([-4780,9170,1265,-2463]),K([163923,-316598,-43876,84852])]) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0} Over the same field, a so-called strict `\QQ`-curve which is not isogenous to one with rational `j`, but whose core field is quadratic. In fact the isogeny class over `K` consists of `6` curves, four with conjugate quartic `j`-invariants and `2` with quadratic conjugate `j`-invariants in `\QQ(\sqrt{3})` (but which are not base-changes from the quadratic subfield):: sage: E = EllipticCurve([K([0,-3,0,1]),K([1,4,0,-1]),K([0,0,0,0]),K([-2,-16,0,4]),K([-19,-32,4,8])]) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': 0, 'N': 2, 'core_degs': [1, 2], 'core_poly': x^2 - 840064*x + 1593413632, 'r': 1, 'rho': 1} """ from sage.rings.number_field.number_field_base import is_NumberField if verbose: print("Checking whether {} is a Q-curve".format(E)) try: assert is_NumberField(E.base_field()) except (AttributeError, AssertionError): raise TypeError( "{} must be an elliptic curve defined over a number field in is_Q_curve()" ) from sage.rings.integer_ring import ZZ from sage.arith.functions import lcm from sage.libs.pari import pari from sage.rings.number_field.number_field import NumberField from sage.schemes.elliptic_curves.constructor import EllipticCurve from sage.schemes.elliptic_curves.cm import cm_j_invariants_and_orders, is_cm_j_invariant # Step 1 # all curves with rational j-invariant are Q-curves: jE = E.j_invariant() if jE in QQ: if verbose: print("Yes: j(E) is in QQ") if certificate: # test for CM for d, f, j in cm_j_invariants_and_orders(QQ): if jE == j: return True, {'CM': d * f**2} # else not CM return True, { 'CM': ZZ(0), 'r': ZZ(0), 'rho': ZZ(0), 'N': ZZ(1), 'core_poly': polygen(QQ) } else: return True # CM curves are Q-curves: flag, df = is_cm_j_invariant(jE) if flag: d, f = df D = d * f**2 if verbose: print("Yes: E is CM (discriminant {})".format(D)) if certificate: return True, {'CM': D} else: return True # Step 2: replace E by a curve defined over Q(j(E)): K = E.base_field() jpoly = jE.minpoly() if jpoly.degree() < K.degree(): if verbose: print("switching to smaller base field: j's minpoly is {}".format( jpoly)) f = pari(jpoly).polredbest().sage({'x': jpoly.parent().gen()}) K2 = NumberField(f, 'b') jE = jpoly.roots(K2)[0][0] if verbose: print("New j is {} over {}, with minpoly {}".format( jE, K2, jE.minpoly())) #assert jE.minpoly()==jpoly E = EllipticCurve(j=jE) K = K2 if verbose: print("New test curve is {}".format(E)) # Step 3: check primes of bad reduction NN = E.conductor().norm() for p in NN.support(): Plist = K.primes_above(p) if len(Plist) < 2: continue # pot_mult = potential multiplicative reduction pot_mult = [jE.valuation(P) < 0 for P in Plist] consistent = all(pot_mult) or not any(pot_mult) if not consistent: if verbose: print("No: inconsistency at the {} primes dividing {}".format( len(Plist), p)) print(" - potentially multiplicative: {}".format(pot_mult)) if certificate: return False, p else: return False # Step 4 check: primes P of good reduction above p<=B: if verbose: print("Applying local tests at good primes above p<={}".format(maxp)) res4, p = Step4Test(E, B=maxp, oldB=0, verbose=verbose) if not res4: if verbose: print("No: local test at p={} failed".format(p)) if certificate: return False, p else: return False if verbose: print("...all local tests pass for p<={}".format(maxp)) # Step 5: compute the (partial) K-isogeny class of E and test the # set of j-invariants in the class: C = E.isogeny_class(algorithm='heuristic', minimal_models=False) jC = [E2.j_invariant() for E2 in C] centrejpols = conjugacy_test(jC, verbose=verbose) if centrejpols: if verbose: print( "Yes: the isogeny class contains a complete conjugacy class of j-invariants" ) if certificate: for f in centrejpols: rho = f.degree().valuation(2) centre_indices = [i for i, j in enumerate(jC) if f(j) == 0] M = C.matrix() core_degs = [M[centre_indices[0], i] for i in centre_indices] level = lcm(core_degs) if level.is_squarefree(): r = len(level.prime_divisors()) cert = { 'CM': ZZ(0), 'core_poly': f, 'rho': rho, 'r': r, 'N': level, 'core_degs': core_degs } return True, cert print("No central curve found") else: return True # Now we are undecided. This can happen if either (1) E is not a # Q-curve but we did not use enough primes in Step 4 to detect # this, or (2) E is a Q-curve but in Step 5 we did not compute the # complete isogeny class. Case (2) is most unlikely since the # heuristic bound used in computing isogeny classes means that we # have all isogenous curves linked to E by an isogeny of degree # supported on primes<1000. # We first rerun Step 4 with a larger bound. xmaxp = 10 * maxp if verbose: print( "Undecided after first round, so we apply more local tests, up to {}" .format(xmaxp)) res4, p = Step4Test(E, B=xmaxp, oldB=maxp, verbose=verbose) if not res4: if verbose: print("No: local test at p={} failed".format(p)) if certificate: return False, p else: return False # Now we rerun Step 5 using a rigorous computation of the complete # isogeny class. This will probably contain no more curves than # before, in which case -- since we already tested that the set of # j-invariants does not contain a complete Galois conjugacy class # -- we can deduce that E is not a Q-curve. if verbose: print("...all local tests pass for p<={}".format(xmaxp)) print("We now compute the complete isogeny class...") Cfull = E.isogeny_class(minimal_models=False) jCfull = [E2.j_invariant() for E2 in Cfull] if len(jC) == len(jCfull): if verbose: print("...and find that we already had the complete class:so No") if certificate: return False, 0 else: return False if verbose: print( "...and find that the class contains {} curves, not just the {} we computed originally" .format(len(jCfull), len(jC))) centrejpols = conjugacy_test(jCfull, verbose=verbose) if cert: if verbose: print( "Yes: the isogeny class contains a complete conjugacy class of j-invariants" ) if certificate: return True, centrejpols else: return True if verbose: print( "No: the isogeny class does *not* contain a complete conjugacy class of j-invariants" ) if certificate: return False, 0 else: return False
#!/usr/bin/env sage from __future__ import print_function from sage import * from sage.rings.number_field.number_field import NumberField #import sys #print(sys.argv) if 1: K = NumberField(x ^ 4 + 1) r2 = e8 + e8 ^ 7 assert r2**2 == 2 G = K.galois_group() assert len(G) == 4
def polyhedron_to_cone_surface(polyhedron, use_AA=False, scaling_factor=ZZ(1)): r"""Construct the Euclidean Cone Surface associated to the surface of a polyhedron and a map from the cone surface to the polyhedron. INPUT: - ``polyhedron`` -- A 3-dimensional polyhedron, which should be define over something that coerces into AA - ``use_AA`` -- If True, the surface returned will be defined over AA. If false, the algorithm will find the smallest NumberField and write the field there. - ``scaling_factor`` -- The surface returned will have a metric scaled by multiplication by this factor (compared with the original polyhendron). This can be used to produce a surface defined over a smaller NumberField. OUTPUT: A pair consisting of a ConeSurface and a ConeSurfaceToPolyhedronMap. EXAMPLES:: sage: from flatsurf.geometry.polyhedra import * sage: vertices=[] sage: for i in range(3): ....: temp=vector([1 if k==i else 0 for k in range(3)]) ....: for j in range(-1,3,2): ....: vertices.append(j*temp) sage: octahedron=Polyhedron(vertices=vertices) sage: surface,surface_to_octahedron = \ ....: polyhedron_to_cone_surface(octahedron,scaling_factor=AA(1/sqrt(2))) sage: TestSuite(surface).run() sage: TestSuite(surface_to_octahedron).run(skip="_test_pickling") sage: surface.num_polygons() 8 sage: surface.base_ring() Number Field in a with defining polynomial y^2 - 3 with a = 1.732050807568878? sage: sqrt3=surface.base_ring().gen() sage: tangent_bundle=surface.tangent_bundle() sage: v=tangent_bundle(0,(0,0),(sqrt3,2)) sage: traj=v.straight_line_trajectory() sage: traj.flow(10) sage: traj.is_saddle_connection() True sage: traj.combinatorial_length() 8 sage: path3d = surface_to_octahedron(traj) sage: len(path3d) 9 sage: # We will show that the length of the path is sqrt(42): sage: total_length = 0 sage: for i in range(8): ....: start = path3d[i] ....: end = path3d[i+1] ....: total_length += (vector(end)-vector(start)).norm() sage: ZZ(total_length**2) 42 """ assert polyhedron.dim() == 3 c = polyhedron.center() vertices = polyhedron.vertices() vertex_order = {} for i, v in enumerate(vertices): vertex_order[v] = i faces = polyhedron.faces(2) face_order = {} face_edges = [] face_vertices = [] face_map_data = [] for i, f in enumerate(faces): face_order[f] = i edges = f.as_polyhedron().faces(1) face_edges_temp = set() for edge in edges: edge_temp = set() for vertex in edge.vertices(): v = vertex.vector() v.set_immutable() edge_temp.add(v) face_edges_temp.add(frozenset(edge_temp)) last_edge = next(iter(face_edges_temp)) v = next(iter(last_edge)) face_vertices_temp = [v] for j in range(len(face_edges_temp) - 1): for edge in face_edges_temp: if v in edge and edge != last_edge: # bingo last_edge = edge for vv in edge: if vv != v: v = vv face_vertices_temp.append(vv) break break v0 = face_vertices_temp[0] v1 = face_vertices_temp[1] v2 = face_vertices_temp[2] n = (v1 - v0).cross_product(v2 - v0) if (v0 - c).dot_product(n) < 0: n = -n face_vertices_temp.reverse() v0 = face_vertices_temp[0] v1 = face_vertices_temp[1] v2 = face_vertices_temp[2] face_vertices.append(face_vertices_temp) n = n / AA(n.norm()) w = v1 - v0 w = w / AA(w.norm()) m = 1 / scaling_factor * matrix( AA, [w, n.cross_product(w), n]).transpose() mi = ~m mis = mi.submatrix(0, 0, 2, 3) face_map_data.append(( v0, # translation to bring origin in plane to v0 m.submatrix(0, 0, 3, 2), -mis * v0, mis)) it = iter(face_vertices_temp) v_last = next(it) face_edge_dict = {} j = 0 for v in it: edge = frozenset([v_last, v]) face_edge_dict[edge] = j j += 1 v_last = v v = next(iter(face_vertices_temp)) edge = frozenset([v_last, v]) face_edge_dict[edge] = j face_edges.append(face_edge_dict) gluings = {} for p1, face_edge_dict1 in enumerate(face_edges): for edge, e1 in face_edge_dict1.items(): found = False for p2, face_edge_dict2 in enumerate(face_edges): if p1 != p2 and edge in face_edge_dict2: e2 = face_edge_dict2[edge] gluings[(p1, e1)] = (p2, e2) found = True break if not found: print(p1) print(e1) print(edge) raise RuntimeError("Failed to find glued edge") polygon_vertices_AA = [] for p, vs in enumerate(face_vertices): trans = face_map_data[p][2] m = face_map_data[p][3] polygon_vertices_AA.append([trans + m * v for v in vs]) if use_AA == True: Polys = ConvexPolygons(AA) polygons = [] for vs in polygon_vertices_AA: polygons.append(Polys(vertices=vs)) S = ConeSurface( surface_list_from_polygons_and_gluings(polygons, gluings)) return S, \ ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data) else: elts = [] for vs in polygon_vertices_AA: for v in vs: elts.append(v[0]) elts.append(v[1]) # Find the best number field: field, elts2, hom = number_field_elements_from_algebraics(elts, minimal=True) if field == QQ: # Defined over the rationals! polygon_vertices_field2 = [] j = 0 for vs in polygon_vertices_AA: vs2 = [] for v in vs: vs2.append(vector(field, [elts2[j], elts2[j + 1]])) j = j + 2 polygon_vertices_field2.append(vs2) Polys = ConvexPolygons(field) polygons = [] for vs in polygon_vertices_field2: polygons.append(Polys(vertices=vs)) S = ConeSurface( surface_list_from_polygons_and_gluings(polygons, gluings)) return S, \ ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data) else: # Unfortunately field doesn't come with an real embedding (which is given by hom!) # So, we make a copy of the field, and add the embedding. field2 = NumberField(field.polynomial(), name="a", embedding=hom(field.gen())) # The following converts from field to field2: hom2 = field.hom(im_gens=[field2.gen()]) polygon_vertices_field2 = [] j = 0 for vs in polygon_vertices_AA: vs2 = [] for v in vs: vs2.append( vector(field2, [hom2(elts2[j]), hom2(elts2[j + 1])])) j = j + 2 polygon_vertices_field2.append(vs2) Polys = ConvexPolygons(field2) polygons = [] for vs in polygon_vertices_field2: polygons.append(Polys(vertices=vs)) S = ConeSurface( surface_list_from_polygons_and_gluings(polygons, gluings)) return S, \ ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data)
def sage_object(self): X = PolynomialRing(QQ, "x") from sage.rings.number_field.number_field import NumberField return NumberField(X(map(str, self.polynomial())), "x")
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False): r""" Enumerates *all* totally real fields of degree `n` with discriminant `\le B`, primitive or otherwise. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). TESTS: The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0,0,0] if len(divisors(n)) > 4: raise ValueError, "Only implemented for n = p*q with p,q prime" for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim(d, int(math.floor((1.*B)**(1.*d/n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "="*80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n/d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(3): counts[i] += T[0][i] S += [[t[0],pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0],t[1]] for t in T] j = i+1 for E in enumerate_totallyreal_fields_prim(n/d, int(math.floor((1.*B)**(1./d)/(1.*Sds[i][0])**(n*1./d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append([EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort() weed_fields(S) # Output. if verbose: saveout = sys.stdout if type(verbose) == str: fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print "="*80 print "Polynomials tested:", counts[0] print "Irreducible polynomials:", counts[1] print "Polynomials with nfdisc <= B:", counts[2] for i in range(len(S)): print S[i] if type(verbose) == str: fsock.close() sys.stdout = saveout if return_seqs: return [counts,[[s[0],s[1].reverse().Vec()] for s in S]] else: return S
class EInfinitySurface(Surface): r""" The surface based on the $E_\infinity$ graph. The biparite graph is shown below, with edges numbered: 0 1 2 -2 3 -3 4 -4 *---o---*---o---*---o---*---o---*... | |-1 o Here, black vertices are colored *, and white o. Black nodes represent vertical cylinders and white nodes represent horizontal cylinders. """ def __init__(self,lambda_squared=None, field=None): TranslationSurface_generic.__init__(self) if lambda_squared==None: from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R=PolynomialRing(ZZ,'x') x = R.gen() from sage.rings.qqbar import AA self._field=NumberField(x**3-ZZ(5)*x**2+ZZ(4)*x-ZZ(1), 'r', embedding=AA(ZZ(4))) self._l=self._field.gen() else: if field is None: self._l=lambda_squared self._field=lambda_squared.parent() else: self._field=field self._l=field(lambda_squared) Surface.__init__(self) def _repr_(self): r""" String representation. """ return "The E-infinity surface" def base_ring(self): r""" Return the rational field. """ return self._field def base_label(self): return ZZ.zero() @cached_method def get_white(self,n): r"""Get the weight of the white endpoint of edge n.""" l=self._l if n==0 or n==1: return l if n==-1: return l-1 if n==2: return 1-3*l+l**2 if n>2: x=self.get_white(n-1) y=self.get_black(n) return l*y-x return self.get_white(-n) @cached_method def get_black(self,n): r"""Get the weight of the black endpoint of edge n.""" l=self._l if n==0: return self._field(1) if n==1 or n==-1 or n==2: return l-1 if n>2: x=self.get_black(n-1) y=self.get_white(n-1) return y-x return self.get_black(1-n) def polygon(self, lab): r""" Return the polygon labeled by ``lab``. """ if lab not in self.polygon_labels(): raise ValueError("lab (=%s) not a valid label"%lab) from flatsurf.geometry.polygon import rectangle return rectangle(2*self.get_black(lab),self.get_white(lab)) def polygon_labels(self): r""" The set of labels used for the polygons. """ return ZZ def opposite_edge(self, p, e): r""" Return the pair ``(pp,ee)`` to which the edge ``(p,e)`` is glued to. """ if p==0: if e==0: return (0,2) if e==1: return (1,3) if e==2: return (0,0) if e==3: return (1,1) if p==1: if e==0: return (-1,2) if e==1: return (0,3) if e==2: return (2,0) if e==3: return (0,1) if p==-1: if e==0: return (2,2) if e==1: return (-1,3) if e==2: return (1,0) if e==3: return (-1,1) if p==2: if e==0: return (1,2) if e==1: return (-2,3) if e==2: return (-1,0) if e==3: return (-2,1) if p>2: if e%2: return -p,(e+2)%4 else: return 1-p,(e+2)%4 else: if e%2: return -p,(e+2)%4 else: return 1-p,(e+2)%4 def is_finite(self): return False