def small_rhombicuboctahedron(self, exact=True, base_ring=None): """ Return the (small) rhombicuboctahedron. The rhombicuboctahedron is an Archimedean solid with 24 vertices and 26 faces. See the :wikipedia:`Rhombicuboctahedron` for more information. INPUT: - ``exact`` -- (boolean, default ``True``) If ``False`` use an approximate ring for the coordinates. - ``base_ring`` -- the ring in which the coordinates will belong to. If it is not provided and ``exact=True`` it will be a the number field `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False`` it will be the real double field. EXAMPLES:: sage: sr = polytopes.small_rhombicuboctahedron() sage: sr.f_vector() (1, 24, 48, 26, 1) sage: sr.volume() 80/3*sqrt2 + 32 The faces are `8` equilateral triangles and `18` squares:: sage: sum(1 for f in sr.faces(2) if len(f.vertices()) == 3) 8 sage: sum(1 for f in sr.faces(2) if len(f.vertices()) == 4) 18 Its non exact version:: sage: sr = polytopes.small_rhombicuboctahedron(False) sage: sr A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices sage: sr.f_vector() (1, 24, 48, 26, 1) """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField K = QuadraticField(2, 'sqrt2') sqrt2 = K.gen() base_ring = K else: if base_ring is None: base_ring = RDF sqrt2 = base_ring(2).sqrt() one = base_ring.one() a = sqrt2 + one verts = [] verts.extend([s1*one, s2*one, s3*a] for s1,s2,s3 in itertools.product([1,-1], repeat=3)) verts.extend([s1*one, s3*a, s2*one] for s1,s2,s3 in itertools.product([1,-1], repeat=3)) verts.extend([s1*a, s2*one, s3*one] for s1,s2,s3 in itertools.product([1,-1], repeat=3)) return Polyhedron(vertices=verts)
def great_rhombicuboctahedron(self, exact=True, base_ring=None): """ Return the great rhombicuboctahedron. The great rohombicuboctahedron (or truncated cuboctahedron) is an Archimedean solid with 48 vertices and 26 faces. For more information see the :wikipedia:`Truncated_cuboctahedron`. INPUT: - ``exact`` -- (boolean, default ``True``) If ``False`` use an approximate ring for the coordinates. - ``base_ring`` -- the ring in which the coordinates will belong to. If it is not provided and ``exact=True`` it will be a the number field `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False`` it will be the real double field. EXAMPLES:: sage: gr = polytopes.great_rhombicuboctahedron() # long time ~ 3sec sage: gr.f_vector() # long time (1, 48, 72, 26, 1) A faster implementation is obtained by setting ``exact=False``:: sage: gr = polytopes.great_rhombicuboctahedron(exact=False) sage: gr.f_vector() (1, 48, 72, 26, 1) Its faces are 4 squares, 8 regular hexagons and 6 regular octagons:: sage: sum(1 for f in gr.faces(2) if len(f.vertices()) == 4) 12 sage: sum(1 for f in gr.faces(2) if len(f.vertices()) == 6) 8 sage: sum(1 for f in gr.faces(2) if len(f.vertices()) == 8) 6 """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField K = QuadraticField(2, 'sqrt2') sqrt2 = K.gen() base_ring = K else: if base_ring is None: base_ring = RDF sqrt2 = base_ring(2).sqrt() one = base_ring.one() v1 = sqrt2 + 1 v2 = 2*sqrt2 + 1 verts = [ [s1*z1, s2*z2, s3*z3] for z1,z2,z3 in itertools.permutations([one,v1,v2]) for s1,s2,s3 in itertools.product([1,-1], repeat=3)] return Polyhedron(vertices=verts, base_ring=base_ring)
def six_hundred_cell(self, exact=False): """ Return the standard 600-cell polytope. The 600-cell is a 4-dimensional regular polytope. In many ways this is an analogue of the icosahedron. .. WARNING:: The coordinates are not exact by default. The computation with exact coordinates takes a huge amount of time. INPUT: - ``exact`` - (boolean, default ``False``) if ``True`` use exact coordinates instead of floating point approximations EXAMPLES:: sage: p600 = polytopes.six_hundred_cell() sage: p600 A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 120 vertices sage: p600.f_vector() (1, 120, 720, 1200, 600, 1) Computation with exact coordinates is currently too long to be useful:: sage: p600 = polytopes.six_hundred_cell(exact=True) # not tested - very long time sage: len(list(p600.bounded_edges())) # not tested - very long time 120 """ if exact: from sage.rings.number_field.number_field import QuadraticField K = QuadraticField(5, 'sqrt5') sqrt5 = K.gen() g = (1 + sqrt5) / 2 base_ring = K else: g = (1 + RDF(5).sqrt()) / 2 base_ring = RDF q12 = base_ring(1) / base_ring(2) z = base_ring.zero() verts = [[s1*q12, s2*q12, s3*q12, s4*q12] for s1,s2,s3,s4 in itertools.product([1,-1], repeat=4)] V = (base_ring)**4 verts.extend(V.basis()) verts.extend(-v for v in V.basis()) pts = [[s1 * q12, s2*g/2, s3/(2*g), z] for (s1,s2,s3) in itertools.product([1,-1], repeat=3)] for p in AlternatingGroup(4): verts.extend(p(x) for x in pts) return Polyhedron(vertices=verts, base_ring=base_ring)
def __init__(self, D): self.D = D assert (D*D - D) % 4 == 0 self.field = QuadraticField(D) self.maxorder = self.field.maximal_order() self.Droot = self.field(D).sqrt() self.DrootHalf_coordinates_in_terms_of_powers = (self.Droot / 2).coordinates_in_terms_of_powers()
def is_quasigeometric(self): """ Decide whether the binary recurrence sequence is degenerate and similar to a geometric sequence, i.e. the union of multiple geometric sequences, or geometric after term ``u0``. If `\\alpha/\\beta` is a `k` th root of unity, where `k>1`, then necessarily `k = 2, 3, 4, 6`. Then `F = [[0,1],[c,b]` is diagonalizable, and `F^k = [[\\alpha^k, 0], [0,\\beta^k]]` is scaler matrix. Thus for all values of `j` mod `k`, the `j` mod `k` terms of `u_n` form a geometric series. If `\\alpha` or `\\beta` is zero, this implies that `c=0`. This is the case when `F` is singular. In this case, `u_1, u_2, u_3, ...` is geometric. EXAMPLES:: sage: S = BinaryRecurrenceSequence(0,1) sage: [S(i) for i in range(10)] [0, 1, 0, 1, 0, 1, 0, 1, 0, 1] sage: S.is_quasigeometric() True sage: R = BinaryRecurrenceSequence(3,0) sage: [R(i) for i in range(10)] [0, 1, 3, 9, 27, 81, 243, 729, 2187, 6561] sage: R.is_quasigeometric() True """ #First test if F is singular... i.e. beta = 0 if self.c == 0: return True #Otherwise test if alpha/beta is a root of unity that is not 1 else: if (self.b**2+4*self.c) != 0: #thus alpha/beta != 1 if (self.b**2+4*self.c).is_square(): A = sqrt((self.b**2+4*self.c)) else: K = QuadraticField((self.b**2+4*self.c), 'x') A = K.gen() if ((self.b+A)/(self.b-A))**(6) == 1: return True return False
def icosidodecahedron(self, exact=True): """ Return the Icosidodecahedron The Icosidodecahedron is a polyhedron with twenty triangular faces and twelve pentagonal faces. For more information see the :wikipedia:`Icosidodecahedron`. INPUT: - ``exact`` -- (boolean, default ``True``) If ``False`` use an approximate ring for the coordinates. EXAMPLES:: sage: gr = polytopes.icosidodecahedron() sage: gr.f_vector() (1, 30, 60, 32, 1) TESTS:: sage: polytopes.icosidodecahedron(exact=False) A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices """ from sage.rings.number_field.number_field import QuadraticField from itertools import product K = QuadraticField(5, 'sqrt5') one = K.one() phi = (one+K.gen())/2 gens = [((-1)**a*one/2, (-1)**b*phi/2, (-1)**c*(one+phi)/2) for a,b,c in product([0,1],repeat=3)] gens.extend([(0,0,phi), (0,0,-phi)]) verts = [] for p in AlternatingGroup(3): verts.extend(p(x) for x in gens) if exact: return Polyhedron(vertices=verts,base_ring=K) else: verts = [(RR(x),RR(y),RR(z)) for x,y,z in verts] return Polyhedron(vertices=verts)
def is_quasigeometric(self): """ Decide whether the binary recurrence sequence is degenerate and similar to a geometric sequence, i.e. the union of multiple geometric sequences, or geometric after term ``u0``. If `\\alpha/\\beta` is a `k` th root of unity, where `k>1`, then necessarily `k = 2, 3, 4, 6`. Then `F = [[0,1],[c,b]` is diagonalizable, and `F^k = [[\\alpha^k, 0], [0,\\beta^k]]` is scaler matrix. Thus for all values of `j` mod `k`, the `j` mod `k` terms of `u_n` form a geometric series. If `\\alpha` or `\\beta` is zero, this implies that `c=0`. This is the case when `F` is singular. In this case, `u_1, u_2, u_3, ...` is geometric. EXAMPLES:: sage: S = BinaryRecurrenceSequence(0,1) sage: [S(i) for i in range(10)] [0, 1, 0, 1, 0, 1, 0, 1, 0, 1] sage: S.is_quasigeometric() True sage: R = BinaryRecurrenceSequence(3,0) sage: [R(i) for i in range(10)] [0, 1, 3, 9, 27, 81, 243, 729, 2187, 6561] sage: R.is_quasigeometric() True """ # First test if F is singular... i.e. beta = 0 if self.c == 0: return True # Otherwise test if alpha/beta is a root of unity that is not 1 D = self.b**2 + 4 * self.c if D != 0: # thus alpha/beta != 1 if D.is_square(): A = sqrt(D) else: K = QuadraticField(D, 'x') A = K.gen() if ((self.b + A) / (self.b - A))**6 == 1: return True return False
def __classcall_private__(cls, data, base_ring=None, index_set=None): """ Normalize arguments to ensure a unique representation. EXAMPLES:: sage: W1 = CoxeterGroup(['A',2], implementation="reflection", base_ring=ZZ) sage: W2 = CoxeterGroup([[1,3],[3,1]], index_set=(1,2)) sage: W1 is W2 True sage: G1 = Graph([(1,2)]) sage: W3 = CoxeterGroup(G1) sage: W1 is W3 True sage: G2 = Graph([(1,2,3)]) sage: W4 = CoxeterGroup(G2) sage: W1 is W4 True """ data = CoxeterMatrix(data, index_set=index_set) if base_ring is None: if data.is_simply_laced(): base_ring = ZZ elif data.is_finite(): letter = data.coxeter_type().cartan_type().type() if letter in ['B', 'C', 'F']: base_ring = QuadraticField(2) elif letter == 'G': base_ring = QuadraticField(3) elif letter == 'H': base_ring = QuadraticField(5) else: base_ring = UniversalCyclotomicField() else: base_ring = UniversalCyclotomicField() return super(CoxeterMatrixGroup, cls).__classcall__(cls, data, base_ring, data.index_set())
def icosahedron(self, exact=True, base_ring=None): """ Return an icosahedron with edge length 1. The icosahedron is one of the Platonic sold. It has 20 faces and is dual to the :meth:`dodecahedron`. INPUT: - ``exact`` -- (boolean, default ``True``) If ``False`` use an approximate ring for the coordinates. - ``base_ring`` -- (optional) the ring in which the coordinates will belong to. Note that this ring must contain `\sqrt(5)`. If it is not provided and ``exact=True`` it will be the number field `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double field. EXAMPLES:: sage: ico = polytopes.icosahedron() sage: ico.f_vector() (1, 12, 30, 20, 1) sage: ico.volume() 5/12*sqrt5 + 5/4 Its non exact version:: sage: ico = polytopes.icosahedron(exact=False) sage: ico.base_ring() Real Double Field sage: ico.volume() 2.1816949907715726 A version using `AA <sage.rings.qqbar.AlgebraicRealField>`:: sage: ico = polytopes.icosahedron(base_ring=AA) # long time sage: ico.base_ring() # long time Algebraic Real Field sage: ico.volume() # long time 2.181694990624913? Note that if base ring is provided it must contain the square root of `5`. Otherwise you will get an error:: sage: polytopes.icosahedron(base_ring=QQ) Traceback (most recent call last): ... TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField K = QuadraticField(5, 'sqrt5') sqrt5 = K.gen() g = (1 + sqrt5) / 2 base_ring = K else: if base_ring is None: base_ring = RDF g = (1 + base_ring(5).sqrt()) / 2 r12 = base_ring.one() / 2 z = base_ring.zero() pts = [[z, s1*r12, s2*g/2] for s1,s2 in itertools.product([1,-1],repeat=2)] verts = [p(v) for p in AlternatingGroup(3) for v in pts] return Polyhedron(vertices=verts, base_ring=base_ring)
def is_degenerate(self): """ Decide whether the binary recurrence sequence is degenerate. Let `\\alpha` and `\\beta` denote the roots of the characteristic polynomial `p(x) = x^2-bx -c`. Let `a = u_1-u_0\\beta/(\\beta - \\alpha)` and `b = u_1-u_0\\alpha/(\\beta - \\alpha)`. The sequence is, thus, given by `u_n = a \\alpha^n - b\\beta^n`. Then we say that the sequence is nondegenerate if and only if `a*b*\\alpha*\\beta \\neq 0` and `\\alpha/\\beta` is not a root of unity. More concretely, there are 4 classes of degeneracy, that can all be formulated in terms of the matrix `F = [[0,1], [c, b]]`. - `F` is singular -- this corresponds to ``c`` = 0, and thus `\\alpha*\\beta = 0`. This sequence is geometric after term ``u0`` and so we call it ``quasigeometric``. - `v = [[u_0], [u_1]]` is an eigenvector of `F` -- this corresponds to a ``geometric`` sequence with `a*b = 0`. - `F` is nondiagonalizable -- this corresponds to `\\alpha = \\beta`. This sequence will be the point-wise product of an arithmetic and geometric sequence. - `F^k` is scaler, for some `k>1` -- this corresponds to `\\alpha/\\beta` a `k` th root of unity. This sequence is a union of several geometric sequences, and so we again call it ``quasigeometric``. EXAMPLES:: sage: S = BinaryRecurrenceSequence(0,1) sage: S.is_degenerate() True sage: S.is_geometric() False sage: S.is_quasigeometric() True sage: R = BinaryRecurrenceSequence(3,-2) sage: R.is_degenerate() False sage: T = BinaryRecurrenceSequence(2,-1) sage: T.is_degenerate() True sage: T.is_arithmetic() True """ if (self.b**2+4*self.c) != 0: if (self.b**2+4*self.c).is_square(): A = sqrt((self.b**2+4*self.c)) else: K = QuadraticField((self.b**2+4*self.c), 'x') A = K.gen() aa = (self.u1 - self.u0*(self.b + A)/2)/(A) #called `a` in Docstring bb = (self.u1 - self.u0*(self.b - A)/2)/(A) #called `b` in Docstring #(b+A)/2 is called alpha in Docstring, (b-A)/2 is called beta in Docstring if (self.b - A) != 0: if ((self.b+A)/(self.b-A))**(6) == 1: return True else: return True if aa*bb*(self.b + A)*(self.b - A) == 0: return True return False return True
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 if E.has_bad_reduction(P): continue tr = E.reduction(P).trace_of_frobenius() 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 range(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 range(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() # See #19229: the name given here, which is not used, should # not be the name of the generator of the base field. F = QuadraticField(a, 'gal_rep_nf_sqrt_a') # Next, we turn K into relative number field over F. K = K.relativize(F.embeddings(K)[0], K.variable_name()+'0') 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
class CurlO: """ This class defines some helper functions for calculations in \curlO, where \curlO is the maximal order of some quadratic imaginary number field \K = \QQ(\sqrt{D}), where `D` is some negative integer. For example, this class defines a function `xgcd` which implements the extended Euclidean algorithm. This is based on the `divmod` function, also in this class. When representing an element `b` in \curlO, we use the representation `b = b1 + b2 (D + \sqrt{D})/2` for b1,b2 \in \ZZ. This can be calculated via the functions `from_tuple_b` and `as_tuple_b`. """ def __init__(self, D): self.D = D assert (D*D - D) % 4 == 0 self.field = QuadraticField(D) self.maxorder = self.field.maximal_order() self.Droot = self.field(D).sqrt() self.DrootHalf_coordinates_in_terms_of_powers = (self.Droot / 2).coordinates_in_terms_of_powers() def divmod(self, a, b): """ Returns q,r such that a = q*b + r. This is division with remainder. It holds that `self.euclidean_func(r) < `self.euclidean_func(b)`. """ # Note that this implementation is quite naive! # Later, we can do better with QuadraticForm(...). (TODO) # Also, read here: http://www.fen.bilkent.edu.tr/~franz/publ/survey.pdf if b == 0: raise ZeroDivisionError a1,a2 = self.as_tuple_b(a) b1,b2 = self.as_tuple_b(b) #B = matrix([ # [b1, -b2 * (self.D**2 - self.D)/4], # [b2, b1 + b2*self.D] #]) Bdet = b1*b1 + b1*b2*self.D + b2*b2*(self.D**2 - self.D)/4 Bdet = _simplify(Bdet) assert Bdet > 0 qq1 = (a1*b1 + a1*b2*self.D + a2*b2*(self.D**2 - self.D)/4) / Bdet qq2 = (-a1*b2 + a2*b1) / Bdet assert _simplify(self.from_tuple_b(qq1,qq2) * b - a) == 0 # Not sure on this. # From qq1 and qq2, we want to select q1,q2 \in \Z such that # `r = a - q * b` is minimal with regards to `self.euclidean_func`, # where `q = self.from_tuple_b(q1,q2)`. # Simply using `round` will not work in all cases; neither does `floor`. # Many test cases are (indirectly) in `test_solveR()`. # Now we are just checking multiple possibilities and use # the smallest one. Note that these are not all possible cases. solutions = [] for q1 in [int(floor(qq1)) + i for i in [-1,0,1,2]]: for q2 in [int(floor(qq2)) + i for i in [-1,0,1,2]]: q = self.from_tuple_b(q1,q2) # q * b + r == a r = _simplify(a - q * b) euc_r = self.euclidean_func(r) solutions += [(euc_r, q, r)] euc_b = self.euclidean_func(b) euc_r, q, r = min(solutions) assert euc_r < euc_b, "%r < %r; r=%r, b=%r, a=%r" % (euc_r, euc_b, r, b, a) return q, r def euclidean_func(self, x): """ The Euclidean function of x. (Returns just |x|.) """ return self.field(x).abs() def divides(self, a, b): """ Returns whether `b` divides `a` without remainder. """ q, r = self.divmod(a, b) return r == 0 def xgcd(self, a, b): """ The extended Euclidean algorithm. Mostly a standard implementation with a few fast paths. For the generic case, it uses `self.divmod`. INPUT: - `a`, `b` -- two elements of `self.maxorder`. OUTPUT: - A tuple `(d, s, t)`, such that `d == a * s + b * t` and `d` divides both `s` and `t`. `d` is the greatest common divisor. These are all elements of `self.maxorder`. """ if a == b: return a, 1, 0 if a == -b: return a, 1, 0 if a == 1: return 1, 1, 0 if b == 1: return 1, 0, 1 if a == 0: return b, 0, 1 if b == 0: return a, 1, 0 a1,a2 = self.as_tuple_b(a) b1,b2 = self.as_tuple_b(b) if a2 == b2 == 0: return orig_xgcd(a1, b1) if a1 == b1 == 0: d,s,t = orig_xgcd(a2, b2) B2 = (self.D + self.Droot) / 2 return d * B2, s, t # a,b = map(self.field, [a,b]) # gcd = 1 # factors_a,factors_b = [dict(list(x.factor())) for x in [a,b]] # for q in factors_a.keys(): # if q in factors_b: # e = min(factors_a[q], factors_b[q]) # factors_a[q] -= e # factors_b[q] -= e # gcd *= q * e a,b = map(self.maxorder, [a,b]) euc_a = self.euclidean_func(a) euc_b = self.euclidean_func(b) if euc_a < euc_b: d,s,t = self.xgcd(b, a) return d,t,s # We have euc_b <= euc_a now. q,r = self.divmod(a, b) # q * b + r == a. assert q * b + r == a # => a - b*q == r d2,s2,t2 = self.xgcd(b, r) # d2 = b * s2 + r * t2 assert d2 == b * s2 + r * t2 # => d2 = b * s2 + (a - b*q) * t2 # => d2 = a * t2 + b * (s2 - q * t2) d = d2 s = _simplify(t2) t = _simplify(s2 - q * t2) assert d == a * s + b * t assert self.divides(a, d) assert self.divides(b, d) return d, s, t def gcd(self, a, b): """ INPUT: - `a`, `b` -- elements of `self.maxorder`. OUTPUT: - An element `d` of `self.maxorder`. `d` is the greatest common divisor. """ d,_,_ = self.xgcd(a, b) return d def common_denom(self, *args): """ INPUT: - `*args` -- elements from `self.field`. OUTPUT: - The smallest element `d` of `self.maxorder` such that `d * arg` is in `self.maxorder`, for all `arg` in `args`. """ tupleargs = [None] * len(args) * 2 for i in range(len(args)): tupleargs[2*i],tupleargs[2*i+1] = self.as_tuple_b(args[i]) return matrix(QQ, 1,len(tupleargs), tupleargs).denominator() def matrix_denom(self, mat): """ INPUT: - `mat` -- matrix over `self.field`. OUTPUT: - The smallest element `d` of `self.maxorder` such that `d * mat` is over `self.maxorder`. """ denom = self.common_denom(*mat.list()) denom = int(ZZ(denom)) for v in mat.list(): assert v * denom in self, "%r (D=%r)" % (mat, self.D) return denom def as_tuple_b(self, a): """ INPUT: - `a` -- an element in `self.field`. OUTPUT: - A tuple of rationals `(b1,b2)`, where `a = b1 + b2 (D + \sqrt{D})/2`. If `a` is in `self.maxorder`, `b1` and `b2` are integers. """ real_part, b2 = self.DrootHalf_coordinates_in_terms_of_powers(a) b1 = real_part - b2 * self.D / 2 return (b1, b2) def from_tuple_b(self, b1, b2): """ INPUT: - `b1`,`b2` -- rational numbers. OUTPUT: - An element `b` in `self.field` with `b = b1 + b2 (D + \sqrt{D})/2`. If `b1`,`b2` are integers, `b` is in `self.maxorder`. """ b1 = QQ(b1) b2 = QQ(b2) return self.field(b1 + b2 * (self.D + self.Droot) / 2) def __contains__(self, item): """ Returns whether `item` is an element of `self.maxorder`. """ try: b1,b2 = self.as_tuple_b(item) b1,b2 = ZZ(b1), ZZ(b2) except TypeError: return False else: return True
def test_contains_imaginary_quadratic_field(D): K = QuadraticField(D) result = contains_imaginary_quadratic_field(K) assert result == (D < 0)
def small_rhombicuboctahedron(self, exact=True, base_ring=None): """ Return the (small) rhombicuboctahedron. The rhombicuboctahedron is an Archimedean solid with 24 vertices and 26 faces. See the :wikipedia:`Rhombicuboctahedron` for more information. INPUT: - ``exact`` -- (boolean, default ``True``) If ``False`` use an approximate ring for the coordinates. - ``base_ring`` -- the ring in which the coordinates will belong to. If it is not provided and ``exact=True`` it will be a the number field `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False`` it will be the real double field. EXAMPLES:: sage: sr = polytopes.small_rhombicuboctahedron() sage: sr.f_vector() (1, 24, 48, 26, 1) sage: sr.volume() 80/3*sqrt2 + 32 The faces are `8` equilateral triangles and `18` squares:: sage: sum(1 for f in sr.faces(2) if len(f.vertices()) == 3) 8 sage: sum(1 for f in sr.faces(2) if len(f.vertices()) == 4) 18 Its non exact version:: sage: sr = polytopes.small_rhombicuboctahedron(False) sage: sr A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices sage: sr.f_vector() (1, 24, 48, 26, 1) """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField K = QuadraticField(2, 'sqrt2') sqrt2 = K.gen() base_ring = K else: if base_ring is None: base_ring = RDF sqrt2 = base_ring(2).sqrt() one = base_ring.one() a = sqrt2 + one verts = [] verts.extend([s1 * one, s2 * one, s3 * a] for s1, s2, s3 in itertools.product([1, -1], repeat=3)) verts.extend([s1 * one, s3 * a, s2 * one] for s1, s2, s3 in itertools.product([1, -1], repeat=3)) verts.extend([s1 * a, s2 * one, s3 * one] for s1, s2, s3 in itertools.product([1, -1], repeat=3)) return Polyhedron(vertices=verts)
def icosahedron(self, exact=True, base_ring=None): """ Return an icosahedron with edge length 1. The icosahedron is one of the Platonic sold. It has 20 faces and is dual to the :meth:`dodecahedron`. INPUT: - ``exact`` -- (boolean, default ``True``) If ``False`` use an approximate ring for the coordinates. - ``base_ring`` -- (optional) the ring in which the coordinates will belong to. Note that this ring must contain `\sqrt(5)`. If it is not provided and ``exact=True`` it will be the number field `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double field. EXAMPLES:: sage: ico = polytopes.icosahedron() sage: ico.f_vector() (1, 12, 30, 20, 1) sage: ico.volume() 5/12*sqrt5 + 5/4 Its non exact version:: sage: ico = polytopes.icosahedron(exact=False) sage: ico.base_ring() Real Double Field sage: ico.volume() 2.1816949907715726 A version using `AA <sage.rings.qqbar.AlgebraicRealField>`:: sage: ico = polytopes.icosahedron(base_ring=AA) # long time sage: ico.base_ring() # long time Algebraic Real Field sage: ico.volume() # long time 2.181694990624913? Note that if base ring is provided it must contain the square root of `5`. Otherwise you will get an error:: sage: polytopes.icosahedron(base_ring=QQ) Traceback (most recent call last): ... TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField K = QuadraticField(5, 'sqrt5') sqrt5 = K.gen() g = (1 + sqrt5) / 2 base_ring = K else: if base_ring is None: base_ring = RDF g = (1 + base_ring(5).sqrt()) / 2 r12 = base_ring.one() / 2 z = base_ring.zero() pts = [[z, s1 * r12, s2 * g / 2] for s1, s2 in itertools.product([1, -1], repeat=2)] verts = [p(v) for p in AlternatingGroup(3) for v in pts] return Polyhedron(vertices=verts, base_ring=base_ring)
def is_degenerate(self): """ Decide whether the binary recurrence sequence is degenerate. Let `\\alpha` and `\\beta` denote the roots of the characteristic polynomial `p(x) = x^2-bx -c`. Let `a = u_1-u_0\\beta/(\\beta - \\alpha)` and `b = u_1-u_0\\alpha/(\\beta - \\alpha)`. The sequence is, thus, given by `u_n = a \\alpha^n - b\\beta^n`. Then we say that the sequence is nondegenerate if and only if `a*b*\\alpha*\\beta \\neq 0` and `\\alpha/\\beta` is not a root of unity. More concretely, there are 4 classes of degeneracy, that can all be formulated in terms of the matrix `F = [[0,1], [c, b]]`. - `F` is singular -- this corresponds to ``c`` = 0, and thus `\\alpha*\\beta = 0`. This sequence is geometric after term ``u0`` and so we call it ``quasigeometric``. - `v = [[u_0], [u_1]]` is an eigenvector of `F` -- this corresponds to a ``geometric`` sequence with `a*b = 0`. - `F` is nondiagonalizable -- this corresponds to `\\alpha = \\beta`. This sequence will be the point-wise product of an arithmetic and geometric sequence. - `F^k` is scaler, for some `k>1` -- this corresponds to `\\alpha/\\beta` a `k` th root of unity. This sequence is a union of several geometric sequences, and so we again call it ``quasigeometric``. EXAMPLES:: sage: S = BinaryRecurrenceSequence(0,1) sage: S.is_degenerate() True sage: S.is_geometric() False sage: S.is_quasigeometric() True sage: R = BinaryRecurrenceSequence(3,-2) sage: R.is_degenerate() False sage: T = BinaryRecurrenceSequence(2,-1) sage: T.is_degenerate() True sage: T.is_arithmetic() True """ if (self.b**2 + 4 * self.c) != 0: if (self.b**2 + 4 * self.c).is_square(): A = sqrt((self.b**2 + 4 * self.c)) else: K = QuadraticField((self.b**2 + 4 * self.c), 'x') A = K.gen() aa = (self.u1 - self.u0 * (self.b + A) / 2) / (A) #called `a` in Docstring bb = (self.u1 - self.u0 * (self.b - A) / 2) / (A) #called `b` in Docstring #(b+A)/2 is called alpha in Docstring, (b-A)/2 is called beta in Docstring if (self.b - A) != 0: if ((self.b + A) / (self.b - A))**(6) == 1: return True else: return True if aa * bb * (self.b + A) * (self.b - A) == 0: return True return False return True
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 if E.has_bad_reduction(P): continue tr = E.reduction(P).trace_of_frobenius() 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() # See #19229: the name given here, which is not used, should # not be the name of the generator of the base field. F = QuadraticField(a, 'gal_rep_nf_sqrt_a') # Next, we turn K into relative number field over F. K = K.relativize(F.embeddings(K)[0], K.variable_name()+'0') 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