Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
    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()
Exemplo n.º 9
0
    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()
Exemplo n.º 10
0
    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])
Exemplo n.º 11
0
    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])
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    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
Exemplo n.º 14
0
    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]
Exemplo n.º 15
0
    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]
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
    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