Example #1
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
    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