예제 #1
0
파일: dual.py 프로젝트: BrentBaccala/sage
        def sum_of_partitions(self, la):
            r"""
            Return the sum over all sets partitions whose shape is ``la``,
            scaled by `\prod_i m_i!` where `m_i` is the multiplicity
            of `i` in ``la``.

            INPUT:

            - ``la`` -- an integer partition

            OUTPUT:

            - an element of ``self``

            EXAMPLES::

                sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w()
                sage: w.sum_of_partitions([2,1,1])
                2*w{{1}, {2}, {3, 4}} + 2*w{{1}, {2, 3}, {4}} + 2*w{{1}, {2, 4}, {3}}
                 + 2*w{{1, 2}, {3}, {4}} + 2*w{{1, 3}, {2}, {4}} + 2*w{{1, 4}, {2}, {3}}
            """
            la = Partition(la)
            c = prod([factorial(_) for _ in la.to_exp()])
            P = SetPartitions()
            return self.sum_of_terms([(P(m), c) for m in SetPartitions(sum(la), la)], distinct=True)
예제 #2
0
            def is_symmetric(self):
                r"""
                Determine if a `NCSym^*` function, expressed in the
                `\mathbf{w}` basis, is symmetric.

                A function `f` in the `\mathbf{w}` basis is a symmetric
                function if it is in the image of `\chi^*`. That is to say we
                have

                .. MATH::

                    f = \sum_{\lambda} c_{\lambda} \prod_i m_i(\lambda)!
                    \sum_{\lambda(A) = \lambda} \mathbf{w}_A

                where the second sum is over all set partitions `A` whose
                shape `\lambda(A)` is equal to `\lambda` and `m_i(\mu)` is
                the multiplicity of `i` in the partition `\mu`.

                OUTPUT:

                - ``True`` if `\lambda(A)=\lambda(B)` implies the coefficients of
                  `\mathbf{w}_A` and `\mathbf{w}_B` are equal, ``False`` otherwise

                EXAMPLES::

                    sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w()
                    sage: elt = w.sum_of_partitions([2,1,1])
                    sage: elt.is_symmetric()
                    True
                    sage: elt -= 3*w.sum_of_partitions([1,1])
                    sage: elt.is_symmetric()
                    True
                    sage: w = SymmetricFunctionsNonCommutingVariables(ZZ).dual().w()
                    sage: elt = w.sum_of_partitions([2,1,1]) / 2
                    sage: elt.is_symmetric()
                    False
                    sage: elt = w[[1,3],[2]]
                    sage: elt.is_symmetric()
                    False
                    sage: elt = w[[1],[2,3]] + w[[1,2],[3]] + 2*w[[1,3],[2]]
                    sage: elt.is_symmetric()
                    False
                """
                d = {}
                R = self.base_ring()
                for A, coeff in self:
                    la = A.shape()
                    exp = prod(map(factorial, la.to_exp()))
                    if la not in d:
                        if coeff / exp not in R:
                            return False
                        d[la] = [coeff, 1]
                    else:
                        if d[la][0] != coeff:
                            return False
                        d[la][1] += 1
                # Make sure we've seen each set partition of the shape
                return all(
                    d[la][1] == SetPartitions(la.size(), la).cardinality()
                    for la in d)
예제 #3
0
파일: dual.py 프로젝트: BrentBaccala/sage
        def __init__(self, NCSymD):
            """
            EXAMPLES::

                sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w()
                sage: TestSuite(w).run()
            """
            def key_func_set_part(A):
                return sorted(map(sorted, A))
            CombinatorialFreeModule.__init__(self, NCSymD.base_ring(), SetPartitions(),
                                             prefix='w', bracket=False,
                                             sorting_key=key_func_set_part,
                                             category=NCSymDualBases(NCSymD))
예제 #4
0
    def __iter__(self):
        r"""

        EXAMPLES::

            sage: from sage_semigroups.monoids.set_partitions_monoid import SetPartitionsMonoid
            sage: [SetPartitionsMonoid(n).cardinality() for n in range(8)]
            [1, 1, 2, 5, 15, 52, 203, 877]

            sage: S = SetPartitionsMonoid(4); S
            Monoid of set partitions of {1, 2, 3, 4}
            sage: TestSuite(S).run()

        """
        for sp in SetPartitions(self._underlying_set):
            yield self(Set_object_enumerated(sp))
예제 #5
0
    def SetPartitions(n):
        r"""
        Return the lattice of set partitions of the set `\{1,\ldots,n\}`
        ordered by refinement.

        INPUT:

        - ``n`` -- a positive integer

        EXAMPLES::

            sage: Posets.SetPartitions(4)
            Finite lattice containing 15 elements
        """
        from sage.rings.semirings.non_negative_integer_semiring import NN
        if n not in NN:
            raise ValueError('n must be an integer')
        from sage.combinat.set_partition import SetPartitions
        S = SetPartitions(n)
        return LatticePoset((S, S.is_less_than))
예제 #6
0
파일: dual.py 프로젝트: wdv4758h/sage
        def __init__(self, NCSymD):
            """
            EXAMPLES::

                sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w()
                sage: TestSuite(w).run()
            """
            def lt_set_part(A, B):
                A = sorted(map(sorted, A))
                B = sorted(map(sorted, B))
                for i in range(len(A)):
                    if A[i] > B[i]:
                        return 1
                    elif A[i] < B[i]:
                        return -1
                return 0
            CombinatorialFreeModule.__init__(self, NCSymD.base_ring(), SetPartitions(),
                                             prefix='w', bracket=False,
                                             monomial_cmp=lt_set_part,
                                             category=NCSymDualBases(NCSymD))
예제 #7
0
def dcrossvec_setp(n):
    """
    Return a list with the distribution of k-dcrossings on set partitions of [1..n].

    INPUT:

    n -- a nonnegative integer.

    OUTPUT:

    A list whose k'th entry is the number of set partitions p for
    which dcrossing(p) = k. For example, let L = dcrossvec_setp(3).
    We have L = [1, 0, 4]. L[0] is 1 because there's 1 partition of
    [1..3] that has 0-dcrossing: [(1, 2, 3)].

    One tricky bit is that noncrossing matchings get put at the end,
    because L[-1] is the last element of the list. Above, we have
    L[-1] = 4 because the other four set partitions are all
    d-noncrossing. Because of this, you should not think of the last
    element of the list as having index n-1, but rather -1.

    EXAMPLES::

        sage: from sage.tests.arxiv_0812_2725 import *
        sage: dcrossvec_setp(3)
        [1, 0, 4]

        sage: dcrossvec_setp(4)
        [5, 1, 0, 9]

    The one set partition of 1 element is noncrossing, so the last
    element of the list is 1::

        sage: dcrossvec_setp(1)
        [1]
    """
    vec = [0] * n
    for p in SetPartitions(n):
        vec[dcrossing(setp_to_edges(p))] += 1
    return vec
예제 #8
0
파일: dual.py 프로젝트: BrentBaccala/sage
            def expand(self, n, letter='x'):
                r"""
                Expand ``self`` written in the `\mathbf{w}` basis in `n^2`
                commuting variables which satisfy the relation
                `x_{ij} x_{ik} = 0` for all `i`, `j`, and `k`.

                The expansion of an element of the `\mathbf{w}` basis is
                given by equations (26) and (55) in [HNT06]_.

                INPUT:

                - ``n`` -- an integer
                - ``letter`` -- (default: ``'x'``) a string

                OUTPUT:

                - The symmetric function of ``self`` expressed in the ``n*n``
                  non-commuting variables described by ``letter``.

                REFERENCES:

                .. [HNT06] \F. Hivert, J.-C. Novelli, J.-Y. Thibon.
                   *Commutative combinatorial Hopf algebras*. (2006).
                   :arxiv:`0605262v1`.

                EXAMPLES::

                    sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w()
                    sage: w[[1,3],[2]].expand(4)
                    x02*x11*x20 + x03*x11*x30 + x03*x22*x30 + x13*x22*x31

                One can use a different set of variable by using the
                optional argument ``letter``::

                    sage: w[[1,3],[2]].expand(3, letter='y')
                    y02*y11*y20
                """
                from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
                from sage.combinat.permutation import Permutations

                m = self.parent()
                names = ['{}{}{}'.format(letter, i, j) for i in range(n) for j in range(n)]
                R = PolynomialRing(m.base_ring(), n*n, names)
                x = [[R.gens()[i*n+j] for j in range(n)] for i in range(n)]
                I = R.ideal([x[i][j]*x[i][k] for j in range(n) for k in range(n) for i in range(n)])
                Q = R.quotient(I, names)
                x = [[Q.gens()[i*n+j] for j in range(n)] for i in range(n)]
                P = SetPartitions()

                def on_basis(A):
                    k = A.size()
                    ret = R.zero()
                    if n < k:
                        return ret

                    for p in Permutations(k):
                        if P(p.to_cycles()) == A:
                            # -1 for indexing
                            ret += R.sum(prod(x[I[i]][I[p[i]-1]] for i in range(k))
                                         for I in Subsets(range(n), k))
                    return ret

                return m._apply_module_morphism(self, on_basis, codomain=R)
예제 #9
0
파일: dual.py 프로젝트: BrentBaccala/sage
        def product_on_basis(self, A, B):
            r"""
            The product on `\mathbf{w}` basis elements.

            The product on the `\mathbf{w}` is the dual to the coproduct on the
            `\mathbf{m}` basis.  On the basis `\mathbf{w}` it is defined as

            .. MATH::

                \mathbf{w}_A \mathbf{w}_B = \sum_{S \subseteq [n]}
                \mathbf{w}_{A\uparrow_S \cup B\uparrow_{S^c}}

            where the sum is over all possible subsets `S` of `[n]` such that
            `|S| = |A|` with a term indexed the union of `A \uparrow_S` and
            `B \uparrow_{S^c}`. The notation `A \uparrow_S` represents the
            unique set partition of the set `S` such that the standardization
            is `A`.  This product is commutative.

            INPUT:

            - ``A``, ``B`` -- set partitions

            OUTPUT:

            - an element of the `\mathbf{w}` basis

            EXAMPLES::

                sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w()
                sage: A = SetPartition([[1], [2,3]])
                sage: B = SetPartition([[1, 2, 3]])
                sage: w.product_on_basis(A, B)
                w{{1}, {2, 3}, {4, 5, 6}} + w{{1}, {2, 3, 4}, {5, 6}}
                 + w{{1}, {2, 3, 5}, {4, 6}} + w{{1}, {2, 3, 6}, {4, 5}}
                 + w{{1}, {2, 4}, {3, 5, 6}} + w{{1}, {2, 4, 5}, {3, 6}}
                 + w{{1}, {2, 4, 6}, {3, 5}} + w{{1}, {2, 5}, {3, 4, 6}}
                 + w{{1}, {2, 5, 6}, {3, 4}} + w{{1}, {2, 6}, {3, 4, 5}}
                 + w{{1, 2, 3}, {4}, {5, 6}} + w{{1, 2, 4}, {3}, {5, 6}}
                 + w{{1, 2, 5}, {3}, {4, 6}} + w{{1, 2, 6}, {3}, {4, 5}}
                 + w{{1, 3, 4}, {2}, {5, 6}} + w{{1, 3, 5}, {2}, {4, 6}}
                 + w{{1, 3, 6}, {2}, {4, 5}} + w{{1, 4, 5}, {2}, {3, 6}}
                 + w{{1, 4, 6}, {2}, {3, 5}} + w{{1, 5, 6}, {2}, {3, 4}}
                sage: B = SetPartition([[1], [2]])
                sage: w.product_on_basis(A, B)
                3*w{{1}, {2}, {3}, {4, 5}} + 2*w{{1}, {2}, {3, 4}, {5}}
                 + 2*w{{1}, {2}, {3, 5}, {4}} + w{{1}, {2, 3}, {4}, {5}}
                 + w{{1}, {2, 4}, {3}, {5}} + w{{1}, {2, 5}, {3}, {4}}
                sage: w.product_on_basis(A, SetPartition([]))
                w{{1}, {2, 3}}
            """
            if len(A) == 0:
                return self.monomial(B)
            if len(B) == 0:
                return self.monomial(A)

            P = SetPartitions()
            n = A.size()
            k = B.size()
            def unions(s):
                a = sorted(s)
                b = sorted(Set(range(1, n+k+1)).difference(s))
                # -1 for indexing
                ret = [[a[i-1] for i in sorted(part)] for part in A]
                ret += [[b[i-1] for i in sorted(part)] for part in B]
                return P(ret)
            return self.sum_of_terms([(unions(s), 1)
                    for s in Subsets(n+k, n)])
예제 #10
0
        def duality_pairing_matrix(self, basis, degree):
            r"""
            The matrix of scalar products between elements of `NCSym` and
            elements of `NCSym^*`.

            INPUT:

            - ``basis`` -- a basis of the dual Hopf algebra
            - ``degree`` -- a non-negative integer

            OUTPUT:

            - the matrix of scalar products between the basis ``self`` and the
              basis ``basis`` in the dual Hopf algebra of degree ``degree``

            EXAMPLES:

            The matrix between the `\mathbf{m}` basis and the
            `\mathbf{w}` basis::

                sage: NCSym = SymmetricFunctionsNonCommutingVariables(QQ)
                sage: m = NCSym.m()
                sage: w = NCSym.dual().w()
                sage: m.duality_pairing_matrix(w, 3)
                [1 0 0 0 0]
                [0 1 0 0 0]
                [0 0 1 0 0]
                [0 0 0 1 0]
                [0 0 0 0 1]

            Similarly for some of the other basis of `NCSym` and the `\mathbf{w}`
            basis::

                sage: e = NCSym.e()
                sage: e.duality_pairing_matrix(w, 3)
                [0 0 0 0 1]
                [0 0 1 1 1]
                [0 1 0 1 1]
                [0 1 1 0 1]
                [1 1 1 1 1]
                sage: p = NCSym.p()
                sage: p.duality_pairing_matrix(w, 3)
                [1 0 0 0 0]
                [1 1 0 0 0]
                [1 0 1 0 0]
                [1 0 0 1 0]
                [1 1 1 1 1]
                sage: q = NCSym.q()
                sage: q.duality_pairing_matrix(w, 3)
                [1 0 0 0 0]
                [1 1 0 0 0]
                [0 0 1 0 0]
                [1 0 0 1 0]
                [1 1 1 1 1]
                sage: x = NCSym.x()
                sage: w.duality_pairing_matrix(x, 3)
                [ 0  0  0  0  1]
                [ 1  0 -1 -1  1]
                [ 1 -1  0 -1  1]
                [ 1 -1 -1  0  1]
                [ 2 -1 -1 -1  1]

            A base case test::

                sage: m.duality_pairing_matrix(w, 0)
                [1]
            """
            from sage.matrix.constructor import matrix
            # TODO: generalize to keys indexing the basis of the graded component
            return matrix(self.base_ring(),
                    [[self.duality_pairing(self[I], basis[J]) \
                            for J in SetPartitions(degree)] \
                            for I in SetPartitions(degree)])
예제 #11
0
def sieve(p, n, r=0, accept=None):
    '''
    Given p, n and r, find the smallest integer m such that there
    exists an element of multiplicative order r in ℤ/m.

    This is equivalent to the condition: for each prime power r'|r,
    there exists a prime power m'|m such that r'|φ(m'). One immediate
    consequence is that r|φ(m).

    The output is a list of pairs of integers (m_i, r_i, o_i), such that

    - m_i is a prime power;
    - the m_i are pairwise coprime and m = ∏ m_i;
    - the r_i are pairwise coprime and r = ∏ r_i;
    - r_i · o_i = φ(m_i).

    The optional parameter `accept` can be passed in order to put
    additional constraints on m. If it is given, it must be a function
    satisfying:

    - it takes six arguments (p, n, r, l, e, s), where p, n, r, l and e
      are integers and s is the factorization of an integer
      (a `Factorization` object);
    - it returns a boolean;
    - let s and t be coprime, accept(p, n, r, l, e, s·t) returns `True` iff
      accept(p, n, r, l, e, s) and accept(p, n, r, l, e, t) also return `True`.

    Then, assuming m_i = l_i^e_i, with l_i prime, the output also
    satisfies

    - accept(p, n, r, l_i, e_i, r_i) returns `True` for any i.

    The obvious use case for `accept` is to ensure that a specific
    element of ℤ/m has order r (or divisible by r), instead of any
    element, e.g. p in the case of Rains' algorithm.

    ## Algorithm

    The algorithm starts by factoring r into prime powers r_i. For
    each r_i, it finds the smallest prime power m_i = l_i^e_i such
    that r_i|φ(m_i) and `accept(p, n, r, l_i, e_i, r_i)` returns `True`
    (if `accept` is provided).

    At this point, the lcm of all the m_i is an acceptable value for
    m, although not necessarily the smallest one. More generally, let

      r_{1,...,s} = r₁ · r₂ ··· r_s

    for some subset of the r_i (up to renumbering), then the lcm of
    m₁, ..., m_s is an acceptable value for r_{1,...,s}, although not
    necessarily the smallest one. Call m_{1,...,s} this optimal value.
    The algorithm goes on by computing the optimal values m_X for
    larger and larger subsets X ⊂ {r_i}, until r is reached.

    This is done by testing all the possible prime powers between the
    largest m_Y already computed for any proper subset Y ⊂ X, and the
    smallest lcm(m_W, m_Z) for any proper partition X = W ∪ Z with
    W ∩ Z = ∅.

    For any given r' = r_{1,...,s}, the algorithm looks for primes of
    the form k·r' + 1. If r_s = l_s^e_s is the largest prime factor of
    r' and if

      r₁ · r₂ ··· r_{s-1} | l_s - 1

    the algorithm also looks for powers of l_s. It is easy to show
    that the algorithm needs consider no other integer.

    ### Example

    Let r = 60 = 4·3·5

    The algorithm starts by computing

      m_{4} = 1·4 + 1 = 5,
      m_{3} = 2·3 + 1 = 7,
      m_{5} = 2·5 + 1 = 11.

    Then it considers all possible pairs of factors. For {4,3}, one
    possible value is 5·7 = 35. 2 divides 3-1, hence the algorithm
    looks for primes and powers of 3 comprised between 7 and 35. It
    finds that 13 is a suitable value, hence

      m_{4,3} = 1·12 + 1 = 13,

    and similarly

      m_{4,5} = 25, m_{3,5} = 31.

    Finally, it considers all three factors. It is useless to test
    prime powers below 31, because 3 and 5 must divide them. There are
    three possible partitions of {4,3,5} into two disjoints sets:

      lcm(m_{4}, m_{3,5}) = 155
      lcm(m_{3}, m_{4,5}) = 175
      lcm(m_{5}, m_{4,3}) = 143

    hence 143 is an acceptable value, and the algorithm needs to test
    no further. The first value tried is 1·60 + 1 = 61, and it turns
    out it is a prime, hence the algorithm returns

      [(61, 60)].

    ### Complexity

    The complexity is obviously polynomial in r. Heuristically, it
    should be sublinear, the dominating step being the factorization
    of r, but it is not so easy to prove it.

    If c is the number of primary factors of r, the main loop is
    executed 2^c times, which is clearly sublinear in r.

    At each iteration, all partitions of the current subset into two
    disjoint subsets must be considered, hence a very crude lower
    bound for this combinatorial step is

      ∑_{i=1}^{c} binom(c,i) (2^{i-1} - 1) < 3^c << e^{o(log r)}

    The most expensive operation of each cycle is the primality
    testing (and, eventually, the `accept` function). Heuristically,
    at each iteration O(log r) primality tests are needed, each with a
    polynomial cost in log r. As noted in `find_root_order`, the best
    bounds under GRH give O(r^{1.4 + ε}) primality tests, instead.

    Whatever the provable complexity is, this algorithm is extremely
    fast in practice, and can handle sizes which are way beyond the
    tractability of the other steps of Rains' algorithm.
    '''
    # Degrees of the the ambient fields.
    ngcd, nlcm = n

    # Actual extension degree within the ambient fields
    if r == 0:
        r = ngcd

    # If accept is not given, always accept
    if accept is None:
        accept = lambda p, n, r, l, e, s: True

    class factorization:
        '''
        This class represents the factorization of an integer,
        carrying one more piece piece of information along each
        primary factor: factors are pairs (p^e, o), with o an
        integer.
        '''
        def __init__(self, f):
            '''
            f must be a dictionary with entries of the form
                l : (e, o)
            '''
            self.factors = f

        def lcm(self, other):
            '''
            Compute the lcm of two factorizations. The auxiliary
            information is multiplied together:

              lcm ( (l^e, s), (l^d, t) ) = (l^max(e,d), s·t)
            '''
            lcm = self.factors.copy()
            for (l, (e, s)) in other.factors.iteritems():
                try:
                    E, S = lcm[l]
                    lcm[l] = (max(E, e), S * s)
                except KeyError:
                    lcm[l] = (e, s)
            return factorization(lcm)

        @cached_method
        def expand(self):
            'Return the integer represented by this factorization'
            return prod(map(lambda (l, (e, _)): l**e, self.factors.items()))

        def __str__(self):
            return ' * '.join('%d^%d<--%d' % (l, e, s)
                              for (l, (e, s)) in self.factors.iteritems())

        def __repr__(self):
            return 'factorization(%s)' % repr(self.factors)

    # Represent the factorization of r as a set of primary factors
    fact = Set(list(r.factor()))
    # This dictionary holds the values r_X for each subset of `fact`
    optima = {}

    # Main loop, execute for each subset `S` ⊂ `fact`
    # It assumes subsets are enumerated by growing size
    for S in fact.subsets():
        # ignore the empty set
        if S.is_empty():
            continue

        # A Factorization object corresponding to S
        Sfact = Factorization(S.list())

        # find `L` the greatest prime in `S`
        L, E = max(S, key=lambda (l, e): l)
        # the product of the remaining prime powers
        c = prod(l**e for l, e in S if l != L)
        # a boolean, True only if it is worth testing powers of `L` in
        # the sequel
        powers = (L - 1) % c == 0
        # the product of all the factors of `S`
        s = c * L**E

        # For singletons, we don't have an upper bound
        if S.cardinality() == 1:
            start = 1
            end = None
        # For larger subsets, we compute the minimum and maximum value
        # to test
        else:
            parts = SetPartitions(S, 2)
            # Start from the largest m_X already computed for any
            # strict subset of `S`
            start = max(optima[T].expand() for T in S.subsets()
                        if (not T.is_empty()) and (T != S))
            # Stop at the smallest lcm of any 2-partition of `S`
            end = min((optima[T0].lcm(optima[T1]) for T0, T1 in parts),
                      key=lambda x: x.expand())

        # We only consider primes of the form
        #    k·s + 1
        # and powers of `L` of the form
        #    k·s + L^E
        # if `powers` is true.
        # We determine the starting `k`
        k = start // s or 1
        while True:
            m = k * s + 1
            # Once the upper bound, is reached, it is used with no
            # further test
            if end is not None and m >= end.expand():
                optima[S] = end
                break
            # Test primes
            elif m.is_prime() and accept(p, n, r, m, 1, Sfact):
                optima[S] = factorization({m: (1, s)})
                break
            # Test powers of `L`
            # notice the correction for L = 2 on the second line
            d = k * c + 1
            if (powers and d.is_power_of(L) and (L != 2 or E == 1 or k > 1)
                    and accept(p, n, r, L, E + d.valuation(L), Sfact)):
                optima[S] = factorization({L: (E + d.valuation(L), s)})
                break

            k += 1

    # the last computed m_X is the optimum for r
    return [(l**e, s, (l - 1) * l**(e - 1) // s)
            for (l, (e, s)) in optima[fact].factors.iteritems()]
예제 #12
0
 def semigroup_generators(self):
     from sage.sets.family import Family
     return Family([
         self(Set_object_enumerated(X))
         for X in SetPartitions(self._underlying_set, 2)
     ])
예제 #13
0
def conjugacy_class_iterator(part, S=None):
    r"""
    Return an iterator over the conjugacy class associated to
    the partition ``part``.
    
    The elements are given as a list of tuples, each tuple being a cycle.

    INPUT:

    - ``part`` -- partition

    - ``S`` -- (optional, default: `\{ 1, 2, \ldots, n \}`, where `n`
      is the size of ``part``) a set

    OUTPUT:

    An iterator over the conjugacy class consisting of all
    permutations of the set ``S`` whose cycle type is ``part``.

    EXAMPLES::

        sage: from sage.groups.perm_gps.symgp_conjugacy_class import conjugacy_class_iterator
        sage: for p in conjugacy_class_iterator([2,2]): print p
        [(1, 2), (3, 4)]
        [(1, 3), (2, 4)]
        [(1, 4), (2, 3)]

    In order to get permutations, one can use ``imap`` from the Python
    module ``itertools``::

        sage: from itertools import imap
        sage: S = SymmetricGroup(5)
        sage: for p in imap(S, conjugacy_class_iterator([3,2])): print p
        (1,2)(3,4,5)
        (1,2)(3,5,4)
        (1,3)(2,4,5)
        (1,3)(2,5,4)
        ...
        (1,4,2)(3,5)
        (1,2,3)(4,5)
        (1,3,2)(4,5)

    Check that the number of elements is the number of elements in
    the conjugacy class::

        sage: s = lambda p: sum(1 for _ in conjugacy_class_iterator(p))
        sage: all(s(p) == p.conjugacy_class_size() for p in Partitions(5))
        True

    It is also possible to specify any underlying set::

        sage: it = conjugacy_class_iterator([2,2,2], 'abcdef')
        sage: next(it)
        [('a', 'c'), ('b', 'e'), ('d', 'f')]
        sage: next(it)
        [('a', 'c'), ('b', 'd'), ('e', 'f')]
    """
    n = sum(part)
    if part not in _Partitions:
        raise ValueError("invalid partition")
    if S is None:
        S = range(1, n+1)
    else:
        S = list(S)
        if n != len(S):
            raise ValueError("the sum of the partition %s does not match the size of %s"%(part,S))

    m = len(part)
    for s in SetPartitions(S, part):
        firsts = [t[0] for t in s]
        rests = [t[1:] for t in s]
        iterator = tuple(itertools.permutations(r) for r in rests)
        for r in itertools.product(*iterator):
            yield [(firsts[i],)+r[i] for i in xrange(m)]