80
80
- Kwankyu Lee (2019-02-24): initial version
81
81
- Tejasvi Chebrolu, Martin Rubey, Travis Scrimshaw (2021-08):
82
82
refactored and expanded functionality
83
-
84
83
"""
85
84
86
85
# ****************************************************************************
@@ -168,6 +167,15 @@ class Stream_inexact(Stream):
168
167
- ``sparse`` -- boolean; whether the implementation of the stream is sparse
169
168
- ``approximate_order`` -- integer; a lower bound for the order
170
169
of the stream
170
+
171
+ .. TODO::
172
+
173
+ The ``approximate_order`` is currently only updated when
174
+ invoking :meth:`order`. It might make sense to update it
175
+ whenever the coefficient one larger than the current
176
+ ``approximate_order`` is computed, since in some methods this
177
+ will allow shortcuts.
178
+
171
179
"""
172
180
def __init__ (self , is_sparse , approximate_order ):
173
181
"""
@@ -1659,30 +1667,73 @@ class Stream_plethysm(Stream_binary):
1659
1667
- ``f`` -- a :class:`Stream`
1660
1668
- ``g`` -- a :class:`Stream` with positive order
1661
1669
- ``p`` -- the powersum symmetric functions
1670
+ - ``ring`` (optional, default ``None``) -- the ring the result
1671
+ should be in, by default ``p``
1672
+ - ``include`` -- a list of variables to be treated as degree one
1673
+ elements instead of the default degree one elements
1674
+ - ``exclude`` -- a list of variables to be excluded from the
1675
+ default degree one elements
1662
1676
1663
1677
EXAMPLES::
1664
1678
1665
- sage: from sage.data_structures.stream import Stream_function, Stream_plethysm
1679
+ sage: from sage.data_structures.stream import Stream_function, Stream_plethysm
1666
1680
sage: s = SymmetricFunctions(QQ).s()
1667
1681
sage: p = SymmetricFunctions(QQ).p()
1668
1682
sage: f = Stream_function(lambda n: s[n], s, True, 1)
1669
1683
sage: g = Stream_function(lambda n: s[[1]*n], s, True, 1)
1670
- sage: h = Stream_plethysm(f, g, p)
1671
- sage: [s( h[i]) for i in range(5)]
1684
+ sage: h = Stream_plethysm(f, g, p, s )
1685
+ sage: [h[i] for i in range(5)]
1672
1686
[0,
1673
1687
s[1],
1674
1688
s[1, 1] + s[2],
1675
1689
2*s[1, 1, 1] + s[2, 1] + s[3],
1676
1690
3*s[1, 1, 1, 1] + 2*s[2, 1, 1] + s[2, 2] + s[3, 1] + s[4]]
1677
- sage: u = Stream_plethysm(g, f, p)
1678
- sage: [s( u[i]) for i in range(5)]
1691
+ sage: u = Stream_plethysm(g, f, p, s )
1692
+ sage: [u[i] for i in range(5)]
1679
1693
[0,
1680
1694
s[1],
1681
1695
s[1, 1] + s[2],
1682
1696
s[1, 1, 1] + s[2, 1] + 2*s[3],
1683
1697
s[1, 1, 1, 1] + s[2, 1, 1] + 3*s[3, 1] + 2*s[4]]
1698
+
1699
+ TESTS:
1700
+
1701
+ Check corner cases::
1702
+
1703
+ sage: from sage.data_structures.stream import Stream_exact
1704
+ sage: p = SymmetricFunctions(QQ).p()
1705
+ sage: f0 = Stream_exact([p([])], True)
1706
+ sage: f1 = Stream_exact([p[1]], True, order=1)
1707
+ sage: f2 = Stream_exact([p[2]], True, order=2 )
1708
+ sage: f11 = Stream_exact([p[1,1]], True, order=2 )
1709
+ sage: r = Stream_plethysm(f0, f1, p); [r[n] for n in range(3)]
1710
+ [p[], 0, 0]
1711
+ sage: r = Stream_plethysm(f0, f2, p); [r[n] for n in range(3)]
1712
+ [p[], 0, 0]
1713
+ sage: r = Stream_plethysm(f0, f11, p); [r[n] for n in range(3)]
1714
+ [p[], 0, 0]
1715
+
1716
+ Check that degree one elements are treated in the correct way::
1717
+
1718
+ sage: R.<a1,a2,a11,b1,b21,b111> = QQ[]; p = SymmetricFunctions(R).p()
1719
+ sage: f_s = a1*p[1] + a2*p[2] + a11*p[1,1]
1720
+ sage: g_s = b1*p[1] + b21*p[2,1] + b111*p[1,1,1]
1721
+ sage: r_s = f_s(g_s)
1722
+ sage: f = Stream_exact([f_s.restrict_degree(k) for k in range(f_s.degree()+1)], True)
1723
+ sage: g = Stream_exact([g_s.restrict_degree(k) for k in range(g_s.degree()+1)], True)
1724
+ sage: r = Stream_plethysm(f, g, p)
1725
+ sage: r_s == sum(r[n] for n in range(2*(r_s.degree()+1)))
1726
+ True
1727
+
1728
+ sage: r_s - f_s(g_s, include=[])
1729
+ (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2]
1730
+
1731
+ sage: r2 = Stream_plethysm(f, g, p, include=[])
1732
+ sage: r_s - sum(r2[n] for n in range(2*(r_s.degree()+1)))
1733
+ (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2]
1734
+
1684
1735
"""
1685
- def __init__ (self , f , g , p ):
1736
+ def __init__ (self , f , g , p , ring = None , include = None , exclude = None ):
1686
1737
r"""
1687
1738
Initialize ``self``.
1688
1739
@@ -1695,12 +1746,38 @@ def __init__(self, f, g, p):
1695
1746
sage: g = Stream_function(lambda n: s[n-1,1], s, True, 2)
1696
1747
sage: h = Stream_plethysm(f, g, p)
1697
1748
"""
1698
- #assert g._approximate_order > 0
1699
- self ._fv = f ._approximate_order
1700
- self ._gv = g ._approximate_order
1749
+ # handle degree one elements
1750
+ if include is not None and exclude is not None :
1751
+ raise RuntimeError ("include and exclude cannot both be specified" )
1752
+ R = p .base_ring ()
1753
+
1754
+ if include is not None :
1755
+ degree_one = [R (g ) for g in include ]
1756
+ else :
1757
+ try :
1758
+ degree_one = [R (g ) for g in R .variable_names_recursive ()]
1759
+ except AttributeError :
1760
+ try :
1761
+ degree_one = R .gens ()
1762
+ except NotImplementedError :
1763
+ degree_one = []
1764
+ if exclude is not None :
1765
+ degree_one = [g for g in degree_one if g not in exclude ]
1766
+
1767
+ self ._degree_one = [g for g in degree_one if g != R .one ()]
1768
+
1769
+ # assert g._approximate_order > 0
1770
+ val = f ._approximate_order * g ._approximate_order
1771
+ p_f = Stream_map_coefficients (f , lambda x : x , p )
1772
+ p_g = Stream_map_coefficients (g , lambda x : x , p )
1773
+ self ._powers = [p_g ] # a cache for the powers of p_g
1774
+ if ring is None :
1775
+ self ._basis = p
1776
+ else :
1777
+ self ._basis = ring
1701
1778
self ._p = p
1702
- val = self . _fv * self . _gv
1703
- super ().__init__ (f , g , f ._is_sparse , val )
1779
+
1780
+ super ().__init__ (p_f , p_g , f ._is_sparse , val )
1704
1781
1705
1782
def get_coefficient (self , n ):
1706
1783
r"""
@@ -1731,18 +1808,12 @@ def get_coefficient(self, n):
1731
1808
if not n : # special case of 0
1732
1809
return self ._left [0 ]
1733
1810
1734
- # We assume n > 0
1735
- p = self ._p
1736
- ret = p .zero ()
1737
- for k in range (n + 1 ):
1738
- temp = p (self ._left [k ])
1739
- for la , c in temp :
1740
- inner = self ._compute_product (n , la , c )
1741
- if inner is not None :
1742
- ret += inner
1743
- return ret
1811
+ return sum ((c * self ._compute_product (n , la )
1812
+ for k in range (self ._left ._approximate_order , n + 1 )
1813
+ for la , c in self ._left [k ]),
1814
+ self ._basis .zero ())
1744
1815
1745
- def _compute_product (self , n , la , c ):
1816
+ def _compute_product (self , n , la ):
1746
1817
"""
1747
1818
Compute the product ``c * p[la](self._right)`` in degree ``n``.
1748
1819
@@ -1754,25 +1825,53 @@ def _compute_product(self, n, la, c):
1754
1825
sage: f = Stream_function(lambda n: s[n], s, True, 1)
1755
1826
sage: g = Stream_exact([s[2], s[3]], False, 0, 4, 2)
1756
1827
sage: h = Stream_plethysm(f, g, p)
1757
- sage: ret = h._compute_product(7, [2, 1], 1 ); ret
1828
+ sage: ret = h._compute_product(7, Partition( [2, 1]) ); ret
1758
1829
1/12*p[2, 2, 1, 1, 1] + 1/4*p[2, 2, 2, 1] + 1/6*p[3, 2, 2]
1759
1830
+ 1/12*p[4, 1, 1, 1] + 1/4*p[4, 2, 1] + 1/6*p[4, 3]
1760
1831
sage: ret == p[2,1](s[2] + s[3]).homogeneous_component(7)
1761
1832
True
1762
1833
"""
1763
- p = self ._p
1764
- ret = p .zero ()
1765
- for mu in wt_int_vec_iter (n , la ):
1766
- temp = c
1767
- for i , j in zip (la , mu ):
1768
- gs = self ._right [j ]
1769
- if not gs :
1770
- temp = p .zero ()
1771
- break
1772
- temp *= p [i ](gs )
1834
+ ret = self ._basis .zero ()
1835
+ la_exp = la .to_exp ()
1836
+ wgt = [i for i , m in enumerate (la_exp , 1 ) if m ]
1837
+ exp = [m for m in la_exp if m ]
1838
+ for k in wt_int_vec_iter (n , wgt ):
1839
+ # TODO: it may make a big difference here if the
1840
+ # approximate order would be updated
1841
+ if any (d < self ._right ._approximate_order * m
1842
+ for m , d in zip (exp , k )):
1843
+ continue
1844
+ temp = self ._basis .one ()
1845
+ for i , m , d in zip (wgt , exp , k ):
1846
+ temp *= self ._stretched_power_restrict_degree (i , m , d )
1773
1847
ret += temp
1774
1848
return ret
1775
1849
1850
+ def _scale_part (self , mu , i ):
1851
+ return mu .__class__ (mu .parent (), [j * i for j in mu ])
1852
+
1853
+ def _raise_c (self , c , i ):
1854
+ return c .subs (** {str (g ): g ** i
1855
+ for g in self ._degree_one })
1856
+
1857
+ def _stretched_power_restrict_degree (self , i , m , d ):
1858
+ """
1859
+ Return the degree ``d*i`` part of ``p([i]*m)(g)``.
1860
+ """
1861
+ while len (self ._powers ) < m :
1862
+ self ._powers .append (Stream_cauchy_mul (self ._powers [- 1 ], self ._powers [0 ]))
1863
+ power = self ._powers [m - 1 ]
1864
+
1865
+ # we have to check power[d] for zero because it might be an
1866
+ # integer and not a symmetric function
1867
+ if power [d ]:
1868
+ terms = [(self ._scale_part (m , i ), self ._raise_c (c , i )) for m , c in power [d ]]
1869
+ else :
1870
+ terms = []
1871
+
1872
+ return self ._p .sum_of_terms (terms , distinct = True )
1873
+
1874
+
1776
1875
#####################################################################
1777
1876
# Unary operations
1778
1877
@@ -2324,4 +2423,3 @@ def is_nonzero(self):
2324
2423
True
2325
2424
"""
2326
2425
return self ._series .is_nonzero ()
2327
-
0 commit comments