passagemath-combinat 10.6.1__cp312-cp312-musllinux_1_2_aarch64.whl → 10.8.1a1__cp312-cp312-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. passagemath_combinat/__init__.py +3 -0
  2. {passagemath_combinat-10.6.1.dist-info → passagemath_combinat-10.8.1a1.dist-info}/METADATA +17 -20
  3. {passagemath_combinat-10.6.1.dist-info → passagemath_combinat-10.8.1a1.dist-info}/RECORD +220 -218
  4. {passagemath_combinat-10.6.1.dist-info → passagemath_combinat-10.8.1a1.dist-info}/WHEEL +1 -1
  5. passagemath_combinat-10.8.1a1.dist-info/top_level.txt +3 -0
  6. sage/algebras/affine_nil_temperley_lieb.py +3 -3
  7. sage/algebras/all.py +0 -1
  8. sage/algebras/askey_wilson.py +1 -1
  9. sage/algebras/associated_graded.py +2 -2
  10. sage/algebras/cellular_basis.py +3 -6
  11. sage/algebras/cluster_algebra.py +2 -3
  12. sage/algebras/down_up_algebra.py +6 -6
  13. sage/algebras/free_algebra.py +3 -32
  14. sage/algebras/free_algebra_element.py +21 -25
  15. sage/algebras/free_algebra_quotient_element.py +9 -38
  16. sage/algebras/free_zinbiel_algebra.py +4 -3
  17. sage/algebras/hall_algebra.py +2 -2
  18. sage/algebras/hecke_algebras/ariki_koike_algebra.py +8 -8
  19. sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +2 -2
  20. sage/algebras/hecke_algebras/cubic_hecke_algebra.py +11 -14
  21. sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1 -1
  22. sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +5 -5
  23. sage/algebras/iwahori_hecke_algebra.py +59 -57
  24. sage/algebras/jordan_algebra.py +97 -89
  25. sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +6 -6
  26. sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +14 -12
  27. sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +6 -6
  28. sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +4 -4
  29. sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +13 -13
  30. sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +8 -6
  31. sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +7 -5
  32. sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +7 -7
  33. sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +6 -5
  34. sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +12 -11
  35. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +3 -3
  36. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +3 -3
  37. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +11 -11
  38. sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +3 -3
  39. sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +8 -7
  40. sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +9 -8
  41. sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +6 -5
  42. sage/algebras/nil_coxeter_algebra.py +4 -4
  43. sage/algebras/q_commuting_polynomials.py +6 -6
  44. sage/algebras/q_system.py +3 -3
  45. sage/algebras/quantum_clifford.py +8 -8
  46. sage/algebras/quantum_groups/fock_space.py +48 -8
  47. sage/algebras/quantum_groups/quantum_group_gap.py +5 -7
  48. sage/algebras/quantum_matrix_coordinate_algebra.py +7 -7
  49. sage/algebras/quantum_oscillator.py +3 -3
  50. sage/algebras/quaternion_algebra_element.py +5 -3
  51. sage/algebras/schur_algebra.py +3 -3
  52. sage/algebras/shuffle_algebra.py +5 -8
  53. sage/algebras/splitting_algebra.py +129 -85
  54. sage/algebras/tensor_algebra.py +7 -7
  55. sage/algebras/yangian.py +16 -15
  56. sage/algebras/yokonuma_hecke_algebra.py +13 -11
  57. sage/combinat/all.py +9 -0
  58. sage/combinat/all__sagemath_combinat.py +1 -0
  59. sage/combinat/alternating_sign_matrix.py +36 -29
  60. sage/combinat/baxter_permutations.py +32 -12
  61. sage/combinat/bijectionist.py +13 -17
  62. sage/combinat/chas/fsym.py +6 -6
  63. sage/combinat/chas/wqsym.py +23 -29
  64. sage/combinat/colored_permutations.py +9 -11
  65. sage/combinat/colored_permutations_representations.py +13 -12
  66. sage/combinat/composition_tableau.py +2 -2
  67. sage/combinat/constellation.py +57 -30
  68. sage/combinat/crystals/affine_factorization.py +5 -4
  69. sage/combinat/crystals/alcove_path.py +2 -2
  70. sage/combinat/crystals/fully_commutative_stable_grothendieck.py +3 -2
  71. sage/combinat/crystals/infinity_crystals.py +18 -18
  72. sage/combinat/crystals/kac_modules.py +1 -1
  73. sage/combinat/crystals/kirillov_reshetikhin.py +2 -2
  74. sage/combinat/crystals/letters.cpython-312-aarch64-linux-musl.so +0 -0
  75. sage/combinat/crystals/littelmann_path.py +1 -1
  76. sage/combinat/crystals/pbw_datum.cpython-312-aarch64-linux-musl.so +0 -0
  77. sage/combinat/crystals/pbw_datum.pyx +3 -2
  78. sage/combinat/crystals/spins.cpython-312-aarch64-linux-musl.so +0 -0
  79. sage/combinat/crystals/tensor_product.py +7 -5
  80. sage/combinat/crystals/tensor_product_element.cpython-312-aarch64-linux-musl.so +0 -0
  81. sage/combinat/debruijn_sequence.cpython-312-aarch64-linux-musl.so +0 -0
  82. sage/combinat/debruijn_sequence.pyx +1 -2
  83. sage/combinat/degree_sequences.cpython-312-aarch64-linux-musl.so +0 -0
  84. sage/combinat/degree_sequences.pyx +241 -188
  85. sage/combinat/derangements.py +28 -22
  86. sage/combinat/diagram_algebras.py +12 -14
  87. sage/combinat/dyck_word.py +15 -14
  88. sage/combinat/e_one_star.py +1 -1
  89. sage/combinat/expnums.cpython-312-aarch64-linux-musl.so +0 -0
  90. sage/combinat/fast_vector_partitions.cpython-312-aarch64-linux-musl.so +0 -0
  91. sage/combinat/fqsym.py +13 -19
  92. sage/combinat/free_dendriform_algebra.py +2 -2
  93. sage/combinat/free_prelie_algebra.py +2 -2
  94. sage/combinat/fully_commutative_elements.py +8 -8
  95. sage/combinat/fully_packed_loop.py +9 -9
  96. sage/combinat/gelfand_tsetlin_patterns.py +4 -5
  97. sage/combinat/gray_codes.py +3 -4
  98. sage/combinat/grossman_larson_algebras.py +2 -2
  99. sage/combinat/growth.py +13 -13
  100. sage/combinat/hall_polynomial.py +1 -1
  101. sage/combinat/hillman_grassl.py +1 -1
  102. sage/combinat/integer_matrices.py +5 -7
  103. sage/combinat/k_tableau.py +8 -7
  104. sage/combinat/kazhdan_lusztig.py +3 -3
  105. sage/combinat/key_polynomial.py +845 -298
  106. sage/combinat/knutson_tao_puzzles.py +11 -13
  107. sage/combinat/matrices/hadamard_matrix.py +1 -1
  108. sage/combinat/matrices/latin.py +75 -92
  109. sage/combinat/misc.py +3 -3
  110. sage/combinat/multiset_partition_into_sets_ordered.py +27 -10
  111. sage/combinat/ncsf_qsym/generic_basis_code.py +5 -5
  112. sage/combinat/ncsf_qsym/ncsf.py +6 -5
  113. sage/combinat/ncsf_qsym/qsym.py +9 -17
  114. sage/combinat/ncsym/ncsym.py +8 -12
  115. sage/combinat/nu_dyck_word.py +1 -1
  116. sage/combinat/parallelogram_polyomino.py +3 -5
  117. sage/combinat/parking_functions.py +6 -5
  118. sage/combinat/partition_algebra.py +22 -57
  119. sage/combinat/partition_kleshchev.py +4 -4
  120. sage/combinat/partition_tuple.py +12 -10
  121. sage/combinat/plane_partition.py +10 -13
  122. sage/combinat/positive_integer_semigroup_test.py +17 -0
  123. sage/combinat/q_bernoulli.cpython-312-aarch64-linux-musl.so +0 -0
  124. sage/combinat/quickref.py +2 -2
  125. sage/combinat/recognizable_series.py +2 -2
  126. sage/combinat/regular_sequence.py +7 -7
  127. sage/combinat/regular_sequence_bounded.py +15 -21
  128. sage/combinat/restricted_growth.py +3 -3
  129. sage/combinat/ribbon.py +3 -3
  130. sage/combinat/rigged_configurations/bijection.py +3 -3
  131. sage/combinat/rigged_configurations/rigged_partition.cpython-312-aarch64-linux-musl.so +0 -0
  132. sage/combinat/rsk.py +2 -0
  133. sage/combinat/schubert_polynomial.py +11 -2
  134. sage/combinat/set_partition.py +3 -7
  135. sage/combinat/set_partition_iterator.cpython-312-aarch64-linux-musl.so +0 -0
  136. sage/combinat/set_partition_iterator.pyx +0 -1
  137. sage/combinat/set_partition_ordered.py +2 -2
  138. sage/combinat/sf/classical.py +1 -1
  139. sage/combinat/sf/dual.py +4 -8
  140. sage/combinat/sf/elementary.py +13 -7
  141. sage/combinat/sf/hall_littlewood.py +10 -8
  142. sage/combinat/sf/homogeneous.py +6 -3
  143. sage/combinat/sf/jack.py +11 -9
  144. sage/combinat/sf/llt.py +4 -5
  145. sage/combinat/sf/macdonald.py +10 -11
  146. sage/combinat/sf/monomial.py +6 -0
  147. sage/combinat/sf/ns_macdonald.py +92 -51
  148. sage/combinat/sf/powersum.py +9 -14
  149. sage/combinat/sf/schur.py +6 -0
  150. sage/combinat/sf/sf.py +21 -19
  151. sage/combinat/sf/sfa.py +13 -64
  152. sage/combinat/shifted_primed_tableau.py +5 -7
  153. sage/combinat/shuffle.py +1 -1
  154. sage/combinat/sine_gordon.py +18 -38
  155. sage/combinat/skew_partition.py +9 -12
  156. sage/combinat/skew_tableau.py +2 -7
  157. sage/combinat/sloane_functions.py +1 -1
  158. sage/combinat/species/all.py +67 -2
  159. sage/combinat/species/characteristic_species.py +3 -0
  160. sage/combinat/species/composition_species.py +3 -0
  161. sage/combinat/species/cycle_species.py +4 -0
  162. sage/combinat/species/empty_species.py +3 -0
  163. sage/combinat/species/functorial_composition_species.py +3 -0
  164. sage/combinat/species/generating_series.py +3 -0
  165. sage/combinat/species/library.py +3 -0
  166. sage/combinat/species/linear_order_species.py +3 -0
  167. sage/combinat/species/partition_species.py +3 -0
  168. sage/combinat/species/permutation_species.py +4 -0
  169. sage/combinat/species/product_species.py +3 -0
  170. sage/combinat/species/recursive_species.py +3 -0
  171. sage/combinat/species/set_species.py +3 -0
  172. sage/combinat/species/species.py +13 -7
  173. sage/combinat/species/structure.py +8 -9
  174. sage/combinat/species/subset_species.py +3 -0
  175. sage/combinat/species/sum_species.py +3 -0
  176. sage/combinat/subword.py +4 -1
  177. sage/combinat/subword_complex.py +7 -7
  178. sage/combinat/subword_complex_c.cpython-312-aarch64-linux-musl.so +0 -0
  179. sage/combinat/superpartition.py +1 -1
  180. sage/combinat/symmetric_group_algebra.py +9 -9
  181. sage/combinat/symmetric_group_representations.py +5 -5
  182. sage/combinat/t_sequences.py +4 -4
  183. sage/combinat/tableau.py +3 -4
  184. sage/combinat/tableau_tuple.py +2 -2
  185. sage/combinat/tiling.py +39 -42
  186. sage/combinat/triangles_FHM.py +38 -15
  187. sage/combinat/tutorial.py +2 -2
  188. sage/combinat/vector_partition.py +43 -31
  189. sage/combinat/words/abstract_word.py +4 -4
  190. sage/combinat/words/alphabet.py +12 -12
  191. sage/combinat/words/finite_word.py +25 -229
  192. sage/combinat/words/infinite_word.py +1 -1
  193. sage/combinat/words/morphic.py +13 -13
  194. sage/combinat/words/morphism.py +3 -12
  195. sage/combinat/words/paths.py +16 -17
  196. sage/combinat/words/word.py +60 -35
  197. sage/combinat/words/word_char.cpython-312-aarch64-linux-musl.so +0 -0
  198. sage/combinat/words/word_char.pyx +46 -7
  199. sage/combinat/words/word_datatypes.cpython-312-aarch64-linux-musl.so +0 -0
  200. sage/combinat/words/word_generators.py +39 -38
  201. sage/databases/findstat.py +72 -31
  202. sage/databases/oeis.py +125 -25
  203. sage/databases/sloane.py +14 -8
  204. sage/games/sudoku_backtrack.cpython-312-aarch64-linux-musl.so +0 -0
  205. sage/groups/indexed_free_group.py +3 -4
  206. sage/libs/symmetrica/symmetrica.cpython-312-aarch64-linux-musl.so +0 -0
  207. sage/libs/symmetrica/symmetrica.pxi +1 -0
  208. sage/monoids/automatic_semigroup.py +1 -3
  209. sage/monoids/free_abelian_monoid.py +7 -33
  210. sage/monoids/free_abelian_monoid_element.cpython-312-aarch64-linux-musl.so +0 -0
  211. sage/monoids/free_monoid.py +8 -40
  212. sage/monoids/free_monoid_element.py +1 -9
  213. sage/monoids/string_monoid.py +5 -2
  214. sage/monoids/string_monoid_element.py +12 -66
  215. sage/rings/all__sagemath_combinat.py +7 -0
  216. sage/sat/solvers/__init__.py +3 -4
  217. sage/sat/solvers/cryptominisat.py +2 -3
  218. sage/sat/solvers/picosat.py +2 -3
  219. sage/sat/solvers/sat_lp.py +2 -2
  220. sage/sat/solvers/satsolver.cpython-312-aarch64-linux-musl.so +0 -0
  221. passagemath_combinat-10.6.1.dist-info/top_level.txt +0 -2
@@ -1,10 +1,10 @@
1
1
  # sage_setup: distribution = sagemath-combinat
2
2
  # sage.doctest: needs sage.combinat sage.modules
3
3
  r"""
4
- Key polynomials
4
+ Key and Atom Polynomials
5
5
 
6
6
  Key polynomials (also known as type A Demazure characters) are defined by
7
- applying the divided difference operator `\pi_\sigma`, where `\sigma` is
7
+ applying the divided difference operator `\pi_{\sigma}`, where `\sigma` is
8
8
  a permutation, to a monomial corresponding to an integer partition
9
9
  `\mu \vdash n`.
10
10
 
@@ -15,13 +15,18 @@ a permutation, to a monomial corresponding to an integer partition
15
15
  - :meth:`sage.combinat.root_system.weyl_characters.WeylCharacterRing.demazure_character`
16
16
  - :meth:`sage.categories.classical_crystals.ClassicalCrystals.ParentMethods.demazure_character`
17
17
 
18
+ Atom polynomials (also known as Demazure atoms) are defined analogously by
19
+ using a deformed divided difference operator `\bar{\pi}_i = \pi_i - 1`.
20
+
18
21
  AUTHORS:
19
22
 
20
23
  - Trevor K. Karn (2022-08-17): initial version
24
+ - Travis Scrimshaw (2025-10-10): expanded to atoms
21
25
  """
22
26
 
23
27
  # ****************************************************************************
24
28
  # Copyright (C) 2022 Trevor K. Karn <karnx018 (at) umn.edu>
29
+ # 2025 Travis Scrimshaw <tcscrims at gmail.com>
25
30
  #
26
31
  # This program is free software: you can redistribute it and/or modify
27
32
  # it under the terms of the GNU General Public License as published by
@@ -44,24 +49,215 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
44
49
  from sage.structure.element import parent
45
50
 
46
51
 
47
- class KeyPolynomial(CombinatorialFreeModule.Element):
52
+ #####################################################################
53
+ # Helper functions
54
+
55
+
56
+ def sorting_word(alpha):
48
57
  r"""
49
- A key polynomial.
58
+ Get a reduced word for the permutation which sorts ``alpha``
59
+ into a partition.
50
60
 
51
- Key polynomials are polynomials that form a basis for a polynomial ring
52
- and are indexed by weak compositions.
61
+ The result is a list ``l = [i0, i1, i2, ...]`` where each ``ij``
62
+ is a positive integer such that it applies the simple
63
+ transposition `(i_j, i_j+1)`. The transpositions are applied
64
+ starting with ``i0``, then ``i1`` is applied, followed by ``i2``,
65
+ and so on. See :meth:`sage.combinat.permutation.Permutation.reduced_words`
66
+ for the convention used.
53
67
 
54
- Elements should be created by first creating the basis
55
- :class:`KeyPolynomialBasis` and passing a list representing the indexing
56
- composition.
68
+ EXAMPLES::
69
+
70
+ sage: IV = IntegerVectors()
71
+ sage: from sage.combinat.key_polynomial import sorting_word
72
+ sage: list(sorting_word(IV([2,3,2]))[0])
73
+ [1]
74
+ sage: sorting_word(IV([2,3,2]))[1]
75
+ [3, 2, 2]
76
+ sage: list(sorting_word(IV([5,6,7]))[0])
77
+ [1, 2, 1]
78
+ sage: list(sorting_word(IV([0,3,2]))[0])
79
+ [2, 1]
80
+ sage: list(sorting_word(IV([0,3,0,2]))[0])
81
+ [2, 3, 1]
82
+ sage: list(sorting_word(IV([3,2,1]))[0])
83
+ []
84
+ sage: list(sorting_word(IV([2,3,3]))[0])
85
+ [2, 1]
86
+ """
87
+ w = []
88
+ L = list(alpha)
89
+ n = len(L)
90
+
91
+ # bubble sort to get the shortest sorting word
92
+ for i in range(n-1):
93
+ for j in range(n-i-1):
94
+ if L[j] < L[j + 1]:
95
+ w.append(j+1)
96
+ L[j], L[j + 1] = L[j + 1], L[j]
97
+ return reversed(w), L
98
+
99
+
100
+ #####################################################################
101
+ # Divided difference operators
102
+
103
+
104
+ def divided_difference(f, i):
105
+ r"""
106
+ Apply the ``i``-th divided difference operator to the polynomial ``f``.
57
107
 
58
108
  EXAMPLES::
59
109
 
110
+ sage: from sage.combinat.key_polynomial import divided_difference
60
111
  sage: k = KeyPolynomials(QQ)
61
- sage: f = k([4,3,2,1]) + k([1,2,3,4]); f
62
- k[1, 2, 3, 4] + k[4, 3, 2, 1]
63
- sage: f in k
64
- True
112
+ sage: z = k.poly_gens()
113
+ sage: f = z[1]*z[2]^3 + z[1]*z[2]*z[3]
114
+ sage: divided_difference(f, 3)
115
+ z_3^2*z_1 + z_3*z_2*z_1 + z_2^2*z_1
116
+
117
+ sage: k = KeyPolynomials(QQ, 4)
118
+ sage: z = k.poly_gens()
119
+ sage: f = z[1]*z[2]^3 + z[1]*z[2]*z[3]
120
+ sage: divided_difference(f, 3)
121
+ z_1*z_2^2 + z_1*z_2*z_3 + z_1*z_3^2
122
+
123
+ sage: k = KeyPolynomials(QQ)
124
+ sage: R = k.polynomial_ring(); R
125
+ Infinite polynomial ring in z over Rational Field
126
+ sage: z = R.gen()
127
+ sage: divided_difference(z[1]*z[2]^3, 2)
128
+ -z_2^2*z_1 - z_2*z_1^2
129
+ sage: divided_difference(z[1]*z[2]*z[3], 3)
130
+ 0
131
+ sage: divided_difference(z[1]*z[2]*z[3], 4)
132
+ z_2*z_1
133
+ sage: divided_difference(z[1]*z[2]*z[4], 4)
134
+ -z_2*z_1
135
+
136
+ sage: k = KeyPolynomials(QQ, 5)
137
+ sage: z = k.polynomial_ring().gens()
138
+ sage: divided_difference(z[1]*z[2]^3, 2)
139
+ -z_1^2*z_2 - z_1*z_2^2
140
+ sage: divided_difference(z[1]*z[2]*z[3], 3)
141
+ 0
142
+ sage: divided_difference(z[1]*z[2]*z[3], 4)
143
+ z_1*z_2
144
+ sage: divided_difference(z[1]*z[2]*z[4], 4)
145
+ -z_1*z_2
146
+ """
147
+ P = parent(f)
148
+ if isinstance(P, InfinitePolynomialRing_sparse):
149
+ z = P.gen()
150
+ else:
151
+ z = P.gens()
152
+
153
+ si_f = f.subs({z[i]: z[i-1], z[i-1]: z[i]})
154
+ return (si_f - f) // (z[i] - z[i-1])
155
+
156
+
157
+ def isobaric_divided_difference(f, w):
158
+ r"""
159
+ Apply the isobaric divided difference operator `\pi_w` to the
160
+ polynomial `f`.
161
+
162
+ ``w`` may be either a single index or a list of
163
+ indices of simple transpositions.
164
+
165
+ .. WARNING::
166
+
167
+ The simple transpositions should be applied from left to right.
168
+
169
+ EXAMPLES::
170
+
171
+ sage: from sage.combinat.key_polynomial import isobaric_divided_difference as idd
172
+ sage: R.<z> = InfinitePolynomialRing(GF(3))
173
+ sage: idd(z[1]^4*z[2]^2*z[4], 4)
174
+ 0
175
+
176
+ sage: idd(z[1]^4*z[2]^2*z[3]*z[4], 3)
177
+ z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4
178
+
179
+ sage: idd(z[1]^4*z[2]^2*z[3]*z[4], [3, 4])
180
+ z_4^2*z_3*z_2*z_1^4 + z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4
181
+
182
+ sage: idd(z[1]^4*z[2]^2*z[3]*z[4], [4, 3])
183
+ z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4
184
+
185
+ sage: idd(z[1]^2*z[2], [3, 2])
186
+ z_3*z_2^2 + z_3*z_2*z_1 + z_3*z_1^2 + z_2^2*z_1 + z_2*z_1^2
187
+ """
188
+ P = parent(f)
189
+ if isinstance(P, InfinitePolynomialRing_sparse):
190
+ z = P.gen()
191
+ else:
192
+ z = P.gens()
193
+
194
+ if not hasattr(w, "__iter__"): # this allows us to pass i instead of a word
195
+ w = [w]
196
+ for i in w:
197
+ fp = z[i-1] * f
198
+ si_fp = fp.subs({z[i]: z[i-1], z[i-1]: z[i]})
199
+ f = (si_fp - fp) // (z[i] - z[i-1])
200
+ return f
201
+
202
+
203
+ def isobaric_divided_difference_bar(f, w):
204
+ r"""
205
+ Apply the isobaric divided difference operator `\bar{\pi}_w` to the
206
+ polynomial `f`.
207
+
208
+ This map is defined by `\bar{\pi}_i := \pi_i - 1`.
209
+
210
+ ``w`` may be either a single index or a list of
211
+ indices of simple transpositions.
212
+
213
+ .. WARNING::
214
+
215
+ The simple transpositions should be applied from left to right.
216
+
217
+ EXAMPLES::
218
+
219
+ sage: from sage.combinat.key_polynomial import isobaric_divided_difference_bar as iddb
220
+ sage: R.<z> = InfinitePolynomialRing(GF(3))
221
+ sage: iddb(z[1]^4*z[2]^2*z[4], 4)
222
+ -z_4*z_2^2*z_1^4
223
+
224
+ sage: iddb(z[1]^4*z[2]^2*z[3]*z[4], 3)
225
+ z_4*z_3^2*z_2*z_1^4
226
+
227
+ sage: iddb(z[1]^4*z[2]^2*z[3]*z[4], [3, 4])
228
+ z_4^2*z_3*z_2*z_1^4
229
+
230
+ sage: iddb(z[1]^4*z[2]^2*z[3]*z[4], [4, 3])
231
+ 0
232
+
233
+ sage: iddb(z[1]^2*z[2], [3, 2])
234
+ z_3*z_2^2 + z_3*z_2*z_1
235
+ """
236
+ P = parent(f)
237
+ if isinstance(P, InfinitePolynomialRing_sparse):
238
+ z = P.gen()
239
+ else:
240
+ z = P.gens()
241
+
242
+ if not hasattr(w, "__iter__"): # this allows us to pass i instead of a word
243
+ w = [w]
244
+ for i in w:
245
+ sif = f.subs({z[i]: z[i-1], z[i-1]: z[i]})
246
+ f = z[i] * (f - sif) // (z[i-1] - z[i])
247
+ return f
248
+
249
+
250
+ #####################################################################
251
+ # Main classes
252
+
253
+
254
+ class OperatorPolynomial(CombinatorialFreeModule.Element):
255
+ r"""
256
+ Abstract base class for a polynomial defined recursively by
257
+ divided difference type operators.
258
+
259
+ Parents should implement the divided difference operator as
260
+ a ``staticmethod`` ``_operator``.
65
261
  """
66
262
  def _mul_(self, other):
67
263
  r"""
@@ -109,12 +305,86 @@ class KeyPolynomial(CombinatorialFreeModule.Element):
109
305
  # create the monomial to apply
110
306
  monom = R.prod(z[i] ** mi for i, mi in enumerate(mu) if mi)
111
307
 
112
- out += c * isobaric_divided_difference(monom, w)
308
+ out += c * P._operator(monom, w)
113
309
 
114
310
  return out
115
311
 
116
312
  to_polynomial = expand
117
313
 
314
+ def divided_difference(self, w):
315
+ r"""
316
+ Apply the divided difference operator `\partial_w` to ``self``.
317
+
318
+ The convention is to apply from left to right so if
319
+ ``w = [w1, w2, ..., wm]`` then we apply
320
+ `\partial_{w_2 \cdots w_m} \circ \partial_{w_1}`
321
+
322
+ EXAMPLES::
323
+
324
+ sage: k = KeyPolynomials(QQ)
325
+ sage: k([3,2,1]).divided_difference(2)
326
+ k[3, 1, 1]
327
+ sage: k([3,2,1]).divided_difference([2,3])
328
+ k[3, 1]
329
+
330
+ sage: k = KeyPolynomials(QQ, 4)
331
+ sage: k([3,2,1,0]).divided_difference(2)
332
+ k[3, 1, 1, 0]
333
+ """
334
+ if not isinstance(w, Collection):
335
+ w = [w]
336
+ f = self.expand()
337
+ for wi in w:
338
+ f = divided_difference(f, wi)
339
+ return self.parent().from_polynomial(f)
340
+
341
+ def isobaric_divided_difference(self, w):
342
+ r"""
343
+ Apply the isobaric divided difference operator `\pi_w` to ``self``.
344
+
345
+ The convention is to apply from left to right so if
346
+ ``w = [w1, w2, ..., wm]`` then we apply
347
+ `\pi_{w_2 \cdots w_m} \circ \pi_{w_1}`
348
+
349
+ EXAMPLES::
350
+
351
+ sage: a = AtomPolynomials(QQ)
352
+ sage: a([3,2,1]).isobaric_divided_difference(2)
353
+ a[3, 1, 2] + a[3, 2, 1]
354
+ sage: a([3,2,1]).isobaric_divided_difference([2, 3])
355
+ a[3, 1, 0, 2] + a[3, 1, 2] + a[3, 2, 0, 1] + a[3, 2, 1]
356
+
357
+ sage: a = AtomPolynomials(QQ, 4)
358
+ sage: a([3,2,1,0]).divided_difference(2)
359
+ a[3, 1, 1, 0]
360
+ """
361
+ if not isinstance(w, Collection):
362
+ w = [w]
363
+ f = self.expand()
364
+ for wi in w:
365
+ f = divided_difference(f, wi)
366
+ return self.parent().from_polynomial(f)
367
+
368
+
369
+ class KeyPolynomial(OperatorPolynomial):
370
+ r"""
371
+ A key polynomial.
372
+
373
+ Key polynomials are polynomials that form a basis for a polynomial ring
374
+ and are indexed by weak compositions.
375
+
376
+ Elements should be created by first creating the basis
377
+ :class:`KeyPolynomialBasis` and passing a list representing the indexing
378
+ composition.
379
+
380
+ EXAMPLES::
381
+
382
+ sage: k = KeyPolynomials(QQ)
383
+ sage: f = k([4,3,2,1]) + k([1,2,3,4]); f
384
+ k[1, 2, 3, 4] + k[4, 3, 2, 1]
385
+ sage: f in k
386
+ True
387
+ """
118
388
  def pi(self, w):
119
389
  r"""
120
390
  Apply the operator `\pi_w` to ``self``.
@@ -210,173 +480,236 @@ class KeyPolynomial(CombinatorialFreeModule.Element):
210
480
  else:
211
481
  ret._monomial_coefficients[m] = c
212
482
  if not ret._monomial_coefficients[m]:
213
- del ret._monomial_coefficients
483
+ del ret._monomial_coefficients[m]
214
484
  return ret
215
485
 
216
486
  isobaric_divided_difference = pi
217
487
 
218
- def divided_difference(self, w):
488
+ def pibar(self, w):
219
489
  r"""
220
- Apply the divided difference operator `\partial_w` to ``self``.
221
-
222
- The convention is to apply from left to right so if
223
- ``w = [w1, w2, ..., wm]`` then we apply
224
- `\partial_{w_2 \cdots w_m} \circ \partial_{w_1}`
490
+ Apply the operator `\bar{\pi}_w` to ``self``.
225
491
 
226
492
  EXAMPLES::
227
493
 
228
494
  sage: k = KeyPolynomials(QQ)
229
- sage: k([3,2,1]).divided_difference(2)
230
- k[3, 1, 1]
231
- sage: k([3,2,1]).divided_difference([2,3])
232
- k[3, 1]
495
+ sage: k[2,3,1].pibar(2)
496
+ k[2, 1, 3] - k[2, 3, 1]
497
+ sage: k[2,3,1].pibar([2,3])
498
+ k[2, 1, 0, 3] - k[2, 1, 3] - k[2, 3, 0, 1] + k[2, 3, 1]
499
+ sage: k[2,3,1].pibar([2,2]) == -k[2,3,1].pibar(2)
500
+ True
501
+
502
+ TESTS:
503
+
504
+ We check that this is consistent with the definition via the
505
+ isobaric divided difference operators::
233
506
 
507
+ sage: from sage.combinat.key_polynomial import isobaric_divided_difference_bar as iddb
234
508
  sage: k = KeyPolynomials(QQ, 4)
235
- sage: k([3,2,1,0]).divided_difference(2)
236
- k[3, 1, 1, 0]
509
+ sage: S4 = Permutations(4)
510
+ sage: f = k[4,2,2,0]
511
+ sage: all(iddb(f.expand(), w.reduced_word()) == f.pibar(w).expand() for w in S4)
512
+ True
513
+
514
+ sage: f = k([4,2,0,1]) - 3 * k([2,0,1,2])
515
+ sage: all(iddb(f.expand(), w.reduced_word()) == f.pibar(w).expand() for w in S4)
516
+ True
237
517
  """
518
+ if isinstance(w, Permutation):
519
+ w = w.reduced_word()
238
520
  if not isinstance(w, Collection):
239
521
  w = [w]
240
- f = self.expand()
241
- for wi in w:
242
- f = divided_difference(f, wi)
243
- return self.parent().from_polynomial(f)
244
522
 
523
+ if not w or not self:
524
+ return self
245
525
 
246
- class KeyPolynomialBasis(CombinatorialFreeModule):
247
- r"""
248
- The key polynomial basis for a polynomial ring.
526
+ ret = self
527
+ for i in w:
528
+ ret = ret.pi([i]) - ret
529
+ if not ret:
530
+ return ret
531
+ return ret
249
532
 
250
- For a full definition, see
251
- `SymmetricFunctions.com <https://www.symmetricfunctions.com/key.htm>`_.
252
- Key polynomials are indexed by weak compositions with no trailing zeros,
253
- and `\sigma` is the permutation of shortest length which sorts the
254
- indexing composition into a partition.
255
533
 
256
- EXAMPLES:
534
+ class AtomPolynomial(OperatorPolynomial):
535
+ r"""
536
+ An atom polynomial.
257
537
 
258
- Key polynomials are a basis, indexed by (weak) compositions,
259
- for polynomial rings::
538
+ Atom polynomials are polynomials that form a basis for a polynomial
539
+ ring and are indexed by weak compositions.
260
540
 
261
- sage: k = KeyPolynomials(QQ)
262
- sage: k([3,0,1,2])
263
- k[3, 0, 1, 2]
264
- sage: k([3,0,1,2])/2
265
- 1/2*k[3, 0, 1, 2]
266
- sage: R = k.polynomial_ring(); R
267
- Infinite polynomial ring in z over Rational Field
541
+ Elements should be created by first creating the basis
542
+ :class:`AtomPolynomialBasis` and passing a list representing
543
+ the indexing composition.
268
544
 
269
- sage: K = KeyPolynomials(GF(5)); K
270
- Key polynomial basis over Finite Field of size 5
271
- sage: 2*K([3,0,1,2])
272
- 2*k[3, 0, 1, 2]
273
- sage: 5*(K([3,0,1,2]) + K([3,1,1]))
274
- 0
545
+ EXAMPLES::
275
546
 
276
- We can expand them in the standard monomial basis::
547
+ sage: a = AtomPolynomials(QQ)
548
+ sage: f = a([4,3,2,1]) + a([1,2,3,4]); f
549
+ a[1, 2, 3, 4] + a[4, 3, 2, 1]
550
+ sage: f in a
551
+ True
552
+ """
553
+ def pibar(self, w):
554
+ r"""
555
+ Apply the operator `\bar{\pi}_w` to ``self``.
277
556
 
278
- sage: k([3,0,1,2]).expand()
279
- z_3^2*z_2*z_0^3 + z_3^2*z_1*z_0^3 + z_3*z_2^2*z_0^3
280
- + 2*z_3*z_2*z_1*z_0^3 + z_3*z_1^2*z_0^3 + z_2^2*z_1*z_0^3
281
- + z_2*z_1^2*z_0^3
557
+ ``w`` may be either a ``Permutation`` or a list of indices of simple
558
+ transpositions (1-based).
282
559
 
283
- sage: k([0,0,2]).expand()
284
- z_2^2 + z_2*z_1 + z_2*z_0 + z_1^2 + z_1*z_0 + z_0^2
560
+ The convention is to apply from left to right so if
561
+ ``w = [w1, w2, ..., wm]`` then we apply
562
+ `\bar{\pi}_{w_2 \cdots w_m} \circ \bar{\pi}_{w_1}`.
285
563
 
286
- If we have a polynomial, we can express it in the key basis::
564
+ EXAMPLES::
287
565
 
288
- sage: z = R.gen()
289
- sage: k.from_polynomial(z[2]^2*z[1]*z[0])
290
- k[1, 1, 2] - k[1, 2, 1]
566
+ sage: a = AtomPolynomials(QQ)
567
+ sage: a([3,2,1]).pibar(2)
568
+ a[3, 1, 2]
569
+ sage: a([3,2,1]).pibar([2,1])
570
+ a[1, 3, 2]
571
+ sage: a([3,2,1]).pibar(Permutation([3,2,1]))
572
+ a[1, 2, 3]
573
+ sage: f = a([3,2,1]) + a([3,2,1,1])
574
+ sage: f.pibar(2)
575
+ a[3, 1, 2] + a[3, 1, 2, 1]
576
+ sage: a.one().pibar(1)
577
+ 0
578
+
579
+ sage: a([3,2,1,0]).pibar(2).pibar(2)
580
+ -a[3, 1, 2]
581
+ sage: (-a([3,2,1,0]) + 4*a([3,1,2,0])).pibar(2)
582
+ -5*a[3, 1, 2]
583
+
584
+ sage: a = AtomPolynomials(QQ, 4)
585
+ sage: a([3,2,1,0]).pibar(2)
586
+ a[3, 1, 2, 0]
587
+ sage: a([3,2,1,0]).pibar([2,1])
588
+ a[1, 3, 2, 0]
589
+ sage: a([3,2,1,0]).pibar(Permutation([3,2,1,4]))
590
+ a[1, 2, 3, 0]
591
+ sage: f = a([3,2,1,0]) + a([3,2,1,1])
592
+ sage: f.pibar(2)
593
+ a[3, 1, 2, 0] + a[3, 1, 2, 1]
594
+ sage: a.one().pibar(1)
595
+ 0
291
596
 
292
- sage: f = z[3]^2*z[2]*z[0]^3 + z[3]^2*z[1]*z[0]^3 + z[3]*z[2]^2*z[0]^3 + \
293
- ....: 2*z[3]*z[2]*z[1]*z[0]^3 + z[3]*z[1]^2*z[0]^3 + z[2]^2*z[1]*z[0]^3 + \
294
- ....: z[2]*z[1]^2*z[0]^3
295
- sage: k.from_polynomial(f)
296
- k[3, 0, 1, 2]
597
+ TESTS:
297
598
 
298
- Since the ring of key polynomials may be regarded as a different choice of
299
- basis for a polynomial ring, it forms an algebra, so we have
300
- multiplication::
599
+ We check that this is consistent with the definition via the
600
+ defining isobaric divided difference operators::
301
601
 
302
- sage: k([10,5,2])*k([1,1,1])
303
- k[11, 6, 3]
602
+ sage: from sage.combinat.key_polynomial import isobaric_divided_difference_bar as iddb
603
+ sage: a = AtomPolynomials(QQ, 4)
604
+ sage: S4 = Permutations(4)
605
+ sage: f = a([4,2,2,0])
606
+ sage: all(iddb(f.expand(), w.reduced_word()) == f.pibar(w).expand() for w in S4)
607
+ True
304
608
 
305
- We can also multiply by polynomials in the monomial basis::
609
+ sage: f = a([4,2,0,1]) - 3 * a([2,0,1,2])
610
+ sage: all(iddb(f.expand(), w.reduced_word()) == f.pibar(w).expand() for w in S4)
611
+ True
612
+ """
613
+ P = self.parent()
614
+ if isinstance(w, Permutation):
615
+ w = w.reduced_word()
616
+ if not isinstance(w, Collection):
617
+ w = [w]
306
618
 
307
- sage: k([10,9,1])*z[0]
308
- k[11, 9, 1]
309
- sage: z[0] * k([10,9,1])
310
- k[11, 9, 1]
311
- sage: k([10,9,1])*(z[0] + z[3])
312
- k[10, 9, 1, 1] + k[11, 9, 1]
619
+ if not w or not self:
620
+ return self
313
621
 
314
- When the sorting permutation is the longest element, the key polynomial
315
- agrees with the Schur polynomial::
622
+ N = max(w) + 1
316
623
 
317
- sage: s = SymmetricFunctions(QQ).schur()
318
- sage: k([1,2,3]).expand()
319
- z_2^3*z_1^2*z_0 + z_2^3*z_1*z_0^2 + z_2^2*z_1^3*z_0
320
- + 2*z_2^2*z_1^2*z_0^2 + z_2^2*z_1*z_0^3 + z_2*z_1^3*z_0^2
321
- + z_2*z_1^2*z_0^3
322
- sage: s[3,2,1].expand(3)
323
- x0^3*x1^2*x2 + x0^2*x1^3*x2 + x0^3*x1*x2^2 + 2*x0^2*x1^2*x2^2
324
- + x0*x1^3*x2^2 + x0^2*x1*x2^3 + x0*x1^2*x2^3
624
+ if P._k is not None and N > P._k:
625
+ raise ValueError(f"pi_{N-1} does not exist for this polynomial ring")
325
626
 
326
- The polynomial expansions can be computed using crystals and expressed in
327
- terms of the key basis::
627
+ ret = P.element_class(P, {})
628
+ for m, c in self._monomial_coefficients.items():
629
+ m = list(m)
630
+ n = len(m)
631
+ sign = 1
632
+ for i in w:
633
+ if i == n:
634
+ m += [0]
635
+ n += 1
636
+ if i > n or m[i-1] == m[i]:
637
+ m = None
638
+ break
639
+ if m[i-1] < m[i]:
640
+ sign = -sign
641
+ continue
642
+ m[i-1], m[i] = m[i], m[i-1]
643
+ if m is None:
644
+ continue
645
+ m = P._indices(m)
646
+ if P._k is None:
647
+ m = m.trim()
648
+ if m in ret._monomial_coefficients:
649
+ ret._monomial_coefficients[m] += c * sign
650
+ else:
651
+ ret._monomial_coefficients[m] = c * sign
652
+ if not ret._monomial_coefficients[m]:
653
+ del ret._monomial_coefficients[m]
654
+ return ret
328
655
 
329
- sage: T = crystals.Tableaux(['A',3],shape=[2,1])
330
- sage: f = T.demazure_character([3,2,1]) # needs sage.symbolic
331
- sage: k.from_polynomial(f) # needs sage.symbolic
332
- k[1, 0, 0, 2]
656
+ def pi(self, w):
657
+ r"""
658
+ Return the action of the isobaric divided difference operator `\pi_w`
659
+ on ``self``.
333
660
 
334
- The default behavior is to work in a polynomial ring with infinitely many
335
- variables. One can work in a specicfied number of variables::
661
+ EXAMPLES::
336
662
 
337
- sage: k = KeyPolynomials(QQ, 4)
338
- sage: k([3,0,1,2]).expand()
339
- z_0^3*z_1^2*z_2 + z_0^3*z_1*z_2^2 + z_0^3*z_1^2*z_3
340
- + 2*z_0^3*z_1*z_2*z_3 + z_0^3*z_2^2*z_3 + z_0^3*z_1*z_3^2 + z_0^3*z_2*z_3^2
663
+ sage: a = AtomPolynomials(QQ)
664
+ sage: a[2,3,1].pi(2)
665
+ a[2, 1, 3] + a[2, 3, 1]
666
+ sage: a[2,3,1].pi([2,3])
667
+ a[2, 1, 0, 3] + a[2, 1, 3] + a[2, 3, 0, 1] + a[2, 3, 1]
668
+ sage: a[2,3,1].pi([2,2]) == a[2,3,1].pi(2)
669
+ True
341
670
 
342
- sage: k([0,0,2,0]).expand()
343
- z_0^2 + z_0*z_1 + z_1^2 + z_0*z_2 + z_1*z_2 + z_2^2
671
+ TESTS:
344
672
 
345
- sage: k([0,0,2,0]).expand().parent()
346
- Multivariate Polynomial Ring in z_0, z_1, z_2, z_3 over Rational Field
673
+ We check that this is consistent with the definition via the
674
+ isobaric divided difference operators::
347
675
 
348
- If working in a specified number of variables, the length of the indexing
349
- composition must be the same as the number of variables::
676
+ sage: from sage.combinat.key_polynomial import isobaric_divided_difference as idd
677
+ sage: a = AtomPolynomials(QQ, 4)
678
+ sage: S4 = Permutations(4)
679
+ sage: f = a[4,2,2,0]
680
+ sage: all(idd(f.expand(), w.reduced_word()) == f.pi(w).expand() for w in S4)
681
+ True
350
682
 
351
- sage: k([0,0,2])
352
- Traceback (most recent call last):
353
- ...
354
- TypeError: do not know how to make x (= [0, 0, 2]) an element of self
355
- (=Key polynomial basis over Rational Field)
683
+ sage: f = a([4,2,0,1]) - 3 * a([2,0,1,2])
684
+ sage: all(idd(f.expand(), w.reduced_word()) == f.pi(w).expand() for w in S4)
685
+ True
686
+ """
687
+ if isinstance(w, Permutation):
688
+ w = w.reduced_word()
689
+ if not isinstance(w, Collection):
690
+ w = [w]
356
691
 
357
- One can also work in a specified polynomial ring::
692
+ if not w or not self:
693
+ return self
358
694
 
359
- sage: k = KeyPolynomials(QQ['x0', 'x1', 'x2', 'x3'])
360
- sage: k([0,2,0,0])
361
- k[0, 2, 0, 0]
362
- sage: k([4,0,0,0]).expand()
363
- x0^4
695
+ ret = self
696
+ for i in w:
697
+ ret = ret.pibar([i]) + ret
698
+ if not ret:
699
+ return ret
700
+ return ret
364
701
 
365
- If one wishes to use a polynomial ring as coefficients for the key
366
- polynomials, pass the keyword argument ``poly_coeffs=True``::
702
+ isobaric_divided_difference = pi
367
703
 
368
- sage: k = KeyPolynomials(QQ['q'], poly_coeffs=True)
369
- sage: R = k.base_ring(); R
370
- Univariate Polynomial Ring in q over Rational Field
371
- sage: R.inject_variables()
372
- Defining q
373
- sage: (q^2 + q + 1)*k([0,2,2,0,3,2])
374
- (q^2+q+1)*k[0, 2, 2, 0, 3, 2]
375
- """
376
- Element = KeyPolynomial
377
704
 
705
+ class OperatorPolynomialBasis(CombinatorialFreeModule):
706
+ r"""
707
+ A basis for a polynomial ring defined recursively by divided difference
708
+ type operators such that the result is a triangular change of basis with
709
+ the natural monomial basis.
710
+ """
378
711
  @staticmethod
379
- def __classcall_private__(cls, R=None, k=None, poly_ring=None, poly_coeffs=False):
712
+ def __classcall__(cls, R=None, k=None, poly_ring=None, poly_coeffs=False):
380
713
  r"""
381
714
  Normalize input.
382
715
 
@@ -416,16 +749,16 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
416
749
  if isinstance(R.base_ring(), poly_type[0:2]):
417
750
  # if R is of the form K[t_1, ..., t_n][z_*]
418
751
  # or K[t_1, ..., t_n][z_1, ..., z_k]
419
- return cls.__classcall__(cls, k=k, poly_ring=R)
752
+ return super().__classcall__(cls, k=k, poly_ring=R)
420
753
  if poly_coeffs:
421
754
  # if R is a polynomial ring, but its base ring is not
422
755
  # and poly_coeffs is true, then we should interpret
423
756
  # R as the base ring
424
- return cls.__classcall__(cls, R=R)
425
- return cls.__classcall__(cls, k=k, poly_ring=R)
757
+ return super().__classcall__(cls, R=R)
758
+ return super().__classcall__(cls, k=k, poly_ring=R)
426
759
  else:
427
760
  # if R is not a polynomial ring, we know it is self.base_ring()
428
- return cls.__classcall__(cls, R=R, k=k)
761
+ return super().__classcall__(cls, R=R, k=k)
429
762
 
430
763
  def __init__(self, R=None, k=None, poly_ring=None):
431
764
  r"""
@@ -451,7 +784,11 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
451
784
  return self._indices(m)
452
785
  else:
453
786
  def build_index(m):
454
- return self._indices(reversed(m)).trim()
787
+ mc = m.monomial_coefficients()
788
+ v = [0 for _ in range(max(mc, default=-1) + 1)]
789
+ for i, e in mc.items():
790
+ v[i] = e
791
+ return self._indices(v)
455
792
 
456
793
  self._build_index = build_index
457
794
 
@@ -468,11 +805,9 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
468
805
  R = poly_ring.base_ring()
469
806
  self._polynomial_ring = poly_ring
470
807
 
471
- self._name = "Key polynomial basis"
472
-
473
808
  CombinatorialFreeModule.__init__(self, R, IntegerVectors(k=k),
474
809
  category=GradedAlgebrasWithBasis(R),
475
- prefix='k', bracket=False)
810
+ prefix=self._prefix, bracket=False)
476
811
 
477
812
  def _coerce_map_from_(self, R):
478
813
  r"""
@@ -501,10 +836,6 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
501
836
  if R is P:
502
837
  return self.from_polynomial
503
838
 
504
- from sage.combinat.schubert_polynomial import SchubertPolynomialRing_xbasis
505
- if isinstance(R, SchubertPolynomialRing_xbasis):
506
- return self.from_schubert_polynomial
507
-
508
839
  phi = P.coerce_map_from(R)
509
840
  if phi is not None:
510
841
  return self.coerce_map_from(P) * phi
@@ -515,6 +846,8 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
515
846
  EXAMPLES::
516
847
 
517
848
  sage: k = KeyPolynomials(QQ)
849
+ sage: k._monomial(k._indices([1, 3, 2, 0, 2]))
850
+ k[1, 3, 2, 0, 2]
518
851
  sage: k([3, 2, 3, 4, 0])
519
852
  k[3, 2, 3, 4]
520
853
  sage: k = KeyPolynomials(QQ, 5)
@@ -632,7 +965,7 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
632
965
 
633
966
  def from_polynomial(self, f):
634
967
  r"""
635
- Expand a polynomial in terms of the key basis.
968
+ Expand a polynomial in terms of ``self``.
636
969
 
637
970
  EXAMPLES::
638
971
 
@@ -647,7 +980,7 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
647
980
  True
648
981
 
649
982
  sage: T = crystals.Tableaux(['A', 4], shape=[4,2,1,1])
650
- sage: k.from_polynomial(T.demazure_character([2])) # needs sage.symbolic
983
+ sage: k.from_polynomial(T.demazure_character([2]))
651
984
  k[4, 1, 2, 1]
652
985
  """
653
986
  if f not in self._polynomial_ring:
@@ -660,60 +993,232 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
660
993
  raise ValueError(f"f must be an element of {self._polynomial_ring}")
661
994
 
662
995
  out = self.zero()
663
-
664
996
  while f:
665
997
  M = f.monomials()[0]
666
998
  c = f.monomial_coefficient(M)
667
999
 
668
1000
  new_term = self._from_dict({self._build_index(*M.exponents()): c})
669
1001
 
670
- f -= new_term.expand()
1002
+ temp = new_term.expand()
1003
+ f -= temp
671
1004
  out += new_term
672
1005
 
673
1006
  return out
674
1007
 
675
- def from_schubert_polynomial(self, x):
676
- r"""
677
- Expand a Schubert polynomial in the key basis.
678
1008
 
679
- EXAMPLES::
1009
+ class KeyPolynomialBasis(OperatorPolynomialBasis):
1010
+ r"""
1011
+ The key polynomial basis for a polynomial ring.
680
1012
 
681
- sage: k = KeyPolynomials(ZZ)
682
- sage: X = SchubertPolynomialRing(ZZ)
683
- sage: f = X([2,1,5,4,3])
684
- sage: k.from_schubert_polynomial(f)
685
- k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1]
686
- sage: k.from_schubert_polynomial(2)
687
- 2*k[]
688
- sage: k(f)
689
- k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1]
1013
+ For a full definition, see
1014
+ `SymmetricFunctions.com <https://www.symmetricfunctions.com/key.htm>`__.
1015
+ Key polynomials are indexed by weak compositions with no trailing zeros,
1016
+ and `\sigma` is the permutation of shortest length which sorts the
1017
+ indexing composition into a partition.
690
1018
 
691
- sage: k = KeyPolynomials(GF(7), 4)
692
- sage: k.from_schubert_polynomial(f)
693
- k[1, 0, 2, 1] + k[2, 0, 2, 0] + k[3, 0, 0, 1]
1019
+ EXAMPLES:
694
1020
 
695
- TESTS::
1021
+ Key polynomials are a basis, indexed by (weak) compositions,
1022
+ for polynomial rings::
696
1023
 
697
- sage: k = KeyPolynomials(ZZ)
698
- sage: k.from_schubert_polynomial(k([3,2]))
699
- Traceback (most recent call last):
700
- ...
701
- ValueError: not a Schubert polynomial
1024
+ sage: k = KeyPolynomials(QQ)
1025
+ sage: k([3,0,1,2])
1026
+ k[3, 0, 1, 2]
1027
+ sage: k([3,0,1,2])/2
1028
+ 1/2*k[3, 0, 1, 2]
1029
+ sage: R = k.polynomial_ring(); R
1030
+ Infinite polynomial ring in z over Rational Field
702
1031
 
703
- sage: k = KeyPolynomials(ZZ)
704
- sage: X = SchubertPolynomialRing(ZZ)
705
- sage: it = iter(Compositions())
706
- sage: for _ in range(50):
707
- ....: C = next(it)
708
- ....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C
1032
+ sage: K = KeyPolynomials(GF(5)); K
1033
+ Key polynomial basis over Finite Field of size 5
1034
+ sage: 2*K([3,0,1,2])
1035
+ 2*k[3, 0, 1, 2]
1036
+ sage: 5*(K([3,0,1,2]) + K([3,1,1]))
1037
+ 0
709
1038
 
710
- sage: k = KeyPolynomials(ZZ, 4)
711
- sage: X = SchubertPolynomialRing(ZZ)
712
- sage: it = iter(k.basis().keys())
713
- sage: for _ in range(50):
714
- ....: C = next(it)
715
- ....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C
716
- """
1039
+ We can expand them in the standard monomial basis::
1040
+
1041
+ sage: k([3,0,1,2]).expand()
1042
+ z_3^2*z_2*z_0^3 + z_3^2*z_1*z_0^3 + z_3*z_2^2*z_0^3
1043
+ + 2*z_3*z_2*z_1*z_0^3 + z_3*z_1^2*z_0^3 + z_2^2*z_1*z_0^3
1044
+ + z_2*z_1^2*z_0^3
1045
+
1046
+ sage: k([0,0,2]).expand()
1047
+ z_2^2 + z_2*z_1 + z_2*z_0 + z_1^2 + z_1*z_0 + z_0^2
1048
+
1049
+ If we have a polynomial, we can express it in the key basis::
1050
+
1051
+ sage: z = R.gen()
1052
+ sage: k.from_polynomial(z[2]^2*z[1]*z[0])
1053
+ k[1, 1, 2] - k[1, 2, 1]
1054
+
1055
+ sage: f = z[3]^2*z[2]*z[0]^3 + z[3]^2*z[1]*z[0]^3 + z[3]*z[2]^2*z[0]^3 + \
1056
+ ....: 2*z[3]*z[2]*z[1]*z[0]^3 + z[3]*z[1]^2*z[0]^3 + z[2]^2*z[1]*z[0]^3 + \
1057
+ ....: z[2]*z[1]^2*z[0]^3
1058
+ sage: k.from_polynomial(f)
1059
+ k[3, 0, 1, 2]
1060
+
1061
+ Since the ring of key polynomials may be regarded as a different choice of
1062
+ basis for a polynomial ring, it forms an algebra, so we have
1063
+ multiplication::
1064
+
1065
+ sage: k([10,5,2])*k([1,1,1])
1066
+ k[11, 6, 3]
1067
+
1068
+ We can also multiply by polynomials in the monomial basis::
1069
+
1070
+ sage: k([10,9,1])*z[0]
1071
+ k[11, 9, 1]
1072
+ sage: z[0] * k([10,9,1])
1073
+ k[11, 9, 1]
1074
+ sage: k([10,9,1])*(z[0] + z[3])
1075
+ k[10, 9, 1, 1] + k[11, 9, 1]
1076
+
1077
+ When the sorting permutation is the longest element, the key polynomial
1078
+ agrees with the Schur polynomial::
1079
+
1080
+ sage: s = SymmetricFunctions(QQ).schur()
1081
+ sage: k([1,2,3]).expand()
1082
+ z_2^3*z_1^2*z_0 + z_2^3*z_1*z_0^2 + z_2^2*z_1^3*z_0
1083
+ + 2*z_2^2*z_1^2*z_0^2 + z_2^2*z_1*z_0^3 + z_2*z_1^3*z_0^2
1084
+ + z_2*z_1^2*z_0^3
1085
+ sage: s[3,2,1].expand(3)
1086
+ x0^3*x1^2*x2 + x0^2*x1^3*x2 + x0^3*x1*x2^2 + 2*x0^2*x1^2*x2^2
1087
+ + x0*x1^3*x2^2 + x0^2*x1*x2^3 + x0*x1^2*x2^3
1088
+
1089
+ The polynomial expansions can be computed using crystals and expressed in
1090
+ terms of the key basis::
1091
+
1092
+ sage: T = crystals.Tableaux(['A',3],shape=[2,1])
1093
+ sage: f = T.demazure_character([3,2,1]) # needs sage.symbolic
1094
+ sage: k.from_polynomial(f) # needs sage.symbolic
1095
+ k[1, 0, 0, 2]
1096
+
1097
+ The default behavior is to work in a polynomial ring with infinitely many
1098
+ variables. One can work in a specicfied number of variables::
1099
+
1100
+ sage: k = KeyPolynomials(QQ, 4)
1101
+ sage: k([3,0,1,2]).expand()
1102
+ z_0^3*z_1^2*z_2 + z_0^3*z_1*z_2^2 + z_0^3*z_1^2*z_3
1103
+ + 2*z_0^3*z_1*z_2*z_3 + z_0^3*z_2^2*z_3 + z_0^3*z_1*z_3^2 + z_0^3*z_2*z_3^2
1104
+
1105
+ sage: k([0,0,2,0]).expand()
1106
+ z_0^2 + z_0*z_1 + z_1^2 + z_0*z_2 + z_1*z_2 + z_2^2
1107
+
1108
+ sage: k([0,0,2,0]).expand().parent()
1109
+ Multivariate Polynomial Ring in z_0, z_1, z_2, z_3 over Rational Field
1110
+
1111
+ If working in a specified number of variables, the length of the indexing
1112
+ composition must be the same as the number of variables::
1113
+
1114
+ sage: k([0,0,2])
1115
+ Traceback (most recent call last):
1116
+ ...
1117
+ TypeError: do not know how to make x (= [0, 0, 2]) an element of self
1118
+ (=Key polynomial basis over Rational Field)
1119
+
1120
+ One can also work in a specified polynomial ring::
1121
+
1122
+ sage: k = KeyPolynomials(QQ['x0', 'x1', 'x2', 'x3'])
1123
+ sage: k([0,2,0,0])
1124
+ k[0, 2, 0, 0]
1125
+ sage: k([4,0,0,0]).expand()
1126
+ x0^4
1127
+
1128
+ If one wishes to use a polynomial ring as coefficients for the key
1129
+ polynomials, pass the keyword argument ``poly_coeffs=True``::
1130
+
1131
+ sage: k = KeyPolynomials(QQ['q'], poly_coeffs=True)
1132
+ sage: R = k.base_ring(); R
1133
+ Univariate Polynomial Ring in q over Rational Field
1134
+ sage: R.inject_variables()
1135
+ Defining q
1136
+ sage: (q^2 + q + 1)*k([0,2,2,0,3,2])
1137
+ (q^2+q+1)*k[0, 2, 2, 0, 3, 2]
1138
+ """
1139
+ Element = KeyPolynomial
1140
+ _name = "Key polynomial basis"
1141
+ _prefix = 'k'
1142
+ _operator = staticmethod(isobaric_divided_difference)
1143
+
1144
+ def _coerce_map_from_(self, R):
1145
+ r"""
1146
+ Return the coercion map from ``R`` if it exists.
1147
+
1148
+ EXAMPLES::
1149
+
1150
+ sage: k = KeyPolynomials(QQ)
1151
+ sage: m1 = k([3, 2, 4, 0]); m1
1152
+ k[3, 2, 4]
1153
+ sage: m2 = k(Composition([3, 2, 4])); m2
1154
+ k[3, 2, 4]
1155
+ sage: m1 == m2
1156
+ True
1157
+
1158
+ sage: R = k.polynomial_ring()
1159
+ sage: z = R.gen()
1160
+ sage: z[0] * k([4, 3, 3, 2])
1161
+ k[5, 3, 3, 2]
1162
+
1163
+ sage: X = SchubertPolynomialRing(QQ)
1164
+ sage: k(X([4, 3, 2, 1]))
1165
+ k[3, 2, 1]
1166
+ """
1167
+ P = self._polynomial_ring
1168
+ if R is P:
1169
+ return self.from_polynomial
1170
+
1171
+ from sage.combinat.schubert_polynomial import SchubertPolynomialRing_xbasis
1172
+ if isinstance(R, SchubertPolynomialRing_xbasis):
1173
+ return self.from_schubert_polynomial
1174
+
1175
+ phi = P.coerce_map_from(R)
1176
+ if phi is not None:
1177
+ return self.coerce_map_from(P) * phi
1178
+ return None
1179
+
1180
+ def from_schubert_polynomial(self, x):
1181
+ r"""
1182
+ Expand a Schubert polynomial in ``self``.
1183
+
1184
+ EXAMPLES::
1185
+
1186
+ sage: k = KeyPolynomials(ZZ)
1187
+ sage: X = SchubertPolynomialRing(ZZ)
1188
+ sage: f = X([2,1,5,4,3])
1189
+ sage: k.from_schubert_polynomial(f)
1190
+ k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1]
1191
+ sage: k.from_schubert_polynomial(2)
1192
+ 2*k[]
1193
+ sage: k(f)
1194
+ k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1]
1195
+
1196
+ sage: k = KeyPolynomials(GF(7), 4)
1197
+ sage: k.from_schubert_polynomial(f)
1198
+ k[1, 0, 2, 1] + k[2, 0, 2, 0] + k[3, 0, 0, 1]
1199
+
1200
+ TESTS::
1201
+
1202
+ sage: k = KeyPolynomials(ZZ)
1203
+ sage: k.from_schubert_polynomial(k([3,2]))
1204
+ Traceback (most recent call last):
1205
+ ...
1206
+ ValueError: not a Schubert polynomial
1207
+
1208
+ sage: k = KeyPolynomials(ZZ)
1209
+ sage: X = SchubertPolynomialRing(ZZ)
1210
+ sage: it = iter(Compositions())
1211
+ sage: for _ in range(50):
1212
+ ....: C = next(it)
1213
+ ....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C
1214
+
1215
+ sage: k = KeyPolynomials(ZZ, 4)
1216
+ sage: X = SchubertPolynomialRing(ZZ)
1217
+ sage: it = iter(k.basis().keys())
1218
+ sage: for _ in range(50):
1219
+ ....: C = next(it)
1220
+ ....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C
1221
+ """
717
1222
  if x in self.base_ring():
718
1223
  return self(x)
719
1224
 
@@ -742,144 +1247,186 @@ class KeyPolynomialBasis(CombinatorialFreeModule):
742
1247
  return out
743
1248
 
744
1249
 
745
- def divided_difference(f, i):
1250
+ class AtomPolynomialBasis(OperatorPolynomialBasis):
746
1251
  r"""
747
- Apply the ``i``-th divided difference operator to the polynomial ``f``.
1252
+ The atom polynomial basis for a polynomial ring.
748
1253
 
749
- EXAMPLES::
1254
+ For a full definition, see
1255
+ `SymmetricFunctions.com <https://www.symmetricfunctions.com/key.htm#atom>`__.
1256
+ Atom polynomials are indexed by weak compositions `\alpha` with no
1257
+ trailing zeros. A basis element is given by
750
1258
 
751
- sage: from sage.combinat.key_polynomial import divided_difference
752
- sage: k = KeyPolynomials(QQ)
753
- sage: z = k.poly_gens()
754
- sage: f = z[1]*z[2]^3 + z[1]*z[2]*z[3]
755
- sage: divided_difference(f, 3)
756
- z_3^2*z_1 + z_3*z_2*z_1 + z_2^2*z_1
1259
+ .. MATH::
757
1260
 
758
- sage: k = KeyPolynomials(QQ, 4)
759
- sage: z = k.poly_gens()
760
- sage: f = z[1]*z[2]^3 + z[1]*z[2]*z[3]
761
- sage: divided_difference(f, 3)
762
- z_1*z_2^2 + z_1*z_2*z_3 + z_1*z_3^2
1261
+ a_{\alpha} = \bar{\pi}_{\sigma} x^{\mu},
763
1262
 
764
- sage: k = KeyPolynomials(QQ)
765
- sage: R = k.polynomial_ring(); R
1263
+ where `\sigma` is the permutation of shortest length which sorts the
1264
+ `\alpha` into a partition `\mu`.
1265
+
1266
+ EXAMPLES:
1267
+
1268
+ Atom polynomials are a basis, indexed by (weak) compositions,
1269
+ for polynomial rings::
1270
+
1271
+ sage: a = AtomPolynomials(QQ)
1272
+ sage: a([3,0,1,2])
1273
+ a[3, 0, 1, 2]
1274
+ sage: a([3,0,1,2])/2
1275
+ 1/2*a[3, 0, 1, 2]
1276
+ sage: R = a.polynomial_ring(); R
766
1277
  Infinite polynomial ring in z over Rational Field
767
- sage: z = R.gen()
768
- sage: divided_difference(z[1]*z[2]^3, 2)
769
- -z_2^2*z_1 - z_2*z_1^2
770
- sage: divided_difference(z[1]*z[2]*z[3], 3)
771
- 0
772
- sage: divided_difference(z[1]*z[2]*z[3], 4)
773
- z_2*z_1
774
- sage: divided_difference(z[1]*z[2]*z[4], 4)
775
- -z_2*z_1
776
1278
 
777
- sage: k = KeyPolynomials(QQ, 5)
778
- sage: z = k.polynomial_ring().gens()
779
- sage: divided_difference(z[1]*z[2]^3, 2)
780
- -z_1^2*z_2 - z_1*z_2^2
781
- sage: divided_difference(z[1]*z[2]*z[3], 3)
1279
+ sage: A = AtomPolynomials(GF(5)); A
1280
+ Atom polynomial basis over Finite Field of size 5
1281
+ sage: 2*A([3,0,1,2])
1282
+ 2*a[3, 0, 1, 2]
1283
+ sage: 5*(A([3,0,1,2]) + A([3,1,1]))
782
1284
  0
783
- sage: divided_difference(z[1]*z[2]*z[3], 4)
784
- z_1*z_2
785
- sage: divided_difference(z[1]*z[2]*z[4], 4)
786
- -z_1*z_2
1285
+
1286
+ We can expand them in the standard monomial basis::
1287
+
1288
+ sage: a([2,1,3,2]).expand()
1289
+ z_3^2*z_2^3*z_1*z_0^2 + z_3^2*z_2^2*z_1^2*z_0^2
1290
+
1291
+ sage: a[0,2,2].expand()
1292
+ z_2^2*z_1^2 + z_2^2*z_1*z_0 + z_2*z_1^2*z_0
1293
+
1294
+ If we have a polynomial, we can express it in the atom basis::
1295
+
1296
+ sage: z = R.gen()
1297
+ sage: a.from_polynomial(z[2]^2*z[1]*z[0])
1298
+ a[1, 1, 2]
1299
+ sage: a.from_polynomial(z[2]^3*z[1]^2*z[0])
1300
+ a[1, 2, 3]
1301
+
1302
+ sage: f = (z[3]^2*z[2]*z[0]^3 + z[3]^2*z[1]*z[0]^3 + z[3]*z[2]^2*z[0]^3
1303
+ ....: + 2*z[3]*z[2]*z[1]*z[0]^3 + z[3]*z[1]^2*z[0]^3
1304
+ ....: + z[2]^2*z[1]*z[0]^3 + z[2]*z[1]^2*z[0]^3)
1305
+ sage: a.from_polynomial(f)
1306
+ a[3, 0, 1, 2] + a[3, 0, 2, 1] + a[3, 1, 0, 2] + a[3, 1, 2]
1307
+ + a[3, 2, 0, 1] + a[3, 2, 1]
1308
+
1309
+ Since the ring of atom polynomials may be regarded as a different
1310
+ choice of basis for a polynomial ring, it forms an algebra, so
1311
+ we have multiplication::
1312
+
1313
+ sage: a[10,5,2] * a[1,1,1]
1314
+ a[11, 6, 3]
1315
+
1316
+ We can also multiply by polynomials in the monomial basis::
1317
+
1318
+ sage: a([10,9,1]) * z[0]
1319
+ a[11, 9, 1]
1320
+ sage: z[0] * a([10,9,1])
1321
+ a[11, 9, 1]
1322
+ sage: a([10,9,1]) * (z[0] + z[3])
1323
+ a[10, 9, 1, 1] + a[11, 9, 1]
787
1324
  """
788
- P = parent(f)
789
- if isinstance(P, InfinitePolynomialRing_sparse):
790
- z = P.gen()
791
- else:
792
- z = P.gens()
1325
+ Element = AtomPolynomial
1326
+ _name = "Atom polynomial basis"
1327
+ _prefix = 'a'
1328
+ _operator = staticmethod(isobaric_divided_difference_bar)
793
1329
 
794
- si_f = f.subs({z[i]: z[i-1], z[i-1]: z[i]})
795
- return (si_f - f) // (z[i] - z[i-1])
1330
+ def from_key_polynomial(self, x):
1331
+ r"""
1332
+ Expand a key polynomial in ``self``.
796
1333
 
1334
+ EXAMPLES::
797
1335
 
798
- def isobaric_divided_difference(f, w):
799
- r"""
800
- Apply the isobaric divided difference operator `\pi_w` to the
801
- polynomial `f`.
1336
+ sage: a = AtomPolynomials(ZZ)
1337
+ sage: k = KeyPolynomials(ZZ)
1338
+ sage: f = k[1,2,3]
1339
+ sage: a.from_key_polynomial(f)
1340
+ a[1, 2, 3] + a[1, 3, 2] + a[2, 1, 3] + a[2, 3, 1] + a[3, 1, 2] + a[3, 2, 1]
1341
+ sage: a.from_key_polynomial(2)
1342
+ 2*a[]
1343
+ sage: a(f)
1344
+ a[1, 2, 3] + a[1, 3, 2] + a[2, 1, 3] + a[2, 3, 1] + a[3, 1, 2] + a[3, 2, 1]
1345
+
1346
+ sage: a = AtomPolynomials(GF(7), 4)
1347
+ sage: a.from_key_polynomial(f)
1348
+ a[1, 2, 3, 0] + a[1, 3, 2, 0] + a[2, 1, 3, 0]
1349
+ + a[2, 3, 1, 0] + a[3, 1, 2, 0] + a[3, 2, 1, 0]
802
1350
 
803
- ``w`` may be either a single index or a list of
804
- indices of simple transpositions.
1351
+ TESTS::
805
1352
 
806
- .. WARNING::
1353
+ sage: a = AtomPolynomials(ZZ)
1354
+ sage: a.from_key_polynomial(a([3,2]))
1355
+ Traceback (most recent call last):
1356
+ ...
1357
+ ValueError: not a key polynomial
807
1358
 
808
- The simple transpositions should be applied from left to right.
1359
+ sage: a = AtomPolynomials(ZZ)
1360
+ sage: k = KeyPolynomials(ZZ)
1361
+ sage: it = iter(a.basis().keys())
1362
+ sage: for _ in range(50):
1363
+ ....: C = next(it)
1364
+ ....: assert a.from_key_polynomial(k.from_polynomial(a[C].expand())) == a[C], C
809
1365
 
810
- EXAMPLES::
1366
+ sage: a = AtomPolynomials(ZZ, 4)
1367
+ sage: k = KeyPolynomials(ZZ, 4)
1368
+ sage: it = iter(a.basis().keys())
1369
+ sage: for _ in range(50):
1370
+ ....: C = next(it)
1371
+ ....: assert a.from_key_polynomial(k.from_polynomial(a[C].expand())) == a[C], C
1372
+ """
1373
+ if x in self.base_ring():
1374
+ return self(x)
811
1375
 
812
- sage: from sage.combinat.key_polynomial import isobaric_divided_difference as idd
813
- sage: R.<z> = InfinitePolynomialRing(GF(3))
814
- sage: idd(z[1]^4*z[2]^2*z[4], 4)
815
- 0
1376
+ if not isinstance(x, KeyPolynomial):
1377
+ raise ValueError('not a key polynomial')
816
1378
 
817
- sage: idd(z[1]^4*z[2]^2*z[3]*z[4], 3)
818
- z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4
1379
+ from sage.combinat.permutation import Permutations
819
1380
 
820
- sage: idd(z[1]^4*z[2]^2*z[3]*z[4], [3, 4])
821
- z_4^2*z_3*z_2*z_1^4 + z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4
1381
+ def on_basis(m):
1382
+ w, mu = sorting_word(m)
1383
+ if self._k and len(mu) < self._k:
1384
+ mu += [0] * (self._k - len(mu))
1385
+ dom = self[mu]
1386
+ w = list(w)
1387
+ if not w:
1388
+ return dom
1389
+ sigma = Permutations(max(w)+1).from_reduced_word(w)
1390
+ return self.sum(dom.pibar(wp.reduced_word()) for wp in sigma.bruhat_smaller())
822
1391
 
823
- sage: idd(z[1]^4*z[2]^2*z[3]*z[4], [4, 3])
824
- z_4*z_3^2*z_2*z_1^4 + z_4*z_3*z_2^2*z_1^4
1392
+ return self.linear_combination((on_basis(m), c) for m, c in x)
825
1393
 
826
- sage: idd(z[1]^2*z[2], [3, 2])
827
- z_3*z_2^2 + z_3*z_2*z_1 + z_3*z_1^2 + z_2^2*z_1 + z_2*z_1^2
828
- """
829
- P = parent(f)
830
- if isinstance(P, InfinitePolynomialRing_sparse):
831
- z = P.gen()
832
- else:
833
- z = P.gens()
1394
+ def _coerce_map_from_(self, R):
1395
+ r"""
1396
+ Return the coercion map from ``R`` if it exists.
834
1397
 
835
- if not hasattr(w, "__iter__"): # this allows us to pass i instead of a word
836
- w = [w]
837
- for i in w:
838
- fp = z[i-1] * f
839
- si_fp = fp.subs({z[i]: z[i-1], z[i-1]: z[i]})
840
- f = (si_fp - fp) // (z[i] - z[i-1])
841
- return f
1398
+ EXAMPLES::
842
1399
 
1400
+ sage: a = AtomPolynomials(QQ)
1401
+ sage: m1 = a([3, 2, 4, 0]); m1
1402
+ a[3, 2, 4]
1403
+ sage: m2 = a(Composition([3, 2, 4])); m2
1404
+ a[3, 2, 4]
1405
+ sage: m1 == m2
1406
+ True
843
1407
 
844
- def sorting_word(alpha):
845
- r"""
846
- Get a reduced word for the permutation which sorts ``alpha``
847
- into a partition.
1408
+ sage: R = a.polynomial_ring()
1409
+ sage: z = R.gen()
1410
+ sage: z[0] * a([4, 3, 3, 2])
1411
+ a[5, 3, 3, 2]
848
1412
 
849
- The result is a list ``l = [i0, i1, i2, ...]`` where each ``ij``
850
- is a positive integer such that it applies the simple
851
- transposition `(i_j, i_j+1)`. The transpositions are applied
852
- starting with ``i0``, then ``i1`` is applied, followed by ``i2``,
853
- and so on. See :meth:`sage.combinat.permutation.Permutation.reduced_words`
854
- for the convention used.
1413
+ sage: X = SchubertPolynomialRing(QQ)
1414
+ sage: a(X([4, 3, 2, 1]))
1415
+ a[3, 2, 1]
1416
+ """
1417
+ P = self._polynomial_ring
1418
+ if R is P:
1419
+ return self.from_polynomial
855
1420
 
856
- EXAMPLES::
1421
+ if isinstance(R, KeyPolynomialBasis):
1422
+ return self.from_key_polynomial
857
1423
 
858
- sage: IV = IntegerVectors()
859
- sage: from sage.combinat.key_polynomial import sorting_word
860
- sage: list(sorting_word(IV([2,3,2]))[0])
861
- [1]
862
- sage: sorting_word(IV([2,3,2]))[1]
863
- [3, 2, 2]
864
- sage: list(sorting_word(IV([5,6,7]))[0])
865
- [1, 2, 1]
866
- sage: list(sorting_word(IV([0,3,2]))[0])
867
- [2, 1]
868
- sage: list(sorting_word(IV([0,3,0,2]))[0])
869
- [2, 3, 1]
870
- sage: list(sorting_word(IV([3,2,1]))[0])
871
- []
872
- sage: list(sorting_word(IV([2,3,3]))[0])
873
- [2, 1]
874
- """
875
- w = []
876
- L = list(alpha)
877
- n = len(L)
1424
+ from sage.combinat.schubert_polynomial import SchubertPolynomialRing_xbasis
1425
+ if isinstance(R, SchubertPolynomialRing_xbasis):
1426
+ K = KeyPolynomialBasis(self.base_ring(), self._k, self._polynomial_ring)
1427
+ return self._coerce_map_via([K], R)
878
1428
 
879
- # bubble sort to get the shortest sorting word
880
- for i in range(n-1):
881
- for j in range(n-i-1):
882
- if L[j] < L[j + 1]:
883
- w.append(j+1)
884
- L[j], L[j + 1] = L[j + 1], L[j]
885
- return reversed(w), L
1429
+ phi = P.coerce_map_from(R)
1430
+ if phi is not None:
1431
+ return self.coerce_map_from(P) * phi
1432
+ return None