def is_discriminant(self, D, primitive=True): r""" Returns whether ``D`` is a discriminant of an element of ``self``. Note: Checking that something isn't a discriminant takes much longer than checking for valid discriminants. INPUT: - ``D`` -- An element of the base ring. - ``primitive`` -- If ``True`` (default) then only primitive elements are considered. OUPUT: ``True`` if ``D`` is a primitive discriminant (a discriminant of a primitive element) and ``False`` otherwise. If ``primitive=False`` then also non-primitive elements are considered. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.is_discriminant(68) True sage: G.is_discriminant(196, primitive=False) # long time True sage: G.is_discriminant(2) False """ self._conjugacy_representatives(0) t_bound = max(coerce_AA(0), coerce_AA( (D + 4) / (self.lam()**2))).sqrt().floor() for t in range(self._max_block_length + 1, t_bound + 1): self._conjugacy_representatives(t) if D in self._conj_prim: return True if not primitive and D in self._conj_nonprim: return True if D in self._conj_prim: return True elif not primitive and D in self._conj_nonprim: return True else: return False
def is_discriminant(self, D, primitive=True): r""" Returns whether ``D`` is a discriminant of an element of ``self``. Note: Checking that something isn't a discriminant takes much longer than checking for valid discriminants. INPUT: - ``D`` -- An element of the base ring. - ``primitive`` -- If ``True`` (default) then only primitive elements are considered. OUTPUT: ``True`` if ``D`` is a primitive discriminant (a discriminant of a primitive element) and ``False`` otherwise. If ``primitive=False`` then also non-primitive elements are considered. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.is_discriminant(68) True sage: G.is_discriminant(196, primitive=False) # long time True sage: G.is_discriminant(2) False """ self._conjugacy_representatives(0) t_bound = max(coerce_AA(0), coerce_AA((D + 4)/(self.lam()**2))).sqrt().floor() for t in range(self._max_block_length + 1, t_bound + 1): self._conjugacy_representatives(t) if D in self._conj_prim: return True if not primitive and D in self._conj_nonprim: return True if D in self._conj_prim: return True elif not primitive and D in self._conj_nonprim: return True else: return False
def rho(self): r""" Return the vertex ``rho`` of the basic hyperbolic triangle which describes ``self``. ``rho`` has absolute value 1 and angle ``pi/n``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: HeckeTriangleGroup(3).rho() == 1/2 + sqrt(3)/2*i True sage: HeckeTriangleGroup(4).rho() == sqrt(2)/2*(1 + i) True sage: HeckeTriangleGroup(6).rho() == sqrt(3)/2 + 1/2*i True sage: HeckeTriangleGroup(10).rho() 0.95105651629515...? + 0.30901699437494...?*I sage: HeckeTriangleGroup(infinity).rho() 1 """ # TODO: maybe rho should be replaced by -rhobar # Also we could use NumberFields... if (self._n == infinity): return coerce_AA(1) else: rho = AlgebraicField()(exp(pi / self._n * i)) rho.simplify() return rho
def rho(self): r""" Return the vertex ``rho`` of the basic hyperbolic triangle which describes ``self``. ``rho`` has absolute value 1 and angle ``pi/n``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: HeckeTriangleGroup(3).rho() == 1/2 + sqrt(3)/2*i True sage: HeckeTriangleGroup(4).rho() == sqrt(2)/2*(1 + i) True sage: HeckeTriangleGroup(6).rho() == sqrt(3)/2 + 1/2*i True sage: HeckeTriangleGroup(10).rho() 0.95105651629515...? + 0.30901699437494...?*I sage: HeckeTriangleGroup(infinity).rho() 1 """ # TODO: maybe rho should be replaced by -rhobar # Also we could use NumberFields... if (self._n == infinity): return coerce_AA(1) else: rho = AlgebraicField()(exp(pi/self._n*i)) rho.simplify() return rho
def list_discriminants(self, D, primitive=True, hyperbolic=True, incomplete=False): r""" Returns a list of all discriminants up to some upper bound ``D``. INPUT: - ``D`` -- An element/discriminant of the base ring or more generally an upper bound for the discriminant. - ``primitive`` -- If ``True`` (default) then only primitive discriminants are listed. - ``hyperbolic`` -- If ``True`` (default) then only positive discriminants are listed. - ``incomplete`` -- If ``True`` (default: ``False``) then all (also higher) discriminants which were gathered so far are listed (however there might be missing discriminants inbetween). OUTPUT: A list of discriminants less than or equal to ``D``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.list_discriminants(D=68) [4, 12, 14, 28, 32, 46, 60, 68] sage: G.list_discriminants(D=0, hyperbolic=False, primitive=False) [-4, -2, 0] sage: G = HeckeTriangleGroup(n=5) sage: G.list_discriminants(D=20) [4*lam, 7*lam + 6, 9*lam + 5] sage: G.list_discriminants(D=0, hyperbolic=False, primitive=False) [-4, -lam - 2, lam - 3, 0] """ self._conjugacy_representatives(D=D) if incomplete: max_D = infinity else: max_D = coerce_AA(D) L = [] if hyperbolic: L += [key for key in self._conj_prim if coerce_AA(key) > 0 and coerce_AA(key) <= max_D] else: L += [key for key in self._conj_prim if coerce_AA(key) <= max_D] if not primitive: if hyperbolic: L += [key for key in self._conj_nonprim if coerce_AA(key) > 0 and coerce_AA(key) <= max_D and key not in L] else: L += [key for key in self._conj_nonprim if coerce_AA(key) <= max_D and key not in L] return sorted(L, key=coerce_AA)
def class_number(self, D, primitive=True): r""" Return the class number of ``self`` for the discriminant ``D``. I.e. the number of conjugacy classes of (primitive) elements of discriminant ``D``. Note: Due to the 1-1 correspondence with hyperbolic fixed points resp. hyperbolic binary quadratic forms this also gives the class number in those cases. INPUT: - ``D`` -- An element of the base ring corresponding to a valid discriminant. - ``primitive`` -- If ``True`` (default) then only primitive elements are considered. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.class_number(4) 1 sage: G.class_number(4, primitive=False) 1 sage: G.class_number(14) 2 sage: G.class_number(32) 2 sage: G.class_number(32, primitive=False) 3 sage: G.class_number(68) 4 """ if coerce_AA(D) <= 0: raise NotImplementedError self._conjugacy_representatives(D=D) num = ZZ(0) if D in self._conj_prim: num = len(self._conj_prim[D]) if not primitive and D in self._conj_nonprim: num += len(self._conj_nonprim[D]) if num == 0: raise ValueError("D = {} is not a{} discriminant for {}".format( D, " primitive" if primitive else "", self)) else: return num
def class_number(self, D, primitive=True): r""" Return the class number of ``self`` for the discriminant ``D``. I.e. the number of conjugacy classes of (primitive) elements of discriminant ``D``. Note: Due to the 1-1 correspondence with hyperbolic fixed points resp. hyperbolic binary quadratic forms this also gives the class number in those cases. INPUT: - ``D`` -- An element of the base ring corresponding to a valid discriminant. - ``primitive`` -- If ``True`` (default) then only primitive elements are considered. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.class_number(4) 1 sage: G.class_number(4, primitive=False) 1 sage: G.class_number(14) 2 sage: G.class_number(32) 2 sage: G.class_number(32, primitive=False) 3 sage: G.class_number(68) 4 """ if coerce_AA(D) <= 0: raise NotImplementedError self._conjugacy_representatives(D=D) num = ZZ(0) if D in self._conj_prim: num = len(self._conj_prim[D]) if not primitive and D in self._conj_nonprim: num += len(self._conj_nonprim[D]) if num == 0: raise ValueError("D = {} is not a{} discriminant for {}".format(D, " primitive" if primitive else "", self)) else: return num
def lam_minpoly(self): r""" Return the minimal polynomial of the corresponding lambda parameter of ``self``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: HeckeTriangleGroup(10).lam_minpoly() x^4 - 5*x^2 + 5 sage: HeckeTriangleGroup(17).lam_minpoly() x^8 - x^7 - 7*x^6 + 6*x^5 + 15*x^4 - 10*x^3 - 10*x^2 + 4*x + 1 sage: HeckeTriangleGroup(infinity).lam_minpoly() x - 2 """ # TODO: Write an explicit (faster) implementation lam_symbolic = 2 * cos(pi / self._n) return coerce_AA(lam_symbolic).minpoly()
def lam_minpoly(self): r""" Return the minimal polynomial of the corresponding lambda parameter of ``self``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: HeckeTriangleGroup(10).lam_minpoly() x^4 - 5*x^2 + 5 sage: HeckeTriangleGroup(17).lam_minpoly() x^8 - x^7 - 7*x^6 + 6*x^5 + 15*x^4 - 10*x^3 - 10*x^2 + 4*x + 1 sage: HeckeTriangleGroup(infinity).lam_minpoly() x - 2 """ # TODO: Write an explicit (faster) implementation lam_symbolic = 2*cos(pi/self._n) return coerce_AA(lam_symbolic).minpoly()
def __init__(self, n): r""" Hecke triangle group (2, n, infinity). Namely the von Dyck group corresponding to the triangle group with angles (pi/2, pi/n, 0). INPUT: - ``n`` - ``infinity`` or an integer greater or equal to ``3``. OUTPUT: The Hecke triangle group for the given parameter ``n``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(12) sage: G Hecke triangle group for n = 12 sage: G.category() Category of groups """ self._n = n self.element_repr_method("default") if n in [3, infinity]: self._base_ring = ZZ self._lam = ZZ(1) if n == 3 else ZZ(2) else: lam_symbolic = 2 * cos(pi / n) K = NumberField(self.lam_minpoly(), 'lam', embedding=coerce_AA(lam_symbolic)) #self._base_ring = K.order(K.gens()) self._base_ring = K.maximal_order() self._lam = self._base_ring.gen(1) T = matrix(self._base_ring, [[1, self._lam], [0, 1]]) S = matrix(self._base_ring, [[0, -1], [1, 0]]) FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(2), self._base_ring, [S, T])
def __init__(self, n): r""" Hecke triangle group (2, n, infinity). Namely the von Dyck group corresponding to the triangle group with angles (pi/2, pi/n, 0). INPUT: - ``n`` - ``infinity`` or an integer greater or equal to ``3``. OUTPUT: The Hecke triangle group for the given parameter ``n``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(12) sage: G Hecke triangle group for n = 12 sage: G.category() Category of groups """ self._n = n self.element_repr_method("default") if n in [3, infinity]: self._base_ring = ZZ self._lam = ZZ(1) if n==3 else ZZ(2) else: lam_symbolic = 2*cos(pi/n) K = NumberField(self.lam_minpoly(), 'lam', embedding = coerce_AA(lam_symbolic)) #self._base_ring = K.order(K.gens()) self._base_ring = K.maximal_order() self._lam = self._base_ring.gen(1) T = matrix(self._base_ring, [[1,self._lam],[0,1]]) S = matrix(self._base_ring, [[0,-1],[1,0]]) FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(2), self._base_ring, [S, T])
def list_discriminants(self, D, primitive=True, hyperbolic=True, incomplete=False): r""" Returns a list of all discriminants up to some upper bound ``D``. INPUT: - ``D`` -- An element/discriminant of the base ring or more generally an upper bound for the discriminant. - ``primitive`` -- If ``True`` (default) then only primitive discriminants are listed. - ``hyperbolic`` -- If ``True`` (default) then only positive discriminants are listed. - ``incomplete`` -- If ``True`` (default: ``False``) then all (also higher) discriminants which were gathered so far are listed (however there might be missing discriminants inbetween). OUPUT: A list of discriminants less than or equal to ``D``. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.list_discriminants(D=68) [4, 12, 14, 28, 32, 46, 60, 68] sage: G.list_discriminants(D=0, hyperbolic=False, primitive=False) [-4, -2, 0] sage: G = HeckeTriangleGroup(n=5) sage: G.list_discriminants(D=20) [4*lam, 7*lam + 6, 9*lam + 5] sage: G.list_discriminants(D=0, hyperbolic=False, primitive=False) [-4, -lam - 2, lam - 3, 0] """ self._conjugacy_representatives(D=D) if incomplete: max_D = infinity else: max_D = coerce_AA(D) L = [] if hyperbolic: L += [ key for key in self._conj_prim if coerce_AA(key) > 0 and coerce_AA(key) <= max_D ] else: L += [key for key in self._conj_prim if coerce_AA(key) <= max_D] if not primitive: if hyperbolic: L += [ key for key in self._conj_nonprim if coerce_AA(key) > 0 and coerce_AA(key) <= max_D and key not in L ] else: L += [ key for key in self._conj_nonprim if coerce_AA(key) <= max_D and key not in L ] return sorted(L, key=coerce_AA)
def _conjugacy_representatives(self, max_block_length=ZZ(0), D=None): r""" Store conjugacy representatives up to block length ``max_block_length`` (a non-negative integer, default: 0) in the internal dictionary. Previously calculated data is reused. This is a helper function for e.g. :meth:`class_number`. The set of all (hyperbolic) conjugacy types of block length ``t`` is stored in ``self._conj_block[t]``. The set of all primitive representatives (so far) with discriminant ``D`` is stored in ``self._conj_prim[D]``. The set of all non-primitive representatives (so far) with discriminant ``D`` is stored in ``self._conj_nonprim[D]``. The case of non-positive discriminants is done manually. INPUT: - ``max_block_length`` -- A non-negative integer (default: ``0``), the maximal block length. - ``D`` -- An element/discriminant of the base ring or more generally an upper bound for the involved discriminants. If ``D != None`` then an upper bound for ``max_block_length`` is deduced from ``D`` (default: ``None``). EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=5) sage: G.element_repr_method("conj") sage: G._conjugacy_representatives(2) sage: list(G._conj_block[2]) [((4, 1), (3, 1)), ((2, 2),), ((3, 2),), ((3, 1), (1, 1)), ((4, 1), (1, 1)), ((4, 1), (2, 1)), ((3, 1), (2, 1)), ((2, 1), (1, 1))] sage: [key for key in sorted(G._conj_prim)] [-4, lam - 3, 0, 4*lam, 7*lam + 6, 9*lam + 5, 15*lam + 6, 33*lam + 21] sage: for key in sorted(G._conj_prim): ....: print G._conj_prim[key] [[S], [S]] [[U], [U]] [[V(4)]] [[V(3)], [V(2)]] [[V(1)*V(4)]] [[V(3)*V(4)], [V(1)*V(2)]] [[V(1)*V(3)], [V(2)*V(4)]] [[V(2)*V(3)]] sage: [key for key in sorted(G._conj_nonprim)] [-lam - 2, lam - 3, 32*lam + 16] sage: for key in sorted(G._conj_nonprim): ....: print G._conj_nonprim[key] [[U^(-2)], [U^2], [U^(-2)], [U^2]] [[U^(-1)], [U^(-1)]] [[V(2)^2], [V(3)^2]] sage: G.element_repr_method("default") """ from sage.combinat.partition import OrderedPartitions from sage.combinat.combinat import tuples from sage.arith.all import divisors if not D is None: max_block_length = max(coerce_AA(0), coerce_AA((D + 4)/(self.lam()**2))).sqrt().floor() else: try: max_block_length = ZZ(max_block_length) if max_block_length < 0: raise TypeError except TypeError: raise ValueError("max_block_length must be a non-negative integer!") if not hasattr(self, "_max_block_length"): self._max_block_length = ZZ(0) self._conj_block = {} self._conj_nonprim = {} self._conj_prim = {} # It is not clear how to define the class number for D=0: # Conjugacy classes are V(n-1)^(+-k) for arbitrary k # and the trivial class (what about self_conj_block[0]?). # # One way is to define it using the fixed points and in # that case V(n-1) would be a good representative. # The non-primitive case is unclear however... # # We set it here to ensure that 0 is enlisted as a discriminant... # self._conj_prim[ZZ(0)] = [] self._conj_prim[ZZ(0)].append(self.V(self.n()-1)) self._elliptic_conj_reps() if max_block_length <= self._max_block_length: return def is_cycle(seq): length = len(seq) for n in divisors(length): if n < length and is_cycle_of_length(seq, n): return True return False def is_cycle_of_length(seq, n): for i in range(n, len(seq)): if seq[i] != seq[i % n]: return False return True j_list = range(1, self.n()) for t in range(self._max_block_length + 1, max_block_length + 1): t_ZZ = ZZ(t) if t_ZZ not in self._conj_block: self._conj_block[t_ZZ] = set() partitions = OrderedPartitions(t).list() for par in partitions: len_par = len(par) exp_list = tuples(j_list, len_par) for ex in exp_list: keep = True if len_par > 1: for k in range(-1,len_par-1): if ex[k] == ex[k+1]: keep = False break # We don't want powers of V(1) elif ex[0] == 1: keep = False # But: Do we maybe want powers of V(n-1)?? # For now we exclude the parabolic cases... elif ex[0] == self.n()-1: keep = False if keep: conj_type = cyclic_representative(tuple((ZZ(ex[k]), ZZ(par[k])) for k in range(len_par))) self._conj_block[t_ZZ].add(conj_type) for el in self._conj_block[t_ZZ]: group_el = prod([self.V(el[k][0])**el[k][1] for k in range(len(el))]) #if el != group_el.conjugacy_type(): # raise AssertionError("This shouldn't happen!") D = group_el.discriminant() if coerce_AA(D) < 0: raise AssertionError("This shouldn't happen!") if coerce_AA(D) == 0: raise AssertionError("This shouldn't happen!") #continue # The primitive cases #if group_el.is_primitive(): if not ((len(el) == 1 and el[0][1] > 1) or is_cycle(el)): if D not in self._conj_prim: self._conj_prim[D] = [] self._conj_prim[D].append(group_el) # The remaining cases else: if D not in self._conj_nonprim: self._conj_nonprim[D] = [] self._conj_nonprim[D].append(group_el) self._max_block_length = max_block_length
def root_extension_embedding(self, D, K=None): r""" Return the correct embedding from the root extension field of the given discriminant ``D`` to the field ``K``. Also see the method ``root_extension_embedding(K)`` of ``HeckeTriangleGroupElement`` for more examples. INPUT: - ``D`` -- An element of the base ring of ``self`` corresponding to a discriminant. - ``K`` -- A field to which we want the (correct) embeddding. If ``K=None`` (default) then ``AlgebraicField()`` is used for positive ``D`` and ``AlgebraicRealField()`` otherwise. OUTPUT: The corresponding embedding if it was found. Otherwise a ValueError is raised. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=infinity) sage: G.root_extension_embedding(32) Ring morphism: From: Number Field in e with defining polynomial x^2 - 32 To: Algebraic Real Field Defn: e |--> 5.656854249492...? sage: G.root_extension_embedding(-4) Ring morphism: From: Number Field in e with defining polynomial x^2 + 4 To: Algebraic Field Defn: e |--> 2*I sage: G.root_extension_embedding(4) Ring Coercion morphism: From: Rational Field To: Algebraic Real Field sage: G = HeckeTriangleGroup(n=7) sage: lam = G.lam() sage: D = 4*lam^2 + 4*lam - 4 sage: G.root_extension_embedding(D, CC) Relative number field morphism: From: Number Field in e with defining polynomial x^2 - 4*lam^2 - 4*lam + 4 over its base field To: Complex Field with 53 bits of precision Defn: e |--> 4.02438434522... lam |--> 1.80193773580... sage: D = lam^2 - 4 sage: G.root_extension_embedding(D) Relative number field morphism: From: Number Field in e with defining polynomial x^2 - lam^2 + 4 over its base field To: Algebraic Field Defn: e |--> 0.?... + 0.867767478235...?*I lam |--> 1.801937735804...? """ D = self.base_ring()(D) F = self.root_extension_field(D) if K is None: if coerce_AA(D) > 0: K = AA else: K = AlgebraicField() L = [emb for emb in F.embeddings(K)] # Three possibilities up to numerical artefacts: # (1) emb = e, purely imaginary # (2) emb = e or lam (can't distinguish), purely real # (3) emb = (e,lam), e purely imaginary, lam purely real # (4) emb = (e,lam), e purely real, lam purely real # There always exists one emb with "e" positive resp. positive imaginary # and if there is a lam there exists a positive one... # # Criteria to pick the correct "maximum": # 1. First figure out if e resp. lam is purely real or imaginary # (using "abs(e.imag()) > abs(e.real())") # 2. In the purely imaginary case we don't want anything negative imaginary # and we know the positive case is unique after sorting lam # 3. For the remaining cases we want the biggest real part # (and lam should get comparison priority) def emb_key(emb): L = [] gens_len = len(emb.im_gens()) for k in range(gens_len): a = emb.im_gens()[k] try: a.simplify() a.exactify() except AttributeError: pass # If a is purely imaginary: if abs(a.imag()) > abs(a.real()): if a.imag() < 0: a = -infinity else: a = ZZ(0) else: a = a.real() L.append(a) L.reverse() return L if len(L) > 1: L.sort(key = emb_key) return L[-1]
def root_extension_embedding(self, D, K=None): r""" Return the correct embedding from the root extension field of the given discriminant ``D`` to the field ``K``. Also see the method ``root_extension_embedding(K)`` of ``HeckeTriangleGroupElement`` for more examples. INPUT: - ``D`` -- An element of the base ring of ``self`` corresponding to a discriminant. - ``K`` -- A field to which we want the (correct) embeddding. If ``K=None`` (default) then ``AlgebraicField()`` is used for positive ``D`` and ``AlgebraicRealField()`` otherwise. OUTPUT: The corresponding embedding if it was found. Otherwise a ValueError is raised. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=infinity) sage: G.root_extension_embedding(32) Ring morphism: From: Number Field in e with defining polynomial x^2 - 32 To: Algebraic Real Field Defn: e |--> 5.656854249492...? sage: G.root_extension_embedding(-4) Ring morphism: From: Number Field in e with defining polynomial x^2 + 4 To: Algebraic Field Defn: e |--> 2*I sage: G.root_extension_embedding(4) Ring Coercion morphism: From: Rational Field To: Algebraic Real Field sage: G = HeckeTriangleGroup(n=7) sage: lam = G.lam() sage: D = 4*lam^2 + 4*lam - 4 sage: G.root_extension_embedding(D, CC) Relative number field morphism: From: Number Field in e with defining polynomial x^2 - 4*lam^2 - 4*lam + 4 over its base field To: Complex Field with 53 bits of precision Defn: e |--> 4.02438434522... lam |--> 1.80193773580... sage: D = lam^2 - 4 sage: G.root_extension_embedding(D) Relative number field morphism: From: Number Field in e with defining polynomial x^2 - lam^2 + 4 over its base field To: Algebraic Field Defn: e |--> 0.?... + 0.867767478235...?*I lam |--> 1.801937735804...? """ D = self.base_ring()(D) F = self.root_extension_field(D) if K is None: if coerce_AA(D) > 0: K = AA else: K = AlgebraicField() L = [emb for emb in F.embeddings(K)] # Three possibilities up to numerical artefacts: # (1) emb = e, purely imaginary # (2) emb = e or lam (can't distinguish), purely real # (3) emb = (e,lam), e purely imaginary, lam purely real # (4) emb = (e,lam), e purely real, lam purely real # There always exists one emb with "e" positive resp. positive imaginary # and if there is a lam there exists a positive one... # # Criteria to pick the correct "maximum": # 1. First figure out if e resp. lam is purely real or imaginary # (using "abs(e.imag()) > abs(e.real())") # 2. In the purely imaginary case we don't want anything negative imaginary # and we know the positive case is unique after sorting lam # 3. For the remaining cases we want the biggest real part # (and lam should get comparison priority) def emb_key(emb): L = [] gens_len = len(emb.im_gens()) for k in range(gens_len): a = emb.im_gens()[k] try: a.simplify() a.exactify() except AttributeError: pass # If a is purely imaginary: if abs(a.imag()) > abs(a.real()): if a.imag() < 0: a = -infinity else: a = ZZ(0) else: a = a.real() L.append(a) L.reverse() return L if len(L) > 1: L.sort(key=emb_key) return L[-1]
def class_representatives(self, D, primitive=True): r""" Return a representative for each conjugacy class for the discriminant ``D`` (ignoring the sign). If ``primitive=True`` only one representative for each fixed point is returned (ignoring sign). INPUT: - ``D`` -- An element of the base ring corresponding to a valid discriminant. - ``primitive`` -- If ``True`` (default) then only primitive representatives are considered. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.element_repr_method("conj") sage: R = G.class_representatives(4) sage: R [[V(2)]] sage: [v.continued_fraction()[1] for v in R] [(2,)] sage: R = G.class_representatives(0) sage: R [[V(3)]] sage: [v.continued_fraction()[1] for v in R] [(1, 2)] sage: R = G.class_representatives(-4) sage: R [[S]] sage: R = G.class_representatives(-4, primitive=False) sage: R [[S], [U^2]] sage: R = G.class_representatives(G.lam()^2 - 4) sage: R [[U]] sage: R = G.class_representatives(G.lam()^2 - 4, primitive=False) sage: R [[U], [U^(-1)]] sage: R = G.class_representatives(14) sage: R [[V(2)*V(3)], [V(1)*V(2)]] sage: [v.continued_fraction()[1] for v in R] [(1, 2, 2), (3,)] sage: R = G.class_representatives(32) sage: R [[V(3)^2*V(1)], [V(1)^2*V(3)]] sage: [v.continued_fraction()[1] for v in R] [(1, 2, 1, 3), (1, 4)] sage: R = G.class_representatives(32, primitive=False) sage: R [[V(3)^2*V(1)], [V(1)^2*V(3)], [V(2)^2]] sage: G.element_repr_method("default") """ if coerce_AA(D) == 0 and not primitive: raise ValueError( "There are infinitely many non-primitive conjugacy classes of discriminant 0." ) self._conjugacy_representatives(D=D) L = [] if D in self._conj_prim: L += self._conj_prim[D] if not primitive and D in self._conj_nonprim: L += self._conj_nonprim[D] if len(L) == 0: raise ValueError("D = {} is not a{} discriminant for {}".format( D, " primitive" if primitive else "", self)) else: return L
def _conjugacy_representatives(self, max_block_length=ZZ(0), D=None): r""" Store conjugacy representatives up to block length ``max_block_length`` (a non-negative integer, default: 0) in the internal dictionary. Previously calculated data is reused. This is a helper function for e.g. :meth:`class_number`. The set of all (hyperbolic) conjugacy types of block length ``t`` is stored in ``self._conj_block[t]``. The set of all primitive representatives (so far) with discriminant ``D`` is stored in ``self._conj_prim[D]``. The set of all non-primitive representatives (so far) with discriminant ``D`` is stored in ``self._conj_nonprim[D]``. The case of non-positive discriminants is done manually. INPUT: - ``max_block_length`` -- A non-negative integer (default: ``0``), the maximal block length. - ``D`` -- An element/discriminant of the base ring or more generally an upper bound for the involved discriminants. If ``D != None`` then an upper bound for ``max_block_length`` is deduced from ``D`` (default: ``None``). EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=5) sage: G.element_repr_method("conj") sage: G._conjugacy_representatives(2) sage: list(G._conj_block[2]) [((4, 1), (3, 1)), ((2, 2),), ((3, 2),), ((3, 1), (1, 1)), ((4, 1), (1, 1)), ((4, 1), (2, 1)), ((3, 1), (2, 1)), ((2, 1), (1, 1))] sage: [key for key in G._conj_prim] [0, lam - 3, 15*lam + 6, 7*lam + 6, 4*lam, 9*lam + 5, 33*lam + 21, -4] sage: for key in G._conj_prim: print G._conj_prim[key] [[V(4)]] [[U], [U]] [[V(1)*V(3)], [V(2)*V(4)]] [[V(1)*V(4)]] [[V(3)], [V(2)]] [[V(3)*V(4)], [V(1)*V(2)]] [[V(2)*V(3)]] [[S], [S]] sage: [key for key in G._conj_nonprim] [lam - 3, 32*lam + 16, -lam - 2] sage: for key in G._conj_nonprim: print G._conj_nonprim[key] [[U^(-1)], [U^(-1)]] [[V(2)^2], [V(3)^2]] [[U^(-2)], [U^2], [U^(-2)], [U^2]] sage: G.element_repr_method("default") """ from sage.combinat.partition import OrderedPartitions from sage.combinat.combinat import tuples from sage.rings.arith import divisors if not D is None: max_block_length = max(coerce_AA(0), coerce_AA((D + 4) / (self.lam()**2))).sqrt().floor() else: try: max_block_length = ZZ(max_block_length) if max_block_length < 0: raise TypeError except TypeError: raise ValueError( "max_block_length must be a non-negative integer!") if not hasattr(self, "_max_block_length"): self._max_block_length = ZZ(0) self._conj_block = {} self._conj_nonprim = {} self._conj_prim = {} # It is not clear how to define the class number for D=0: # Conjugacy classes are V(n-1)^(+-k) for arbitrary k # and the trivial class (what about self_conj_block[0]?). # # One way is to define it using the fixed points and in # that case V(n-1) would be a good representative. # The non-primitive case is unclear however... # # We set it here to ensure that 0 is enlisted as a discriminant... # self._conj_prim[ZZ(0)] = [] self._conj_prim[ZZ(0)].append(self.V(self.n() - 1)) self._elliptic_conj_reps() if max_block_length <= self._max_block_length: return def is_cycle(seq): length = len(seq) for n in divisors(length): if n < length and is_cycle_of_length(seq, n): return True return False def is_cycle_of_length(seq, n): for i in range(n, len(seq)): if seq[i] != seq[i % n]: return False return True j_list = range(1, self.n()) for t in range(self._max_block_length + 1, max_block_length + 1): t_ZZ = ZZ(t) if t_ZZ not in self._conj_block: self._conj_block[t_ZZ] = set() partitions = OrderedPartitions(t).list() for par in partitions: len_par = len(par) exp_list = tuples(j_list, len_par) for ex in exp_list: keep = True if len_par > 1: for k in range(-1, len_par - 1): if ex[k] == ex[k + 1]: keep = False break # We don't want powers of V(1) elif ex[0] == 1: keep = False # But: Do we maybe want powers of V(n-1)?? # For now we exclude the parabolic cases... elif ex[0] == self.n() - 1: keep = False if keep: conj_type = cyclic_representative( tuple((ZZ(ex[k]), ZZ(par[k])) for k in range(len_par))) self._conj_block[t_ZZ].add(conj_type) for el in self._conj_block[t_ZZ]: group_el = prod( [self.V(el[k][0])**el[k][1] for k in range(len(el))]) #if el != group_el.conjugacy_type(): # raise AssertionError("This shouldn't happen!") D = group_el.discriminant() if coerce_AA(D) < 0: raise AssertionError("This shouldn't happen!") if coerce_AA(D) == 0: raise AssertionError("This shouldn't happen!") #continue # The primitive cases #if group_el.is_primitive(): if not ((len(el) == 1 and el[0][1] > 1) or is_cycle(el)): if D not in self._conj_prim: self._conj_prim[D] = [] self._conj_prim[D].append(group_el) # The remaining cases else: if D not in self._conj_nonprim: self._conj_nonprim[D] = [] self._conj_nonprim[D].append(group_el) self._max_block_length = max_block_length
def class_representatives(self, D, primitive=True): r""" Return a representative for each conjugacy class for the discriminant ``D`` (ignoring the sign). If ``primitive=True`` only one representative for each fixed point is returned (ignoring sign). INPUT: - ``D`` -- An element of the base ring corresponding to a valid discriminant. - ``primitive`` -- If ``True`` (default) then only primitive representatives are considered. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=4) sage: G.element_repr_method("conj") sage: R = G.class_representatives(4) sage: R [[V(2)]] sage: [v.continued_fraction()[1] for v in R] [(2,)] sage: R = G.class_representatives(0) sage: R [[V(3)]] sage: [v.continued_fraction()[1] for v in R] [(1, 2)] sage: R = G.class_representatives(-4) sage: R [[S]] sage: R = G.class_representatives(-4, primitive=False) sage: R [[S], [U^2]] sage: R = G.class_representatives(G.lam()^2 - 4) sage: R [[U]] sage: R = G.class_representatives(G.lam()^2 - 4, primitive=False) sage: R [[U], [U^(-1)]] sage: R = G.class_representatives(14) sage: R [[V(2)*V(3)], [V(1)*V(2)]] sage: [v.continued_fraction()[1] for v in R] [(1, 2, 2), (3,)] sage: R = G.class_representatives(32) sage: R [[V(3)^2*V(1)], [V(1)^2*V(3)]] sage: [v.continued_fraction()[1] for v in R] [(1, 2, 1, 3), (1, 4)] sage: R = G.class_representatives(32, primitive=False) sage: R [[V(3)^2*V(1)], [V(1)^2*V(3)], [V(2)^2]] sage: G.element_repr_method("default") """ if coerce_AA(D) == 0 and not primitive: raise ValueError("There are infinitely many non-primitive conjugacy classes of discriminant 0.") self._conjugacy_representatives(D=D) L = [] if D in self._conj_prim: L += self._conj_prim[D] if not primitive and D in self._conj_nonprim: L += self._conj_nonprim[D] if len(L) == 0: raise ValueError("D = {} is not a{} discriminant for {}".format(D, " primitive" if primitive else "", self)) else: return L