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)
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 __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)
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))
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))
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())
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)
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)
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 __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 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))