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.
- passagemath_combinat/__init__.py +3 -0
- {passagemath_combinat-10.6.1.dist-info → passagemath_combinat-10.8.1a1.dist-info}/METADATA +17 -20
- {passagemath_combinat-10.6.1.dist-info → passagemath_combinat-10.8.1a1.dist-info}/RECORD +220 -218
- {passagemath_combinat-10.6.1.dist-info → passagemath_combinat-10.8.1a1.dist-info}/WHEEL +1 -1
- passagemath_combinat-10.8.1a1.dist-info/top_level.txt +3 -0
- sage/algebras/affine_nil_temperley_lieb.py +3 -3
- sage/algebras/all.py +0 -1
- sage/algebras/askey_wilson.py +1 -1
- sage/algebras/associated_graded.py +2 -2
- sage/algebras/cellular_basis.py +3 -6
- sage/algebras/cluster_algebra.py +2 -3
- sage/algebras/down_up_algebra.py +6 -6
- sage/algebras/free_algebra.py +3 -32
- sage/algebras/free_algebra_element.py +21 -25
- sage/algebras/free_algebra_quotient_element.py +9 -38
- sage/algebras/free_zinbiel_algebra.py +4 -3
- sage/algebras/hall_algebra.py +2 -2
- sage/algebras/hecke_algebras/ariki_koike_algebra.py +8 -8
- sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +2 -2
- sage/algebras/hecke_algebras/cubic_hecke_algebra.py +11 -14
- sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1 -1
- sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +5 -5
- sage/algebras/iwahori_hecke_algebra.py +59 -57
- sage/algebras/jordan_algebra.py +97 -89
- sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +6 -6
- sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +14 -12
- sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +6 -6
- sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +4 -4
- sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +13 -13
- sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +8 -6
- sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +7 -5
- sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +7 -7
- sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +6 -5
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +12 -11
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +3 -3
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +3 -3
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +11 -11
- sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +3 -3
- sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +8 -7
- sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +9 -8
- sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +6 -5
- sage/algebras/nil_coxeter_algebra.py +4 -4
- sage/algebras/q_commuting_polynomials.py +6 -6
- sage/algebras/q_system.py +3 -3
- sage/algebras/quantum_clifford.py +8 -8
- sage/algebras/quantum_groups/fock_space.py +48 -8
- sage/algebras/quantum_groups/quantum_group_gap.py +5 -7
- sage/algebras/quantum_matrix_coordinate_algebra.py +7 -7
- sage/algebras/quantum_oscillator.py +3 -3
- sage/algebras/quaternion_algebra_element.py +5 -3
- sage/algebras/schur_algebra.py +3 -3
- sage/algebras/shuffle_algebra.py +5 -8
- sage/algebras/splitting_algebra.py +129 -85
- sage/algebras/tensor_algebra.py +7 -7
- sage/algebras/yangian.py +16 -15
- sage/algebras/yokonuma_hecke_algebra.py +13 -11
- sage/combinat/all.py +9 -0
- sage/combinat/all__sagemath_combinat.py +1 -0
- sage/combinat/alternating_sign_matrix.py +36 -29
- sage/combinat/baxter_permutations.py +32 -12
- sage/combinat/bijectionist.py +13 -17
- sage/combinat/chas/fsym.py +6 -6
- sage/combinat/chas/wqsym.py +23 -29
- sage/combinat/colored_permutations.py +9 -11
- sage/combinat/colored_permutations_representations.py +13 -12
- sage/combinat/composition_tableau.py +2 -2
- sage/combinat/constellation.py +57 -30
- sage/combinat/crystals/affine_factorization.py +5 -4
- sage/combinat/crystals/alcove_path.py +2 -2
- sage/combinat/crystals/fully_commutative_stable_grothendieck.py +3 -2
- sage/combinat/crystals/infinity_crystals.py +18 -18
- sage/combinat/crystals/kac_modules.py +1 -1
- sage/combinat/crystals/kirillov_reshetikhin.py +2 -2
- sage/combinat/crystals/letters.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/crystals/littelmann_path.py +1 -1
- sage/combinat/crystals/pbw_datum.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/crystals/pbw_datum.pyx +3 -2
- sage/combinat/crystals/spins.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/crystals/tensor_product.py +7 -5
- sage/combinat/crystals/tensor_product_element.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/debruijn_sequence.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/debruijn_sequence.pyx +1 -2
- sage/combinat/degree_sequences.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/degree_sequences.pyx +241 -188
- sage/combinat/derangements.py +28 -22
- sage/combinat/diagram_algebras.py +12 -14
- sage/combinat/dyck_word.py +15 -14
- sage/combinat/e_one_star.py +1 -1
- sage/combinat/expnums.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/fast_vector_partitions.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/fqsym.py +13 -19
- sage/combinat/free_dendriform_algebra.py +2 -2
- sage/combinat/free_prelie_algebra.py +2 -2
- sage/combinat/fully_commutative_elements.py +8 -8
- sage/combinat/fully_packed_loop.py +9 -9
- sage/combinat/gelfand_tsetlin_patterns.py +4 -5
- sage/combinat/gray_codes.py +3 -4
- sage/combinat/grossman_larson_algebras.py +2 -2
- sage/combinat/growth.py +13 -13
- sage/combinat/hall_polynomial.py +1 -1
- sage/combinat/hillman_grassl.py +1 -1
- sage/combinat/integer_matrices.py +5 -7
- sage/combinat/k_tableau.py +8 -7
- sage/combinat/kazhdan_lusztig.py +3 -3
- sage/combinat/key_polynomial.py +845 -298
- sage/combinat/knutson_tao_puzzles.py +11 -13
- sage/combinat/matrices/hadamard_matrix.py +1 -1
- sage/combinat/matrices/latin.py +75 -92
- sage/combinat/misc.py +3 -3
- sage/combinat/multiset_partition_into_sets_ordered.py +27 -10
- sage/combinat/ncsf_qsym/generic_basis_code.py +5 -5
- sage/combinat/ncsf_qsym/ncsf.py +6 -5
- sage/combinat/ncsf_qsym/qsym.py +9 -17
- sage/combinat/ncsym/ncsym.py +8 -12
- sage/combinat/nu_dyck_word.py +1 -1
- sage/combinat/parallelogram_polyomino.py +3 -5
- sage/combinat/parking_functions.py +6 -5
- sage/combinat/partition_algebra.py +22 -57
- sage/combinat/partition_kleshchev.py +4 -4
- sage/combinat/partition_tuple.py +12 -10
- sage/combinat/plane_partition.py +10 -13
- sage/combinat/positive_integer_semigroup_test.py +17 -0
- sage/combinat/q_bernoulli.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/quickref.py +2 -2
- sage/combinat/recognizable_series.py +2 -2
- sage/combinat/regular_sequence.py +7 -7
- sage/combinat/regular_sequence_bounded.py +15 -21
- sage/combinat/restricted_growth.py +3 -3
- sage/combinat/ribbon.py +3 -3
- sage/combinat/rigged_configurations/bijection.py +3 -3
- sage/combinat/rigged_configurations/rigged_partition.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/rsk.py +2 -0
- sage/combinat/schubert_polynomial.py +11 -2
- sage/combinat/set_partition.py +3 -7
- sage/combinat/set_partition_iterator.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/set_partition_iterator.pyx +0 -1
- sage/combinat/set_partition_ordered.py +2 -2
- sage/combinat/sf/classical.py +1 -1
- sage/combinat/sf/dual.py +4 -8
- sage/combinat/sf/elementary.py +13 -7
- sage/combinat/sf/hall_littlewood.py +10 -8
- sage/combinat/sf/homogeneous.py +6 -3
- sage/combinat/sf/jack.py +11 -9
- sage/combinat/sf/llt.py +4 -5
- sage/combinat/sf/macdonald.py +10 -11
- sage/combinat/sf/monomial.py +6 -0
- sage/combinat/sf/ns_macdonald.py +92 -51
- sage/combinat/sf/powersum.py +9 -14
- sage/combinat/sf/schur.py +6 -0
- sage/combinat/sf/sf.py +21 -19
- sage/combinat/sf/sfa.py +13 -64
- sage/combinat/shifted_primed_tableau.py +5 -7
- sage/combinat/shuffle.py +1 -1
- sage/combinat/sine_gordon.py +18 -38
- sage/combinat/skew_partition.py +9 -12
- sage/combinat/skew_tableau.py +2 -7
- sage/combinat/sloane_functions.py +1 -1
- sage/combinat/species/all.py +67 -2
- sage/combinat/species/characteristic_species.py +3 -0
- sage/combinat/species/composition_species.py +3 -0
- sage/combinat/species/cycle_species.py +4 -0
- sage/combinat/species/empty_species.py +3 -0
- sage/combinat/species/functorial_composition_species.py +3 -0
- sage/combinat/species/generating_series.py +3 -0
- sage/combinat/species/library.py +3 -0
- sage/combinat/species/linear_order_species.py +3 -0
- sage/combinat/species/partition_species.py +3 -0
- sage/combinat/species/permutation_species.py +4 -0
- sage/combinat/species/product_species.py +3 -0
- sage/combinat/species/recursive_species.py +3 -0
- sage/combinat/species/set_species.py +3 -0
- sage/combinat/species/species.py +13 -7
- sage/combinat/species/structure.py +8 -9
- sage/combinat/species/subset_species.py +3 -0
- sage/combinat/species/sum_species.py +3 -0
- sage/combinat/subword.py +4 -1
- sage/combinat/subword_complex.py +7 -7
- sage/combinat/subword_complex_c.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/superpartition.py +1 -1
- sage/combinat/symmetric_group_algebra.py +9 -9
- sage/combinat/symmetric_group_representations.py +5 -5
- sage/combinat/t_sequences.py +4 -4
- sage/combinat/tableau.py +3 -4
- sage/combinat/tableau_tuple.py +2 -2
- sage/combinat/tiling.py +39 -42
- sage/combinat/triangles_FHM.py +38 -15
- sage/combinat/tutorial.py +2 -2
- sage/combinat/vector_partition.py +43 -31
- sage/combinat/words/abstract_word.py +4 -4
- sage/combinat/words/alphabet.py +12 -12
- sage/combinat/words/finite_word.py +25 -229
- sage/combinat/words/infinite_word.py +1 -1
- sage/combinat/words/morphic.py +13 -13
- sage/combinat/words/morphism.py +3 -12
- sage/combinat/words/paths.py +16 -17
- sage/combinat/words/word.py +60 -35
- sage/combinat/words/word_char.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/words/word_char.pyx +46 -7
- sage/combinat/words/word_datatypes.cpython-312-aarch64-linux-musl.so +0 -0
- sage/combinat/words/word_generators.py +39 -38
- sage/databases/findstat.py +72 -31
- sage/databases/oeis.py +125 -25
- sage/databases/sloane.py +14 -8
- sage/games/sudoku_backtrack.cpython-312-aarch64-linux-musl.so +0 -0
- sage/groups/indexed_free_group.py +3 -4
- sage/libs/symmetrica/symmetrica.cpython-312-aarch64-linux-musl.so +0 -0
- sage/libs/symmetrica/symmetrica.pxi +1 -0
- sage/monoids/automatic_semigroup.py +1 -3
- sage/monoids/free_abelian_monoid.py +7 -33
- sage/monoids/free_abelian_monoid_element.cpython-312-aarch64-linux-musl.so +0 -0
- sage/monoids/free_monoid.py +8 -40
- sage/monoids/free_monoid_element.py +1 -9
- sage/monoids/string_monoid.py +5 -2
- sage/monoids/string_monoid_element.py +12 -66
- sage/rings/all__sagemath_combinat.py +7 -0
- sage/sat/solvers/__init__.py +3 -4
- sage/sat/solvers/cryptominisat.py +2 -3
- sage/sat/solvers/picosat.py +2 -3
- sage/sat/solvers/sat_lp.py +2 -2
- sage/sat/solvers/satsolver.cpython-312-aarch64-linux-musl.so +0 -0
- passagemath_combinat-10.6.1.dist-info/top_level.txt +0 -2
sage/combinat/key_polynomial.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
52
|
+
#####################################################################
|
|
53
|
+
# Helper functions
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def sorting_word(alpha):
|
|
48
57
|
r"""
|
|
49
|
-
|
|
58
|
+
Get a reduced word for the permutation which sorts ``alpha``
|
|
59
|
+
into a partition.
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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:
|
|
62
|
-
|
|
63
|
-
sage: f
|
|
64
|
-
|
|
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 *
|
|
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
|
|
488
|
+
def pibar(self, w):
|
|
219
489
|
r"""
|
|
220
|
-
Apply the
|
|
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
|
|
230
|
-
k[
|
|
231
|
-
sage: k
|
|
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:
|
|
236
|
-
k[
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
534
|
+
class AtomPolynomial(OperatorPolynomial):
|
|
535
|
+
r"""
|
|
536
|
+
An atom polynomial.
|
|
257
537
|
|
|
258
|
-
|
|
259
|
-
|
|
538
|
+
Atom polynomials are polynomials that form a basis for a polynomial
|
|
539
|
+
ring and are indexed by weak compositions.
|
|
260
540
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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
|
-
|
|
284
|
-
|
|
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
|
-
|
|
564
|
+
EXAMPLES::
|
|
287
565
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
multiplication::
|
|
599
|
+
We check that this is consistent with the definition via the
|
|
600
|
+
defining isobaric divided difference operators::
|
|
301
601
|
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
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
|
-
|
|
308
|
-
|
|
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
|
-
|
|
315
|
-
agrees with the Schur polynomial::
|
|
622
|
+
N = max(w) + 1
|
|
316
623
|
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
327
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
335
|
-
variables. One can work in a specicfied number of variables::
|
|
661
|
+
EXAMPLES::
|
|
336
662
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
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
|
-
|
|
346
|
-
|
|
673
|
+
We check that this is consistent with the definition via the
|
|
674
|
+
isobaric divided difference operators::
|
|
347
675
|
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
692
|
+
if not w or not self:
|
|
693
|
+
return self
|
|
358
694
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
425
|
-
return
|
|
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
|
|
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
|
-
|
|
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=
|
|
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
|
|
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]))
|
|
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
|
-
|
|
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
|
-
|
|
1009
|
+
class KeyPolynomialBasis(OperatorPolynomialBasis):
|
|
1010
|
+
r"""
|
|
1011
|
+
The key polynomial basis for a polynomial ring.
|
|
680
1012
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1021
|
+
Key polynomials are a basis, indexed by (weak) compositions,
|
|
1022
|
+
for polynomial rings::
|
|
696
1023
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
|
|
1250
|
+
class AtomPolynomialBasis(OperatorPolynomialBasis):
|
|
746
1251
|
r"""
|
|
747
|
-
|
|
1252
|
+
The atom polynomial basis for a polynomial ring.
|
|
748
1253
|
|
|
749
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
765
|
-
|
|
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:
|
|
778
|
-
|
|
779
|
-
sage:
|
|
780
|
-
|
|
781
|
-
sage:
|
|
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
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
795
|
-
|
|
1330
|
+
def from_key_polynomial(self, x):
|
|
1331
|
+
r"""
|
|
1332
|
+
Expand a key polynomial in ``self``.
|
|
796
1333
|
|
|
1334
|
+
EXAMPLES::
|
|
797
1335
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
|
|
804
|
-
indices of simple transpositions.
|
|
1351
|
+
TESTS::
|
|
805
1352
|
|
|
806
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
813
|
-
|
|
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
|
|
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
|
-
|
|
821
|
-
|
|
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
|
-
|
|
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
|
-
|
|
827
|
-
|
|
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
|
-
|
|
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
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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
|
-
|
|
1421
|
+
if isinstance(R, KeyPolynomialBasis):
|
|
1422
|
+
return self.from_key_polynomial
|
|
857
1423
|
|
|
858
|
-
sage
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
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
|