def _compose_gen(self, y, ao):
        """
        Return a generator for the coefficients of the composition of this 
        cycle index series and the cycle index series ``y``. This overrides 
        the method defined in ``LazyPowerSeries``. 

        The notion "composition" means plethystic substitution here, as 
        defined in Section 2.2 of [BLL-Intro]_.

        EXAMPLES::

            sage: E = species.SetSpecies(); C = species.CycleSpecies()
            sage: E_cis = E.cycle_index_series()
            sage: g = E_cis._compose_gen(C.cycle_index_series(),0)
            sage: [next(g) for i in range(4)]
            [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]]
        """
        assert y.coefficient(0) == 0
        y_powers = Stream(y._power_gen())

        parent = self.parent()
        res = parent.sum_generator(
            self._compose_term(self.coefficient(i), y_powers)
            for i in _integers_from(0))

        for i in _integers_from(0):
            yield res.coefficient(i)
    def _compose_gen(self, y, ao):
        """
        Return a generator for the coefficients of the composition of this 
        cycle index series and the cycle index series ``y``. This overrides 
        the method defined in ``LazyPowerSeries``. 

        The notion "composition" means plethystic substitution here, as 
        defined in Section 2.2 of [BLL-Intro]_.

        EXAMPLES::

            sage: E = species.SetSpecies(); C = species.CycleSpecies()
            sage: E_cis = E.cycle_index_series()
            sage: g = E_cis._compose_gen(C.cycle_index_series(),0)
            sage: [next(g) for i in range(4)]
            [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]]
        """
        assert y.coefficient(0) == 0
        y_powers = Stream(y._power_gen())

        parent = self.parent()
        res =  parent.sum_generator(self._compose_term(self.coefficient(i), y_powers)
                                    for i in _integers_from(0))

        for i in _integers_from(0):
            yield res.coefficient(i)
Beispiel #3
0
    def __invert__(self):
        """
        Return the multiplicative inverse of self.
        This algorithm is derived from [BLL]_.

        EXAMPLES::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: E.__invert__().coefficients(4)
            [p[], -p[1], 1/2*p[1, 1] - 1/2*p[2], -1/6*p[1, 1, 1] + 1/2*p[2, 1] - 1/3*p[3]]

        The defining characteristic of the multiplicative inverse `F^{-1}` of a cycle index series `F`
        is that `F \cdot F^{-1} = F^{-1} \cdot F = 1` (that is, both products with `F` yield the multiplicative identity `1`)::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: (E * ~E).coefficients(6)
            [p[], 0, 0, 0, 0, 0]

        REFERENCES:

        .. [BLL] F. Bergeron, G. Labelle, and P. Leroux. "Combinatorial species and tree-like structures". Encyclopedia of Mathematics and its Applications, vol. 67, Cambridge Univ. Press. 1998.

        AUTHORS:

        - Andrew Gainer-Dewar
        """
        if self.coefficient(0) == 0:
            raise ValueError("Constant term must be non-zero")

        def multinv_builder(i):
            return self.coefficient(0)**(-i-1) * (self.coefficient(0) + (-1)*self)**i

        return self.parent().sum_generator(multinv_builder(i) for i in _integers_from(0))
Beispiel #4
0
    def __invert__(self):
        """
        Return the multiplicative inverse of self.
        This algorithm is derived from [BLL]_.

        EXAMPLES::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: E.__invert__().coefficients(4)
            [p[], -p[1], 1/2*p[1, 1] - 1/2*p[2], -1/6*p[1, 1, 1] + 1/2*p[2, 1] - 1/3*p[3]]

        The defining characteristic of the multiplicative inverse `F^{-1}` of a cycle index series `F`
        is that `F \cdot F^{-1} = F^{-1} \cdot F = 1` (that is, both products with `F` yield the multiplicative identity `1`)::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: (E * ~E).coefficients(6)
            [p[], 0, 0, 0, 0, 0]

        REFERENCES:

        .. [BLL] F. Bergeron, G. Labelle, and P. Leroux. "Combinatorial species and tree-like structures". Encyclopedia of Mathematics and its Applications, vol. 67, Cambridge Univ. Press. 1998.

        AUTHORS:

        - Andrew Gainer-Dewar
        """
        if self.coefficient(0) == 0:
            raise ValueError("Constant term must be non-zero")

        def multinv_builder(i):
            return self.coefficient(0)**(-i - 1) * (self.coefficient(0) +
                                                    (-1) * self)**i

        return self.parent().sum_generator(
            multinv_builder(i) for i in _integers_from(0))
    def expand_as_sf(self, n, alphabet='x'):
        """
        Returns the expansion of a cycle index series as a symmetric function in
        ``n`` variables.

        Specifically, this returns a :class:`~sage.combinat.species.series.LazyPowerSeries` whose
        ith term is obtained by calling :meth:`~sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element.expand`
        on the ith term of ``self``.

        This relies on the (standard) interpretation of a cycle index series as a symmetric function
        in the power sum basis.

        INPUT:

        - ``self`` -- a cycle index series

        - ``n`` -- a positive integer

        - ``alphabet`` -- a variable for the expansion (default: `x`)

        EXAMPLES::

            sage: from sage.combinat.species.set_species import SetSpecies
            sage: SetSpecies().cycle_index_series().expand_as_sf(2).coefficients(4)
            [1, x0 + x1, x0^2 + x0*x1 + x1^2, x0^3 + x0^2*x1 + x0*x1^2 + x1^3]

        """
        expanded_poly_ring = self.coefficient(0).expand(n, alphabet).parent()
        LPSR = LazyPowerSeriesRing(expanded_poly_ring)

        expander_gen = (LPSR.term(self.coefficient(i).expand(n, alphabet), i)
                        for i in _integers_from(0))

        return LPSR.sum_generator(expander_gen)
    def _weighted_compose_gen(self, y_species, ao):
        """
        Returns an iterator for the composition of this cycle index series
        and the cycle index series of the weighted species y_species.

        EXAMPLES::

            sage: E = species.SetSpecies(); C = species.CycleSpecies()
            sage: E_cis = E.cycle_index_series()
            sage: g = E_cis._weighted_compose_gen(C,0)
            sage: [next(g) for i in range(4)]
            [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]]
        """
        parent = self.parent()
        res =  parent.sum_generator(self._weighted_compose_term(self.coefficient(i), y_species)
                                    for i in _integers_from(0))

        for i in _integers_from(0):
            yield res.coefficient(i)
Beispiel #7
0
    def _weighted_compose_gen(self, y_species, ao):
        """
        Returns an iterator for the composition of this cycle index series
        and the cycle index series of the weighted species y_species.

        EXAMPLES::

            sage: E = species.SetSpecies(); C = species.CycleSpecies()
            sage: E_cis = E.cycle_index_series()
            sage: g = E_cis._weighted_compose_gen(C,0)
            sage: [next(g) for i in range(4)]
            [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]]
        """
        parent = self.parent()
        res =  parent.sum_generator(self._weighted_compose_term(self.coefficient(i), y_species)
                                    for i in _integers_from(0))

        for i in _integers_from(0):
            yield res.coefficient(i)
Beispiel #8
0
def _exp_gen(R = RationalField()):
    """
    Produce a generator which yields the terms of the cycle index series of the species `E` of sets.

    EXAMPLES::

        sage: from sage.combinat.species.generating_series import _exp_gen
        sage: g = _exp_gen()
        sage: [g.next() for i in range(4)]
        [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], 1/6*p[1, 1, 1] + 1/2*p[2, 1] + 1/3*p[3]]
    """
    return (_exp_term(i, R) for i in _integers_from(0))
Beispiel #9
0
def _cl_gen (R = RationalField()):
    """
    Produce a generator which yields the terms of the cycle index series of the virtual species
    `\Omega`, the compositional inverse of the species `E^{+}` of nonempty sets.

    EXAMPLES::

        sage: from sage.combinat.species.generating_series import _cl_gen
        sage: g = _cl_gen()
        sage: [g.next() for i in range(4)]
        [0, p[1], -1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3]]
    """
    return (_cl_term(i, R) for i in _integers_from(0))
Beispiel #10
0
    def _compose_gen(self, y, ao):
        """
        Returns a generator for the coefficients of the composition of this
        cycle index series and the cycle index series y. This overrides the
        the method defined in LazyPowerSeries.
        
        EXAMPLES::
        
            sage: E = species.SetSpecies(); C = species.CycleSpecies()
            sage: E_cis = E.cycle_index_series()
            sage: g = E_cis._compose_gen(C.cycle_index_series(),0)
            sage: [g.next() for i in range(4)]
            [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]]
        """
        assert y.coefficient(0) == 0
        y_powers = Stream(y._power_gen())

        parent = self.parent()
        res =  parent.sum_generator(self._compose_term(self.coefficient(i), y_powers)
                                    for i in _integers_from(0))

        for i in _integers_from(0):
            yield res.coefficient(i)
Beispiel #11
0
    def _compose_gen(self, y, ao):
        """
        Returns a generator for the coefficients of the composition of this
        cycle index series and the cycle index series y. This overrides the
        the method defined in LazyPowerSeries.

        EXAMPLES::

            sage: E = species.SetSpecies(); C = species.CycleSpecies()
            sage: E_cis = E.cycle_index_series()
            sage: g = E_cis._compose_gen(C.cycle_index_series(),0)
            sage: [g.next() for i in range(4)]
            [p[], p[1], p[1, 1] + p[2], p[1, 1, 1] + p[2, 1] + p[3]]
        """
        assert y.coefficient(0) == 0
        y_powers = Stream(y._power_gen())

        parent = self.parent()
        res =  parent.sum_generator(self._compose_term(self.coefficient(i), y_powers)
                                    for i in _integers_from(0))

        for i in _integers_from(0):
            yield res.coefficient(i)
    def _egs_gen(self, ao):
        """
        Returns a generator for the coefficients of the exponential
        generating series obtained from a cycle index series.

        EXAMPLES::

            sage: P = species.PermutationSpecies()
            sage: cis = P.cycle_index_series()
            sage: g = cis._egs_gen(0)
            sage: [next(g) for i in range(10)]
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        """
        for i in range(ao):
            yield 0
        for i in _integers_from(ao):
            yield self.coefficient(i).coefficient([1]*i)
    def _ogs_gen(self, ao):
        """
        Returns a generator for the coefficients of the ordinary generating
        series obtained from a cycle index series.

        EXAMPLES::

            sage: P = species.PermutationSpecies()
            sage: cis = P.cycle_index_series()
            sage: g = cis._ogs_gen(0)
            sage: [next(g) for i in range(10)]
            [1, 1, 2, 3, 5, 7, 11, 15, 22, 30]
        """
        for i in range(ao):
            yield 0
        for i in _integers_from(ao):
            yield sum( self.coefficient(i).coefficients() )
    def _egs_gen(self, ao):
        """
        Returns a generator for the coefficients of the exponential
        generating series obtained from a cycle index series.

        EXAMPLES::

            sage: P = species.PermutationSpecies()
            sage: cis = P.cycle_index_series()
            sage: g = cis._egs_gen(0)
            sage: [next(g) for i in range(10)]
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        """
        for i in range(ao):
            yield 0
        for i in _integers_from(ao):
            yield self.coefficient(i).coefficient([1] * i)
    def _ogs_gen(self, ao):
        """
        Returns a generator for the coefficients of the ordinary generating
        series obtained from a cycle index series.

        EXAMPLES::

            sage: P = species.PermutationSpecies()
            sage: cis = P.cycle_index_series()
            sage: g = cis._ogs_gen(0)
            sage: [next(g) for i in range(10)]
            [1, 1, 2, 3, 5, 7, 11, 15, 22, 30]
        """
        for i in range(ao):
            yield 0
        for i in _integers_from(ao):
            yield sum(self.coefficient(i).coefficients())
Beispiel #16
0
    def derivative(self, order=1):
        r"""
        Return the species-theoretic nth derivative of ``self``, where n is ``order``.

        For a cycle index series `F (p_{1}, p_{2}, p_{3}, \dots)`, its derivative is the cycle index series
        `F' = D_{p_{1}} F` (that is, the formal derivative of `F` with respect to the variable `p_{1}`).

        If `F` is the cycle index series of a species `S` then `F'` is the cycle index series of an associated
        species `S'` of `S`-structures with a "hole".

        EXAMPLES:

        The species `E` of sets satisfies the relationship `E' = E`::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: E.coefficients(8) == E.derivative().coefficients(8)
            True

        The species `C` of cyclic orderings and the species `L` of linear orderings satisfy the relationship `C' = L`::

            sage: C = species.CycleSpecies().cycle_index_series()
            sage: L = species.LinearOrderSpecies().cycle_index_series()
            sage: L.coefficients(8) == C.derivative().coefficients(8)
            True

        """

        # Make sure that order is integral
        order = Integer(order)

        if order < 0:
            raise ValueError("Order must be a non-negative integer")

        elif order == 0:
            return self

        elif order == 1:
            parent = self.parent()
            derivative_term = lambda n: parent.term(
                self.coefficient(n + 1).derivative_with_respect_to_p1(), n)
            return parent.sum_generator(
                derivative_term(i) for i in _integers_from(0))

        else:
            return self.derivative(order - 1)
Beispiel #17
0
    def derivative(self, order=1):
        r"""
        Return the species-theoretic nth derivative of ``self``, where n is ``order``.

        For a cycle index series `F (p_{1}, p_{2}, p_{3}, \dots)`, its derivative is the cycle index series
        `F' = D_{p_{1}} F` (that is, the formal derivative of `F` with respect to the variable `p_{1}`).

        If `F` is the cycle index series of a species `S` then `F'` is the cycle index series of an associated
        species `S'` of `S`-structures with a "hole".

        EXAMPLES:

        The species `E` of sets satisfies the relationship `E' = E`::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: E.coefficients(8) == E.derivative().coefficients(8)
            True

        The species `C` of cyclic orderings and the species `L` of linear orderings satisfy the relationship `C' = L`::

            sage: C = species.CycleSpecies().cycle_index_series()
            sage: L = species.LinearOrderSpecies().cycle_index_series()
            sage: L.coefficients(8) == C.derivative().coefficients(8)
            True

        """

        # Make sure that order is integral
        order = Integer(order)

        if order < 0:
            raise ValueError("Order must be a non-negative integer")

        elif order == 0:
            return self

        elif order == 1:
            parent = self.parent()
            derivative_term = lambda n: parent.term(self.coefficient(n+1).derivative_with_respect_to_p1(), n)
            return parent.sum_generator(derivative_term(i) for i in _integers_from(0))

        else:
            return self.derivative(order-1)
Beispiel #18
0
    def __invert__(self):
        """
        Return the multiplicative inverse of ``self``.

        This algorithm is derived from [BLL]_.

        EXAMPLES::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: E.__invert__().coefficients(4)
            [p[], -p[1], 1/2*p[1, 1] - 1/2*p[2], -1/6*p[1, 1, 1] + 1/2*p[2, 1] - 1/3*p[3]]

        The defining characteristic of the multiplicative inverse `F^{-1}` of a cycle index series `F`
        is that `F \cdot F^{-1} = F^{-1} \cdot F = 1` (that is, both products with `F` yield the multiplicative identity `1`)::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: (E * ~E).coefficients(6)
            [p[], 0, 0, 0, 0, 0]

        REFERENCES:

        [BLL]_

        [BLL-Intro]_

        http://bergeron.math.uqam.ca/Site/bergeron_anglais_files/livre_combinatoire.pdf

        AUTHORS:

        - Andrew Gainer-Dewar
        """
        if self.coefficient(0) == 0:
            raise ValueError("Constant term must be non-zero")

        def multinv_builder(i):
            return self.coefficient(0)**(-i - 1) * (self.coefficient(0) +
                                                    (-1) * self)**i

        return self.parent().sum_generator(
            multinv_builder(i) for i in _integers_from(0))
Beispiel #19
0
    def __invert__(self):
        """
        Return the multiplicative inverse of ``self``.

        This algorithm is derived from [BLL]_.

        EXAMPLES::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: E.__invert__().coefficients(4)
            [p[], -p[1], 1/2*p[1, 1] - 1/2*p[2], -1/6*p[1, 1, 1] + 1/2*p[2, 1] - 1/3*p[3]]

        The defining characteristic of the multiplicative inverse `F^{-1}` of a cycle index series `F`
        is that `F \cdot F^{-1} = F^{-1} \cdot F = 1` (that is, both products with `F` yield the multiplicative identity `1`)::

            sage: E = species.SetSpecies().cycle_index_series()
            sage: (E * ~E).coefficients(6)
            [p[], 0, 0, 0, 0, 0]

        REFERENCES:

        [BLL]_

        [BLL-Intro]_

        http://bergeron.math.uqam.ca/Site/bergeron_anglais_files/livre_combinatoire.pdf

        AUTHORS:

        - Andrew Gainer-Dewar
        """
        if self.coefficient(0) == 0:
            raise ValueError("Constant term must be non-zero")

        def multinv_builder(i):
            return self.coefficient(0)**(-i-1) * (self.coefficient(0) + (-1)*self)**i

        return self.parent().sum_generator(multinv_builder(i) for i in _integers_from(0))
    def arithmetic_product(self, g, check_input = True):
        """
        Return the arithmetic product of ``self`` with ``g``.

        For species `M` and `N` such that `M[\\varnothing] = N[\\varnothing] = \\varnothing`,
        their arithmetic product is the species `M \\boxdot N` of "`M`-assemblies of cloned `N`-structures".
        This operation is defined and several examples are given in [MM]_.

        The cycle index series for `M \\boxdot N` can be computed in terms of the component series `Z_M` and `Z_N`,
        as implemented in this method.

        INPUT:

        - ``g`` -- a cycle index series having the same parent as ``self``.

        - ``check_input`` -- (default: ``True``) a Boolean which, when set
          to ``False``, will cause input checks to be skipped.

        OUTPUT:

        The arithmetic product of ``self`` with ``g``. This is a cycle
        index series defined in terms of ``self`` and ``g`` such that
        if ``self`` and ``g`` are the cycle index series of two species
        `M` and `N`, their arithmetic product is the cycle index series
        of the species `M \\boxdot N`.

        EXAMPLES:

        For `C` the species of (oriented) cycles and `L_{+}` the species of nonempty linear orders, `C \\boxdot L_{+}` corresponds
        to the species of "regular octopuses"; a `(C \\boxdot L_{+})`-structure is a cycle of some length, each of whose elements
        is an ordered list of a length which is consistent for all the lists in the structure. ::

            sage: C = species.CycleSpecies().cycle_index_series()
            sage: Lplus = species.LinearOrderSpecies(min=1).cycle_index_series()
            sage: RegularOctopuses = C.arithmetic_product(Lplus)
            sage: RegOctSpeciesSeq = RegularOctopuses.generating_series().counts(8)
            sage: RegOctSpeciesSeq
            [0, 1, 3, 8, 42, 144, 1440, 5760]

        It is shown in [MM]_ that the exponential generating function for regular octopuses satisfies
        `(C \\boxdot L_{+}) (x) = \\sum_{n \geq 1} \\sigma (n) (n - 1)! \\frac{x^{n}}{n!}` (where `\\sigma (n)` is
        the sum of the divisors of `n`). ::

            sage: RegOctDirectSeq = [0] + [sum(divisors(i))*factorial(i-1) for i in range(1,8)]
            sage: RegOctDirectSeq == RegOctSpeciesSeq
            True

        AUTHORS:

        - Andrew Gainer-Dewar (2013)

        REFERENCES:

        .. [MM] M. Maia and M. Mendez. "On the arithmetic product of combinatorial species".
           Discrete Mathematics, vol. 308, issue 23, 2008, pp. 5407-5427.
           :arXiv:`math/0503436v2`.

        """
        from sage.rings.arith import gcd, lcm, divisors
        from itertools import product, repeat, chain

        p = self.base_ring()

        if check_input:
            assert self.coefficient(0) == p.zero()
            assert g.coefficient(0) == p.zero()

        # We first define an operation `\\boxtimes` on partitions as in Lemma 2.1 of [MM]_.
        def arith_prod_of_partitions(l1, l2):
            # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by
            # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes
            # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2`
            # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a
            # partition of `nm`.
            term_iterable = chain.from_iterable( repeat(lcm(pair), times=gcd(pair)) for pair in product(l1, l2) )
            term_list = sorted(term_iterable, reverse=True)
            res = Partition(term_list)
            return res

        # We then extend this to an operation on symmetric functions as per eq. (52) of [MM]_.
        # (Maia and Mendez, in [MM]_, are talking about polynomials instead of symmetric
        # functions, but this boils down to the same: Their x_i corresponds to the i-th power
        # sum symmetric function.)
        def arith_prod_sf(x, y):
            ap_sf_wrapper = lambda l1, l2: p(arith_prod_of_partitions(l1, l2))
            return p._apply_multi_module_morphism(x, y, ap_sf_wrapper)

        # Sage stores cycle index series by degree.
        # Thus, to compute the arithmetic product `Z_M \\boxdot Z_N` it is useful
        # to compute all terms of a given degree `n` at once.
        def arith_prod_coeff(n):
            if n == 0:
                res = p.zero()
            else:
                index_set = ((d, n // d) for d in divisors(n))
                res = sum(arith_prod_sf(self.coefficient(i), g.coefficient(j)) for i,j in index_set)

            # Build a list which has res in the `n`th slot and 0's before and after
            # to feed to sum_generator
            res_in_seq = [p.zero()]*n + [res, p.zero()]

            return self.parent(res_in_seq)

        # Finally, we use the sum_generator method to assemble these results into a single
        # LazyPowerSeries object.
        return self.parent().sum_generator(arith_prod_coeff(n) for n in _integers_from(0))
    def expand_as_sf(self, n, alphabet='x'):
        """
        Returns the expansion of a cycle index series as a symmetric function in
        ``n`` variables.

        Specifically, this returns a :class:`~sage.combinat.species.series.LazyPowerSeries` whose
        ith term is obtained by calling :meth:`~sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element.expand`
        on the ith term of ``self``.

        This relies on the (standard) interpretation of a cycle index series as a symmetric function
        in the power sum basis.

        INPUT:

        - ``self`` -- a cycle index series

        - ``n`` -- a positive integer

        - ``alphabet`` -- a variable for the expansion (default: `x`)

        EXAMPLES::

            sage: from sage.combinat.species.set_species import SetSpecies
            sage: SetSpecies().cycle_index_series().expand_as_sf(2).coefficients(4)
            [1, x0 + x1, x0^2 + x0*x1 + x1^2, x0^3 + x0^2*x1 + x0*x1^2 + x1^3]

        """
        expanded_poly_ring = self.coefficient(0).expand(n, alphabet).parent()
        LPSR = LazyPowerSeriesRing(expanded_poly_ring)

        expander_gen = (LPSR.term(self.coefficient(i).expand(n, alphabet), i) for i in _integers_from(0))

        return LPSR.sum_generator(expander_gen)
    def arithmetic_product(self, g, check_input=True):
        """
        Return the arithmetic product of ``self`` with ``g``.

        For species `M` and `N` such that `M[\\varnothing] = N[\\varnothing] = \\varnothing`,
        their arithmetic product is the species `M \\boxdot N` of "`M`-assemblies of cloned `N`-structures".
        This operation is defined and several examples are given in [MM]_.

        The cycle index series for `M \\boxdot N` can be computed in terms of the component series `Z_M` and `Z_N`,
        as implemented in this method.

        INPUT:

        - ``g`` -- a cycle index series having the same parent as ``self``.

        - ``check_input`` -- (default: ``True``) a Boolean which, when set
          to ``False``, will cause input checks to be skipped.

        OUTPUT:

        The arithmetic product of ``self`` with ``g``. This is a cycle
        index series defined in terms of ``self`` and ``g`` such that
        if ``self`` and ``g`` are the cycle index series of two species
        `M` and `N`, their arithmetic product is the cycle index series
        of the species `M \\boxdot N`.

        EXAMPLES:

        For `C` the species of (oriented) cycles and `L_{+}` the species of nonempty linear orders, `C \\boxdot L_{+}` corresponds
        to the species of "regular octopuses"; a `(C \\boxdot L_{+})`-structure is a cycle of some length, each of whose elements
        is an ordered list of a length which is consistent for all the lists in the structure. ::

            sage: C = species.CycleSpecies().cycle_index_series()
            sage: Lplus = species.LinearOrderSpecies(min=1).cycle_index_series()
            sage: RegularOctopuses = C.arithmetic_product(Lplus)
            sage: RegOctSpeciesSeq = RegularOctopuses.generating_series().counts(8)
            sage: RegOctSpeciesSeq
            [0, 1, 3, 8, 42, 144, 1440, 5760]

        It is shown in [MM]_ that the exponential generating function for regular octopuses satisfies
        `(C \\boxdot L_{+}) (x) = \\sum_{n \geq 1} \\sigma (n) (n - 1)! \\frac{x^{n}}{n!}` (where `\\sigma (n)` is
        the sum of the divisors of `n`). ::

            sage: RegOctDirectSeq = [0] + [sum(divisors(i))*factorial(i-1) for i in range(1,8)]
            sage: RegOctDirectSeq == RegOctSpeciesSeq
            True

        AUTHORS:

        - Andrew Gainer-Dewar (2013)

        REFERENCES:

        .. [MM] M. Maia and M. Mendez. "On the arithmetic product of combinatorial species".
           Discrete Mathematics, vol. 308, issue 23, 2008, pp. 5407-5427.
           :arXiv:`math/0503436v2`.

        """
        from sage.rings.arith import gcd, lcm, divisors
        from itertools import product, repeat, chain

        p = self.base_ring()

        if check_input:
            assert self.coefficient(0) == p.zero()
            assert g.coefficient(0) == p.zero()

        # We first define an operation `\\boxtimes` on partitions as in Lemma 2.1 of [MM]_.
        def arith_prod_of_partitions(l1, l2):
            # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by
            # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes
            # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2`
            # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a
            # partition of `nm`.
            term_iterable = chain.from_iterable(
                repeat(lcm(pair), times=gcd(pair)) for pair in product(l1, l2))
            term_list = sorted(term_iterable, reverse=True)
            res = Partition(term_list)
            return res

        # We then extend this to an operation on symmetric functions as per eq. (52) of [MM]_.
        # (Maia and Mendez, in [MM]_, are talking about polynomials instead of symmetric
        # functions, but this boils down to the same: Their x_i corresponds to the i-th power
        # sum symmetric function.)
        def arith_prod_sf(x, y):
            ap_sf_wrapper = lambda l1, l2: p(arith_prod_of_partitions(l1, l2))
            return p._apply_multi_module_morphism(x, y, ap_sf_wrapper)

        # Sage stores cycle index series by degree.
        # Thus, to compute the arithmetic product `Z_M \\boxdot Z_N` it is useful
        # to compute all terms of a given degree `n` at once.
        def arith_prod_coeff(n):
            if n == 0:
                res = p.zero()
            else:
                index_set = ((d, n // d) for d in divisors(n))
                res = sum(
                    arith_prod_sf(self.coefficient(i), g.coefficient(j))
                    for i, j in index_set)

            # Build a list which has res in the `n`th slot and 0's before and after
            # to feed to sum_generator
            res_in_seq = [p.zero()] * n + [res, p.zero()]

            return self.parent(res_in_seq)

        # Finally, we use the sum_generator method to assemble these results into a single
        # LazyPowerSeries object.
        return self.parent().sum_generator(
            arith_prod_coeff(n) for n in _integers_from(0))