@@ -4375,7 +4375,7 @@ def __call__(self, *g, check=True):
4375
4375
sage: E1 = S(lambda n: s[n], valuation=1)
4376
4376
sage: E = 1 + E1
4377
4377
sage: P = E(E1)
4378
- sage: [s(x) for x in P[:5] ]
4378
+ sage: P[:5]
4379
4379
[s[], s[1], 2*s[2], s[2, 1] + 3*s[3], 2*s[2, 2] + 2*s[3, 1] + 5*s[4]]
4380
4380
4381
4381
The plethysm with a tensor product is also implemented::
@@ -4496,15 +4496,16 @@ def __call__(self, *g, check=True):
4496
4496
g ._coeff_stream ._approximate_order = 1
4497
4497
4498
4498
if P ._arity == 1 :
4499
- ps = P . _laurent_poly_ring .realization_of ().p ()
4499
+ ps = R .realization_of ().p ()
4500
4500
else :
4501
- ps = tensor ([P ._laurent_poly_ring ._sets [0 ].realization_of ().p ()]* P ._arity )
4502
- coeff_stream = Stream_plethysm (self ._coeff_stream , g ._coeff_stream , ps )
4501
+ ps = tensor ([R ._sets [0 ].realization_of ().p ()]* P ._arity )
4502
+ coeff_stream = Stream_plethysm (self ._coeff_stream , g ._coeff_stream ,
4503
+ ps , R )
4504
+ return P .element_class (P , coeff_stream )
4505
+
4503
4506
else :
4504
4507
raise NotImplementedError ("only implemented for arity 1" )
4505
4508
4506
- return P .element_class (P , coeff_stream )
4507
-
4508
4509
plethysm = __call__
4509
4510
4510
4511
def revert (self ):
@@ -4675,14 +4676,22 @@ def functorial_composition(self, *args):
4675
4676
r"""
4676
4677
Return the functorial composition of ``self`` and ``g``.
4677
4678
4678
- If `F` and `G` are species, their functorial composition is
4679
- the species `F \Box G` obtained by setting `(F \Box G) [A] =
4680
- F[ G[A] ]`. In other words, an `(F \Box G)`-structure on a
4681
- set `A` of labels is an `F`-structure whose labels are the
4682
- set of all `G`-structures on `A`.
4679
+ Let `X` be a finite set of cardinality `m`. For a group
4680
+ action of the symmetric group `g: S_n \to S_X` and a
4681
+ (possibly virtual) representation of the symmetric group on
4682
+ `X`, `f: S_X \to GL(V)`, the functorial composition is the
4683
+ (virtual) representation of the symmetric group `f \Box g:
4684
+ S_n \to GL(V)` given by `\sigma \mapsto f(g(\sigma))`.
4683
4685
4684
- It can be shown (as in section 2.2 of [BLL]_) that there is a
4685
- corresponding operation on cycle indices:
4686
+ This is more naturally phrased in the language of
4687
+ combinatorial species. Let `F` and `G` be species, then
4688
+ their functorial composition is the species `F \Box G` with
4689
+ `(F \Box G) [A] = F[ G[A] ]`. In other words, an `(F \Box
4690
+ G)`-structure on a set `A` of labels is an `F`-structure
4691
+ whose labels are the set of all `G`-structures on `A`.
4692
+
4693
+ The Frobenius character (or cycle index series) of `F \Box G`
4694
+ can be computed as follows, see section 2.2 of [BLL]_):
4686
4695
4687
4696
.. MATH::
4688
4697
@@ -4691,8 +4700,6 @@ def functorial_composition(self, *args):
4691
4700
\operatorname{fix} F[ (G[\sigma])_{1}, (G[\sigma])_{2}, \ldots ]
4692
4701
\, p_{1}^{\sigma_{1}} p_{2}^{\sigma_{2}} \cdots.
4693
4702
4694
- This method implements that operation on cycle index series.
4695
-
4696
4703
.. WARNING::
4697
4704
4698
4705
The operation `f \Box g` only makes sense when `g`
@@ -4701,9 +4708,10 @@ def functorial_composition(self, *args):
4701
4708
4702
4709
EXAMPLES:
4703
4710
4704
- The species `G` of simple graphs can be expressed in terms of a functorial
4705
- composition: `G = \mathfrak{p} \Box \mathfrak{p}_{2}`, where
4706
- `\mathfrak{p}` is the :class:`~sage.combinat.species.subset_species.SubsetSpecies`.::
4711
+ The species `G` of simple graphs can be expressed in terms of
4712
+ a functorial composition: `G = \mathfrak{p} \Box
4713
+ \mathfrak{p}_{2}`, where `\mathfrak{p}` is the
4714
+ :class:`~sage.combinat.species.subset_species.SubsetSpecies`.::
4707
4715
4708
4716
sage: R.<q> = QQ[]
4709
4717
sage: h = SymmetricFunctions(R).h()
@@ -4735,14 +4743,24 @@ def functorial_composition(self, *args):
4735
4743
4736
4744
sage: p = SymmetricFunctions(QQ).p()
4737
4745
sage: h = SymmetricFunctions(QQ).h()
4746
+ sage: e = SymmetricFunctions(QQ).e()
4738
4747
sage: L = LazySymmetricFunctions(h)
4739
4748
sage: E = L(lambda n: h[n])
4740
4749
sage: Ep = p[1]*E.derivative_with_respect_to_p1(); Ep
4741
4750
h[1] + (h[1,1]) + (h[2,1]) + (h[3,1]) + (h[4,1]) + (h[5,1]) + O^7
4742
- sage: f = L(lambda n: randint(3, 6)* h[n])
4751
+ sage: f = L(lambda n: h[n-n//2, n//2 ])
4743
4752
sage: f - Ep.functorial_composition(f)
4744
4753
O^7
4745
4754
4755
+ The functorial composition distributes over the sum::
4756
+
4757
+ sage: F1 = L(lambda n: h[n])
4758
+ sage: F2 = L(lambda n: e[n])
4759
+ sage: f1 = F1.functorial_composition(f)
4760
+ sage: f2 = F2.functorial_composition(f)
4761
+ sage: (F1 + F2).functorial_composition(f) - f1 - f2
4762
+ O^7
4763
+
4746
4764
TESTS:
4747
4765
4748
4766
Check a corner case::
@@ -4752,6 +4770,17 @@ def functorial_composition(self, *args):
4752
4770
sage: L(h[2,1]).functorial_composition(L([3*h[0]]))
4753
4771
3*h[] + O^7
4754
4772
4773
+ Check an instance of a non-group action::
4774
+
4775
+ sage: s = SymmetricFunctions(QQ).s()
4776
+ sage: p = SymmetricFunctions(QQ).p()
4777
+ sage: L = LazySymmetricFunctions(p)
4778
+ sage: f = L(lambda n: s[n])
4779
+ sage: g = L(2*s[2, 1, 1] + s[2, 2] + 3*s[4])
4780
+ sage: r = f.functorial_composition(g); r[4]
4781
+ Traceback (most recent call last):
4782
+ ...
4783
+ ValueError: the argument is not the Frobenius character of a permutation representation
4755
4784
"""
4756
4785
if len (args ) != self .parent ()._arity :
4757
4786
raise ValueError ("arity must be equal to the number of arguments provided" )
@@ -4770,39 +4799,49 @@ def functorial_composition(self, *args):
4770
4799
g = Stream_map_coefficients (g ._coeff_stream , p )
4771
4800
f = Stream_map_coefficients (self ._coeff_stream , p )
4772
4801
4773
- def g_cycle_type (s ):
4802
+ def g_cycle_type (s , n ):
4774
4803
# the cycle type of G[sigma] of any permutation sigma
4775
- # with cycle type s
4776
- if not s :
4804
+ # with cycle type s, which is a partition of n
4805
+ if not n :
4777
4806
if g [0 ]:
4778
4807
return Partition ([1 ]* ZZ (g [0 ].coefficient ([])))
4779
4808
return Partition ([])
4780
4809
res = []
4781
4810
# in the species case, k is at most
4782
- # factorial(n) * g[n].coefficient([1]*n) with n = sum(s)
4811
+ # factorial(n) * g[n].coefficient([1]*n)
4783
4812
for k in range (1 , lcm (s ) + 1 ):
4784
4813
e = 0
4785
4814
for d in divisors (k ):
4786
4815
m = moebius (d )
4787
4816
if not m :
4788
4817
continue
4789
4818
u = s .power (k // d )
4790
- g_u = g [sum (u )]
4819
+ # it could be, that we never need to compute
4820
+ # g[n], so we only do this here
4821
+ g_u = g [n ]
4791
4822
if g_u :
4792
4823
e += m * u .aut () * g_u .coefficient (u )
4793
- res .extend ([k ] * int (e // k ))
4824
+ # e / k might not be an integer if g is not a
4825
+ # group action, so it is good to check
4826
+ res .extend ([k ] * ZZ (e / k ))
4794
4827
res .reverse ()
4795
4828
return Partition (res )
4796
4829
4797
4830
def coefficient (n ):
4798
- res = p .zero ()
4831
+ terms = {}
4832
+ t_size = None
4799
4833
for s in Partitions (n ):
4800
- t = g_cycle_type (s )
4801
- f_t = f [t .size ()]
4802
- if f_t :
4803
- q = t .aut () * f_t .coefficient (t ) / s .aut ()
4804
- res += q * p (s )
4805
- return res
4834
+ t = g_cycle_type (s , n )
4835
+ if t_size is None :
4836
+ t_size = sum (t )
4837
+ f_t = f [t_size ]
4838
+ if not f_t :
4839
+ break
4840
+ elif t_size != sum (t ):
4841
+ raise ValueError ("the argument is not the Frobenius character of a permutation representation" )
4842
+
4843
+ terms [s ] = t .aut () * f_t .coefficient (t ) / s .aut ()
4844
+ return R (p .element_class (p , terms ))
4806
4845
4807
4846
coeff_stream = Stream_function (coefficient , P ._sparse , 0 )
4808
4847
return P .element_class (P , coeff_stream )
0 commit comments